diff --git a/.config_files.xml b/.config_files.xml index c6dcbbcdb3..d1a592e5db 100644 --- a/.config_files.xml +++ b/.config_files.xml @@ -5,31 +5,31 @@ char unset - $SRCROOT - $SRCROOT - $SRCROOT/components/cpl7/components/data_comps_mct/dlnd - $SRCROOT/components/cdeps/dlnd - $SRCROOT/components/cpl7/components/stub_comps_mct/slnd - $SRCROOT/components/cpl7/components/xcpl_comps_mct/xlnd - $CIMEROOT/src/components/stub_comps_nuopc/slnd - $CIMEROOT/src/components/xcpl_comps_nuopc/xlnd + $SRCROOT + $SRCROOT/components/slim/ + $SRCROOT/components/cpl7/components/data_comps_$COMP_INTERFACE/dlnd + $SRCROOT/components/cdeps/dlnd + $SRCROOT/components/cpl7/components/stub_comps_$COMP_INTERFACE/slnd + $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/slnd + $SRCROOT/components/cpl7/components/xcpl_comps_$COMP_INTERFACE/xlnd + $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xlnd case_comps env_case.xml Root directory of the case land model component - $CIMEROOT/config/xml_schemas/config_compsets.xsd + $CIMEROOT/CIME/data/config/xml_schemas/config_compsets.xsd diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index a3795e4c6b..8708f8e0c2 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,4 +1,5 @@ # Ran python directory through black python formatter +d229b5c6689efc4c2a6cef077515c4ccd5c18ff6 4cd83cb3ee6d85eb909403487abf5eeaf4d98911 0aa2957c1f8603c63fa30b11295c06cfddff44a5 2cdb380febb274478e84cd90945aee93f29fa2e6 @@ -7,3 +8,25 @@ e44dc469439e02e9ee582dab274d890ebdfab104 b88e1cd1b28e3609684c79a2ec0e88f26cfc362b 51c102c5df2e0ef971b5f8eeeb477567899af63a 7dacad70e74e2ec97f6492d4e7a3cb5dd498bcd7 +b771971e3299c4fa56534b93421f7a2b9c7282fd +9de88bb57ea9855da408cbec1dc8acb9079eda47 +8bc4688e52ea23ef688e283698f70a44388373eb +c8bd4c6f98c0b411391b4355da449507db3aab4e +4ee49e3e516ca7dee5df378f65664f93a7db4415 +0207bc98dd5c75cd69a0e788bc53e41093712f5c +e4d38681df23ccca0ae29581a45f8362574e0630 +0a5a9e803b56ec1bbd6232eff1c99dbbeef25eb7 +810cb346f05ac1aabfff931ab1a2b7b584add241 +5933b0018f8e29413e30dda9b906370d147bad45 +025d5e7c2e80263717fb029101d65cbbf261c3c4 +a9d96219902cf609636886c7073a84407f450d9a +d866510188d26d51bcd6d37239283db690af7e82 +0dcd0a3c1abcaffe5529f8d79a6bc34734b195c7 +# Ran SystemTests and python/ctsm through black python formatter +5364ad66eaceb55dde2d3d598fe4ce37ac83a93c +8056ae649c1b37f5e10aaaac79005d6e3a8b2380 +0bc3f00115d86d026a977918661c93779b3b19f9 +540b256d1f3382f4619d7b0877c32d54ce5c40b6 +8a168bb0895f4f2421608dd2589398e13a6663e6 +183fc26a6691bbdf87f515dc47924a64be3ced9b +6fccf682eaf718615407d9bacdd3903b8786a03d diff --git a/.github/workflows/assign-to-project.yml b/.github/workflows/assign-to-project.yml new file mode 100644 index 0000000000..225c223bde --- /dev/null +++ b/.github/workflows/assign-to-project.yml @@ -0,0 +1,23 @@ +name: Auto Assign to Project(s) + +on: + issues: + types: [opened, labeled] + pull_request: + types: [opened, labeled] + issue_comment: + types: [created] + +jobs: + assign_high_priority: + runs-on: ubuntu-latest + name: Assign to High Priority project + steps: + - name: Assign issues and pull requests with priority-high label to project 25 + uses: srggrs/assign-one-project-github-action@1.3.1 + if: | + contains(github.event.issue.labels.*.name, 'priority: high') || + contains(github.event.pull_request.labels.*.name, 'priority: high') + with: + project: 'https://github.com/ESCOMP/CTSM/projects/25' + column_name: 'Needs triage' diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index a2115d2833..3759fa84c3 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -16,6 +16,22 @@ jobs: # Use options and version identical to the conda environment # Using pyproject.toml makes sure this testing is consistent with our python directory testing options: "--check --config python/pyproject.toml" - src: "./python" - # Version should be coordinated with the ctsm_py conda environment under the python directory + src: "./python" + # Version should be coordinated with the ctsm_pylib conda environment under the python directory + version: "22.3.0" + # Actions identical to above for each directory and source file we need to check (arrays aren't allowed for src: field) + - uses: psf/black@stable + with: + options: "--check --config python/pyproject.toml" + src: "./cime_config/SystemTests" + version: "22.3.0" + - uses: psf/black@stable + with: + options: "--check --config python/pyproject.toml" + src: "./cime_config/buildlib" + version: "22.3.0" + - uses: psf/black@stable + with: + options: "--check --config python/pyproject.toml" + src: "./cime_config/buildnml" version: "22.3.0" diff --git a/.gitignore b/.gitignore index 4b998f4dcb..ca701132a7 100644 --- a/.gitignore +++ b/.gitignore @@ -105,6 +105,13 @@ unit_test_build /tools/mkmapdata/regrid.o* /tools/mkmapdata/map*.nc +# run_neon output directories +/tools/site_and_regional/listing.csv +/tools/site_and_regional/????/ +/tools/site_and_regional/????.ad/ +/tools/site_and_regional/????.postad/ +/tools/site_and_regional/????.transient/ + # build output *.o *.mod diff --git a/Externals.cfg b/Externals.cfg index 491405a33b..539995247b 100644 --- a/Externals.cfg +++ b/Externals.cfg @@ -8,7 +8,7 @@ required = True local_path = components/cism protocol = git repo_url = https://github.com/ESCOMP/CISM-wrapper -tag = cismwrap_2_1_95 +tag = cismwrap_2_1_96 externals = Externals_CISM.cfg required = True @@ -23,7 +23,7 @@ required = True local_path = components/mosart protocol = git repo_url = https://github.com/ESCOMP/MOSART -tag = mosart1_0_45 +tag = mosart1_0_48 required = True [mizuRoute] @@ -34,9 +34,9 @@ hash = 34723c2 required = True [ccs_config] -tag = ccs_config_cesm0.0.38 +tag = ccs_config_noresm0.0.22 protocol = git -repo_url = https://github.com/ESMCI/ccs_config_cesm.git +repo_url = https://github.com/NorESMhub/ccs_config_noresm.git local_path = ccs_config required = True @@ -44,18 +44,18 @@ required = True local_path = cime protocol = git repo_url = https://github.com/ESMCI/cime -tag = cime6.0.45 +tag = cime6.0.175 required = True [cmeps] -tag = cmeps0.13.71 +tag = cmeps0.14.43 protocol = git repo_url = https://github.com/ESCOMP/CMEPS.git local_path = components/cmeps required = True [cdeps] -tag = cdeps0.12.65 +tag = cdeps1.0.24 protocol = git repo_url = https://github.com/ESCOMP/CDEPS.git local_path = components/cdeps @@ -63,14 +63,14 @@ externals = Externals_CDEPS.cfg required = True [cpl7] -tag = cpl7.0.14 +tag = cpl77.0.7 protocol = git repo_url = https://github.com/ESCOMP/CESM_CPL7andDataComps local_path = components/cpl7 required = True [share] -tag = share1.0.13 +tag = share1.0.17 protocol = git repo_url = https://github.com/ESCOMP/CESM_share local_path = share @@ -84,7 +84,7 @@ local_path = libraries/mct required = True [parallelio] -tag = pio2_5_7 +tag = pio2_6_2 protocol = git repo_url = https://github.com/NCAR/ParallelIO local_path = libraries/parallelio diff --git a/Externals_CLM.cfg b/Externals_CLM.cfg index 2284518c58..a6fae66356 100644 --- a/Externals_CLM.cfg +++ b/Externals_CLM.cfg @@ -2,7 +2,7 @@ local_path = src/fates protocol = git repo_url = https://github.com/NGEET/fates -tag = sci.1.58.1_api.24.1.0 +tag = sci.1.71.0_api.33.0.0 required = True [externals_description] diff --git a/README.md b/README.md index c56c0d4852..9de22e3663 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,11 @@ CTSM code management is provided primarily by: Software engineering team: - [Erik Kluzek](https://github.com/ekluzek) - [Bill Sacks](https://github.com/billsacks) -- [Mariana Vertenstein](https://github.com/mvertens) -- [Negin Sobhani](https://github.com/negin513) - [Sam Levis](https://github.com/slevisconsulting) +- [Adrianna Foster](https://github.com/adrifoster) +- [Sam Rabin](https://github.com/samsrabin) +- [Greg Lemieux](https://github.com/glemieux) +- [Ryan Knox](https://github.com/rgknox) Science team: - [Dave Lawrence](https://github.com/dlawrenncar) @@ -53,5 +55,7 @@ Science team: - [Danica Lombardozzi](https://github.com/danicalombardozzi) - [Keith Oleson](https://github.com/olyson) - [Sean Swenson](https://github.com/swensosc) -- [Mike Barlage](https://github.com/barlage) +- [Jackie Shuman](https://github.com/jkshuman) +- [Peter Lawrence](https://github.com/lawrencepj1) - [Rosie Fisher](https://github.com/rosiealice) +- Gordon Bonan diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index d9eeb1e5a0..dae7b5f7f0 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -91,11 +91,13 @@ OPTIONS This toggles off the namelist variable: use_cn bgc = Carbon Nitrogen with methane, nitrification, vertical soil C, CENTURY or MIMICS decomposition - This toggles on the namelist variables: + This toggles on the namelist variables: use_cn, use_lch4, use_nitrif_denitrif - fates = FATES/Ecosystem Demography with below ground BGC - This toggles on the namelist variables: - use_fates + fates = FATES/Ecosystem Demography with below ground BGC + CENTURY or MIMICS decomposition + This toggles on the namelist variables: + use_fates. use_lch4 and use_nitrif_denitrif are optional + (Only for CLM4.5/CLM5.0) -[no-]chk_res Also check [do NOT check] to make sure the resolution and land-mask is valid. @@ -168,7 +170,8 @@ OPTIONS (default is 0) (standard option with land-ice model is 10) -glc_use_antarctica Set defaults appropriate for runs that include Antarctica -help [or -h] Print usage to STDOUT. - -light_res Resolution of lightning dataset to use for CN fire (360x720 or 94x192) + -light_res Resolution of lightning dataset to use for CN or FATES fire (360x720, 106x174, or 94x192) + 106x174 can only be used for NEON sites -lilac If CTSM is being run through LILAC (normally not used) (LILAC is the Lightweight Infrastructure for Land-Atmosphere Coupling) -ignore_ic_date Ignore the date on the initial condition files @@ -610,7 +613,7 @@ sub process_namelist_user_input { process_namelist_commandline_infile($opts, $definition, $nl, $envxml_ref); # Apply the commandline options and make sure the user didn't change it above - process_namelist_commandline_options($opts, $nl_flags, $definition, $defaults, $nl, $physv); + process_namelist_commandline_options($opts, $nl_flags, $definition, $defaults, $nl, $envxml_ref, $physv); # The last two process command line arguments for usr_name and use_case # They require that process_namelist_commandline_options was called before this @@ -631,10 +634,10 @@ sub process_namelist_commandline_options { # Obtain default values for the following build-namelist input arguments # : res, mask, ssp_rcp, sim_year, sim_year_range, and clm_accelerated_spinup. - my ($opts, $nl_flags, $definition, $defaults, $nl, $physv) = @_; + my ($opts, $nl_flags, $definition, $defaults, $nl, $envxml_ref, $physv) = @_; setup_cmdl_chk_res($opts, $defaults); - setup_cmdl_resolution($opts, $nl_flags, $definition, $defaults); + setup_cmdl_resolution($opts, $nl_flags, $definition, $defaults, $envxml_ref); setup_cmdl_mask($opts, $nl_flags, $definition, $defaults, $nl); setup_cmdl_configuration_and_structure($opts, $nl_flags, $definition, $defaults, $nl); setup_cmdl_bgc($opts, $nl_flags, $definition, $defaults, $nl); @@ -665,7 +668,7 @@ sub setup_cmdl_chk_res { } sub setup_cmdl_resolution { - my ($opts, $nl_flags, $definition, $defaults) = @_; + my ($opts, $nl_flags, $definition, $defaults, $envxml_ref) = @_; my $var = "res"; my $val; @@ -683,11 +686,32 @@ sub setup_cmdl_resolution { $val = "e_string( $nl_flags->{'res'} ); if ( ! $definition->is_valid_value( $var, $val ) ) { my @valid_values = $definition->get_valid_values( $var ); - if ( ! defined($opts->{'clm_usr_name'}) || $nl_flags->{'res'} ne $opts->{'clm_usr_name'} ) { + if ( $nl_flags->{'res'} ne "CLM_USRDAT" ) { $log->fatal_error("$var has a value ($val) that is NOT valid. Valid values are: @valid_values"); } } } + if ( $nl_flags->{'res'} eq "CLM_USRDAT" ) { + if ( ! defined($opts->{'clm_usr_name'}) ) { + $log->fatal_error("Resolution is CLM_USRDAT, but --clm_usr_name option is NOT set, and it is required for CLM_USRDAT resolutions"); + } + } + # + # For NEON sites + # + $nl_flags->{'neon'} = ".false."; + $nl_flags->{'neonsite'} = ""; + if ( $nl_flags->{'res'} eq "CLM_USRDAT" ) { + if ( $opts->{'clm_usr_name'} eq "NEON" ) { + $nl_flags->{'neon'} = ".true."; + $nl_flags->{'neonsite'} = $envxml_ref->{'NEONSITE'}; + $log->verbose_message( "This is a NEON site with NEONSITE = " . $nl_flags->{'neonsite'} ); + } + } + if ( ! &value_is_true( $nl_flags->{'neon'} ) ) { + $log->verbose_message( "This is NOT a NEON site" ); + } + } #------------------------------------------------------------------------------- @@ -755,26 +779,12 @@ sub setup_cmdl_fates_mode { } } - # The following variables may be set by the user and are compatible with use_fates - # no need to set defaults, covered in a different routine - my @list = ( "use_lch4" ); - foreach my $var ( @list ) { - if ( defined($nl->get_value($var)) ) { - $nl_flags->{$var} = $nl->get_value($var); - $val = $nl_flags->{$var}; - my $group = $definition->get_group_name($var); - $nl->set_variable_value($group, $var, $val); - if ( ! $definition->is_valid_value( $var, $val ) ) { - my @valid_values = $definition->get_valid_values( $var ); - $log->fatal_error("$var has a value ($val) that is NOT valid. Valid values are: @valid_values"); - } - } - } } else { # dis-allow fates specific namelist items with non-fates runs my @list = ( "fates_spitfire_mode", "use_fates_planthydro", "use_fates_ed_st3", "use_fates_ed_prescribed_phys", - "use_fates_cohort_age_tracking", - "use_fates_inventory_init","use_fates_fixed_biogeog","use_fates_nocomp","use_fates_sp","fates_inventory_ctrl_filename","use_fates_logging","fates_parteh_mode","use_fates_tree_damage" ); + "use_fates_cohort_age_tracking","use_fates_inventory_init","use_fates_fixed_biogeog", + "use_fates_nocomp","use_fates_sp","fates_inventory_ctrl_filename","use_fates_logging", + "fates_parteh_mode","use_fates_tree_damage","fates_seeddisp_cadence","use_fates_luh","fluh_timeseries" ); # dis-allow fates specific namelist items with non-fates runs foreach my $var ( @list ) { if ( defined($nl->get_value($var)) ) { @@ -952,59 +962,84 @@ sub setup_cmdl_fire_light_res { my $var = "light_res"; my $val = $opts->{$var}; + if ( &value_is_true($nl->get_value('use_cn')) ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'fire_method'); + } + my $fire_method = remove_leading_and_trailing_quotes( $nl->get_value('fire_method') ); if ( $val eq "default" ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, 'phys'=>$nl_flags->{'phys'}, 'use_cn'=>$nl_flags->{'use_cn'}, + 'neon'=>$nl_flags->{'neon'}, 'fates_spitfire_mode'=>$nl->get_value('fates_spitfire_mode'), - 'use_fates'=>$nl_flags->{'use_fates'}, fire_method=>$nl->get_value('fire_method') ); + 'use_fates'=>$nl_flags->{'use_fates'}, fire_method=>$fire_method ); $val = remove_leading_and_trailing_quotes( $nl->get_value($var) ); $nl_flags->{$var} = $val; } else { - my $fire_method = remove_leading_and_trailing_quotes( $nl->get_value('fire_method') ); if ( defined($fire_method) && $val ne "none" ) { if ( $fire_method eq "nofire" ) { $log->fatal_error("-$var option used with fire_method='nofire'. -$var can ONLY be used without the nofire option"); + } + } + my $stream_fldfilename_lightng = remove_leading_and_trailing_quotes( $nl->get_value('stream_fldfilename_lightng') ); + if ( defined($stream_fldfilename_lightng) && $val ne "none" ) { + $log->fatal_error("-$var option used while also explicitly setting stream_fldfilename_lightng filename which is a contradiction. Use one or the other not both."); + } + if ( ! &value_is_true($nl->get_value('use_cn')) ) { + if ( &value_is_true($nl_flags->{'use_fates'}) ) { + if ( $nl->get_value('fates_spitfire_mode') < 2) { + if ( $val ne "none" ) { + $log->fatal_error("-$var option used when FATES is on, but fates_spitfire_mode does NOT use lightning data"); + } + } else { + if ( $val eq "none" ) { + $log->fatal_error("-$var option is set to none, but FATES is on and fates_spitfire_mode requires lightning data"); + } + } + } else { + $log->fatal_error("-$var option used when FATES off and CN is NOT on. -$var can only be used when BGC is set to bgc or fates"); + } + } else { + if ( $val eq "none" and $fire_method ne "nofire" ) { + $log->fatal_error("-$var option is set to none, but CN is on (with bgc: cn or bgc) which is a contradiction"); + } + } + $nl_flags->{$var} = $val; + } + # Check that NEON data is only used for NEON sites + if ( $val eq "106x174" ) { + if ( ! &value_is_true($nl_flags->{'neon'}) ) { + if ( defined($opts->{'clm_usr_name'}) ) { + $log->warning("The NEON lightning dataset does NOT cover the entire globe, make sure it covers the region for your grid"); + } else { + $log->fatal_error("The NEON lightning dataset can NOT be used for global grids or regions or points outside of its area as it does NOT cover the entire globe."); } - } - my $stream_fldfilename_lightng = remove_leading_and_trailing_quotes( $nl->get_value('stream_fldfilename_lightng') ); - if ( defined($stream_fldfilename_lightng) && $val ne "none" ) { - $log->fatal_error("-$var option used while also explicitly setting stream_fldfilename_lightng filename which is a contradiction. Use one or the other not both."); - } - if ( ! &value_is_true($nl->get_value('use_cn')) ) { - $log->fatal_error("-$var option used CN is NOT on. -$var can only be used when CN is on (with bgc: cn or bgc)"); - } - if ( &value_is_true($nl->get_value('use_cn')) && $val eq "none" ) { - $log->fatal_error("-$var option is set to none, but CN is on (with bgc: cn or bgc) which is a contradiction"); - } - $nl_flags->{$var} = $val; - } - my $group = $definition->get_group_name($var); - $nl->set_variable_value($group, $var, quote_string($nl_flags->{$var}) ); - if ( ! $definition->is_valid_value( $var, $nl_flags->{$var}, 'noquotes'=>1 ) ) { - my @valid_values = $definition->get_valid_values( $var ); - $log->fatal_error("$var has a value (".$nl_flags->{$var}.") that is NOT valid. Valid values are: @valid_values"); - } - $log->verbose_message("Using $nl_flags->{$var} for $var."); - # - # Set flag if cn-fires are on or not - # - $var = "cnfireson"; - if ( &value_is_true($nl->get_value('use_cn')) ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'fire_method'); - } - my $fire_method = remove_leading_and_trailing_quotes( $nl->get_value('fire_method') ); - if ( defined($fire_method) && ! &value_is_true($nl_flags->{'use_cn'}) && ! &value_is_true($nl_flags->{'use_fates'}) ) { - $log->fatal_error("fire_method is being set while use_cn and use_fates are both false."); - } - if ( defined($fire_method) && $fire_method eq "nofire" ) { - $nl_flags->{$var} = ".false."; -# } elsif ( &value_is_true($nl->get_value('use_cn')) || $nl_flags->{'fates_spitfire_mode'} > 1 ) { - } elsif ( &value_is_true($nl->get_value('use_cn')) || &value_is_true($nl->get_value('use_fates')) ) { - $nl_flags->{$var} = ".true."; - } else { - $nl_flags->{$var} = ".false."; - } + } } + # check for valid values... + my $group = $definition->get_group_name($var); + $nl->set_variable_value($group, $var, quote_string($nl_flags->{$var}) ); + if ( ! $definition->is_valid_value( $var, $nl_flags->{$var}, 'noquotes'=>1 ) ) { + my @valid_values = $definition->get_valid_values( $var ); + $log->fatal_error("$var has a value (".$nl_flags->{$var}.") that is NOT valid. Valid values are: @valid_values"); + } + $log->verbose_message("Using $nl_flags->{$var} for $var."); + # + # Set flag if cn-fires are on or not, only for BGC (not FATES) + # + $var = "cnfireson"; + my $fire_method = remove_leading_and_trailing_quotes( $nl->get_value('fire_method') ); + if ( defined($fire_method) && ! &value_is_true($nl_flags->{'use_cn'}) && ! &value_is_true($nl_flags->{'use_fates'}) ) { + $log->fatal_error("fire_method is being set while use_cn and use_fates are both false."); + } + if ( defined($fire_method) && $fire_method eq "nofire" ) { + $nl_flags->{$var} = ".false."; +# } elsif ( &value_is_true($nl->get_value('use_cn')) || $nl_flags->{'fates_spitfire_mode'} > 1 ) { + } elsif ( &value_is_true($nl->get_value('use_cn')) || &value_is_true($nl->get_value('use_fates')) ) { + $nl_flags->{$var} = ".true."; + } else { + $nl_flags->{$var} = ".false."; + } +} #------------------------------------------------------------------------------- @@ -1536,6 +1571,8 @@ sub process_namelist_inline_logic { setup_logic_irrigate($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_start_type($opts, $nl_flags, $nl); setup_logic_decomp_performance($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_roughness_methods($opts, $nl_flags, $definition, $defaults, $nl, $physv); + setup_logic_snicar_methods($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_snow($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_glacier($opts, $nl_flags, $definition, $defaults, $nl, $envxml_ref); setup_logic_dynamic_plant_nitrogen_alloc($opts, $nl_flags, $definition, $defaults, $nl, $physv); @@ -1550,7 +1587,7 @@ sub process_namelist_inline_logic { setup_logic_grainproduct($opts, $nl_flags, $definition, $defaults, $nl, $physv); setup_logic_soilstate($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_demand($opts, $nl_flags, $definition, $defaults, $nl); - setup_logic_surface_dataset($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_surface_dataset($opts, $nl_flags, $definition, $defaults, $nl, $envxml_ref); setup_logic_dynamic_subgrid($opts, $nl_flags, $definition, $defaults, $nl); if ( remove_leading_and_trailing_quotes($nl_flags->{'clm_start_type'}) ne "branch" ) { setup_logic_initial_conditions($opts, $nl_flags, $definition, $defaults, $nl, $physv); @@ -1559,6 +1596,7 @@ sub process_namelist_inline_logic { setup_logic_supplemental_nitrogen($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_snowpack($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_fates($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_z0param($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_misc($opts, $nl_flags, $definition, $defaults, $nl); ######################################### @@ -1592,9 +1630,14 @@ sub process_namelist_inline_logic { setup_logic_urban($opts, $nl_flags, $definition, $defaults, $nl); ############################### - # namelist group: crop # + # namelist group: crop_inparm # + ############################### + setup_logic_crop_inparm($opts, $nl_flags, $definition, $defaults, $nl); + + ############################### + # namelist group: tillage # ############################### - setup_logic_crop($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_tillage($opts, $nl_flags, $definition, $defaults, $nl, $physv); ############################### # namelist group: ch4par_in # @@ -1662,6 +1705,11 @@ sub process_namelist_inline_logic { ################################## setup_logic_lai_streams($opts, $nl_flags, $definition, $defaults, $nl); + ################################## + # namelist group: cropcal_streams # + ################################## + setup_logic_cropcal_streams($opts, $nl_flags, $definition, $defaults, $nl); + ########################################## # namelist group: soil_moisture_streams # ########################################## @@ -1687,11 +1735,6 @@ sub process_namelist_inline_logic { ############################################# setup_logic_rooting_profile($opts, $nl_flags, $definition, $defaults, $nl); - ############################################# - # namelist group: friction_velocity # - ############################################# - setup_logic_friction_vel($opts, $nl_flags, $definition, $defaults, $nl); - ############################# # namelist group: cngeneral # ############################# @@ -1711,6 +1754,11 @@ sub process_namelist_inline_logic { ############################################# setup_logic_canopyfluxes($opts, $nl_flags, $definition, $defaults, $nl); + ########################################################## + # namelist group: friction_velocity (after canopyfluxes) # + ########################################################## + setup_logic_friction_vel($opts, $nl_flags, $definition, $defaults, $nl); + ############################################# # namelist group: canopyhydrology_inparm # ############################################# @@ -1755,6 +1803,11 @@ sub process_namelist_inline_logic { # namelist group: clm_initinterp_inparm # ######################################### setup_logic_initinterp($opts, $nl_flags, $definition, $defaults, $nl); + + ############################### + # namelist group: exice_streams # + ############################### + setup_logic_exice($opts, $nl_flags, $definition, $defaults, $nl); } #------------------------------------------------------------------------------- @@ -1950,10 +2003,83 @@ sub setup_logic_decomp_performance { #------------------------------------------------------------------------------- +sub setup_logic_roughness_methods { + my ($opts, $nl_flags, $definition, $defaults, $nl, $physv) = @_; + + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'z0param_method', + 'phys'=>$nl_flags->{'phys'} ); + + my $var = remove_leading_and_trailing_quotes( $nl->get_value("z0param_method") ); + if ( $var ne "Meier2022" && $var ne "ZengWang2007" ) { + $log->fatal_error("$var is incorrect entry for the namelist variable z0param_method; expected Meier2022 or ZengWang2007"); + } + my $phys = $physv->as_string(); + if ( $phys eq "clm4_5" || $phys eq "clm5_0" ) { + if ( $var eq "Meier2022" ) { + $log->fatal_error("z0param_method = $var and phys = $phys, but this method has been tested only with clm5_1 and later versions; to use with earlier versions, disable this error, and add Meier2022 parameters to the corresponding params file"); + } + } +} +#------------------------------------------------------------------------------- + +sub setup_logic_snicar_methods { + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snicar_snw_shape' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snicar_solarspec' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snicar_dust_optics' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snicar_numrad_snw' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snicar_snobc_intmix' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snicar_snodst_intmix' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snicar_use_aerosol' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_snicar_frc' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'do_sno_oc' ); + + # Error checking in loop + my %supportedSettings = ( 'snicar_solarspec' => "'mid_latitude_winter'", 'snicar_dust_optics' => "'sahara'", 'snicar_numrad_snw' => '5', 'snicar_snobc_intmix' => '.false.', 'snicar_snodst_intmix' => '.false.', 'snicar_use_aerosol' => '.true.', 'do_sno_oc' => '.false.' ); + keys %supportedSettings; + while ( my ($key, $val) = each %supportedSettings ) { + my $var = $nl->get_value($key); + if ( $var ne $val ) { + $log->warning("$key=$val is the supported option; $var is EXPERIMENTAL, UNSUPPORTED, and UNTESTED!"); + } + } + + # Error checking not in loop + my $key1 = 'snicar_snw_shape'; + my $var1 = $nl->get_value($key1); + my $val1a = "'sphere'"; # supported value for this option + my $val1b = "'hexagonal_plate'"; # supported value for this option + if (($var1 ne $val1a) && ($var1 ne $val1b)) { + $log->warning("$key1=$val1a and $val1b are supported; $var1 is EXPERIMENTAL, UNSUPPORTED, and UNTESTED!"); + } + + # snicar_snobc_intmix and snicar_snodst_intmix cannot both be true + my $key1 = 'snicar_snobc_intmix'; + my $key2 = 'snicar_snodst_intmix'; + my $var1 = $nl->get_value($key1); + my $var2 = $nl->get_value($key2); + my $val1 = $supportedSettings{$key1}; # supported value for this option + if (($var1 eq $var2) && ($var1 ne $val1)) { + $log->warning("$key1 = $var1 and $key2 = $var2 do not work together!"); + } +} + +#------------------------------------------------------------------------------- + sub setup_logic_snow { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'fsnowoptics' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snow_thermal_cond_method' ); + + my $var = $nl->get_value('snow_thermal_cond_method'); + if ( $var ne "'Jordan1991'" && $var ne "'Sturm1997'" ) { + $log->fatal_error("$var is incorrect entry for the namelist variable snow_thermal_cond_method; expected Jordan1991 or Sturm1997"); + } + + my $numrad_snw = $nl->get_value('snicar_numrad_snw'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'fsnowoptics', + 'snicar_numrad_snw' => $numrad_snw); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'fsnowaging' ); } @@ -2116,7 +2242,7 @@ sub setup_logic_urban { #------------------------------------------------------------------------------- -sub setup_logic_crop { +sub setup_logic_crop_inparm { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; if ( &value_is_true($nl->get_value('use_crop')) ) { @@ -2134,12 +2260,32 @@ sub setup_logic_crop { } add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, "initial_seed_at_planting", 'use_crop'=>$nl->get_value('use_crop') ); + + my $crop_residue_removal_frac = $nl->get_value('crop_residue_removal_frac'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'crop_residue_removal_frac' ); + if ( $crop_residue_removal_frac < 0.0 or $crop_residue_removal_frac > 1.0 ) { + $log->fatal_error("crop_residue_removal_frac must be in range [0, 1]"); + } } else { - error_if_set( $nl, "Can NOT be set without crop on", "baset_mapping", "baset_latvary_slope", "baset_latvary_intercept" ); + error_if_set( $nl, "Can NOT be set without crop on", "baset_mapping", "baset_latvary_slope", "baset_latvary_intercept", "crop_residue_removal_frac" ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'crop_fsat_equals_zero' ); } } +#------------------------------------------------------------------------------- + +sub setup_logic_tillage { + my ($opts, $nl_flags, $definition, $defaults, $nl, $physv) = @_; + + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'tillage_mode', + 'use_crop'=>$nl_flags->{'use_crop'}, 'phys'=>$physv->as_string() ); + + my $tillage_mode = remove_leading_and_trailing_quotes( $nl->get_value( "tillage_mode" ) ); + if ( $tillage_mode ne "off" && $tillage_mode ne "" && not &value_is_true($nl_flags->{'use_crop'}) ) { + $log->fatal_error( "Tillage only works on crop columns, so use_crop must be true if tillage is enabled." ); + } +} + #------------------------------------------------------------------------------- sub error_if_set { # do a fatal_error and exit if any of the input variable names are set @@ -2159,6 +2305,7 @@ sub setup_logic_soilstate { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'organic_frac_squared' ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_bedrock', 'use_fates'=>$nl_flags->{'use_fates'}, 'vichydro'=>$nl_flags->{'vichydro'} ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_excess_ice'); # excess ice flag should be read before stream vars my $var1 = "soil_layerstruct_predefined"; my $var2 = "soil_layerstruct_userdefined"; @@ -2209,6 +2356,7 @@ sub setup_logic_demand { $settings{'use_lch4'} = $nl_flags->{'use_lch4'}; $settings{'use_nitrif_denitrif'} = $nl_flags->{'use_nitrif_denitrif'}; $settings{'use_crop'} = $nl_flags->{'use_crop'}; + $settings{'neon'} = $nl_flags->{'neon'}; my $demand = $nl->get_value('clm_demand'); if (defined($demand)) { @@ -2261,7 +2409,7 @@ sub setup_logic_surface_dataset { # consistent with it # MUST BE AFTER: setup_logic_demand which is where flanduse_timeseries is set # - my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + my ($opts, $nl_flags, $definition, $defaults, $nl, $xmlvar_ref) = @_; $nl_flags->{'flanduse_timeseries'} = "null"; my $flanduse_timeseries = $nl->get_value('flanduse_timeseries'); @@ -2286,26 +2434,42 @@ sub setup_logic_surface_dataset { if ( ! &value_is_true($nl_flags->{'use_fates'}) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, 'hgrid'=>$nl_flags->{'res'}, 'ssp_rcp'=>$nl_flags->{'ssp_rcp'}, + 'neon'=>$nl_flags->{'neon'}, 'neonsite'=>$nl_flags->{'neonsite'}, 'sim_year'=>$nl_flags->{'sim_year'}, 'irrigate'=>".true.", 'use_vichydro'=>$nl_flags->{'use_vichydro'}, - 'use_crop'=>".true.", 'glc_nec'=>$nl_flags->{'glc_nec'}, 'nofail'=>1); + 'use_crop'=>".true.", 'glc_nec'=>$nl_flags->{'glc_nec'}, 'use_fates'=>$nl_flags->{'use_fates'}, 'nofail'=>1); } # If didn't find the crop version check for the exact match - if ( ! defined($nl->get_value($var) ) ) { + my $fsurdat = $nl->get_value($var); + if ( ! defined($fsurdat) ) { if ( ! &value_is_true($nl_flags->{'use_fates'}) ) { $log->verbose_message( "Crop version of $var NOT found, searching for an exact match" ); } add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, 'hgrid'=>$nl_flags->{'res'}, 'ssp_rcp'=>$nl_flags->{'ssp_rcp'}, 'use_vichydro'=>$nl_flags->{'use_vichydro'}, - 'sim_year'=>$nl_flags->{'sim_year'}, 'irrigate'=>$nl_flags->{'irrigate'}, + 'sim_year'=>$nl_flags->{'sim_year'}, 'irrigate'=>$nl_flags->{'irrigate'}, 'use_fates'=>$nl_flags->{'use_fates'}, + 'neon'=>$nl_flags->{'neon'}, 'neonsite'=>$nl_flags->{'neonsite'}, 'use_crop'=>$nl_flags->{'use_crop'}, 'glc_nec'=>$nl_flags->{'glc_nec'}, 'nofail'=>1 ); - if ( ! defined($nl->get_value($var) ) ) { + if ( ! defined($fsurdat) ) { $log->verbose_message( "Exact match of $var NOT found, searching for version with irrigate true" ); } add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, 'hgrid'=>$nl_flags->{'res'}, 'ssp_rcp'=>$nl_flags->{'ssp_rcp'}, 'use_vichydro'=>$nl_flags->{'use_vichydro'}, - 'sim_year'=>$nl_flags->{'sim_year'}, 'irrigate'=>".true.", + 'sim_year'=>$nl_flags->{'sim_year'}, 'irrigate'=>".true.", 'use_fates'=>$nl_flags->{'use_fates'}, + 'neon'=>$nl_flags->{'neon'}, 'neonsite'=>$nl_flags->{'neonsite'}, 'use_crop'=>$nl_flags->{'use_crop'}, 'glc_nec'=>$nl_flags->{'glc_nec'} ); } + # + # Expand the XML variables for NEON cases so that NEONSITE will be used + # + if ( &value_is_true($nl_flags->{'neon'}) ) { + my $fsurdat = $nl->get_value($var); + my $newval = SetupTools::expand_xml_var( $fsurdat, $xmlvar_ref ); + if ( $newval ne $fsurdat ) { + my $group = $definition->get_group_name($var); + $nl->set_variable_value($group, $var, $newval); + $log->verbose_message( "This is a NEON site and the fsurdat file selected is: $newval" ); + } + } } #------------------------------------------------------------------------------- @@ -2491,6 +2655,7 @@ sub setup_logic_dynamic_subgrid { setup_logic_do_transient_lakes($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_do_transient_urban($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_do_harvest($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_do_grossunrep($opts, $nl_flags, $definition, $defaults, $nl); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'reset_dynbal_baselines'); if ( &value_is_true($nl->get_value('reset_dynbal_baselines')) && @@ -2836,6 +3001,63 @@ sub setup_logic_do_harvest { #------------------------------------------------------------------------------- +sub setup_logic_do_grossunrep { + # + # Set do_grossunrep default value, and perform error checking on do_grossunrep + # + # Assumes the following are already set in the namelist (although it's okay + # for them to be unset if that will be their final state): + # - flanduse_timeseries + # - use_cn + # - use_fates + # + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + + my $var = 'do_grossunrep'; + + # Start by assuming a default value of '.true.'. Then check a number of + # conditions under which do_grossunrep cannot be true. Under these + # conditions: (1) set default value to '.false.'; (2) make sure that the + # value is indeed false (e.g., that the user didn't try to set it to true). + + my $default_val = ".false."; + + # cannot_be_true will be set to a non-empty string in any case where + # do_grossunrep should not be true; if it turns out that do_grossunrep IS true + # in any of these cases, a fatal error will be generated + my $cannot_be_true = ""; + + if (string_is_undef_or_empty($nl->get_value('flanduse_timeseries'))) { + $cannot_be_true = "$var can only be set to true when running a transient case (flanduse_timeseries non-blank)"; + } + elsif (&value_is_true($nl->get_value('use_fates'))) { + $cannot_be_true = "$var currently doesn't work with FATES"; + } + elsif (!&value_is_true($nl->get_value('use_cn'))) { + $cannot_be_true = "$var can only be set to true when running with CN (use_cn = true)"; + } + + if ($cannot_be_true) { + $default_val = ".false."; + } + + if (!$cannot_be_true) { + # Note that, if the variable cannot be true, we don't call add_default + # - so that we don't clutter up the namelist with variables that don't + # matter for this case + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, val=>$default_val); + } + + # Make sure the value is false when it needs to be false - i.e., that the + # user hasn't tried to set a true value at an inappropriate time. + + if (&value_is_true($nl->get_value($var)) && $cannot_be_true) { + $log->fatal_error($cannot_be_true); + } + +} + +#------------------------------------------------------------------------------- sub setup_logic_spinup { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; @@ -2888,24 +3110,30 @@ sub setup_logic_supplemental_nitrogen { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; if ( $nl_flags->{'bgc_mode'} ne "sp" && $nl_flags->{'bgc_mode'} ne "fates" && &value_is_true($nl_flags->{'use_crop'}) ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, - 'suplnitro', 'use_cn'=>$nl_flags->{'use_cn'}, 'use_crop'=>$nl_flags->{'use_crop'}); - } + # If this is non-fates, non-sp and crop is active + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, + 'suplnitro', 'use_cn'=>$nl_flags->{'use_cn'}, 'use_crop'=>$nl_flags->{'use_crop'}); + } elsif ( $nl_flags->{'bgc_mode'} eq "fates" && not &value_is_true( $nl_flags->{'use_fates_sp'}) ) { + # Or... if its fates but not fates-sp + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, + 'suplnitro', 'use_fates'=>$nl_flags->{'use_fates'}); + } + # # Error checking for suplnitro # my $suplnitro = $nl->get_value('suplnitro'); if ( defined($suplnitro) ) { if ( $nl_flags->{'bgc_mode'} eq "sp" ) { - $log->fatal_error("supplemental Nitrogen (suplnitro) is set, but neither CN nor CNDV is active!"); + $log->fatal_error("supplemental Nitrogen (suplnitro) is set, but neither CN nor CNDV nor FATES is active!"); } if ( ! &value_is_true($nl_flags->{'use_crop'}) && $suplnitro =~ /PROG_CROP_ONLY/i ) { $log->fatal_error("supplemental Nitrogen is set to run over prognostic crops, but prognostic crop is NOT active!"); } if ( $suplnitro =~ /ALL/i ) { - if ( $nl_flags->{'bgc_spinup'} eq "on" ) { + if ( $nl_flags->{'bgc_spinup'} eq "on" && $nl_flags->{'bgc_mode'} ne "fates" ) { $log->warning("There is no need to use a bgc_spinup mode when supplemental Nitrogen is on for all PFT's, as these modes spinup Nitrogen" ); } } @@ -3222,6 +3450,12 @@ sub setup_logic_luna { 'use_cn'=>$nl_flags->{'use_cn'} ); } $nl_flags->{'use_luna'} = $nl->get_value('use_luna'); + + # LUNA can NOT be on with FATES + if ( &value_is_true( $nl_flags->{'use_luna'} ) && &value_is_true( $nl_flags->{'use_fates'} )) { + $log->fatal_error("Cannot turn use_luna to true when bgc=fates" ); + } + my $vcmax_opt= $nl->get_value('vcmax_opt'); # lnc_opt only applies if luna is on or for vcmax_opt=3/4 if ( &value_is_true( $nl_flags->{'use_luna'} ) || $vcmax_opt == 3 || $vcmax_opt == 4 ) { @@ -3388,18 +3622,18 @@ sub setup_logic_nitrogen_deposition { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; # - # Nitrogen deposition for bgc=CN + # Nitrogen deposition for bgc=CN or fates # - if ( $nl_flags->{'bgc_mode'} =~/bgc/ ) { + if ( ($nl_flags->{'bgc_mode'} =~/bgc/) ) { # or ($nl_flags->{'bgc_mode'} =~/fates/) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'ndepmapalgo', 'phys'=>$nl_flags->{'phys'}, 'use_cn'=>$nl_flags->{'use_cn'}, 'hgrid'=>$nl_flags->{'res'}, 'clm_accelerated_spinup'=>$nl_flags->{'clm_accelerated_spinup'} ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'ndep_taxmode', 'phys'=>$nl_flags->{'phys'}, - 'use_cn'=>$nl_flags->{'use_cn'}, - 'lnd_tuning_mode'=>$nl_flags->{'lnd_tuning_mode'} ); + 'use_cn'=>$nl_flags->{'use_cn'}, + 'lnd_tuning_mode'=>$nl_flags->{'lnd_tuning_mode'} ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'ndep_varlist', 'phys'=>$nl_flags->{'phys'}, - 'use_cn'=>$nl_flags->{'use_cn'}, - 'lnd_tuning_mode'=>$nl_flags->{'lnd_tuning_mode'} ); + 'use_cn'=>$nl_flags->{'use_cn'}, + 'lnd_tuning_mode'=>$nl_flags->{'lnd_tuning_mode'} ); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_ndep', 'phys'=>$nl_flags->{'phys'}, 'use_cn'=>$nl_flags->{'use_cn'}, 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); @@ -3683,6 +3917,10 @@ sub setup_logic_fire_emis { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; if ($opts->{'fire_emis'} ) { + if ( &value_is_true( $nl_flags->{'use_fates'} ) ) { + $log->warning("Fire emission can NOT be on when FATES is also on.\n" . + " DON'T use the '-fire_emis' option when '-bgc fates' is activated"); + } add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'fire_emis_factors_file'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'fire_emis_specifier'); } else { @@ -3783,8 +4021,7 @@ sub setup_logic_lai_streams { if ( &value_is_true($nl_flags->{'use_crop'}) && &value_is_true($nl->get_value('use_lai_streams')) ) { $log->fatal_error("turning use_lai_streams on is incompatable with use_crop set to true."); } - if ( $nl_flags->{'bgc_mode'} eq "sp" ) { - + if ( $nl_flags->{'bgc_mode'} eq "sp" || ($nl_flags->{'bgc_mode'} eq "fates" && &value_is_true($nl->get_value('use_fates_sp')) )) { if ( &value_is_true($nl->get_value('use_lai_streams')) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_lai_streams'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'lai_mapalgo', @@ -3810,21 +4047,80 @@ sub setup_logic_lai_streams { } } } else { - # If bgc is CN/CNDV then make sure none of the LAI settings are set - if ( defined($nl->get_value('stream_year_first_lai')) || - defined($nl->get_value('stream_year_last_lai')) || - defined($nl->get_value('model_year_align_lai')) || - defined($nl->get_value('lai_tintalgo' )) || + # If bgc is BGC/BGCDV then make sure none of the LAI settings are set + if ( &value_is_true($nl->get_value('use_lai_streams'))) { + $log->fatal_error("When not in SP mode use_lai_streams cannot be .true.\n" . + "(eg. don't use this option with BGC or non-SP FATES), \n" . + "Update compset to use SP)"); + } + if ( defined($nl->get_value('stream_year_first_lai')) || + defined($nl->get_value('stream_year_last_lai')) || + defined($nl->get_value('model_year_align_lai')) || + defined($nl->get_value('lai_tintalgo' )) || defined($nl->get_value('stream_fldfilename_lai')) ) { - $log->fatal_error("When bgc is NOT SP none of the following can be set: stream_year_first_lai,\n" . + $log->fatal_error("When not in SP mode none of the following can be set: stream_year_first_lai,\n" . "stream_year_last_lai, model_year_align_lai, lai_tintalgo nor\n" . - "stream_fldfilename_lai (eg. don't use this option with BGC,CN,CNDV nor BGDCV)."); + "stream_fldfilename_lai (eg. don't use this option with BGC or FATES-SP)."); } } } #------------------------------------------------------------------------------- +sub setup_logic_cropcal_streams { + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + + # Set first and last stream years + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + + # Set align year, if first and last years are different + if ( $nl->get_value('stream_year_first_cropcal') != + $nl->get_value('stream_year_last_cropcal') ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, + 'model_year_align_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + } + + # Set up other crop calendar parameters + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'generate_crop_gdds'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_mxmat'); + + # Option checks + my $generate_crop_gdds = $nl->get_value('generate_crop_gdds') ; + my $use_mxmat = $nl->get_value('use_mxmat') ; + my $swindow_start_file = $nl->get_value('stream_fldFileName_swindow_start') ; + my $swindow_end_file = $nl->get_value('stream_fldFileName_swindow_end') ; + my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; + my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; + if ( ($swindow_start_file eq '' and $swindow_start_file ne '') or ($swindow_start_file ne '' and $swindow_start_file eq '') ) { + $log->fatal_error("When specifying sowing window dates, you must provide both swindow_start_file and swindow_end_file. To specify exact sowing dates, use the same file." ); + } + if ( $generate_crop_gdds eq '.true.' ) { + if ( $use_mxmat eq '.true.' ) { + $log->fatal_error("If generate_crop_gdds is true, you must also set use_mxmat to false" ); + } + if ( $swindow_start_file eq '' or $swindow_end_file eq '' ) { + $log->fatal_error("If generate_crop_gdds is true, you must specify stream_fldFileName_swindow_start and stream_fldFileName_swindow_end") + } + if ( $swindow_start_file ne $swindow_end_file ) { + $log->fatal_error("If generate_crop_gdds is true, you must specify exact sowing dates by setting stream_fldFileName_swindow_start and stream_fldFileName_swindow_end to the same file") + } + if ( $gdd_file ne '' ) { + $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_cultivar_gdds") + } + } + if ( $mesh_file eq '' and ( $swindow_start_file ne '' or $gdd_file ne '' ) ) { + $log->fatal_error("If prescribing crop sowing dates and/or maturity requirements, you must specify stream_meshfile_cropcal") + } +} + +#------------------------------------------------------------------------------- + sub setup_logic_soilwater_movement { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; @@ -3898,10 +4194,11 @@ sub setup_logic_rooting_profile { #------------------------------------------------------------------------------- sub setup_logic_friction_vel { - # + # Must be after canopyfluxes so that use_biomass_heat_storage will be set my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'zetamaxstable' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'zetamaxstable', + 'use_biomass_heat_storage'=>$nl_flags->{'use_biomass_heat_storage'}, 'phys'=>$nl_flags->{'phys'} ); } #------------------------------------------------------------------------------- @@ -3926,6 +4223,11 @@ sub setup_logic_canopyfluxes { if ( &value_is_true($nl->get_value('use_biomass_heat_storage') ) && &value_is_true( $nl_flags->{'use_fates'}) ) { $log->fatal_error('use_biomass_heat_storage can NOT be set to true when fates is on'); } + if ( &value_is_true($nl->get_value('use_biomass_heat_storage')) ) { + $nl_flags->{'use_biomass_heat_storage'} = ".true."; + } else { + $nl_flags->{'use_biomass_heat_storage'} = ".false."; + } } #------------------------------------------------------------------------------- @@ -3955,7 +4257,6 @@ sub setup_logic_snowpack { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snow_overburden_compaction_method'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'lotmp_snowdensity_method'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'upplim_destruct_metamorph'); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'fresh_snw_rds_max'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'reset_snow'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'reset_snow_glc'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'reset_snow_glc_ela'); @@ -4113,12 +4414,19 @@ sub setup_logic_fates { if (&value_is_true( $nl_flags->{'use_fates'}) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'fates_paramfile', 'phys'=>$nl_flags->{'phys'}); my @list = ( "fates_spitfire_mode", "use_fates_planthydro", "use_fates_ed_st3", "use_fates_ed_prescribed_phys", - "use_fates_inventory_init","use_fates_fixed_biogeog","use_fates_nocomp", - "use_fates_logging","fates_parteh_mode", "use_fates_cohort_age_tracking","use_fates_tree_damage" ); + "use_fates_inventory_init","use_fates_fixed_biogeog","use_fates_nocomp","fates_seeddisp_cadence", + "use_fates_logging","fates_parteh_mode", "use_fates_cohort_age_tracking","use_fates_tree_damage","use_fates_luh" ); foreach my $var ( @list ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, 'use_fates'=>$nl_flags->{'use_fates'}, 'use_fates_sp'=>$nl_flags->{'use_fates_sp'} ); } + my $suplnitro = $nl->get_value('suplnitro'); + my $parteh_mode = $nl->get_value('fates_parteh_mode'); + if ( ($parteh_mode == 1) && ($suplnitro !~ /ALL/) && not &value_is_true( $nl_flags->{'use_fates_sp'}) ) { + $log->fatal_error("supplemental Nitrogen (suplnitro) is NOT set to ALL, FATES is on, " . + "but and FATES-SP is not active, but fates_parteh_mode is 1, so Nitrogen is not active" . + "Change suplnitro back to ALL"); + } # # For FATES SP mode make sure no-competetiion, and fixed-biogeography are also set # And also check for other settings that can't be trigged on as well @@ -4135,14 +4443,14 @@ sub setup_logic_fates { # spit-fire can't be on with FATES SP mode is active if ( $nl->get_value('fates_spitfire_mode') > 0 ) { $log->fatal_error('fates_spitfire_mode can NOT be set to greater than 0 when use_fates_sp is true'); + } } - } } my $var = "use_fates_inventory_init"; if ( defined($nl->get_value($var)) ) { if ( &value_is_true($nl->get_value($var)) ) { $var = "fates_inventory_ctrl_filename"; - my $fname = substr $nl->get_value($var), 1, -1; # ignore first and last positions of string because those are quote characters + my $fname = remove_leading_and_trailing_quotes( $nl->get_value($var) ); if ( ! defined($nl->get_value($var)) ) { $log->fatal_error("$var is required when use_fates_inventory_init is set" ); } elsif ( ! -f "$fname" ) { @@ -4150,9 +4458,92 @@ sub setup_logic_fates { } } } + my $var = "use_fates_luh"; + if ( defined($nl->get_value($var)) ) { + if ( &value_is_true($nl->get_value($var)) ) { + $var = "fluh_timeseries"; + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, 'phys'=>$nl_flags->{'phys'}, 'hgrid'=>$nl_flags->{'res'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}, nofail=>1 ); + my $fname = remove_leading_and_trailing_quotes( $nl->get_value($var) ); + if ( ! defined($nl->get_value($var)) ) { + $log->fatal_error("$var is required when use_fates_luh is set" ); + } elsif ( ! -f "$fname" ) { + $log->fatal_error("$fname does NOT point to a valid filename" ); + } + } + } } } +#------------------------------------------------------------------------------- +sub setup_logic_exice { + # + # excess ice streams + # + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + my $use_exice = $nl->get_value( 'use_excess_ice' ); + my $use_exice_streams = $nl->get_value( 'use_excess_ice_streams' ); + # IF excess ice streams is on + if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { + # Can only be true if excess ice is also on, otherwise fail + if (defined($use_exice) && not value_is_true($use_exice)) { + $log->fatal_error("use_excess_ice_streams can NOT be TRUE when use_excess_ice is FALSE" ); + } + # Otherwise if ice streams are off + } else { + my @list = ( "stream_meshfile_exice", "stream_fldfilename_exice" ); + # fail is excess ice streams files are set + foreach my $var ( @list ) { + if ( defined($nl->get_value($var)) ) { + $log->fatal_error("$var should NOT be set when use_excess_ice_streams=FALSE" ); + } + } + # mapalgo can only be none, if excess ice streams are off + my $map_algo = $nl->get_value("stream_mapalgo_exice"); + if ( defined($map_algo) && ($map_algo ne "none") ) { + $log->fatal_error("stream_mapalgo_exice can ONLY be none when use_excess_ice_streams=FALSE" ); + } + } + # If excess ice is on + if (defined($use_exice) && value_is_true($use_exice)) { + # IF nuopc driver and excess ice streams are on get the stream defaults + if (defined($use_exice_streams) && value_is_true($use_exice_streams)) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_exice'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_mapalgo_exice'); + # If excess ice streams on, but NOT the NUOPC driver fail + if ( not $opts->{'driver'} eq "nuopc" ) { + $log->fatal_error("nuopc driver is required when use_excess_ice_streams is set to true" ); + # NUOPC driver needs a mesh file + } else { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_exice'); + } + } + } + + +} # end exice streams + +#------------------------------------------------------------------------------- + +sub setup_logic_z0param { + # + # Set default z0 paramterization + # + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'z0param_method'); + + my $z0param_method = remove_leading_and_trailing_quotes($nl->get_value('z0param_method' )); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_z0m_snowmelt', + 'z0param_method'=>$z0param_method ); + + my $use_z0m_snowmelt = $nl->get_value( 'use_z0m_snowmelt' ); + + if ( $z0param_method eq "ZengWang2007" && defined($use_z0m_snowmelt) && value_is_true($use_z0m_snowmelt)) { + $log->fatal_error("use_z0m_snowmelt must be .false. when z0param_method = $z0param_method.\n $@"); + } + +} + #------------------------------------------------------------------------------- sub setup_logic_misc { @@ -4172,7 +4563,7 @@ sub setup_logic_misc { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'for_testing_use_second_grain_pool'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'for_testing_use_repr_structure_pool'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'for_testing_no_crop_seed_replenishment'); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'hist_master_list_file'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'hist_fields_list_file'); } #------------------------------------------------------------------------------- @@ -4196,6 +4587,7 @@ sub write_output_files { my @groups; @groups = qw(clm_inparm ndepdyn_nml popd_streams urbantv_streams light_streams soil_moisture_streams lai_streams atm2lnd_inparm lnd2atm_inparm clm_canopyhydrology_inparm cnphenology + cropcal_streams clm_soilhydrology_inparm dynamic_subgrid cnvegcarbonstate finidat_consistency_checks dynpft_consistency_checks clm_initinterp_inparm century_soilbgcdecompcascade @@ -4203,8 +4595,8 @@ sub write_output_files { soilwater_movement_inparm rooting_profile_inparm soil_resis_inparm bgc_shared canopyfluxes_inparm aerosol clmu_inparm clm_soilstate_inparm clm_nitrogen clm_snowhydrology_inparm - cnprecision_inparm clm_glacier_behavior crop irrigation_inparm - surfacealbedo_inparm water_tracers_inparm); + cnprecision_inparm clm_glacier_behavior crop_inparm irrigation_inparm + surfacealbedo_inparm water_tracers_inparm tillage_inparm); #@groups = qw(clm_inparm clm_canopyhydrology_inparm clm_soilhydrology_inparm # finidat_consistency_checks dynpft_consistency_checks); @@ -4223,6 +4615,7 @@ sub write_output_files { push @groups, "nitrif_inparm"; push @groups, "lifire_inparm"; push @groups, "ch4finundated"; + push @groups, "exice_streams"; push @groups, "soilbgc_decomp"; push @groups, "clm_canopy_inparm"; if (remove_leading_and_trailing_quotes($nl->get_value('snow_cover_fraction_method')) eq 'SwensonLawrence2012') { @@ -4560,6 +4953,8 @@ sub check_use_case_name { } else { $log->fatal_error($diestring); } + } elsif ( $use_case =~ /^([0-9]+|PI)-PD_*($desc)_transient$/ ) { + # valid name } elsif ( $use_case =~ /^([0-9]+)_*($desc)_control$/ ) { # valid name } elsif ( $use_case =~ /^($desc)_pd$/ ) { diff --git a/bld/env_run.xml b/bld/env_run.xml index 8bf59d0911..f3b7467168 100644 --- a/bld/env_run.xml +++ b/bld/env_run.xml @@ -9,5 +9,6 @@ Sample env_run.xml file that allows build-namelist to be run for testing in this --> + diff --git a/bld/namelist_files/createMapEntry.pl b/bld/namelist_files/createMapEntry.pl index 561683bb05..f9009ba86f 100755 --- a/bld/namelist_files/createMapEntry.pl +++ b/bld/namelist_files/createMapEntry.pl @@ -23,7 +23,7 @@ my $scriptName; ($scriptName = $0) =~ s!(.*)/!!; # get name of script my $cwd = getcwd(); - my $CSMDATA = "/glade/p/cesm/cseg/inputdata"; + my $CSMDATA = "/glade/campaign/cesm/cesmdata/cseg/inputdata"; if ($#ARGV != 0 ) { usage(); diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 2686d62b9a..d3b3cc9715 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -25,6 +25,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). 379.0 388.8 397.5 +408.83 284.7 284.7 @@ -66,8 +67,8 @@ attributes from the config_cache.xml file (with keys converted to upper-case). .false. .false. - -.false. + +.false. .true. @@ -145,7 +146,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). NONE -NONE +ALL 0.50,0.30 @@ -209,9 +210,11 @@ attributes from the config_cache.xml file (with keys converted to upper-case). 1.d-2 -2.0d00 -0.5d00 -0.5d00 +2.0d00 +2.0d00 +0.5d00 +0.5d00 +2.0d00 .true. @@ -322,6 +325,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). 40 3 + .true. 1.0 @@ -427,10 +431,6 @@ attributes from the config_cache.xml file (with keys converted to upper-case). 175.d00 175.d00 -54.526d00 -204.526d00 -204.526d00 - 0.08d00 .false. @@ -441,6 +441,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). 1.e9 SwensonLawrence2012 +Jordan1991 -lnd/clm2/paramdata/ctsm51_params.c211112.nc -lnd/clm2/paramdata/clm50_params.c211112.nc -lnd/clm2/paramdata/clm45_params.c211112.nc +lnd/clm2/paramdata/ctsm51_params.c240207b.nc +lnd/clm2/paramdata/clm50_params.c240207b.nc +lnd/clm2/paramdata/clm45_params.c240207b.nc -lnd/clm2/paramdata/fates_params_api.24.0.0_12pft_c220608.nc +lnd/clm2/paramdata/fates_params_api.32.0.0_12pft_c231215.nc + + + + + +ZengWang2007 +Meier2022 + +.true. +.false. @@ -508,6 +519,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). .true. .false. .false. +.false. .true. @@ -542,6 +554,9 @@ attributes from the config_cache.xml file (with keys converted to upper-case). .true. .true. +0.d+0 +0.5d00 + constant varytropicsbylat @@ -560,6 +575,8 @@ attributes from the config_cache.xml file (with keys converted to upper-case). DependsOnLat .false. Constant +.false. +.true. .false. @@ -664,6 +681,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). set up. If more finidat files are added you may need to add more of these. Or one specific file will be chosen over another. --> + hgrid=0.9x1.25 maxpft=17 mask=gx1v7 use_cn=.true. use_crop=.false. irrigate=.true. glc_nec=10 do_transient_pfts=.false. @@ -708,6 +726,11 @@ attributes from the config_cache.xml file (with keys converted to upper-case). >hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.false. glc_nec=10 do_transient_pfts=.false. + +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.false. glc_nec=10 do_transient_pfts=.false. + + hgrid=1.9x2.5 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. @@ -756,11 +779,26 @@ attributes from the config_cache.xml file (with keys converted to upper-case). >hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + + +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + + hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + + hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. @@ -791,6 +829,42 @@ attributes from the config_cache.xml file (with keys converted to upper-case). >hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + + + +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + + +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + + +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + + +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + + +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + + +hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + @@ -819,7 +893,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). hgrid="ne0np4.ARCTICGRIS.ne30x8" use_cn=".false." maxpft="17" >hgrid=ne0np4.ARCTICGRIS.ne30x8 maxpft=17 mask=tx0.1v2 use_cn=.false. use_crop=.false. irrigate=.true. glc_nec=10 do_transient_pfts=.false. -p + hgrid=ne0np4CONUS.ne30x8 maxpft=17 mask=tx0.1v2 use_cn=.false. use_crop=.false. irrigate=.true. glc_nec=10 do_transient_pfts=.false. - +lnd/clm2/initdata_map/clmi.B1850Clm50BgcCrop.0161-01-01.0.9x1.25_gx1v7_simyr1850_c200729.nc + + lnd/clm2/surfdata_map/surfdata_0.125nldas2_hist_16pfts_Irrig_CMIP6_simyr2005_c190412.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev116/surfdata_1x1_brazil_hist_78pfts_CMIP6_simyr2000_c230123.nc + lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_5x5_amazon_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc - -lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1x1_brazil_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1.9x2.5_hist_78pfts_CMIP6_simyr2000_c190304.nc + +lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_360x720cru_78pfts_CMIP6_simyr2000_c170824.nc lnd/clm2/surfdata_map/release-clm5.0.24/surfdata_0.125x0.125_hist_78pfts_CMIP6_simyr2005_c190624.nc lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_10x15_hist_78pfts_CMIP6_simyr2000_c190214.nc lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_4x5_hist_78pfts_CMIP6_simyr2000_c190214.nc - -lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1x1_numaIA_hist_78pfts_CMIP6_simyr2000_c190214.nc - -lnd/clm2/surfdata_map/ctsm1.0.dev094-2-g633be0eb/surfdata_1x1_smallvilleIA_hist_78pfts_CMIP6_simyr2000_c200521.nc - + +lnd/clm2/surfdata_map/ctsm5.1.dev116/surfdata_1x1_numaIA_hist_78pfts_CMIP6_simyr2000_c230123.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev116/surfdata_1x1_smallvilleIA_hist_78pfts_CMIP6_simyr2000_c230123.nc + + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa480_hist_78pfts_CMIP6_simyr2000_c211110.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa240_hist_78pfts_CMIP6_simyr2000_c211115.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa120_hist_78pfts_CMIP6_simyr2000_c211108.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa60_hist_78pfts_CMIP6_simyr2000_c211110.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa30_hist_78pfts_CMIP6_simyr2000_c211111.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa15_hist_78pfts_CMIP6_simyr2000_c211111.nc + + +lnd/clm2/surfdata_map/ctsm5.1.dev120//surfdata_ne3np4.pg3_hist_78pfts_CMIP6_simyr2000_c230405.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev120/surfdata_ne5np4.pg3_hist_78pfts_CMIP6_simyr2000_c230405.nc lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_ne16np4_hist_78pfts_CMIP6_simyr2000_c190214.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev120/surfdata_ne16np4.pg3_hist_78pfts_CMIP6_simyr2000_c230405.nc lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne30np4_hist_78pfts_CMIP6_simyr2000_c200426.nc @@ -1111,12 +1221,12 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.ARCTIC.ne30x4_hist_78pft lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts_CMIP6_simyr2000_c200426.nc - -lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1x1_vancouverCAN_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc - -lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc - -lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1x1_urbanc_alpha_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev116/surfdata_1x1_vancouverCAN_hist_78pfts_CMIP6_simyr2000_c230123.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev116/surfdata_1x1_mexicocityMEX_hist_78pfts_CMIP6_simyr2000_c230123.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev116/surfdata_1x1_urbanc_alpha_hist_78pfts_CMIP6_simyr2000_c230123.nc @@ -1131,8 +1241,18 @@ lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_10x15_hist_16pfts_Irrig_CMIP6_s lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_4x5_hist_16pfts_Irrig_CMIP6_simyr1850_c190214.nc - -lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1x1_brazil_hist_16pfts_Irrig_CMIP6_simyr1850_c190214.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa480_hist_78pfts_CMIP6_simyr1850_c211110.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa240_hist_78pfts_CMIP6_simyr1850_c211115.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa120_hist_78pfts_CMIP6_simyr1850_c211108.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa60_hist_78pfts_CMIP6_simyr1850_c211110.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa30_hist_78pfts_CMIP6_simyr1850_c211111.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_mpasa15_hist_78pfts_CMIP6_simyr1850_c211111.nc lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_ne30np4_hist_16pfts_Irrig_CMIP6_simyr1850_c190303.nc @@ -1159,19 +1279,25 @@ lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1.9x2.5_hist_78pfts_CMIP6_simyr lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_10x15_hist_78pfts_CMIP6_simyr1850_c190214.nc lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_4x5_hist_78pfts_CMIP6_simyr1850_c190214.nc - -lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1x1_smallvilleIA_hist_78pfts_CMIP6_simyr1850_c190214.nc - -lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1x1_numaIA_hist_78pfts_CMIP6_simyr1850_c190214.nc - - -lnd/clm2/surfdata_map/release-clm5.0.18/surfdata_1x1_brazil_hist_78pfts_CMIP6_simyr1850_c190214.nc - + +lnd/clm2/surfdata_map/ctsm5.1.dev116/surfdata_1x1_smallvilleIA_hist_78pfts_CMIP6_simyr1850_c230123.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev116/surfdata_1x1_numaIA_hist_78pfts_CMIP6_simyr1850_c230123.nc + + +lnd/clm2/surfdata_map/ctsm5.1.dev116/surfdata_1x1_brazil_hist_78pfts_CMIP6_simyr1850_c230123.nc + + +lnd/clm2/surfdata_map/ctsm5.1.dev120/surfdata_ne3np4.pg3_hist_78pfts_CMIP6_simyr1850_c230405.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev120/surfdata_ne5np4.pg3_hist_78pfts_CMIP6_simyr1850_c230405.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev120/surfdata_ne16np4.pg3_hist_78pfts_CMIP6_simyr1850_c230405.nc lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne30np4_hist_78pfts_CMIP6_simyr1850_c200426.nc lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne30np4.pg2_hist_78pfts_CMIP6_simyr1850_c200426.nc - + lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne30np4.pg3_hist_78pfts_CMIP6_simyr1850_c200426.nc lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne120np4_hist_78pfts_CMIP6_simyr1850_c200427.nc @@ -1190,8 +1316,25 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts lnd/clm2/surfdata_map/surfdata_0.9x1.25_hist_16pfts_nourb_CMIP6_simyrPtVg_c181114.nc + + +lnd/clm2/surfdata_map/NEON/16PFT_mixed/surfdata_1x1_NEON_${NEONSITE}_hist_16pfts_Irrig_CMIP6_simyr2000_c230120.nc + +lnd/clm2/surfdata_map/NEON/surfdata_1x1_NEON_${NEONSITE}_hist_78pfts_CMIP6_simyr2000_c230601.nc + + + + + lnd/clm2/surfdata_map/landuse.timeseries_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr1850-2015_c170824.nc lnd/clm2/surfdata_map/release-clm5.0.18/landuse.timeseries_48x96_hist_16pfts_Irrig_CMIP6_simyr1850-2015_c190214.nc -lnd/clm2/surfdata_map/landuse.timeseries_1x1_brazil_hist_16pfts_Irrig_CMIP6_simyr1850-2015_c170824.nc -lnd/clm2/surfdata_map/landuse.timeseries_ne30np4_hist_16pfts_Irrig_CMIP6_simyr1850-2015_c170824.nc +lnd/clm2/surfdata_map/release-clm5.0.30/landuse.timeseries_ne30np4_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c200426.nc +lnd/clm2/surfdata_map/release-clm5.0.30/landuse.timeseries_ne30np4.pg3_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c200426.nc lnd/clm2/surfdata_map/landuse.timeseries_ne0np4.ARCTIC.ne30x4_hist_78pfts_CMIP6_simyr1850-2015_c191023.nc +>lnd/clm2/surfdata_map/release-clm5.0.30/landuse.timeseries_ne0np4.ARCTIC.ne30x4_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c200426.nc @@ -1231,10 +1374,29 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts use_crop=".true." >lnd/clm2/surfdata_map/landuse.timeseries_48x96_hist_78pfts_CMIP6_simyr1850-2015_c170824.nc lnd/clm2/surfdata_map/landuse.timeseries_1x1_brazil_hist_78pfts_CMIP6_simyr1850-2015_c170824.nc +>lnd/clm2/surfdata_map/ctsm5.1.dev116/landuse.timeseries_1x1_brazil_hist_78pfts_CMIP6_simyr1850-2015_c230123.nc lnd/clm2/surfdata_map/landuse.timeseries_1x1_numaIA_hist_78pfts_CMIP6_simyr1850-2015_c170917.nc +lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa480_hist_78pfts_CMIP6_simyr1850-2015_c211110.nc +lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa240_hist_78pfts_CMIP6_simyr1850-2015_c211115.nc +lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa120_hist_78pfts_CMIP6_simyr1850-2015_c211108.nc +lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa60_hist_78pfts_CMIP6_simyr1850-2015_c211110.nc +lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa30_hist_78pfts_CMIP6_simyr1850-2015_c211111.nc +lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa15_hist_78pfts_CMIP6_simyr1850-2015_c211111.nc + +lnd/clm2/surfdata_map/ctsm5.1.dev120/landuse.timeseries_ne3np4.pg3_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c230405.nc +lnd/clm2/surfdata_map/ctsm5.1.dev120/landuse.timeseries_ne5np4.pg3_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c230405.nc +lnd/clm2/surfdata_map/ctsm5.1.dev120/landuse.timeseries_ne16np4.pg3_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c230405.nc lnd/clm2/surfdata_map/release-clm5.0.30/landuse.timeseries_ne30np4_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c200426.nc lnd/clm2/surfdata_map/release-clm5.0.30/landuse.timeseries_C96_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c200317.nc +lnd/clm2/surfdata_map/ctsm5.1.dev120/landuse.timeseries_ne3np4.pg3_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c230405.nc +lnd/clm2/surfdata_map/ctsm5.1.dev120/landuse.timeseries_ne5np4.pg3_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c230405.nc +lnd/clm2/surfdata_map/ctsm5.1.dev120/landuse.timeseries_ne16np4.pg3_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c230405.nc lnd/clm2/surfdata_map/release-clm5.0.30/landuse.timeseries_ne30np4_SSP5-8.5_78pfts_CMIP6_simyr1850-2100_c200426.nc lnd/clm2/surfdata_map/release-clm5.0.18/landuse.timeseries_10x15_SSP5-3.4_16pfts_Irrig_CMIP6_simyr1850-2100_c190228.nc + + +lnd/clm2/surfdata_map/fates-sci.1.68.3_api.31.0.0_tools.1.0.1/LUH2_states_transitions_management.timeseries_4x5_hist_simyr1850-2015_c231101.nc + + +lnd/clm2/surfdata_map/fates-sci.1.68.3_api.31.0.0_tools.1.0.1/LUH2_states_transitions_management.timeseries_4x5_hist_simyr1850-2015_c231101.nc + .true. .true. @@ -1433,8 +1610,24 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts -lnd/clm2/snicardata/snicar_optics_5bnd_c090915.nc -lnd/clm2/snicardata/snicar_drdt_bst_fit_60_c070416.nc +lnd/clm2/snicardata/snicar_drdt_bst_fit_60_c070416.nc + +5 + +lnd/clm2/snicardata/snicar_optics_480bnd_c012422.nc +lnd/clm2/snicardata/snicar_optics_5bnd_c013122.nc + +hexagonal_plate +sphere +sphere + +.false. +mid_latitude_winter +sahara +.false. +.false. +.true. +.false. 2015 @@ -1449,6 +1642,9 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts 2101 2015 +2018 +2018 + 2010 2010 @@ -1556,10 +1752,16 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts nn nn + +1850 +2100 +1850 + none none +106x174 94x192 94x192 360x720 @@ -1570,12 +1772,18 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts 360x720 360x720 360x720 +106x174 +106x174 +106x174 +106x174 0001 0001 atm/datm7/NASA_LIS/clmforc.Li_2012_climo1995-2011.T62.lnfm_Total_c140423.nc atm/datm7/NASA_LIS/clmforc.Li_2012_climo1995-2011.T62_ESMFmesh_cdf5_110621.nc +atm/datm7/NASA_LIS/clmforc.Li_2016_climo1995-2013.360x720.lnfm_Total_NEONarea_c210625.nc +atm/datm7/NASA_LIS/ESMF_MESH.Li_2016.360x720.NEONarea_cdf5_c221104.nc atm/datm7/NASA_LIS/clmforc.Li_2016_climo1995-2013.360x720.lnfm_Total_c160825.nc atm/datm7/NASA_LIS/clmforc.Li_2016_climo1995-2013.360x720_ESMFmesh_cdf5_150621.nc @@ -1601,6 +1809,9 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts 2100 2015 +2018 +2018 + 2010 2010 @@ -1619,6 +1830,9 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts 2000 2000 +2018 +2018 + 2010 2010 @@ -1706,6 +1920,12 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts 2106 2015 +2018 +2018 + +2010 +2010 + 2000 2000 @@ -2548,7 +2768,9 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts .false. .false. .false. +.false. 1 +0 .true. .false. @@ -2578,4 +2800,24 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts general + + + + +.false. + +lnd/clm2/paramdata/exice_init_0.125x0.125_c20220516.nc +lnd/clm2/paramdata/exice_init_0.125x0.125_ESMFmesh_cdf5_c20220802.nc +bilinear + + + + + +off +low + +.false. +0.26d00 + diff --git a/bld/namelist_files/namelist_defaults_overall.xml b/bld/namelist_files/namelist_defaults_overall.xml index c4ccac6467..96db00478a 100644 --- a/bld/namelist_files/namelist_defaults_overall.xml +++ b/bld/namelist_files/namelist_defaults_overall.xml @@ -44,8 +44,10 @@ determine default values for namelists. -flanduse_timeseries -flanduse_timeseries +null +null +flanduse_timeseries +flanduse_timeseries diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index a08795dd1f..3e3735b903 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -150,6 +150,54 @@ specify spatially variable soil thickness. If not present, use bottom of soil column (nlevsoi). + +number of wavelength bands used in SNICAR snow albedo calculation +(snicar_numrad_snw=5 is the only supported option; others are EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) + + + +type of downward solar radiation spectrum for SNICAR snow albedo calculation +(snicar_solarspec='mid_latitude_winter' is the only supported option; others are EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) + + + +dust optics type for SNICAR snow albedo calculation +(snicar_dust_optics='sahara' is the only supported option; others are EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) + + + +snow grain shape used in SNICAR snow albedo calculation +(snicar_dust_optics='hexagonal_plate' is supported in ctsm5.1 and 'sphere' in older model versions; others are EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) + + + +Toggle to turn on/off aerosol deposition flux in snow in SNICAR +(snicar_use_aerosol='.false.' is EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) + + + +option to activate BC-snow internal mixing in SNICAR snow albedo calculation +(snicar_snobc_intmix='.true.' is EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) + + + +option to activate dust-snow internal mixing in SNICAR snow albedo calculation +(snicar_snodst_intmix='.true.' is EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) + + + +option to activate organic carbon (OC) in SNICAR snow albedo calculation +(do_sno_oc='.true.' is EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) + + Index of rooting profile for water @@ -638,6 +686,17 @@ Switch deciding which nutrient model to use in FATES. (Only relevant if FATES is on) + +Switch defining the cadence at which seeds are dispersed across +gridcells. Setting the switch value to zero turns off dispersal. +Setting the switch to 1, 2, or 3 sets the dispersal cadence to +daily, monthly or yearly. The daily cadence is primarily +recommended for test and debug only. Note that turning this +feature on will result in more memory usage. +(Only relevant if FATES is on) + + Toggle to turn on the tree damage module in FATES @@ -718,6 +777,22 @@ Full pathname to the inventory initialization control file. (Only relevant if FATES is on). + + +If TRUE, enable use of land use harmonization (LUH) state and transition data from luh_timeseries file. +(Also, only valid for use_fates = true and is incompatible with transient runs currently.) + + + + +Full pathname of unified land use harmonization (LUH) data file. This causes the land-use +types to vary over time. +(Required, if use_fates_luh=T) +(Only relevant if FATES is on). + + Toggle to turn on the LUNA model, to effect Photosynthesis by leaf Nitrogen @@ -764,9 +839,9 @@ SNICAR (SNow, ICe, and Aerosol Radiative model) optical data file name SNICAR (SNow, ICe, and Aerosol Radiative model) snow aging data file name - -If TRUE, write master field list to separate file for documentation purposes +If TRUE, write list of all output fields to separate file for documentation purposes Toggle to use 25 lake layers instead of 10 -(extralaklayers=".true." is EXPERIMENTAL NOT SUPPORTED! Nor is it Tested!) +(extralaklayers=".true." is EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) Toggle to turn on the VIC hydrologic parameterizations -(vichydro=".true." is EXPERIMENTAL NOT SUPPORTED!) +(vichydro=".true." is EXPERIMENTAL, UNSUPPORTED!) + +Fraction of post-harvest crop residues (leaf and stem) to move to +1-year product pool instead of letting them fall as litter. +Default: 0.0 + + + group="crop_inparm" valid_values="constant,varytropicsbylat" value="constant"> Type of mapping to use for base temperature for prognostic crop model constant = Just use baset from the PFT parameter file varytropicsbylat = Vary the tropics by latitude + group="crop_inparm" valid_values="" value="0.4d00"> Only used when baset_mapping == varytropicsbylat Slope with latitude in degrees to vary tropical baset by + group="crop_inparm" valid_values="" value="12.0d00"> Only used when baset_mapping == varytropicsbylat Intercept at zero latitude to add to baset from the PFT parameter file @@ -1129,6 +1211,16 @@ Phenology onset depends on the vegetation type (only used when CN is on) + +Set to .true. in order to override crop harvesting logic and to instead harvest the day before the next sowing date. Used to generate growing-degree day outputs that can be used with an external script to generate new GDD requirement ("cultivar") files. + + + +Set to .false. in order to ignore crop PFT parameter for maximum growing season length (mxmat). Must be set to .false. when generate_crop_gdds is .true. + + Method for determining what the minimum critical day length for seasonal decidious leaf offset depends on @@ -1141,9 +1233,8 @@ DependsOnLatAndVeg - Arctic vegetation depends on latitude as above, but tempera -Toggle to turn on calculation of SNow and Ice Aerosol Radiation model (SNICAR) radiative forcing -(snicar_frc=".true." is EXPERIMENTAL NOT SUPPORTED!) + group="clm_inparm" value=".false."> +Toggle to turn on calculation of SNow and Ice Aerosol Radiation model (SNICAR) albedo forcing diagnostics for each aerosol species Toggle to turn on use of LAI streams in place of the LAI on the surface dataset when using Satellite Phenology mode. -(EXPERIMENTAL and NOT tested) +(EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) + + + + + + +First year to loop over for crop calendar data + + + +Last year to loop over for crop calendar data + + + +Simulation year that aligns with stream_year_first_cropcal value + + + +By default, a value in stream_fldFileName_swindow_start or _end outside the range [1, 365] (or 366 in leap years) will cause the run to fail. Set this to .true. to instead fall back on the paramfile sowing windows. + + + +Filename of input stream data for date (day of year) of start of sowing window. Cells with the same sowing window start and end date are always planted on that date, regardless of climatic conditions/history. + + + +Filename of input stream data for date (day of year) of end of sowing window. Cells with the same sowing window start and end date are always planted on that date, regardless of climatic conditions/history. + + + +Filename of input stream data for cultivar growing degree-day targets + + + +Filename of input stream data for crop calendar inputs + + @@ -1992,9 +2128,9 @@ to use for methane model -Resolution of Lightning dataset to use for CN fire model -(only applies when CN and the CN fire model are turned on) + group="default_settings" valid_values="none,360x720,106x174,94x192"> +Resolution of Lightning dataset to use for CN or FATES fire model +(only applies when CN or FATES and the fire model is turned on) + valid_values="512x1024,360x720cru,128x256,64x128,48x96,94x192,0.23x0.31,0.47x0.63,0.9x1.25,1.9x2.5,2.5x3.33,4x5,10x15,0.125nldas2,5x5_amazon,1x1_vancouverCAN,1x1_mexicocityMEX,1x1_asphaltjungleNJ,1x1_brazil,1x1_urbanc_alpha,1x1_numaIA,1x1_smallvilleIA,0.25x0.25,0.5x0.5,3x3min,5x5min,10x10min,0.33x0.33,0.125x0.125,ne3np4.pg3,ne4np4,ne5np4.pg3,ne16np4,ne16np4.pg3,ne30np4.pg2,ne30np4.pg3,ne30np4,ne60np4,ne120np4,ne120np4.pg2,ne120np4.pg3,ne0np4CONUS.ne30x8,ne0np4.ARCTIC.ne30x4,ne0np4.ARCTICGRIS.ne30x8,ne240np4,1km-merge-10min,C24,C48,C96,C192,C384,mpasa480,mpasa240,mpasa120,mpasa60,mpasa30,mpasa15"> Horizontal resolutions Note: 0.25x0.25, 0.5x0.5, 5x5min, 10x10min, 3x3min, 1km-merge-10min and 0.33x0.33 are only used for CLM toolsI @@ -2043,7 +2179,7 @@ Land mask description + valid_values="clm4_5_CRUv7,clm4_5_GSWP3v1,clm4_5_cam6.0,clm5_0_cam6.0,clm5_0_CRUv7,clm5_0_GSWP3v1,clm5_1_GSWP3v1,clm5_1_cam6.0"> General configuration of model version and atmospheric forcing to tune the model to run under. This sets the model to run with constants and initial conditions that were set to run well under the configuration of model version and atmospheric forcing. To run well constants would need to be changed @@ -2057,7 +2193,7 @@ If 1, turn on the MEGAN model for BVOC's (Biogenic Volitile Organic Compounds) +"PtVg,1000,850,1100,1350,1600,1850,1855,1865,1875,1885,1895,1905,1915,1925,1935,1945,1955,1965,1975,1979,1980,1982,1985,1995,2000,2005,2010,2013,2015,2018,2025,2035,2045,2055,2065,2075,2085,2095,2105"> Year to simulate and to provide datasets for (such as surface datasets, initial conditions, aerosol-deposition, Nitrogen deposition rates etc.) A sim_year of 1000 corresponds to data used for testing only, NOT corresponding to any real datasets. A sim_year greater than 2015 corresponds to ssp_rcp scenario data @@ -2179,7 +2315,7 @@ Profile over which to distribute C and N coming from surface pools (leaves, stem If true, no denitrification or nitrification in frozen soil layers. -(EXPERIMENTAL and NOT tested) +(EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) If TRUE, weight btran (vegetation soil moisture availability) by unfrozen layers only, assuming that vegetation will allocate roots preferentially to the active layer. -(EXPERIMENTAL and NOT tested) +(EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) If TRUE, weight btran (vegetation soil moisture availability) by the active layer, as defined by the greatest thaw depth over the current and prior years. -(EXPERIMENTAL and NOT tested) +(EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) @@ -2300,7 +2436,7 @@ How much Carbon to initialize vegetation pools (leafc/frootc and storage) to whe Flexible CN ratio used for Phenology -(EXPERIMENTAL and NOT tested) +(EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) Vcmax calculation for Photosynthesis - vcmax_opt = 4 As for vcmax_opt=0, but using leafN, and exponential if tree (EXPERIMENTAL NOT TESTED!) + vcmax_opt = 4 As for vcmax_opt=0, but using leafN, and exponential if tree (EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) vcmax_opt = 3 Based on leafN and VCAD (used with Luna for crop and C4 vegetation) vcmax_opt = 0 Based on canopy top and foilage Nitrogen limitation factor from params file (clm4.5) @@ -2320,13 +2456,13 @@ Vcmax calculation for Photosynthesis Evergreen phenology option for CNPhenology -(EXPERIMENTAL and NOT tested) +(EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) Carbon respiration option to burn off carbon when CN ratio is too high (do NOT use when FUN is on) -(EXPERIMENTAL and NOT tested) +(EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) @@ -2384,7 +2520,7 @@ then don't fix aere (see ch4Mod.F90). If TRUE, turn on methane biogeochemistry model for lake columns, using a simplified version of the CH4 submodel. -(EXPERIMENTAL) +(EXPERIMENTAL, UNSUPPORTED!) If TRUE, use the fine root carbon predicted by CN when calculating the aerenchyma area, rather than the parametrization based on annual NPP, aboveground NPP fraction, and LAI. -(EXPERIMENTAL and NOT tested) +(EXPERIMENTAL, UNSUPPORTED, and UNTESTED!) @@ -2475,6 +2611,13 @@ If TRUE, apply harvest from flanduse_timeseries file. (Also, only valid for use_cn = true.) + +If TRUE, apply gross unrepresented landuse/land-cover change from flanduse_timeseries file. +(Only valid for transient runs, where there is a flanduse_timeseries file.) +(Also, only valid for use_cn = true.) + + If TRUE, reset baseline values of total column water and energy in the @@ -2663,11 +2806,6 @@ Snow compaction overburden exponential factor (1/K) Not used for snow_overburden_compaction_method=Vionnet2012 - -maximum warm (at freezing) fresh snow effective radius [microns] - - If set to .true., then reset the snow pack over non-glacier columns to a small value. @@ -2725,6 +2863,11 @@ NiuYang2007: Niu and Yang 2007 SwensonLawrence2012: Swenson and Lawrence 2012 + +Parameterization to use for snow thermal conductivity + + @@ -2742,6 +2885,24 @@ the related bulk quantities. If .true., run with water isotopes + + + + + +Parameterization/parameters to use for surface roughness +ZengWang2007: Zeng and Wang 2007 +Meier2022: Meier et al. in prep. 2022 + + + +If FALSE use constant snow z0m +If TRUE use parameterization of snow z0m as a function of accumulated +snow melt of Brock et al. (2006) + + @@ -2796,4 +2957,56 @@ use case.) + + + + +If TRUE turn on the excess ice physics, (Lee et al., 2014; Cai et al., 2020) + + + +If TRUE and use_excess_ice is TRUE, use the excess ice stream to determine the initial values of the excess ice field +if FALSE and use_excess_ice is TRUE, expect excess ice to come from the initial conditions or restart file +Expect to be FALSE is use_excess_ice is FALSE + + + +Filename of input stream data for excess ice data + + + +mesh filename of input stream data for excess ice + + + +Mapping method from excess ice input stream data to the model resolution + bilinear = bilinear interpolation + nn = nearest neighbor + none = no interpolation + + + + + + + +Whether to till crop soil, and if so, with what intensity. + + + +Toggle to use original (Graham et al. 2021) tillage logic, with bug for seasons crossing into a new calendar year + + + +Maximum depth to till soil (m). Default 0.26; original (Graham et al., 2021) value was unintentionally 0.32. + + diff --git a/bld/namelist_files/use_cases/2018-PD_transient.xml b/bld/namelist_files/use_cases/2018-PD_transient.xml new file mode 100644 index 0000000000..96f14207ad --- /dev/null +++ b/bld/namelist_files/use_cases/2018-PD_transient.xml @@ -0,0 +1,33 @@ + + + + + + +Simulate transient Nitrogen-deposition, aerosol deposition, urban, and fire related (pop-density, lightning) changes from 2018 to current day with a mix of historical data, and future scenario data +Simulate transient Nitrogen-deposition, aerosol deposition, urban, and fire related (pop-density, lightning) changes from 2018 to current day with a mix of historical data, and future scenario data +Simulate transient urban and aerosol deposition changes from 2018 to current day with a mix of historical data, and future scenario data + + + +2018 + +1850-2100 + + +SSP3-7.0 + +2018 +2022 +2018 + +2018 +2022 +2018 + +2018 +2022 +2018 + + diff --git a/bld/namelist_files/use_cases/2018_control.xml b/bld/namelist_files/use_cases/2018_control.xml new file mode 100644 index 0000000000..28554074c4 --- /dev/null +++ b/bld/namelist_files/use_cases/2018_control.xml @@ -0,0 +1,17 @@ + + + + + + + +Conditions to simulate 2018 land-use + +2018 + +constant + + +SSP3-7.0 + + diff --git a/bld/namelist_files/use_cases/README b/bld/namelist_files/use_cases/README index 4ccaf00bdc..e55fd9285b 100644 --- a/bld/namelist_files/use_cases/README +++ b/bld/namelist_files/use_cases/README @@ -17,6 +17,10 @@ Transient cases: 20thC$desc_transient (means nominal 1850-2000 although some datasets are 1850-2005) + or + + yyyy-PD_$desc_transient (means nominal year yyyy through present day (PD) (with the year for PD advancing) + Control cases: yyyy$desc_control @@ -30,6 +34,7 @@ Where yyyy = Simulation year (such as 1850 or 2000). yyyy-yyyy = Range of simulation years to run over (i.e.. 1850-2000). +yyyy-PD = Range of simulation years to run over until present day (i.e.. 2018-2024). $ssp_rcp = Shared Socieconomic Pathway (SSP) Representative concentration pathway (RCP) description string for future scenarios: SSP#-#.# (for example: SSP5-8.5, SSP1-2.6, SSP4-6.0 diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index f6c8d75937..9b579dd9ce 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -42,7 +42,7 @@ sub make_env_run { my %settings = @_; # Set default settings - my %env_vars = ( DIN_LOC_ROOT=>"MYDINLOCROOT", GLC_TWO_WAY_COUPLING=>"FALSE" ); + my %env_vars = ( DIN_LOC_ROOT=>"MYDINLOCROOT", GLC_TWO_WAY_COUPLING=>"FALSE", NEONSITE=>"" ); # Set any settings that came in from function call foreach my $item ( keys(%settings) ) { $env_vars{$item} = $settings{$item}; @@ -139,7 +139,7 @@ sub cat_and_create_namelistinfile { $inputdata_rootdir = $ENV{'CSMDATA'}; } else { # use yellowstone location as default - $inputdata_rootdir="/glade/p/cesm/cseg/inputdata"; + $inputdata_rootdir="/glade/campaign/cesm/cesmdata/cseg/inputdata"; print("WARNING: -csmdata nor CSMDATA are set, using default yellowstone location: $inputdata_rootdir\n"); } @@ -163,9 +163,10 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 1846; +my $ntests = 1999; + if ( defined($opts{'compare'}) ) { - $ntests += 1254; + $ntests += 1353; } plan( tests=>$ntests ); @@ -319,24 +320,30 @@ sub cat_and_create_namelistinfile { foreach my $driver ( "mct", "nuopc" ) { print " For $driver driver\n\n"; # configuration, structure, irrigate, verbose, clm_demand, ssp_rcp, test, sim_year, use_case - foreach my $options ( "-configuration nwp", - "-structure fast", - "-namelist '&a irrigate=.true./'", "-verbose", "-ssp_rcp SSP1-2.6", "-test", "-sim_year 1850", - "-namelist '&a use_lai_streams=.true.,use_soil_moisture_streams=.true./'", - "-use_case 1850_control", + foreach my $options ( "-res 0.9x1.25 -configuration nwp", + "-res 0.9x1.25 -structure fast", + "-res 0.9x1.25 -namelist '&a irrigate=.true./'", "-res 0.9x1.25 -verbose", "-res 0.9x1.25 -ssp_rcp SSP1-2.6", "-res 0.9x1.25 -test", "-res 0.9x1.25 -sim_year 1850", + "-res 0.9x1.25 -namelist '&a use_lai_streams=.true.,use_soil_moisture_streams=.true./'", + "-res 0.9x1.25 -namelist '&a use_excess_ice=.true. use_excess_ice_streams=.true./'", + "-res 0.9x1.25 -namelist '&a use_excess_ice=.true. use_excess_ice_streams=.false./'", + "-res 0.9x1.25 -use_case 1850_control", "-res 1x1pt_US-UMB -clm_usr_name 1x1pt_US-UMB -namelist '&a fsurdat=\"/dev/null\"/'", "-res 1x1_brazil", - "-clm_start_type startup", "-namelist '&a irrigate=.false./' -crop -bgc bgc", - "-envxml_dir . -infile myuser_nl_clm", - "-ignore_ic_date -clm_start_type branch -namelist '&a nrevsn=\"thing.nc\"/' -bgc bgc -crop", - "-clm_start_type branch -namelist '&a nrevsn=\"thing.nc\",use_init_interp=T/'", - "-ignore_ic_date -clm_start_type startup -namelist '&a finidat=\"thing.nc\"/' -bgc bgc -crop", + "-res 0.9x1.25 -clm_start_type startup", "-namelist '&a irrigate=.false./' -crop -bgc bgc", + "-res 0.9x1.25 -infile myuser_nl_clm", + "-res 0.9x1.25 -ignore_ic_date -clm_start_type branch -namelist '&a nrevsn=\"thing.nc\"/' -bgc bgc -crop", + "-res 0.9x1.25 -clm_start_type branch -namelist '&a nrevsn=\"thing.nc\",use_init_interp=T/'", + "-res 0.9x1.25 -ignore_ic_date -clm_start_type startup -namelist '&a finidat=\"thing.nc\"/' -bgc bgc -crop", ) { my $file = $startfile; &make_env_run(); - my $base_options = "-res 0.9x1.25 -envxml_dir . -driver $driver"; + my $base_options = "-envxml_dir . -driver $driver"; if ( $driver eq "mct" ) { $base_options = "$base_options -lnd_frac $DOMFILE"; + # Skip the MCT test for excess ice streams + if ( $options =~ /use_excess_ice_streams=.true./ ) { + next; + } } else { $base_options = "$base_options -namelist '&a force_send_to_atm = .false./'"; } @@ -374,7 +381,7 @@ sub cat_and_create_namelistinfile { "JORN", "LAJA", "MOAB", "OAES", "OSBS", "SCBI", "SOAP", "STER", "TOOL", "UNDE", "YELL" ) { - &make_env_run(); + &make_env_run( NEONSITE=>"$site" ); # # Concatonate default usermods and specific sitetogether expanding env variables while doing that # @@ -393,7 +400,7 @@ sub cat_and_create_namelistinfile { # # Now run the site # - my $options = "-res CLM_USRDAT -clm_usr_name NEON -no-megan -bgc bgc -sim_year 2000 -infile $namelistfile"; + my $options = "--res CLM_USRDAT --clm_usr_name NEON --no-megan --bgc bgc --use_case 2018_control --infile $namelistfile"; eval{ system( "$bldnml -envxml_dir . $options > $tempfile 2>&1 " ); }; is( $@, '', "options: $options" ); $cfiles->checkfilesexist( "$options", $mode ); @@ -466,6 +473,9 @@ sub cat_and_create_namelistinfile { "-bgc bgc -use_case 2000_control -namelist \"&a fire_method='nofire'/\" -crop", "-res 0.9x1.25 -bgc sp -use_case 1850_noanthro_control -drydep -fire_emis", "-res 0.9x1.25 -bgc bgc -use_case 1850_noanthro_control -drydep -fire_emis -light_res 360x720", + "--bgc bgc --light_res none --namelist \"&a fire_method='nofire'/\"", + "--bgc fates --light_res 360x720 --no-megan --namelist \"&a fates_spitfire_mode=2/\"", + "--bgc fates --light_res none --no-megan --namelist \"&a fates_spitfire_mode=1/\"", ) { my $file = $startfile; &make_env_run(); @@ -521,8 +531,33 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, - "soilm_stream wo use" =>{ options=>"-res 0.9x1.25 -envxml_dir .", - namelst=>"use_soil_moisture_streams = .false.,stream_fldfilename_soilm='missing_file'", + "soilm_stream off w file" =>{ options=>"-res 0.9x1.25 -envxml_dir .", + namelst=>"use_soil_moisture_streams = .false.,stream_fldfilename_soilm='file_provided_when_off'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "exice_stream off w file" =>{ options=>"-res 0.9x1.25 -envxml_dir .", + namelst=>"use_excess_ice=.true., use_excess_ice_streams = .false.,stream_fldfilename_exice='file_provided_when_off'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "exice_stream off w mesh" =>{ options=>"-res 0.9x1.25 -envxml_dir .", + namelst=>"use_excess_ice=.true., use_excess_ice_streams = .false.,stream_meshfile_exice='file_provided_when_off'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "exice off, but stream on" =>{ options=>"-res 0.9x1.25 -envxml_dir .", + namelst=>"use_excess_ice=.false., use_excess_ice_streams = .true.,stream_fldfilename_exice='file_provided', stream_meshfile_exice='file_provided'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "exice stream off, but setmap"=>{ options=>"-res 0.9x1.25 -envxml_dir .", + namelst=>"use_excess_ice=.true., use_excess_ice_streams = .false.,stream_mapalgo_exice='bilinear'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "exice stream on, but mct" =>{ options=>"--res 0.9x1.25 --envxml_dir . --driver mct --lnd_frac $DOMFILE ", + namelst=>"use_excess_ice=.true., use_excess_ice_streams=.true.", GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, @@ -686,6 +721,11 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, + "NEONlightresButGlobal" =>{ options=>"--res 4x5 --bgc bgc --envxml_dir . --light_res 106x740", + namelst=>"", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, "spno-fire" =>{ options=>"-bgc sp -envxml_dir . -use_case 2000_control", namelst=>"fire_method='nofire'", GLC_TWO_WAY_COUPLING=>"FALSE", @@ -971,6 +1011,26 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_1", }, + "FATESwspitfireOffLigtOn" =>{ options=>"-bgc fates -envxml_dir . -no-megan -light_res 360x720", + namelst=>"fates_spitfire_mode=0", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, + "useFATESWluna" =>{ options=>"--bgc fates --envxml_dir . --no-megan", + namelst=>"use_luna=TRUE", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, + "useFATESWfun" =>{ options=>"--bgc fates --envxml_dir . --no-megan", + namelst=>"use_fun=TRUE", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, + "useFATESWOsuplnitro" =>{ options=>"--bgc fates --envxml_dir . --no-megan", + namelst=>"suplnitro='NONE'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, "FireNoneButBGCfireon" =>{ options=>"-bgc bgc -envxml_dir . -light_res none", namelst=>"fire_method='li2021gswpfrc'", GLC_TWO_WAY_COUPLING=>"FALSE", @@ -1001,11 +1061,26 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, + "useinventorybutnotfile" =>{ options=>"--res 0.9x1.25 --bgc fates --envxml_dir . --no-megan", + namelst=>"use_fates_luh=.true.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm4_5", + }, + "inventoryfileDNE" =>{ options=>"-bgc fates -envxml_dir . -no-megan", + namelst=>"use_fates_luh=.true., fluh_timeseries='zztop'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm4_5", + }, "useMEGANwithFATES" =>{ options=>"-bgc fates -envxml_dir . -megan", namelst=>"", GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, + "useFIREEMISwithFATES" =>{ options=>"-bgc fates -envxml_dir . -fire_emis --no-megan", + namelst=>"", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm4_5", + }, "useDRYDEPwithFATES" =>{ options=>"--bgc fates --envxml_dir . --no-megan --drydep", namelst=>"", GLC_TWO_WAY_COUPLING=>"FALSE", @@ -1056,6 +1131,21 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, + "dogrossandsp" =>{ options=>"--envxml_dir . --bgc sp --use_case 20thC_transient", + namelst=>"do_grossunrep=.true.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "dogrossandfates" =>{ options=>"--envxml_dir . --bgc fates --use_case 20thC_transient --no-megan", + namelst=>"do_grossunrep=.true.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "dogrossandnottrans" =>{ options=>"--envxml_dir . --bgc bgc --use_case 2000_control", + namelst=>"do_grossunrep=.true.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, "nocropwfert" =>{ options=>"-envxml_dir . -bgc sp -no-crop", namelst=>"use_fertilizer=T", GLC_TWO_WAY_COUPLING=>"FALSE", @@ -1146,6 +1236,26 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, + "fates_non_sp_laistreams" =>{ options=>"--envxml_dir . --bgc fates", + namelst=>"use_lai_streams=.true., use_fates_sp=.false.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "bgc_non_sp_laistreams" =>{ options=>"--envxml_dir . -bgc bgc", + namelst=>"use_lai_streams=.true.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "bgc_laistreams_input" =>{ options=>"--envxml_dir . --bgc bgc", + namelst=>"stream_year_first_lai=1999", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, + "crop_laistreams_input" =>{ options=>"--envxml_dir . --bgc sp --crop", + namelst=>"use_lai_streams=.true.", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_0", + }, ); foreach my $key ( keys(%failtest) ) { print( "$key\n" ); @@ -1207,6 +1317,11 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_1", }, + "NotNEONbutNEONlightres" =>{ options=>"--res CLM_USRDAT --clm_usr_name regional --envxml_dir . --bgc bgc --light_res 106x174", + namelst=>"fsurdat='build-namelist_test.pl'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, ); foreach my $key ( keys(%warntest) ) { print( "$key\n" ); @@ -1439,7 +1554,7 @@ sub cat_and_create_namelistinfile { my $usecase = "20thC_transient"; my $GLC_NEC = 10; foreach my $res ( @tran_res ) { - $options = "-res $res -use_case $usecase -envxml_dir . -namelist '&a start_ymd=18500101/'"; + $options = "-res $res -use_case $usecase -envxml_dir . -namelist '&a start_ymd=18500101/' -bgc bgc -crop -namelist '&a do_grossunrep=T/'"; &make_env_run(); eval{ system( "$bldnml $options > $tempfile 2>&1 " ); }; is( $@, '', "$options" ); diff --git a/bld/unit_testers/xFail/expectedClmTestFails.xml b/bld/unit_testers/xFail/expectedClmTestFails.xml index 12c954d38b..c7cbfee488 100644 --- a/bld/unit_testers/xFail/expectedClmTestFails.xml +++ b/bld/unit_testers/xFail/expectedClmTestFails.xml @@ -34,19 +34,6 @@ - - - - Doesn't check for valid values - - - - - - - - diff --git a/cime_config/SystemTests/__init__.py b/cime_config/SystemTests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cime_config/SystemTests/fsurdatmodifyctsm.py b/cime_config/SystemTests/fsurdatmodifyctsm.py index 899ab1aead..03e437d5c4 100644 --- a/cime_config/SystemTests/fsurdatmodifyctsm.py +++ b/cime_config/SystemTests/fsurdatmodifyctsm.py @@ -5,114 +5,90 @@ import os import re -import subprocess from CIME.SystemTests.system_tests_common import SystemTestsCommon from CIME.XML.standard_module_setup import * from CIME.SystemTests.test_utils.user_nl_utils import append_to_user_nl_files +# For calling fsurdat_modifier +from argparse import Namespace + +_CTSM_PYTHON = os.path.join( + os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, "python" +) +sys.path.insert(1, _CTSM_PYTHON) + logger = logging.getLogger(__name__) -class FSURDATMODIFYCTSM(SystemTestsCommon): +class FSURDATMODIFYCTSM(SystemTestsCommon): def __init__(self, case): """ initialize an object interface to the SMS system test """ SystemTestsCommon.__init__(self, case) - if not os.path.exists(os.path.join(self._get_caseroot(), - 'done_FSURDATMODIFYCTSM_setup.txt')): + if not os.path.exists( + os.path.join(self._get_caseroot(), "done_FSURDATMODIFYCTSM_setup.txt") + ): # Create out-of-the-box lnd_in to obtain fsurdat_in - case.create_namelists(component='lnd') + case.create_namelists(component="lnd") # If fsurdat_in does not exist, download it from the server case.check_all_input_data() - lnd_in_path = os.path.join(self._get_caseroot(), 'CaseDocs/lnd_in') - with open (lnd_in_path,'r') as lnd_in: + lnd_in_path = os.path.join(self._get_caseroot(), "CaseDocs/lnd_in") + with open(lnd_in_path, "r") as lnd_in: for line in lnd_in: fsurdat_in = re.match(r" *fsurdat *= *'(.*)'", line) if fsurdat_in: self._fsurdat_in = fsurdat_in.group(1) break - self._fsurdat_out = os.path.join(self._get_caseroot(), 'fsurdat.nc') - self._ctsm_root = self._case.get_value( 'COMP_ROOT_DIR_LND') - self._cfg_file_path = os.path.join(self._get_caseroot(), - 'modify_fsurdat.cfg') + self._fsurdat_out = os.path.join(self._get_caseroot(), "fsurdat.nc") + self._ctsm_root = self._case.get_value("COMP_ROOT_DIR_LND") + self._cfg_file_path = os.path.join(self._get_caseroot(), "modify_fsurdat.cfg") + logger.info(" create config file to modify") self._create_config_file() + logger.info(" run modify_fsurdat") self._run_modify_fsurdat() + logger.info(" modify user_nl files") self._modify_user_nl() - with open('done_FSURDATMODIFYCTSM_setup.txt', 'w') as fp: + with open("done_FSURDATMODIFYCTSM_setup.txt", "w") as fp: pass def _create_config_file(self): - cfg_template_path = os.path.join(self._ctsm_root, - 'tools/modify_input_files/modify_fsurdat_template.cfg') + cfg_template_path = os.path.join( + self._ctsm_root, "tools/modify_input_files/modify_fsurdat_template.cfg" + ) - with open (self._cfg_file_path,'w') as cfg_out: - with open (cfg_template_path,'r') as cfg_in: + with open(self._cfg_file_path, "w") as cfg_out: + with open(cfg_template_path, "r") as cfg_in: for line in cfg_in: - if re.match(r' *fsurdat_in *=', line): - line = 'fsurdat_in = {}'.format(self._fsurdat_in) - elif re.match(r' *fsurdat_out *=', line): - line = 'fsurdat_out = {}'.format(self._fsurdat_out) - elif re.match(r' *idealized *=', line): - line = 'idealized = True' + if re.match(r" *fsurdat_in *=", line): + line = "fsurdat_in = {}".format(self._fsurdat_in) + elif re.match(r" *fsurdat_out *=", line): + line = "fsurdat_out = {}".format(self._fsurdat_out) + elif re.match(r" *idealized *=", line): + line = "idealized = True" cfg_out.write(line) - def _run_modify_fsurdat(self): - tool_path = os.path.join(self._ctsm_root, - 'tools/modify_input_files/fsurdat_modifier') - # Need to specify a specific python version that has the required - # dependencies - python_path = _get_python_path() - subprocess.check_call([python_path, tool_path, self._cfg_file_path]) + fsurdat_modifier_args = Namespace( + cfg_path=self._cfg_file_path, + debug=False, + fsurdat_in="UNSET", + fsurdat_out="UNSET", + overwrite=False, + silent=False, + verbose=False, + ) + from ctsm.modify_input_files.fsurdat_modifier import fsurdat_modifier + + fsurdat_modifier(fsurdat_modifier_args) def _modify_user_nl(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "fsurdat = '{}'".format(self._fsurdat_out)) - -def _get_python_path(): - """Get path to ncar_pylib's python on cheyenne - - This is needed because we need a python environment that includes xarray - and its dependencies. This is currently hard-coded for cheyenne until we - come up with a robust way in CIME of ensuring that the correc python - environment is loaded. - - """ - out = subprocess.check_output(['/glade/u/apps/opt/ncar_pylib/ncar_pylib', - '-l'], universal_newlines=True) - - # First look for a loaded ('L') python - path = _find_path_from_pylib_output(out, 'L') - # If no loaded python found, look for a default ('D') python - if path is None: - path = _find_path_from_pylib_output(out, 'D') - - if path is None: - raise RuntimeError('No python found') - - return os.path.join(path, 'bin', 'python') - -def _find_path_from_pylib_output(ncar_pylib_output, char): - """Given line-by-line output from ncar_pylib, return the path to python if found - - Args: - - ncar_pylib_output: line-by-line output from ncar_pylib - - char: the character to look for in the leading parenthetical expression (typically 'L' or 'D') - - Returns a path to python, or None if not found - """ - # The line of interest looks like the following (for char = 'L'): - # (L) ... /path/to/python - regex = r'\(' + char + r'\).* (/\S+)' - for line in ncar_pylib_output.splitlines(): - match_line = re.match(regex, line) - if match_line: - return match_line.group(1) - - return None + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="fsurdat = '{}'".format(self._fsurdat_out), + ) diff --git a/cime_config/SystemTests/funitctsm.py b/cime_config/SystemTests/funitctsm.py index 8634a74c2f..783e5b5d85 100644 --- a/cime_config/SystemTests/funitctsm.py +++ b/cime_config/SystemTests/funitctsm.py @@ -14,8 +14,8 @@ logger = logging.getLogger(__name__) -class FUNITCTSM(FUNIT): +class FUNITCTSM(FUNIT): def __init__(self, case): FUNIT.__init__(self, case) diff --git a/cime_config/SystemTests/lciso.py b/cime_config/SystemTests/lciso.py index 12a74d5d5c..a9edba8e80 100644 --- a/cime_config/SystemTests/lciso.py +++ b/cime_config/SystemTests/lciso.py @@ -14,24 +14,28 @@ logger = logging.getLogger(__name__) -class LCISO(SystemTestsCompareTwo): +class LCISO(SystemTestsCompareTwo): def __init__(self, case): self.comp = case.get_value("COMP_LND") - SystemTestsCompareTwo.__init__(self, case, - separate_builds = False, - run_two_suffix = 'cisoallon', - run_one_description = 'carbon isotopes off', - run_two_description = 'c13 and c14 isotopes on as well as C isotope time series', - ignore_fieldlist_diffs = True) + SystemTestsCompareTwo.__init__( + self, + case, + separate_builds=False, + run_two_suffix="cisoallon", + run_one_description="carbon isotopes off", + run_two_description="c13 and c14 isotopes on as well as C isotope time series", + ignore_fieldlist_diffs=True, + ) def _case_one_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = self.comp, - contents = "use_c13=F, use_c14=F") + append_to_user_nl_files( + caseroot=self._get_caseroot(), component=self.comp, contents="use_c13=F, use_c14=F" + ) def _case_two_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = self.comp, - contents = "use_c13=.true.,use_c14=.true.,use_c13_timeseries=.true.,use_c14_bombspike=.true." ) - + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component=self.comp, + contents="use_c13=.true.,use_c14=.true.,use_c13_timeseries=.true.,use_c14_bombspike=.true.", + ) diff --git a/cime_config/SystemTests/lgrain2.py b/cime_config/SystemTests/lgrain2.py index 129c3275e3..f4e19ad930 100644 --- a/cime_config/SystemTests/lgrain2.py +++ b/cime_config/SystemTests/lgrain2.py @@ -19,22 +19,29 @@ logger = logging.getLogger(__name__) -class LGRAIN2(SystemTestsCompareTwo): +class LGRAIN2(SystemTestsCompareTwo): def __init__(self, case): - SystemTestsCompareTwo.__init__(self, case, - separate_builds = False, - run_two_suffix = 'grain1', - run_one_description = 'use a second grain pool', - run_two_description = 'use a single grain pool', - ignore_fieldlist_diffs = True) + SystemTestsCompareTwo.__init__( + self, + case, + separate_builds=False, + run_two_suffix="grain1", + run_one_description="use a second grain pool", + run_two_description="use a single grain pool", + ignore_fieldlist_diffs=True, + ) def _case_one_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "for_testing_use_second_grain_pool=.true.") + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="for_testing_use_second_grain_pool=.true.", + ) def _case_two_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "for_testing_use_second_grain_pool=.false.") + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="for_testing_use_second_grain_pool=.false.", + ) diff --git a/cime_config/SystemTests/lii.py b/cime_config/SystemTests/lii.py index d89bd0aa09..de274256a3 100644 --- a/cime_config/SystemTests/lii.py +++ b/cime_config/SystemTests/lii.py @@ -35,22 +35,24 @@ logger = logging.getLogger(__name__) -class LII(SystemTestsCompareTwo): +class LII(SystemTestsCompareTwo): def __init__(self, case): - SystemTestsCompareTwo.__init__(self, case, - separate_builds = False, - run_two_suffix = 'no_interp', - run_one_description = 'use_init_interp set to true', - run_two_description = 'use_init_interp set to false') + SystemTestsCompareTwo.__init__( + self, + case, + separate_builds=False, + run_two_suffix="no_interp", + run_one_description="use_init_interp set to true", + run_two_description="use_init_interp set to false", + ) def _case_one_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "use_init_interp = .true.") + append_to_user_nl_files( + caseroot=self._get_caseroot(), component="clm", contents="use_init_interp = .true." + ) def _case_two_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "use_init_interp = .false.") - + append_to_user_nl_files( + caseroot=self._get_caseroot(), component="clm", contents="use_init_interp = .false." + ) diff --git a/cime_config/SystemTests/lii2finidatareas.py b/cime_config/SystemTests/lii2finidatareas.py index f7a0a2f10e..337e46bf39 100644 --- a/cime_config/SystemTests/lii2finidatareas.py +++ b/cime_config/SystemTests/lii2finidatareas.py @@ -58,13 +58,15 @@ logger = logging.getLogger(__name__) -class LII2FINIDATAREAS(LII): +class LII2FINIDATAREAS(LII): def __init__(self, case): super(LII2FINIDATAREAS, self).__init__(case) def _case_one_setup(self): super(LII2FINIDATAREAS, self)._case_one_setup() - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "init_interp_method = 'use_finidat_areas'") + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="init_interp_method = 'use_finidat_areas'", + ) diff --git a/cime_config/SystemTests/lilacsmoke.py b/cime_config/SystemTests/lilacsmoke.py index eec54579a5..66a94068da 100644 --- a/cime_config/SystemTests/lilacsmoke.py +++ b/cime_config/SystemTests/lilacsmoke.py @@ -27,57 +27,61 @@ from CIME.SystemTests.system_tests_common import SystemTestsCommon from CIME.utils import run_cmd, run_cmd_no_fail, symlink_force, new_lid, safe_copy, append_testlog from CIME.build import post_build -from CIME.test_status import NAMELIST_PHASE, GENERATE_PHASE, BASELINE_PHASE, TEST_PASS_STATUS, TEST_FAIL_STATUS +from CIME.test_status import ( + NAMELIST_PHASE, + GENERATE_PHASE, + BASELINE_PHASE, + TEST_PASS_STATUS, + TEST_FAIL_STATUS, +) from CIME.XML.standard_module_setup import * logger = logging.getLogger(__name__) -_LILAC_RUNTIME_FILES = ['lnd_in', 'lnd_modelio.nml', 'lilac_in'] +_LILAC_RUNTIME_FILES = ["lnd_in", "lnd_modelio.nml", "lilac_in"] -class LILACSMOKE(SystemTestsCommon): +class LILACSMOKE(SystemTestsCommon): def __init__(self, case): SystemTestsCommon.__init__(self, case) def build_phase(self, sharedlib_only=False, model_only=False): if not sharedlib_only: - lndroot = self._case.get_value('COMP_ROOT_DIR_LND') - exeroot = self._case.get_value('EXEROOT') + lndroot = self._case.get_value("COMP_ROOT_DIR_LND") + exeroot = self._case.get_value("EXEROOT") build_dir = self._lilac_build_dir() - script_path = os.path.abspath(os.path.join(lndroot, 'lilac', 'build_ctsm')) + script_path = os.path.abspath(os.path.join(lndroot, "lilac", "build_ctsm")) # We only run the initial build command if the build_dir doesn't exist # yet. This is to support rebuilding the test case. (The first time through, # the build_dir won't exist yet; subsequent times, it will already exist, so # we skip to the rebuild command.) if not os.path.isdir(build_dir): - machine = self._case.get_value('MACH') - compiler = self._case.get_value('COMPILER') - debug = self._case.get_value('DEBUG') + machine = self._case.get_value("MACH") + compiler = self._case.get_value("COMPILER") + debug = self._case.get_value("DEBUG") # It would be possible to do this testing via the python interface rather # than through a separate subprocess. However, we do it through a # subprocess in order to test the full build_ctsm script, including # command-line parsing. - cmd = '{script_path} {build_dir} --machine {machine} --compiler {compiler}'.format( - script_path=script_path, - build_dir=build_dir, - machine=machine, - compiler=compiler) + cmd = "{script_path} {build_dir} --machine {machine} --compiler {compiler}".format( + script_path=script_path, build_dir=build_dir, machine=machine, compiler=compiler + ) # It isn't straightforward to determine if pnetcdf is available on a # machine. To keep things simple, always run without pnetcdf. - cmd += ' --no-pnetcdf' + cmd += " --no-pnetcdf" if debug: - cmd += ' --build-debug' - self._run_build_cmd(cmd, exeroot, 'build_ctsm.bldlog') + cmd += " --build-debug" + self._run_build_cmd(cmd, exeroot, "build_ctsm.bldlog") # We call the build script with --rebuild even for an initial build. This is # so we make sure to test the code path for --rebuild. (This is also needed if # the user rebuilds the test case, in which case this will be the only command # run, since the build_dir will already exist.) - cmd = '{script_path} {build_dir} --rebuild'.format( - script_path=script_path, - build_dir=build_dir) - self._run_build_cmd(cmd, exeroot, 'rebuild_ctsm.bldlog') + cmd = "{script_path} {build_dir} --rebuild".format( + script_path=script_path, build_dir=build_dir + ) + self._run_build_cmd(cmd, exeroot, "rebuild_ctsm.bldlog") self._build_atm_driver() @@ -95,51 +99,62 @@ def build_phase(self, sharedlib_only=False, model_only=False): post_build(self._case, logs=[], build_complete=True) def _build_atm_driver(self): - caseroot = self._case.get_value('CASEROOT') - lndroot = self._case.get_value('COMP_ROOT_DIR_LND') - blddir = os.path.join(caseroot, 'lilac_atm_driver', 'bld') + caseroot = self._case.get_value("CASEROOT") + lndroot = self._case.get_value("COMP_ROOT_DIR_LND") + blddir = os.path.join(caseroot, "lilac_atm_driver", "bld") if not os.path.exists(blddir): os.makedirs(blddir) - symlink_force(os.path.join(lndroot, 'lilac', 'atm_driver', 'Makefile'), - os.path.join(blddir, 'Makefile')) - symlink_force(os.path.join(lndroot, 'lilac', 'atm_driver', 'atm_driver.F90'), - os.path.join(blddir, 'atm_driver.F90')) - symlink_force(os.path.join(self._lilac_build_dir(), 'case', 'Macros.make'), - os.path.join(blddir, 'Macros.make')) - - makevars = 'COMPILER={compiler} DEBUG={debug} CTSM_MKFILE={ctsm_mkfile}'.format( - compiler=self._case.get_value('COMPILER'), - debug=str(self._case.get_value('DEBUG')).upper(), - ctsm_mkfile=os.path.join(self._lilac_build_dir(), 'ctsm.mk')) - makecmd = 'make {makevars} atm_driver'.format(makevars=makevars) + symlink_force( + os.path.join(lndroot, "lilac", "atm_driver", "Makefile"), + os.path.join(blddir, "Makefile"), + ) + symlink_force( + os.path.join(lndroot, "lilac", "atm_driver", "atm_driver.F90"), + os.path.join(blddir, "atm_driver.F90"), + ) + symlink_force( + os.path.join(self._lilac_build_dir(), "case", "Macros.make"), + os.path.join(blddir, "Macros.make"), + ) + + makevars = "COMPILER={compiler} DEBUG={debug} CTSM_MKFILE={ctsm_mkfile}".format( + compiler=self._case.get_value("COMPILER"), + debug=str(self._case.get_value("DEBUG")).upper(), + ctsm_mkfile=os.path.join(self._lilac_build_dir(), "ctsm.mk"), + ) + makecmd = "make {makevars} atm_driver".format(makevars=makevars) # Normally the user will source either ctsm_build_environment.sh or # ctsm_build_environment.csh before building the atmosphere model. In the context # of this test case, case.load_env does the equivalent. self._case.load_env() - self._run_build_cmd(makecmd, blddir, 'atm_driver.bldlog') + self._run_build_cmd(makecmd, blddir, "atm_driver.bldlog") def _create_link_to_atm_driver(self): - caseroot = self._case.get_value('CASEROOT') - run_exe = (self._case.get_value('run_exe')).strip() + caseroot = self._case.get_value("CASEROOT") + run_exe = (self._case.get_value("run_exe")).strip() # Make a symlink to the atm_driver executable so that the case's run command finds # it in the expected location - symlink_force(os.path.join(caseroot, 'lilac_atm_driver', 'bld', 'atm_driver.exe'), - run_exe) + symlink_force(os.path.join(caseroot, "lilac_atm_driver", "bld", "atm_driver.exe"), run_exe) def _create_runtime_inputs(self): - caseroot = self._case.get_value('CASEROOT') + caseroot = self._case.get_value("CASEROOT") runtime_inputs = self._runtime_inputs_dir() # NOTE: *** this test is currently tied to this single 10x15 grid resolution *** - lnd_grid = self._case.get_value('LND_GRID') - expect(lnd_grid == '10x15', - "this test is currently tied to this single 10x15 grid resolution") - lnd_domain_file = os.path.join(self._case.get_value('DIN_LOC_ROOT'),"share","domains", - "domain.lnd.fv10x15_gx3v7.180321.nc") + lnd_grid = self._case.get_value("LND_GRID") + expect( + lnd_grid == "10x15", "this test is currently tied to this single 10x15 grid resolution" + ) + lnd_domain_file = os.path.join( + self._case.get_value("DIN_LOC_ROOT"), + "share", + "domains", + "domain.lnd.fv10x15_gx3v7.180321.nc", + ) # Cheat a bit here: Get the fsurdat file from the already-generated lnd_in file in # the host test case - i.e., from the standard cime-based preview_namelists. But @@ -147,75 +162,96 @@ def _create_runtime_inputs(self): # we expect the user to identify fsurdat manually; in this testing situation, we # need to come up with some way to replace this manual identification, so cheating # feels acceptable. - self._case.create_namelists(component='lnd') + self._case.create_namelists(component="lnd") fsurdat = self._extract_var_from_namelist( - nl_filename=os.path.join(caseroot, 'CaseDocs', 'lnd_in'), - varname='fsurdat') + nl_filename=os.path.join(caseroot, "CaseDocs", "lnd_in"), varname="fsurdat" + ) - self._fill_in_variables_in_file(filepath=os.path.join(runtime_inputs, 'ctsm.cfg'), - replacements={'lnd_domain_file':lnd_domain_file, - 'fsurdat':fsurdat}) + self._fill_in_variables_in_file( + filepath=os.path.join(runtime_inputs, "ctsm.cfg"), + replacements={"lnd_domain_file": lnd_domain_file, "fsurdat": fsurdat}, + ) # The user_nl_ctsm in the case directory is set up based on the standard testmods # mechanism. We use that one in place of the standard user_nl_ctsm, since the one # in the case directory may contain test-specific modifications. - shutil.copyfile(src=os.path.join(caseroot, 'user_nl_ctsm'), - dst=os.path.join(runtime_inputs, 'user_nl_ctsm')) - - script_to_run = os.path.join(runtime_inputs, 'make_runtime_inputs') - self._run_build_cmd('{} --rundir {}'.format(script_to_run, runtime_inputs), - runtime_inputs, - 'make_runtime_inputs.log') + shutil.copyfile( + src=os.path.join(caseroot, "user_nl_ctsm"), + dst=os.path.join(runtime_inputs, "user_nl_ctsm"), + ) + + script_to_run = os.path.join(runtime_inputs, "make_runtime_inputs") + self._run_build_cmd( + "{} --rundir {}".format(script_to_run, runtime_inputs), + runtime_inputs, + "make_runtime_inputs.log", + ) # In lilac_in, we intentionally use the land mesh file for both atm_mesh_filename # and lnd_mesh_filename - lnd_mesh = self._case.get_value('LND_DOMAIN_MESH') - casename = self._case.get_value('CASE') - self._fill_in_variables_in_file(filepath=os.path.join(runtime_inputs, 'lilac_in'), - replacements={'caseid':casename, - 'atm_mesh_filename':lnd_mesh, - 'lnd_mesh_filename':lnd_mesh, - 'lilac_histfreq_option':'ndays'}, - placeholders={'caseid':'ctsm_lilac', - 'lilac_histfreq_option':'never'}) + lnd_mesh = self._case.get_value("LND_DOMAIN_MESH") + casename = self._case.get_value("CASE") + self._fill_in_variables_in_file( + filepath=os.path.join(runtime_inputs, "lilac_in"), + replacements={ + "caseid": casename, + "atm_mesh_filename": lnd_mesh, + "lnd_mesh_filename": lnd_mesh, + "lilac_histfreq_option": "ndays", + }, + placeholders={"caseid": "ctsm_lilac", "lilac_histfreq_option": "never"}, + ) # We run download_input_data partly because it may be needed and partly to test # this script. - script_to_run = os.path.join(runtime_inputs, 'download_input_data') - self._run_build_cmd('{} --rundir {}'.format(script_to_run, runtime_inputs), - runtime_inputs, - 'download_input_data.log') + script_to_run = os.path.join(runtime_inputs, "download_input_data") + self._run_build_cmd( + "{} --rundir {}".format(script_to_run, runtime_inputs), + runtime_inputs, + "download_input_data.log", + ) def _setup_atm_driver_rundir(self): """Set up the directory from which we will actually do the run""" - lndroot = self._case.get_value('COMP_ROOT_DIR_LND') + lndroot = self._case.get_value("COMP_ROOT_DIR_LND") rundir = self._atm_driver_rundir() if not os.path.exists(rundir): os.makedirs(rundir) - shutil.copyfile(src=os.path.join(lndroot, 'lilac', 'atm_driver', 'atm_driver_in'), - dst=os.path.join(rundir, 'atm_driver_in')) + shutil.copyfile( + src=os.path.join(lndroot, "lilac", "atm_driver", "atm_driver_in"), + dst=os.path.join(rundir, "atm_driver_in"), + ) # As elsewhere: assume the land variables also apply to the atmosphere - lnd_mesh = self._case.get_value('LND_DOMAIN_MESH') - lnd_nx = self._case.get_value('LND_NX') - lnd_ny = self._case.get_value('LND_NY') - expect(self._case.get_value('STOP_OPTION') == 'ndays', - 'LILAC testing currently assumes STOP_OPTION of ndays, not {}'.format( - self._case.get_value('STOP_OPTION'))) - stop_n = self._case.get_value('STOP_N') - casename = self._case.get_value('CASE') - self._fill_in_variables_in_file(filepath=os.path.join(rundir, 'atm_driver_in'), - replacements={'caseid':casename, - 'atm_mesh_file':lnd_mesh, - 'atm_global_nx':str(lnd_nx), - 'atm_global_ny':str(lnd_ny), - 'atm_stop_day':str(stop_n+1), - 'atm_ndays_all_segs':str(stop_n)}) + lnd_mesh = self._case.get_value("LND_DOMAIN_MESH") + lnd_nx = self._case.get_value("LND_NX") + lnd_ny = self._case.get_value("LND_NY") + expect( + self._case.get_value("STOP_OPTION") == "ndays", + "LILAC testing currently assumes STOP_OPTION of ndays, not {}".format( + self._case.get_value("STOP_OPTION") + ), + ) + stop_n = self._case.get_value("STOP_N") + casename = self._case.get_value("CASE") + self._fill_in_variables_in_file( + filepath=os.path.join(rundir, "atm_driver_in"), + replacements={ + "caseid": casename, + "atm_mesh_file": lnd_mesh, + "atm_global_nx": str(lnd_nx), + "atm_global_ny": str(lnd_ny), + "atm_stop_day": str(stop_n + 1), + "atm_ndays_all_segs": str(stop_n), + }, + ) for file_to_link in _LILAC_RUNTIME_FILES: - symlink_force(os.path.join(self._runtime_inputs_dir(), file_to_link), - os.path.join(rundir, file_to_link)) + symlink_force( + os.path.join(self._runtime_inputs_dir(), file_to_link), + os.path.join(rundir, file_to_link), + ) init_generated_files_dir = os.path.join(rundir, "init_generated_files") if not os.path.exists(init_generated_files_dir): @@ -236,8 +272,8 @@ def _cmpgen_namelists(self): because that one will have compared the test's standard CaseDocs with the files generated from here - and those two sets of namelists can be quite different. """ - caseroot = self._case.get_value('CASEROOT') - casedocs = os.path.join(caseroot, 'CaseDocs') + caseroot = self._case.get_value("CASEROOT") + casedocs = os.path.join(caseroot, "CaseDocs") if os.path.exists(casedocs): shutil.rmtree(casedocs) os.makedirs(casedocs) @@ -245,12 +281,13 @@ def _cmpgen_namelists(self): # case_cmpgen_namelists uses the existence of drv_in to decide whether namelists # need to be regenerated. We do NOT want it to regenerate namelists, so we give it # the file it wants. - with open(os.path.join(casedocs, 'drv_in'), 'a') as drv_in: + with open(os.path.join(casedocs, "drv_in"), "a") as drv_in: pass - for onefile in _LILAC_RUNTIME_FILES + ['atm_driver_in']: - safe_copy(os.path.join(self._atm_driver_rundir(), onefile), - os.path.join(casedocs, onefile)) + for onefile in _LILAC_RUNTIME_FILES + ["atm_driver_in"]: + safe_copy( + os.path.join(self._atm_driver_rundir(), onefile), os.path.join(casedocs, onefile) + ) success = self._case.case_cmpgen_namelists() # The setting of the NLCOMP phase status in case_cmpgen_namelists doesn't work @@ -259,8 +296,11 @@ def _cmpgen_namelists(self): # overwriting whatever was set by case_cmpgen_namelists). So we need to set it # here. with self._test_status: - self._test_status.set_status(NAMELIST_PHASE, TEST_PASS_STATUS if success else TEST_FAIL_STATUS, - comments="(used lilac namelists)") + self._test_status.set_status( + NAMELIST_PHASE, + TEST_PASS_STATUS if success else TEST_FAIL_STATUS, + comments="(used lilac namelists)", + ) def _extract_var_from_namelist(self, nl_filename, varname): """Tries to find a variable named varname in the given file; returns its value @@ -272,7 +312,7 @@ def _extract_var_from_namelist(self, nl_filename, varname): match = re.search(r'^ *{} *= *[\'"]([^\'"]+)'.format(varname), line) if match: return match.group(1) - expect(False, '{} not found in {}'.format(varname, nl_filename)) + expect(False, "{} not found in {}".format(varname, nl_filename)) def _fill_in_variables_in_file(self, filepath, replacements, placeholders=None): """For the given file, make the given replacements @@ -286,35 +326,37 @@ def _fill_in_variables_in_file(self, filepath, replacements, placeholders=None): if placeholders is None: placeholders = {} - orig_filepath = '{}.orig'.format(filepath) + orig_filepath = "{}.orig".format(filepath) if not os.path.exists(orig_filepath): - shutil.copyfile(src=filepath, - dst=orig_filepath) + shutil.copyfile(src=filepath, dst=orig_filepath) os.remove(filepath) counts = dict.fromkeys(replacements, 0) with open(orig_filepath) as orig_file: - with open(filepath, 'w') as new_file: + with open(filepath, "w") as new_file: for orig_line in orig_file: line = orig_line for varname in replacements: if varname in placeholders: this_placeholder = placeholders[varname] else: - this_placeholder = 'FILL_THIS_IN' + this_placeholder = "FILL_THIS_IN" line, replacement_done = self._fill_in_variable( line=line, varname=varname, value=replacements[varname], - placeholder=this_placeholder) + placeholder=this_placeholder, + ) if replacement_done: counts[varname] += 1 break new_file.write(line) for varname in counts: - expect(counts[varname] > 0, - 'Did not find any instances of <{}> to replace in {}'.format(varname, filepath)) + expect( + counts[varname] > 0, + "Did not find any instances of <{}> to replace in {}".format(varname, filepath), + ) def _fill_in_variable(self, line, varname, value, placeholder): """Fill in a placeholder variable in a config or namelist file @@ -324,9 +366,11 @@ def _fill_in_variable(self, line, varname, value, placeholder): line is for varname; otherwise returns line unchanged - replacement_done is True if the replacement was done, otherwise False """ - if re.search(r'^ *{} *='.format(varname), line): - expect(placeholder in line, - 'Placeholder to replace ({}) not found in <{}>'.format(placeholder, line.strip())) + if re.search(r"^ *{} *=".format(varname), line): + expect( + placeholder in line, + "Placeholder to replace ({}) not found in <{}>".format(placeholder, line.strip()), + ) newline = line.replace(placeholder, value) replacement_done = True else: @@ -335,13 +379,13 @@ def _fill_in_variable(self, line, varname, value, placeholder): return (newline, replacement_done) def _lilac_build_dir(self): - return os.path.join(self._case.get_value('CASEROOT'), 'lilac_build') + return os.path.join(self._case.get_value("CASEROOT"), "lilac_build") def _runtime_inputs_dir(self): - return os.path.join(self._lilac_build_dir(), 'runtime_inputs') + return os.path.join(self._lilac_build_dir(), "runtime_inputs") def _atm_driver_rundir(self): - return os.path.join(self._case.get_value('CASEROOT'), 'lilac_atm_driver', 'run') + return os.path.join(self._case.get_value("CASEROOT"), "lilac_atm_driver", "run") @staticmethod def _run_build_cmd(cmd, exeroot, logfile): @@ -378,9 +422,9 @@ def _link_to_output_files(self): directory. But then we need to create these links afterwards for the sake of baseline generation / comparison. """ - casename = self._case.get_value('CASE') - rundir = self._case.get_value('RUNDIR') - pattern = '{}*.nc'.format(casename) + casename = self._case.get_value("CASE") + rundir = self._case.get_value("RUNDIR") + pattern = "{}*.nc".format(casename) # First remove any old files from the run directory old_files = glob.glob(os.path.join(rundir, pattern)) diff --git a/cime_config/SystemTests/lreprstruct.py b/cime_config/SystemTests/lreprstruct.py index 27dab77592..a03fb1815b 100644 --- a/cime_config/SystemTests/lreprstruct.py +++ b/cime_config/SystemTests/lreprstruct.py @@ -22,31 +22,54 @@ logger = logging.getLogger(__name__) -class LREPRSTRUCT(SystemTestsCompareTwo): +class LREPRSTRUCT(SystemTestsCompareTwo): def __init__(self, case): - SystemTestsCompareTwo.__init__(self, case, - separate_builds = False, - run_two_suffix = 'grain1', - run_one_description = 'use a reproductive structure pool', - run_two_description = 'use a single grain pool', - ignore_fieldlist_diffs = True) + SystemTestsCompareTwo.__init__( + self, + case, + separate_builds=False, + run_two_suffix="grain1", + run_one_description="use a reproductive structure pool", + run_two_description="use a single grain pool", + ignore_fieldlist_diffs=True, + ) def _case_one_setup(self): # We don't really need a second grain pool for this test, but we might as well do # this to further exercise the looping over different reproductive components. - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "for_testing_use_second_grain_pool=.true.") - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "for_testing_use_repr_structure_pool=.true.") + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="for_testing_use_second_grain_pool=.true.", + ) + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="for_testing_use_repr_structure_pool=.true.", + ) + + # Replace any GRAIN outputs with the same outputs for REPRODUCTIVE1 and REPRODUCTIVE2 + user_nl_clm_path = os.path.join(self._get_caseroot(), "user_nl_clm") + with open(user_nl_clm_path) as f: + user_nl_clm_text = f.read() + for grain_output in re.findall("GRAIN\w*", user_nl_clm_text): + user_nl_clm_text = user_nl_clm_text.replace( + grain_output, + grain_output.replace("GRAIN", "REPRODUCTIVE1") + + "', '" + + grain_output.replace("GRAIN", "REPRODUCTIVE2"), + ) + with open(user_nl_clm_path, "w") as f: + f.write(user_nl_clm_text) def _case_two_setup(self): # This is needed in the nearly-standard case to prevent grain from being used to # replenish crop seed deficits, thus making grain act like the reproductive # structure pools. (It wouldn't hurt to do this in case one as well, but it # shouldn't be needed there, since we shouldn't have any grain there anyway.) - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "for_testing_no_crop_seed_replenishment=.true.") + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="for_testing_no_crop_seed_replenishment=.true.", + ) diff --git a/cime_config/SystemTests/lvg.py b/cime_config/SystemTests/lvg.py index 36fae196b2..4b990313f5 100644 --- a/cime_config/SystemTests/lvg.py +++ b/cime_config/SystemTests/lvg.py @@ -16,22 +16,28 @@ logger = logging.getLogger(__name__) -class LVG(SystemTestsCompareTwo): +class LVG(SystemTestsCompareTwo): def __init__(self, case): - SystemTestsCompareTwo.__init__(self, case, - separate_builds = False, - run_two_suffix = 'more_virtual', - run_one_description = 'standard set of virtual columns', - run_two_description = 'add virtual columns over Antarctica') + SystemTestsCompareTwo.__init__( + self, + case, + separate_builds=False, + run_two_suffix="more_virtual", + run_one_description="standard set of virtual columns", + run_two_description="add virtual columns over Antarctica", + ) def _case_one_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "glacier_region_behavior = 'single_at_atm_topo', 'virtual', 'virtual', 'multiple'") + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="glacier_region_behavior = 'single_at_atm_topo', 'virtual', 'virtual', 'multiple'", + ) def _case_two_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "glacier_region_behavior = 'single_at_atm_topo', 'virtual', 'virtual', 'virtual'") - + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="glacier_region_behavior = 'single_at_atm_topo', 'virtual', 'virtual', 'virtual'", + ) diff --git a/cime_config/SystemTests/lwiso.py b/cime_config/SystemTests/lwiso.py index 1083e2ff36..37cfd42603 100644 --- a/cime_config/SystemTests/lwiso.py +++ b/cime_config/SystemTests/lwiso.py @@ -17,15 +17,18 @@ logger = logging.getLogger(__name__) -class LWISO(SystemTestsCompareTwo): +class LWISO(SystemTestsCompareTwo): def __init__(self, case): - SystemTestsCompareTwo.__init__(self, case, - separate_builds = False, - run_two_suffix = 'nowiso', - run_one_description = 'water isotopes on', - run_two_description = 'water isotopes off', - ignore_fieldlist_diffs = True) + SystemTestsCompareTwo.__init__( + self, + case, + separate_builds=False, + run_two_suffix="nowiso", + run_one_description="water isotopes on", + run_two_description="water isotopes off", + ignore_fieldlist_diffs=True, + ) def _case_one_setup(self): # BUG(wjs, 2019-07-30, ESCOMP/ctsm#495) We currently can't turn on actual water @@ -33,12 +36,15 @@ def _case_one_setup(self): # enable_water_tracer_consistency_checks rather than enable_water_isotopes; # eventually, though, we should change this to the latter. (See # .) - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "enable_water_tracer_consistency_checks=.true.") + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="enable_water_tracer_consistency_checks=.true.", + ) def _case_two_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "enable_water_tracer_consistency_checks=.false.") - + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="enable_water_tracer_consistency_checks=.false.", + ) diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py new file mode 100644 index 0000000000..5d3b07bfbf --- /dev/null +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -0,0 +1,443 @@ +""" +CTSM-specific test that first performs a GDD-generating run, then calls +Python code to generate the maturity requirement file. This is then used +in a sowing+maturity forced run, which finally is tested to ensure +correct behavior. + +Currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions. Eventually, +this test should be able to generate its own files at whatever resolution it's +called at. Well, really, the ultimate goal would be to give CLM the files +at the original resolution (for GGCMI phase 3, 0.5°) and have the stream +code do the interpolation. However, that wouldn't act on harvest dates +(which are needed for generate_gdds.py). I could have Python interpolate +those, but this would cause a potential inconsistency. +""" + +import os +import re +import systemtest_utils as stu +import subprocess +from CIME.SystemTests.system_tests_common import SystemTestsCommon +from CIME.XML.standard_module_setup import * +from CIME.SystemTests.test_utils.user_nl_utils import append_to_user_nl_files +import shutil, glob + +logger = logging.getLogger(__name__) + + +class RXCROPMATURITY(SystemTestsCommon): + def __init__(self, case): + # initialize an object interface to the SMS system test + SystemTestsCommon.__init__(self, case) + + # Ensure run length is at least 5 years. Minimum to produce one complete growing season (i.e., two complete calendar years) actually 4 years, but that only gets you 1 season usable for GDD generation, so you can't check for season-to-season consistency. + stop_n = self._case.get_value("STOP_N") + stop_option = self._case.get_value("STOP_OPTION") + stop_n_orig = stop_n + stop_option_orig = stop_option + if "nsecond" in stop_option: + stop_n /= 60 + stop_option = "nminutes" + if "nminute" in stop_option: + stop_n /= 60 + stop_option = "nhours" + if "nhour" in stop_option: + stop_n /= 24 + stop_option = "ndays" + if "nday" in stop_option: + stop_n /= 365 + stop_option = "nyears" + if "nmonth" in stop_option: + stop_n /= 12 + stop_option = "nyears" + error_message = None + if "nyear" not in stop_option: + error_message = ( + f"STOP_OPTION ({stop_option_orig}) must be nsecond(s), nminute(s), " + + "nhour(s), nday(s), nmonth(s), or nyear(s)" + ) + elif stop_n < 5: + error_message = ( + "RXCROPMATURITY must be run for at least 5 years; you requested " + + f"{stop_n_orig} {stop_option_orig[1:]}" + ) + if error_message is not None: + logger.error(error_message) + raise RuntimeError(error_message) + + # Get the number of complete years that will be run + self._run_Nyears = int(stop_n) + + # Only allow RXCROPMATURITY to be called with test cropMonthOutput + casebaseid = self._case.get_value("CASEBASEID") + if casebaseid.split("-")[-1] != "cropMonthOutput": + error_message = ( + "Only call RXCROPMATURITY with test cropMonthOutput " + + "to avoid potentially huge sets of daily outputs." + ) + logger.error(error_message) + raise RuntimeError(error_message) + + # Get files with prescribed sowing and harvest dates + self._get_rx_dates() + + # Which conda environment should we use? + self._get_conda_env() + + def run_phase(self): + # Modeling this after the SSP test, we create a clone to be the case whose outputs we don't + # want to be saved as baseline. + + # ------------------------------------------------------------------- + # (1) Set up GDD-generating run + # ------------------------------------------------------------------- + # Create clone to be GDD-Generating case + logger.info("RXCROPMATURITY log: cloning setup") + case_rxboth = self._case + caseroot = self._case.get_value("CASEROOT") + clone_path = f"{caseroot}.gddgen" + self._path_gddgen = clone_path + if os.path.exists(self._path_gddgen): + shutil.rmtree(self._path_gddgen) + logger.info("RXCROPMATURITY log: cloning") + case_gddgen = self._case.create_clone(clone_path, keepexe=True) + logger.info("RXCROPMATURITY log: done cloning") + + os.chdir(self._path_gddgen) + self._set_active_case(case_gddgen) + + # Set up stuff that applies to both tests + self._setup_all() + + # Add stuff specific to GDD-Generating run + logger.info("RXCROPMATURITY log: modify user_nl files: generate GDDs") + self._append_to_user_nl_clm( + [ + "generate_crop_gdds = .true.", + "use_mxmat = .false.", + " ", + "! (h2) Daily outputs for GDD generation and figure-making", + "hist_fincl3 = 'GDDACCUM', 'GDDHARV'", + "hist_nhtfrq(3) = -24", + "hist_mfilt(3) = 365", + "hist_type1d_pertape(3) = 'PFTS'", + "hist_dov2xy(3) = .false.", + ] + ) + + # If flanduse_timeseries is defined, we need to make a static version for this test. This + # should have every crop in most of the world. + self._get_flanduse_timeseries_in(case_gddgen) + if self._flanduse_timeseries_in is not None: + + # Download files from the server, if needed + case_gddgen.check_all_input_data() + + # Make custom version of surface file + logger.info("RXCROPMATURITY log: run fsurdat_modifier") + self._run_fsurdat_modifier() + + # ------------------------------------------------------------------- + # (2) Perform GDD-generating run and generate prescribed GDDs file + # ------------------------------------------------------------------- + logger.info("RXCROPMATURITY log: Start GDD-Generating run") + + # As per SSP test: + # "No history files expected, set suffix=None to avoid compare error" + # We *do* expect history files here, but anyway. This works. + self._skip_pnl = False + self.run_indv(suffix=None, st_archive=True) + + self._run_generate_gdds(case_gddgen) + + # ------------------------------------------------------------------- + # (3) Set up and perform Prescribed Calendars run + # ------------------------------------------------------------------- + os.chdir(caseroot) + self._set_active_case(case_rxboth) + + # Set up stuff that applies to both tests + self._setup_all() + + # Add stuff specific to Prescribed Calendars run + logger.info("RXCROPMATURITY log: modify user_nl files: Prescribed Calendars") + self._append_to_user_nl_clm( + [ + "generate_crop_gdds = .false.", + f"stream_fldFileName_cultivar_gdds = '{self._gdds_file}'", + ] + ) + + self.run_indv() + + # ------------------------------------------------------------------- + # (4) Check Prescribed Calendars run + # ------------------------------------------------------------------- + logger.info("RXCROPMATURITY log: output check: Prescribed Calendars") + self._run_check_rxboth_run() + + # Get sowing and harvest dates for this resolution. + def _get_rx_dates(self): + # Eventually, I want to remove these hard-coded resolutions so that this test can generate + # its own sowing and harvest date files at whatever resolution is requested. + lnd_grid = self._case.get_value("LND_GRID") + input_data_root = self._case.get_value("DIN_LOC_ROOT") + processed_crop_dates_dir = f"{input_data_root}/lnd/clm2/cropdata/calendars/processed" + if lnd_grid == "10x15": + self._sdatefile = os.path.join( + processed_crop_dates_dir, + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", + ) + self._hdatefile = os.path.join( + processed_crop_dates_dir, + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", + ) + elif lnd_grid == "1.9x2.5": + self._sdatefile = os.path.join( + processed_crop_dates_dir, + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", + ) + self._hdatefile = os.path.join( + processed_crop_dates_dir, + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", + ) + elif lnd_grid == "0.9x1.25": + self._sdatefile = os.path.join( + processed_crop_dates_dir, + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134417.nc", + ) + self._hdatefile = os.path.join( + processed_crop_dates_dir, + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134418.nc", + ) + else: + error_message = "ERROR: RXCROPMATURITY currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions" + logger.error(error_message) + raise RuntimeError(error_message) + + # Ensure files exist + error_message = None + if not os.path.exists(self._sdatefile): + error_message = f"ERROR: Sowing date file not found: {self._sdatefile}" + elif not os.path.exists(self._hdatefile): + error_message = f"ERROR: Harvest date file not found: {self._sdatefile}" + if error_message is not None: + logger.error(error_message) + raise RuntimeError(error_message) + + def _setup_all(self): + logger.info("RXCROPMATURITY log: _setup_all start") + + # Get some info + self._ctsm_root = self._case.get_value("COMP_ROOT_DIR_LND") + run_startdate = self._case.get_value("RUN_STARTDATE") + self._run_startyear = int(run_startdate.split("-")[0]) + + # Set sowing dates file (and other crop calendar settings) for all runs + logger.info("RXCROPMATURITY log: modify user_nl files: all tests") + self._modify_user_nl_allruns() + logger.info("RXCROPMATURITY log: _setup_all done") + + # Make a surface dataset that has every crop in every gridcell + def _run_fsurdat_modifier(self): + + # fsurdat should be defined. Where is it? + self._fsurdat_in = None + with open(self._lnd_in_path, "r") as lnd_in: + for line in lnd_in: + fsurdat_in = re.match(r" *fsurdat *= *'(.*)'", line) + if fsurdat_in: + self._fsurdat_in = fsurdat_in.group(1) + break + if self._fsurdat_in is None: + error_message = "fsurdat not defined" + logger.error(error_message) + raise RuntimeError(error_message) + + # Where we will save the fsurdat version for this test + path, ext = os.path.splitext(self._fsurdat_in) + dir_in, filename_in_noext = os.path.split(path) + self._fsurdat_out = os.path.join( + self._path_gddgen, f"{filename_in_noext}.all_crops_everywhere{ext}" + ) + + # Make fsurdat for this test, if not already done + if not os.path.exists(self._fsurdat_out): + tool_path = os.path.join( + self._ctsm_root, + "tools", + "modify_input_files", + "fsurdat_modifier", + ) + + # Create configuration file for fsurdat_modifier + self._cfg_path = os.path.join( + self._path_gddgen, + "modify_fsurdat_allcropseverywhere.cfg", + ) + self._create_config_file_evenlysplitcrop() + + command = f"python3 {tool_path} {self._cfg_path} " + stu.run_python_script( + self._get_caseroot(), + self._this_conda_env, + command, + tool_path, + ) + + # Modify namelist + logger.info("RXCROPMATURITY log: modify user_nl files: new fsurdat") + self._append_to_user_nl_clm( + [ + "fsurdat = '{}'".format(self._fsurdat_out), + "do_transient_crops = .false.", + "flanduse_timeseries = ''", + "use_init_interp = .true.", + ] + ) + + def _create_config_file_evenlysplitcrop(self): + """ + Open the new and the template .cfg files + Loop line by line through the template .cfg file + When string matches, replace that line's content + """ + cfg_template_path = os.path.join( + self._ctsm_root, "tools/modify_input_files/modify_fsurdat_template.cfg" + ) + + with open(self._cfg_path, "w", encoding="utf-8") as cfg_out: + # Copy template, replacing some lines + with open(cfg_template_path, "r", encoding="utf-8") as cfg_in: + for line in cfg_in: + if re.match(r" *evenly_split_cropland *=", line): + line = f"evenly_split_cropland = True" + elif re.match(r" *fsurdat_in *=", line): + line = f"fsurdat_in = {self._fsurdat_in}" + elif re.match(r" *fsurdat_out *=", line): + line = f"fsurdat_out = {self._fsurdat_out}" + elif re.match(r" *process_subgrid_section *=", line): + line = f"process_subgrid_section = True" + cfg_out.write(line) + + # Add new lines + cfg_out.write("\n") + cfg_out.write("[modify_fsurdat_subgrid_fractions]\n") + cfg_out.write("PCT_CROP = 100.0\n") + cfg_out.write("PCT_NATVEG = 0.0\n") + cfg_out.write("PCT_GLACIER = 0.0\n") + cfg_out.write("PCT_WETLAND = 0.0\n") + cfg_out.write("PCT_LAKE = 0.0\n") + cfg_out.write("PCT_URBAN = 0.0 0.0 0.0\n") + + def _run_check_rxboth_run(self): + + output_dir = os.path.join(self._get_caseroot(), "run") + first_usable_year = self._run_startyear + 2 + last_usable_year = self._run_startyear + self._run_Nyears - 2 + + tool_path = os.path.join( + self._ctsm_root, "python", "ctsm", "crop_calendars", "check_rxboth_run.py" + ) + command = ( + f"python3 {tool_path} " + + f"--directory {output_dir} " + + f"-y1 {first_usable_year} " + + f"-yN {last_usable_year} " + + f"--rx-sdates-file {self._sdatefile} " + + f"--rx-gdds-file {self._gdds_file} " + ) + stu.run_python_script( + self._get_caseroot(), + self._this_conda_env, + command, + tool_path, + ) + + def _modify_user_nl_allruns(self): + nl_additions = [ + "stream_meshfile_cropcal = '{}'".format(self._case.get_value("LND_DOMAIN_MESH")), + "stream_fldFileName_swindow_start = '{}'".format(self._sdatefile), + "stream_fldFileName_swindow_end = '{}'".format(self._sdatefile), + "stream_year_first_cropcal = 2000", + "stream_year_last_cropcal = 2000", + "model_year_align_cropcal = 2000", + " ", + "! (h1) Annual outputs on sowing or harvest axis", + "hist_fincl2 = 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV'", + "hist_nhtfrq(2) = 17520", + "hist_mfilt(2) = 999", + "hist_type1d_pertape(2) = 'PFTS'", + "hist_dov2xy(2) = .false.", + ] + self._append_to_user_nl_clm(nl_additions) + + def _run_generate_gdds(self, case_gddgen): + self._generate_gdds_dir = os.path.join(self._path_gddgen, "generate_gdds_out") + os.makedirs(self._generate_gdds_dir) + + # Get arguments to generate_gdds.py + dout_sr = case_gddgen.get_value("DOUT_S_ROOT") + input_dir = os.path.join(dout_sr, "lnd", "hist") + first_season = self._run_startyear + 2 + last_season = self._run_startyear + self._run_Nyears - 2 + sdates_file = self._sdatefile + hdates_file = self._hdatefile + + # It'd be much nicer to call generate_gdds.main(), but I can't import generate_gdds. + tool_path = os.path.join( + self._ctsm_root, "python", "ctsm", "crop_calendars", "generate_gdds.py" + ) + command = " ".join( + [ + f"python3 {tool_path}", + f"--input-dir {input_dir}", + f"--first-season {first_season}", + f"--last-season {last_season}", + f"--sdates-file {sdates_file}", + f"--hdates-file {hdates_file}", + f"--output-dir generate_gdds_out", + f"--skip-crops miscanthus,irrigated_miscanthus", + ] + ) + stu.run_python_script( + self._get_caseroot(), + self._this_conda_env, + command, + tool_path, + ) + + # Where were the prescribed maturity requirements saved? + generated_gdd_files = glob.glob(os.path.join(self._generate_gdds_dir, "gdds_*.nc")) + if len(generated_gdd_files) != 1: + error_message = f"ERROR: Expected one matching prescribed maturity requirements file; found {len(generated_gdd_files)}: {generated_gdd_files}" + logger.error(error_message) + raise RuntimeError(error_message) + self._gdds_file = generated_gdd_files[0] + + def _get_conda_env(self): + conda_setup_commands = stu.cmds_to_setup_conda(self._get_caseroot()) + + # If npl conda environment is available, use that (It has dask, which + # enables chunking, which makes reading daily 1-degree netCDF files + # much more efficient. + if "npl " in os.popen(conda_setup_commands + "conda env list").read(): + self._this_conda_env = "npl" + else: + self._this_conda_env = "ctsm_pylib" + + def _append_to_user_nl_clm(self, additions): + caseroot = self._get_caseroot() + append_to_user_nl_files(caseroot=caseroot, component="clm", contents=additions) + + # Is flanduse_timeseries defined? If so, where is it? + def _get_flanduse_timeseries_in(self, case): + case.create_namelists(component="lnd") + self._lnd_in_path = os.path.join(self._path_gddgen, "CaseDocs", "lnd_in") + self._flanduse_timeseries_in = None + with open(self._lnd_in_path, "r") as lnd_in: + for line in lnd_in: + flanduse_timeseries_in = re.match(r" *flanduse_timeseries *= *'(.*)'", line) + if flanduse_timeseries_in: + self._flanduse_timeseries_in = flanduse_timeseries_in.group(1) + break diff --git a/cime_config/SystemTests/soilstructud.py b/cime_config/SystemTests/soilstructud.py index b454695f6e..42ffae54be 100644 --- a/cime_config/SystemTests/soilstructud.py +++ b/cime_config/SystemTests/soilstructud.py @@ -16,22 +16,28 @@ logger = logging.getLogger(__name__) -class SOILSTRUCTUD(SystemTestsCompareTwo): +class SOILSTRUCTUD(SystemTestsCompareTwo): def __init__(self, case): - SystemTestsCompareTwo.__init__(self, case, - separate_builds = False, - run_two_suffix = '4SL_2m', - run_one_description = 'soil_layerstruct_userdefined', - run_two_description = 'soil_layerstruct_predefined') + SystemTestsCompareTwo.__init__( + self, + case, + separate_builds=False, + run_two_suffix="4SL_2m", + run_one_description="soil_layerstruct_userdefined", + run_two_description="soil_layerstruct_predefined", + ) def _case_one_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "soil_layerstruct_userdefined_nlevsoi = 4,soil_layerstruct_userdefined = 0.1d0,0.3d0,0.6d0,1.0d0,1.0d0") + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="soil_layerstruct_userdefined_nlevsoi = 4,soil_layerstruct_userdefined = 0.1d0,0.3d0,0.6d0,1.0d0,1.0d0", + ) def _case_two_setup(self): - append_to_user_nl_files(caseroot = self._get_caseroot(), - component = "clm", - contents = "soil_layerstruct_predefined = '4SL_2m'") - + append_to_user_nl_files( + caseroot=self._get_caseroot(), + component="clm", + contents="soil_layerstruct_predefined = '4SL_2m'", + ) diff --git a/cime_config/SystemTests/ssp.py b/cime_config/SystemTests/ssp.py index 26337d323d..bd554aeae9 100644 --- a/cime_config/SystemTests/ssp.py +++ b/cime_config/SystemTests/ssp.py @@ -18,8 +18,8 @@ logger = logging.getLogger(__name__) -class SSP(SystemTestsCommon): +class SSP(SystemTestsCommon): def __init__(self, case): """ initialize an object interface to the SSP system test @@ -48,10 +48,10 @@ def run_phase(self): stop_n1 = int(stop_nf / 2) stop_n2 = stop_nf - stop_n1 - #------------------------------------------------------------------- + # ------------------------------------------------------------------- # (1) do a spinup run in the main case in the cloned ref case # (short term archiving is on) - #------------------------------------------------------------------- + # ------------------------------------------------------------------- os.chdir(clone_path) self._set_active_case(clone) @@ -61,20 +61,24 @@ def run_phase(self): with clone: clone.set_value("CLM_ACCELERATED_SPINUP", "on") - clone.set_value("STOP_N",stop_n1) + clone.set_value("STOP_N", stop_n1) dout_sr = clone.get_value("DOUT_S_ROOT") # No history files expected, set suffix=None to avoid compare error self._skip_pnl = False self.run_indv(suffix=None, st_archive=True) - #------------------------------------------------------------------- + # ------------------------------------------------------------------- # (2) do a hybrid, non-spinup run in orig_case - #------------------------------------------------------------------- + # ------------------------------------------------------------------- os.chdir(caseroot) self._set_active_case(orig_case) - refdate = run_cmd_no_fail(r'ls -1dt {}/rest/*-00000* | head -1 | sed "s/-00000.*//" | sed "s/^.*rest\///"'.format(dout_sr)) + refdate = run_cmd_no_fail( + r'ls -1dt {}/rest/*-00000* | head -1 | sed "s/-00000.*//" | sed "s/^.*rest\///"'.format( + dout_sr + ) + ) refsec = "00000" # obtain rpointer files and necessary restart files from short term archiving directory diff --git a/cime_config/SystemTests/systemtest_utils.py b/cime_config/SystemTests/systemtest_utils.py new file mode 100644 index 0000000000..c5ac986abd --- /dev/null +++ b/cime_config/SystemTests/systemtest_utils.py @@ -0,0 +1,86 @@ +""" +Reduce code duplication by putting reused functions here. +""" + +import os, subprocess + + +def cmds_to_setup_conda(caseroot): + # Add specific commands needed on different machines to get conda available + # Use semicolon here since it's OK to fail + # + conda_setup_commands = ". " + caseroot + "/.env_mach_specific.sh; " + # Setting CONDA_PREFIX to empty ensures that this works even if called from + # a shell with a conda environment activated + conda_setup_commands += "CONDA_PREFIX=; " + # Execute the module unload/load when "which conda" fails + # eg on cheyenne + try: + subprocess.run("which conda", shell=True, check=True) + except subprocess.CalledProcessError: + # Remove python and add conda to environment for cheyennne + unload_python_load_conda = "module unload python; module load conda;" + # Make sure that adding this actually loads conda + subprocess.run(unload_python_load_conda + "which conda", shell=True, check=True) + # Save + conda_setup_commands += " " + unload_python_load_conda + + return conda_setup_commands + + +def cmds_to_run_via_conda(caseroot, conda_run_call, command): + # Run in the specified conda environment + conda_setup_commands = cmds_to_setup_conda(caseroot) + conda_setup_commands += " " + conda_run_call + + # Finish with Python script call + command = conda_setup_commands + " " + command + print(f"command: {command}") + + return command + + +def run_python_script(caseroot, this_conda_env, command_in, tool_path): + + # First, try with "conda run -n" + command = cmds_to_run_via_conda(caseroot, f"conda run -n {this_conda_env}", command_in) + + # Run with logfile + tool_name = os.path.split(tool_path)[-1] + try: + with open(tool_name + ".log", "w") as f: + subprocess.run( + command, shell=True, check=True, text=True, stdout=f, stderr=subprocess.STDOUT + ) + except subprocess.CalledProcessError as error: + # Retry with the original "conda activate" method + command = cmds_to_run_via_conda( + caseroot, + f"conda activate {this_conda_env} && ", + command_in, + ) + try: + with open(tool_name + ".log2", "w") as f: + subprocess.run( + command, shell=True, check=True, text=True, stdout=f, stderr=subprocess.STDOUT + ) + except subprocess.CalledProcessError as error: + print("ERROR while getting the conda environment and/or ") + print(f"running the {tool_name} tool: ") + print(f"(1) If your {this_conda_env} environment is out of date or you ") + print(f"have not created the {this_conda_env} environment, yet, you may ") + print("get past this error by running ./py_env_create ") + print("in your ctsm directory and trying this test again. ") + print("(2) If conda is not available, install and load conda, ") + print("run ./py_env_create, and then try this test again. ") + print("(3) If (1) and (2) are not the issue, then you may be ") + print(f"getting an error within {tool_name} itself. ") + print("Default error message: ") + print(error.output) + raise + except: + print(f"ERROR trying to run {tool_name}.") + raise + except: + print(f"ERROR trying to run {tool_name}.") + raise diff --git a/cime_config/buildlib b/cime_config/buildlib index 31b7fe3d38..0e253c9d98 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -27,19 +27,26 @@ def _write_ctsm_mk(gmake, gmake_opts, makefile, exeroot, libroot): This file can be included by atmosphere model builds outside of cime. """ - cime_output_file = os.path.join(exeroot, 'cime_variables.mk') + cime_output_file = os.path.join(exeroot, "cime_variables.mk") # Set COMP_NAME=driver because some link flags are set differently when COMP_NAME=driver, and # those are the ones we want here. - cmd = ("{gmake} write_include_and_link_flags OUTPUT_FILE={cime_output_file} " - "COMP_NAME=driver {gmake_opts} -f {makefile} ").format( - gmake=gmake, cime_output_file=cime_output_file, gmake_opts=gmake_opts, makefile=makefile) + cmd = ( + "{gmake} write_include_and_link_flags OUTPUT_FILE={cime_output_file} " + "COMP_NAME=driver {gmake_opts} -f {makefile} " + ).format( + gmake=gmake, + cime_output_file=cime_output_file, + gmake_opts=gmake_opts, + makefile=makefile, + ) rc, out, err = run_cmd(cmd) - logger.info("%s: \n\n output:\n %s \n\n err:\n\n%s\n"%(cmd,out,err)) + logger.info("%s: \n\n output:\n %s \n\n err:\n\n%s\n" % (cmd, out, err)) expect(rc == 0, "Command %s failed with rc=%s" % (cmd, rc)) - ctsm_mk_path = os.path.join(exeroot, 'ctsm.mk') - with open(ctsm_mk_path, 'w') as ctsm_mk: - ctsm_mk.write(""" + ctsm_mk_path = os.path.join(exeroot, "ctsm.mk") + with open(ctsm_mk_path, "w") as ctsm_mk: + ctsm_mk.write( + """ # ====================================================================== # Include this file to get makefile variables needed to include / link # LILAC/CTSM in an atmosphere model's build @@ -54,13 +61,15 @@ def _write_ctsm_mk(gmake, gmake_opts, makefile, exeroot, libroot): # should not be included directly in an atmosphere model's build. # ====================================================================== -""") +""" + ) with open(cime_output_file) as infile: ctsm_mk.write(infile.read()) ctsm_bld_dir = os.path.abspath(os.path.join(libroot, os.pardir)) - with open(ctsm_mk_path, 'a') as ctsm_mk: - ctsm_mk.write(""" + with open(ctsm_mk_path, "a") as ctsm_mk: + ctsm_mk.write( + """ # ====================================================================== # The following settings are meant for internal use, and generally # should not be included directly in an atmosphere model's build. @@ -77,11 +86,15 @@ CTSM_INC = $(CTSM_BLD_DIR)/clm/obj CTSM_INCLUDES = $(CIME_ESMF_F90COMPILEPATHS) -I$(CIME_CSM_SHR_INCLUDE) -I$(CTSM_INC) CTSM_LIBS = -L$(CTSM_BLD_DIR)/lib -lclm $(CIME_ULIBS) $(CIME_SLIBS) $(CIME_MLIBS) $(CIME_F90_LDFLAGS) -""".format(ctsm_bld_dir=ctsm_bld_dir)) +""".format( + ctsm_bld_dir=ctsm_bld_dir + ) + ) + ############################################################################### def _main_func(): -############################################################################### + ############################################################################### caseroot, libroot, bldroot = parse_input(sys.argv) @@ -96,77 +109,89 @@ def _main_func(): driver = case.get_value("COMP_INTERFACE").lower() lilac_mode = case.get_value("LILAC_MODE") - if lilac_mode == 'on': + if lilac_mode == "on": driver = "lilac" - #------------------------------------------------------- + # ------------------------------------------------------- # create Filepath file - #------------------------------------------------------- + # ------------------------------------------------------- compname = case.get_value("COMP_LND") - filepath_file = os.path.join(bldroot,"Filepath") + filepath_file = os.path.join(bldroot, "Filepath") if not os.path.isfile(filepath_file): caseroot = case.get_value("CASEROOT") - expect( ((compname == "clm") or (compname == "ctsm")), "Unexpected COMP_LND name: %s" % (compname)) - - paths = [os.path.join(caseroot,"SourceMods","src."+compname), - os.path.join(lnd_root,"src","cpl",driver), - os.path.join(lnd_root,"src","main"), - os.path.join(lnd_root,"src","biogeophys"), - os.path.join(lnd_root,"src","biogeochem"), - os.path.join(lnd_root,"src","soilbiogeochem"), - os.path.join(lnd_root,"src","dyn_subgrid"), - os.path.join(lnd_root,"src","init_interp"), - os.path.join(lnd_root,"src","self_tests"), - os.path.join(lnd_root,"src","fates"), - os.path.join(lnd_root,"src","fates","main"), - os.path.join(lnd_root,"src","fates","biogeophys"), - os.path.join(lnd_root,"src","fates","biogeochem"), - os.path.join(lnd_root,"src","fates","fire"), - os.path.join(lnd_root,"src","fates","parteh"), - os.path.join(lnd_root,"src","utils"), - os.path.join(lnd_root,"src","cpl"), - os.path.join(lnd_root,"src","cpl","utils")] - - if lilac_mode == 'on': - paths.append(os.path.join(lnd_root,"lilac","src")) + expect( + ((compname == "clm") or (compname == "ctsm")), + "Unexpected COMP_LND name: %s" % (compname), + ) + + paths = [ + os.path.join(caseroot, "SourceMods", "src." + compname), + os.path.join(lnd_root, "src", "cpl", driver), + os.path.join(lnd_root, "src", "main"), + os.path.join(lnd_root, "src", "biogeophys"), + os.path.join(lnd_root, "src", "biogeochem"), + os.path.join(lnd_root, "src", "soilbiogeochem"), + os.path.join(lnd_root, "src", "dyn_subgrid"), + os.path.join(lnd_root, "src", "init_interp"), + os.path.join(lnd_root, "src", "self_tests"), + os.path.join(lnd_root, "src", "fates"), + os.path.join(lnd_root, "src", "fates", "main"), + os.path.join(lnd_root, "src", "fates", "biogeophys"), + os.path.join(lnd_root, "src", "fates", "biogeochem"), + os.path.join(lnd_root, "src", "fates", "fire"), + os.path.join(lnd_root, "src", "fates", "parteh"), + os.path.join(lnd_root, "src", "fates", "radiation"), + os.path.join(lnd_root, "src", "utils"), + os.path.join(lnd_root, "src", "cpl"), + os.path.join(lnd_root, "src", "cpl", "utils"), + ] + + if lilac_mode == "on": + paths.append(os.path.join(lnd_root, "lilac", "src")) # If we want to build with a real river model (e.g., MOSART), we'll need # to use its directories in place of stub_rof - paths.append(os.path.join(lnd_root,"lilac","stub_rof")) + paths.append(os.path.join(lnd_root, "lilac", "stub_rof")) - if (driver == 'lilac' or driver == 'nuopc'): - paths.append(os.path.join(lnd_root,"src","cpl","share_esmf")) + if driver == "lilac" or driver == "nuopc": + paths.append(os.path.join(lnd_root, "src", "cpl", "share_esmf")) with open(filepath_file, "w") as filepath: filepath.write("\n".join(paths)) filepath.write("\n") - #------------------------------------------------------- + # ------------------------------------------------------- # create the library in libroot - #------------------------------------------------------- + # ------------------------------------------------------- - complib = os.path.join(libroot,"lib%s.a"%(compname)) + complib = os.path.join(libroot, "lib%s.a" % (compname)) - cmd = "{} complib -j {} COMP_NAME={} COMPLIB={} -f {} {}" \ - .format(gmake, gmake_j, compname, complib, makefile, gmake_opts) + cmd = "{} complib -j {} COMP_NAME={} COMPLIB={} -f {} {}".format( + gmake, gmake_j, compname, complib, makefile, gmake_opts + ) rc, out, err = run_cmd(cmd) - logger.info("%s: \n\n output:\n %s \n\n err:\n\n%s\n"%(cmd,out,err)) + logger.info("%s: \n\n output:\n %s \n\n err:\n\n%s\n" % (cmd, out, err)) expect(rc == 0, "Command %s failed with rc=%s" % (cmd, rc)) # ------------------------------------------------------------------------ # for lilac usage, we need a file containing some Makefile variables (for the atmosphere model's build) # ------------------------------------------------------------------------ - if lilac_mode == 'on': - _write_ctsm_mk(gmake=gmake, - gmake_opts=gmake_opts, - makefile=makefile, - exeroot=case.get_value("EXEROOT"), - libroot=libroot) + if lilac_mode == "on": + _write_ctsm_mk( + gmake=gmake, + gmake_opts=gmake_opts, + makefile=makefile, + exeroot=case.get_value("EXEROOT"), + libroot=libroot, + ) + ############################################################################### if __name__ == "__main__": - logger.warning( "WARNING: buildlib is being called as a program rather than a subroutine as " + - "it is expected to be in the CESM context" ) + logger.warning( + "WARNING: buildlib is being called as a program rather than a subroutine as " + + "it is expected to be in the CESM context" + ) _main_func() diff --git a/cime_config/buildnml b/cime_config/buildnml index 4e04951474..d2e7ddc886 100755 --- a/cime_config/buildnml +++ b/cime_config/buildnml @@ -3,7 +3,7 @@ """ CTSM namelist creator """ -import sys, os, shutil +import sys, os, shutil, re _CIMEROOT = os.environ.get("CIMEROOT") if _CIMEROOT is None: @@ -12,10 +12,10 @@ if _CIMEROOT is None: _LIBDIR = os.path.join(_CIMEROOT, "CIME", "Tools") sys.path.append(_LIBDIR) -from standard_script_setup import * -from CIME.buildnml import create_namelist_infile, parse_input -from CIME.case import Case -from CIME.utils import expect, run_cmd +from standard_script_setup import * +from CIME.buildnml import create_namelist_infile, parse_input +from CIME.case import Case +from CIME.utils import expect, run_cmd logger = logging.getLogger(__name__) @@ -29,8 +29,8 @@ _config_cache_template = """ ############################################################################### def buildnml(case, caseroot, compname): -############################################################################### - """Build the CTSM namelist """ + ############################################################################### + """Build the CTSM namelist""" # Build the component namelist if compname != "ctsm" and compname != "clm": @@ -41,6 +41,7 @@ def buildnml(case, caseroot, compname): configuration = case.get_value("CLM_CONFIGURATION") structure = case.get_value("CLM_STRUCTURE") ccsm_co2_ppmv = case.get_value("CCSM_CO2_PPMV") + casename = case.get_value("CASE") clm_co2_type = case.get_value("CLM_CO2_TYPE") clm_namelist_opts = case.get_value("CLM_NAMELIST_OPTS") clm_bldnml_opts = case.get_value("CLM_BLDNML_OPTS") @@ -59,26 +60,30 @@ def buildnml(case, caseroot, compname): run_reftod = case.get_value("RUN_REFTOD") glc_nec = case.get_value("GLC_NEC") cism_use_antarctica = case.get_value("CISM_USE_ANTARCTICA") + dglc_use_antarctica = case.get_value("DGLC_USE_ANTARCTICA") mask = case.get_value("MASK_GRID") driver = case.get_value("COMP_INTERFACE").lower() # Create init_generated_files directory if not there - newdir = os.path.join(rundir,"init_generated_files") + newdir = os.path.join(rundir, "init_generated_files") if not os.path.exists(newdir): os.mkdir(newdir) # ----------------------------------------------------- # Error checking # ----------------------------------------------------- - if ( clm_bldnml_opts.find("-namelist") >= 0 ): - expect(False, "The -namelist option is NOT allowed to be part of CLM_BLDNML_OPTS, " + \ - "use the CLM_NAMELIST_OPTS option or add namelist items to user_nl_clm instead " ); + if clm_bldnml_opts.find("-namelist") >= 0: + expect( + False, + "The -namelist option is NOT allowed to be part of CLM_BLDNML_OPTS, " + + "use the CLM_NAMELIST_OPTS option or add namelist items to user_nl_clm instead ", + ) # ----------------------------------------------------- # Set ctsmconf # ----------------------------------------------------- - ctsmconf = os.path.join(caseroot, "Buildconf", compname+"conf") + ctsmconf = os.path.join(caseroot, "Buildconf", compname + "conf") if not os.path.isdir(ctsmconf): os.makedirs(ctsmconf) @@ -92,8 +97,8 @@ def buildnml(case, caseroot, compname): clm_phys = case.get_value("CLM_PHYSICS_VERSION") config_cache_text = _config_cache_template.format(clm_phys=clm_phys) - config_cache_path = os.path.join(caseroot, "Buildconf", compname+"conf", "config_cache.xml") - with open(config_cache_path, 'w') as config_cache_file: + config_cache_path = os.path.join(caseroot, "Buildconf", compname + "conf", "config_cache.xml") + with open(config_cache_path, "w") as config_cache_file: config_cache_file.write(config_cache_text) # ----------------------------------------------------- @@ -111,80 +116,103 @@ def buildnml(case, caseroot, compname): startfile_type = "nrevsn" if clm_force_coldstart == "on": clm_force_coldstart = "off" - logger.warning( "WARNING: You've turned on CLM_FORCE_COLDSTART for a branch run_type, which is a contradiction, the coldstart will be ignored\n" + - " turn off CLM_FORCE_COLDSTART, or set RUN_TYPE=hybrid to get rid of this warning") - + logger.warning( + "WARNING: You've turned on CLM_FORCE_COLDSTART for a branch run_type, which is a contradiction, the coldstart will be ignored\n" + + " turn off CLM_FORCE_COLDSTART, or set RUN_TYPE=hybrid to get rid of this warning" + ) - if (clm_force_coldstart == "on"): - logger.warning( "WARNING: CLM is starting up from a cold state" ) + if clm_force_coldstart == "on": + logger.warning("WARNING: CLM is starting up from a cold state") start_type = "cold" - if lnd_grid == 'T31': - lnd_grid = '48x96' - if lnd_grid == 'T42': - lnd_grid = '64x128' - if lnd_grid == 'T85': - lnd_grid = '128x256' - if lnd_grid == 'T341': - lnd_grid = '512x1024' + if lnd_grid == "T31": + lnd_grid = "48x96" + if lnd_grid == "T42": + lnd_grid = "64x128" + if lnd_grid == "T85": + lnd_grid = "128x256" + if lnd_grid == "T341": + lnd_grid = "512x1024" clmusr = "" if lnd_grid == "CLM_USRDAT": clm_usrdat_name = case.get_value("CLM_USRDAT_NAME") - lnd_grid = clm_usrdat_name - clmusr = " -clm_usr_name %s "%clm_usrdat_name + clmusr = " -clm_usr_name %s " % clm_usrdat_name + # Write warning about initial condition data + if "NEON" in clm_usrdat_name and clm_force_coldstart == "off": + if ("_transient" in clm_nml_use_case) and ( + re.fullmatch(r"\w\w\w\w\.transient", casename) is None + or clm_usrdat_name is "NEON.PRISM" + ): + logger.warning( + "WARNING: Do you have appropriate initial conditions for this simulation?" + + " Check that the finidat file used in the lnd_in namelist is appropriately spunup for your case" + ) if comp_atm != "datm": nomeg = "-no-megan" else: nomeg = "" - if cism_use_antarctica is None: - # This is the case for compsets without CISM, where the CISM_USE_ANTARCTICA xml - # variable isn't defined + + glc_use_antarctica = cism_use_antarctica + if glc_use_antarctica is None: + glc_use_antarctica = dglc_use_antarctica + if glc_use_antarctica is None: + # This is the case for compsets without CISM or DGLC, where the + # CISM_USE_ANTARCTICA and DGLC_USE_ANTARCTICA xml variables are not defined glc_use_antarctica_flag = "" - elif isinstance(cism_use_antarctica, bool): - if cism_use_antarctica: + elif isinstance(glc_use_antarctica, bool): + if glc_use_antarctica: glc_use_antarctica_flag = "-glc_use_antarctica" else: glc_use_antarctica_flag = "" else: - expect(False, "Unexpected value for CISM_USE_ANTARCTICA: {}".format(cism_use_antarctica)) + if cism_use_antarctia: + expect( + False, + "Unexpected value for CISM_USE_ANTARCTICA: {}".format(cism_use_antarctica), + ) + else: + expect( + False, + "Unexpected value for DGLC_USE_ANTARCTICA: {}".format(dglc_use_antarctica), + ) if clm_nml_use_case != "UNSET": - usecase = "-use_case %s" %clm_nml_use_case + usecase = "-use_case %s" % clm_nml_use_case else: usecase = "" - if ( (mask != "null") and (mask != "UNSET") ): - gridmask = "-mask %s" %mask + if (mask != "null") and (mask != "UNSET"): + gridmask = "-mask %s" % mask else: gridmask = "" - start_ymd = run_startdate.replace('-','') + start_ymd = run_startdate.replace("-", "") - if ('-01-01' in run_startdate) or ('-09-01' in run_startdate): + if ("-01-01" in run_startdate) or ("-09-01" in run_startdate): ignore = "-ignore_ic_year" else: ignore = "-ignore_ic_date" - tuning = "-lnd_tuning_mode %s "%lnd_tuning_mode + tuning = "-lnd_tuning_mode %s " % lnd_tuning_mode - spinup = "-clm_accelerated_spinup %s "%clm_accelerated_spinup + spinup = "-clm_accelerated_spinup %s " % clm_accelerated_spinup infile = os.path.join(ctsmconf, "namelist") - inputdata_file = os.path.join(caseroot,"Buildconf","ctsm.input_data_list") + inputdata_file = os.path.join(caseroot, "Buildconf", "ctsm.input_data_list") if driver == "nuopc": lndfrac_setting = " " else: lnd_domain_path = case.get_value("LND_DOMAIN_PATH") lnd_domain_file = case.get_value("LND_DOMAIN_FILE") - lndfrac_file = os.path.join(lnd_domain_path,lnd_domain_file) - lndfrac_setting = "-lnd_frac "+lndfrac_file + lndfrac_file = os.path.join(lnd_domain_path, lnd_domain_file) + lndfrac_setting = "-lnd_frac " + lndfrac_file - config_cache_file = os.path.join(caseroot,"Buildconf", compname+"conf","config_cache.xml") + config_cache_file = os.path.join(caseroot, "Buildconf", compname + "conf", "config_cache.xml") # ----------------------------------------------------- # Clear out old data @@ -198,40 +226,57 @@ def buildnml(case, caseroot, compname): # ----------------------------------------------------- ninst = int(ninst_lnd) - for inst_counter in range(1, ninst+1): + for inst_counter in range(1, ninst + 1): # determine instance string inst_string = "" if ninst > 1: - inst_string = '_' + '%04d' % inst_counter + inst_string = "_" + "%04d" % inst_counter # If multi-instance case does not have restart file, use # single-case restart for each instance rpointer = "rpointer.lnd" - if (os.path.isfile(os.path.join(rundir,rpointer)) and - (not os.path.isfile(os.path.join(rundir,rpointer + inst_string)))): - shutil.copy(os.path.join(rundir, rpointer), - os.path.join(rundir, rpointer + inst_string)) + if os.path.isfile(os.path.join(rundir, rpointer)) and ( + not os.path.isfile(os.path.join(rundir, rpointer + inst_string)) + ): + shutil.copy( + os.path.join(rundir, rpointer), + os.path.join(rundir, rpointer + inst_string), + ) # ----------------------------------------------------- # call build-namelist # ----------------------------------------------------- if run_type == "hybrid" or run_type == "branch": - compnames = [ "clm4", "clm5", "clm2" ] + compnames = ["clm4", "clm5", "clm2"] for comp in compnames: - clm_startfile = "%s.%s%s.r.%s-%s.nc"%(run_refcase,comp,inst_string,run_refdate,run_reftod) - if os.path.exists(os.path.join(rundir, clm_startfile)): - break - else: - clm_startfile = "%s.%s.r.%s-%s.nc"%(run_refcase,comp,run_refdate,run_reftod) - if os.path.exists(os.path.join(rundir, clm_startfile)): - logger.warning( "WARNING: the start file being used for a multi-instance case is a single instance: "+clm_startfile ) + clm_startfile = "%s.%s%s.r.%s-%s.nc" % ( + run_refcase, + comp, + inst_string, + run_refdate, + run_reftod, + ) + if os.path.exists(os.path.join(rundir, clm_startfile)): break + else: + clm_startfile = "%s.%s.r.%s-%s.nc" % ( + run_refcase, + comp, + run_refdate, + run_reftod, + ) + if os.path.exists(os.path.join(rundir, clm_startfile)): + logger.warning( + "WARNING: the start file being used for a multi-instance case is a single instance: " + + clm_startfile + ) + break if not os.path.exists(os.path.join(rundir, clm_startfile)): - logger.warning( "WARNING: Could NOT find a start file to use using"+clm_startfile ) - clm_icfile = "%s = \'%s\'"%(startfile_type, clm_startfile) + logger.warning("WARNING: Could NOT find a start file to use using" + clm_startfile) + clm_icfile = "%s = '%s'" % (startfile_type, clm_startfile) else: clm_icfile = "" @@ -243,25 +288,51 @@ def buildnml(case, caseroot, compname): create_namelist_infile(case, user_nl_file, namelist_infile, "\n".join(infile_lines)) - cmd = os.path.join(lnd_root,"bld","build-namelist") - - command = ("%s -cimeroot %s -infile %s -csmdata %s -inputdata %s %s -namelist \"&clm_inparm start_ymd=%s %s/ \" " - "%s %s -res %s %s -clm_start_type %s -envxml_dir %s " - "-configuration %s -structure %s " - "%s -glc_nec %s %s -co2_ppmv %s -co2_type %s -config %s -driver %s " - "%s %s %s %s" - %(cmd, _CIMEROOT, infile, din_loc_root, inputdata_file, ignore, start_ymd, clm_namelist_opts, - nomeg, usecase, lnd_grid, clmusr, start_type, caseroot, - configuration, structure, - lndfrac_setting, glc_nec, glc_use_antarctica_flag, ccsm_co2_ppmv, clm_co2_type, config_cache_file, driver, - clm_bldnml_opts, spinup, tuning, gridmask)) + cmd = os.path.join(lnd_root, "bld", "build-namelist") + + command = ( + '%s -cimeroot %s -infile %s -csmdata %s -inputdata %s %s -namelist "&clm_inparm start_ymd=%s %s/ " ' + "%s %s -res %s %s -clm_start_type %s -envxml_dir %s " + "-configuration %s -structure %s " + "%s -glc_nec %s %s -co2_ppmv %s -co2_type %s -config %s -driver %s " + "%s %s %s %s" + % ( + cmd, + _CIMEROOT, + infile, + din_loc_root, + inputdata_file, + ignore, + start_ymd, + clm_namelist_opts, + nomeg, + usecase, + lnd_grid, + clmusr, + start_type, + caseroot, + configuration, + structure, + lndfrac_setting, + glc_nec, + glc_use_antarctica_flag, + ccsm_co2_ppmv, + clm_co2_type, + config_cache_file, + driver, + clm_bldnml_opts, + spinup, + tuning, + gridmask, + ) + ) rc, out, err = run_cmd(command, from_dir=ctsmconf) - expect(rc==0,"Command %s failed rc=%d\nout=%s\nerr=%s"%(cmd,rc,out,err)) + expect(rc == 0, "Command %s failed rc=%d\nout=%s\nerr=%s" % (cmd, rc, out, err)) if out is not None: - logger.debug(" %s"%out) + logger.debug(" %s" % out) if err is not None: - logger.debug(" %s"%err) + logger.debug(" %s" % err) # ----------------------------------------------------- # copy resolved namelist to rundir @@ -272,8 +343,9 @@ def buildnml(case, caseroot, compname): file2 = os.path.join(rundir, "lnd_in") if ninst > 1: file2 += inst_string - logger.debug("CTSM namelist copy: file1 %s file2 %s " %(file1, file2)) - shutil.copy(file1,file2) + logger.debug("CTSM namelist copy: file1 %s file2 %s " % (file1, file2)) + shutil.copy(file1, file2) + ############################################################################### def _main_func(): @@ -281,9 +353,12 @@ def _main_func(): caseroot = parse_input(sys.argv) with Case(caseroot) as case: compname = case.get_value("COMP_LND") - logger.warning( "WARNING: buildnml is being called a s program rather than a subroutine " + - "as it is expected to be in the CESM context" ) + logger.warning( + "WARNING: buildnml is being called a s program rather than a subroutine " + + "as it is expected to be in the CESM context" + ) buildnml(case, caseroot, compname) + if __name__ == "__main__": _main_func() diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 7520050cf5..a949a15a17 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -16,7 +16,7 @@ clm4.5: clm5.0: - clm5.1: + clm5.1: Satellite phenology: Satellite phenology with VIC hydrology: @@ -26,6 +26,7 @@ BGC (vert. resol. CN and methane) without anthropomorphic influences: FATES (Functionally Assembled Terrestrial Ecosystem Simulator) Ecosystem Demography model: Satellite phenology with FATES (Functionally Assembled Terrestrial Ecosystem Simulator) Ecosystem Demography model: + No Competition mode with FATES (Functionally Assembled Terrestrial Ecosystem Simulator) Ecosystem Demography model: BGC (vert. resol. CN and methane) with dynamic vegetation: BGC (vert. resol. CN and methane) with dynamic vegetation and prognostic crop: BGC (vert. resol. CN and methane) with prognostic crop, with modifications appropriate for CMIP6 DECK experiments: @@ -63,17 +64,20 @@ Tuning parameters and initial conditions should be optimized for what CLM model version and what meteorlogical forcing combination? UNSET - clm5_0_cam6.0,clm5_0_GSWP3v1,clm5_0_CRUv7,clm4_5_CRUv7,clm4_5_GSWP3v1,clm4_5_cam6.0,clm5_1_GSWP3v1 + clm5_0_cam6.0,clm5_0_GSWP3v1,clm5_0_CRUv7,clm4_5_CRUv7,clm4_5_GSWP3v1,clm4_5_cam6.0,clm5_1_GSWP3v1,clm5_1_cam6.0 clm4_5_CRUv7 clm4_5_CRUv7 clm4_5_GSWP3v1 clm4_5_cam6.0 + clm4_5_cam6.0 clm5_0_CRUv7 clm5_0_CRUv7 clm5_0_GSWP3v1 clm5_0_cam6.0 + clm5_0_cam6.0 clm5_1_GSWP3v1 + clm5_1_cam6.0 @@ -171,6 +175,7 @@ -bgc bgc -crop -bgc fates -no-megan -bgc fates -no-megan + -bgc fates -bgc bgc -dynamic_vegetation @@ -239,10 +244,13 @@ UNSET run_component_ctsm env_run.xml - Dataset name for user-created datasets. This is used as the argument - in Buildconf/clm.buildnml to build-namelist -clm_usr_name. An example of - such a dataset would be 1x1pt_boulderCO_c090722. The default value is UNSET. - This is an advanced flag and should only be used by expert users. + Resolution name for user-created resolutions. This is especially used + for single point and regional resolutions created via subset_data from + the global datasets. This should be set when you use CLM_USRDAT as the grid + to create_newcase. The default value is UNSET. + For NEON cases, this can be set to either NEON or NEON.PRISM, the latter of which would + use PRISM precipitation instead of the default NEON precipitation. NEON cases then also + use the variable NEONSITE to specify the exact site. @@ -277,6 +285,7 @@ $COMP_ROOT_DIR_LND/cime_config/usermods_dirs/cmip6_deck $COMP_ROOT_DIR_LND/cime_config/usermods_dirs/fates_sp + $COMP_ROOT_DIR_LND/cime_config/usermods_dirs/fates_nocomp $COMP_ROOT_DIR_LND/cime_config/usermods_dirs/cmip6_nociso_deck $COMP_ROOT_DIR_LND/cime_config/usermods_dirs/cmip6_waccm_deck $COMP_ROOT_DIR_LND/cime_config/usermods_dirs/cmip6_waccm_nociso_deck @@ -288,10 +297,31 @@ char + ABBY,BLAN,CPER,DEJU,GRSM,HEAL,KONA,LENO,NIWO,ONAQ,PUUM,SERC,SRER,TALL,TREE,WOOD, BARR,BONA,DCFS,DELA,GUAN,JERC,KONZ,MLBS,NOGP,ORNL,RMNP,SJER,STEI,TEAK,UKFS,WREF, - BART,CLBJ,DSNY,HARV,JORN,LAJA,MOAB,OAES,OSBS,SCBI,SOAP,STER,TOOL,UNDE,YELL + BART,CLBJ,DSNY,HARV,JORN,LAJA,MOAB,OAES,OSBS,SCBI,SOAP,STER,TOOL,UNDE,YELL, + NEON_PRECIP.ABBY,NEON_PRECIP.BLAN,NEON_PRECIP.CPER,NEON_PRECIP.DEJU,NEON_PRECIP.GRSM, + NEON_PRECIP.HEAL,NEON_PRECIP.KONA,NEON_PRECIP.LENO,NEON_PRECIP.NIWO,NEON_PRECIP.ONAQ, + NEON_PRECIP.PUUM,NEON_PRECIP.SERC,NEON_PRECIP.SRER,NEON_PRECIP.TALL,NEON_PRECIP.TREE, + NEON_PRECIP.WOOD,NEON_PRECIP.BARR,NEON_PRECIP.BONA,NEON_PRECIP.DCFS,NEON_PRECIP.DELA, + NEON_PRECIP.GUAN,NEON_PRECIP.JERC,NEON_PRECIP.KONZ,NEON_PRECIP.MLBS,NEON_PRECIP.NOGP, + NEON_PRECIP.ORNL,NEON_PRECIP.RMNP,NEON_PRECIP.SJER,NEON_PRECIP.STEI,NEON_PRECIP.TEAK, + NEON_PRECIP.UKFS,NEON_PRECIP.WREF,NEON_PRECIP.BART,NEON_PRECIP.CLBJ,NEON_PRECIP.DSNY, + NEON_PRECIP.HARV,NEON_PRECIP.JORN,NEON_PRECIP.LAJA,NEON_PRECIP.MOAB,NEON_PRECIP.OAES, + NEON_PRECIP.OSBS,NEON_PRECIP.SCBI,NEON_PRECIP.SOAP,NEON_PRECIP.STER,NEON_PRECIP.TOOL, + NEON_PRECIP.UNDE,NEON_PRECIP.YELL, + PRISM_PRECIP.ABBY,PRISM_PRECIP.BLAN,PRISM_PRECIP.CPER,PRISM_PRECIP.GRSM, + PRISM_PRECIP.KONA,PRISM_PRECIP.LENO,PRISM_PRECIP.NIWO,PRISM_PRECIP.ONAQ, + PRISM_PRECIP.SERC,PRISM_PRECIP.SRER,PRISM_PRECIP.TALL,PRISM_PRECIP.TREE, + PRISM_PRECIP.WOOD,PRISM_PRECIP.DCFS,PRISM_PRECIP.DELA,PRISM_PRECIP.JERC, + PRISM_PRECIP.KONZ,PRISM_PRECIP.MLBS,PRISM_PRECIP.NOGP,PRISM_PRECIP.ORNL, + PRISM_PRECIP.RMNP,PRISM_PRECIP.SJER,PRISM_PRECIP.STEI,PRISM_PRECIP.TEAK, + PRISM_PRECIP.UKFS,PRISM_PRECIP.WREF,PRISM_PRECIP.BART,PRISM_PRECIP.CLBJ, + PRISM_PRECIP.DSNY,PRISM_PRECIP.HARV,PRISM_PRECIP.JORN,PRISM_PRECIP.MOAB, + PRISM_PRECIP.OAES,PRISM_PRECIP.OSBS,PRISM_PRECIP.SCBI,PRISM_PRECIP.SOAP, + PRISM_PRECIP.STER,PRISM_PRECIP.UNDE,PRISM_PRECIP.YELL, run_component_ctsm @@ -301,7 +331,7 @@ char - v1,v2,latest + v1,v2,v3,latest latest run_component_ctsm env_run.xml diff --git a/cime_config/config_compsets.xml b/cime_config/config_compsets.xml index 649306b05a..aaa5c68394 100644 --- a/cime_config/config_compsets.xml +++ b/cime_config/config_compsets.xml @@ -41,11 +41,21 @@ 2000_DATM%1PT_CLM51%BGC_SICE_SOCN_SROF_SGLC_SWAV + + I1PtClm51Fates + 2000_DATM%1PT_CLM51%FATES_SICE_SOCN_SROF_SGLC_SWAV + + IHist1PtClm51Bgc HIST_DATM%1PT_CLM51%BGC_SICE_SOCN_SROF_SGLC_SWAV + + IHist1PtClm51Fates + HIST_DATM%1PT_CLM51%FATES_SICE_SOCN_SROF_SGLC_SWAV + + I1PtClm51SpRs 2000_DATM%1PT_CLM51%SP_SICE_SOCN_SROF_SGLC_SWAV @@ -151,7 +161,7 @@ I1850Clm51BgcCrop - 1850_DATM%GSWP3v1_CLM50%BGC-CROP_SICE_SOCN_MOSART_SGLC_SWAV + 1850_DATM%GSWP3v1_CLM51%BGC-CROP_SICE_SOCN_MOSART_SGLC_SWAV @@ -195,6 +205,10 @@ I2000Clm45BgcCropQianRs 2000_DATM%QIA_CLM45%BGC-CROP_SICE_SOCN_SROF_SGLC_SWAV + + I2000Clm50FatesQian + 2000_DATM%QIA_CLM50%FATES_SICE_SOCN_MOSART_SGLC_SWAV + I2000Clm50BgcCruRs @@ -264,12 +278,6 @@ - - I1850Clm51Bgc - 1850_DATM%GSWP3v1_CLM50%BGC_SICE_SOCN_MOSART_SGLC_SWAV - - - I1850Clm51SpNoAnthro 1850_DATM%GSWP3v1_CLM51%SP-NOANTHRO_SICE_SOCN_MOSART_SGLC_SWAV diff --git a/cime_config/config_pes.xml b/cime_config/config_pes.xml index aad134be86..a77ea8ff1f 100644 --- a/cime_config/config_pes.xml +++ b/cime_config/config_pes.xml @@ -113,6 +113,43 @@ + + + + none + + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + @@ -261,6 +298,80 @@ + + + + none + + -1 + -12 + -12 + -12 + -12 + -12 + -12 + -12 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + + + + + Much lower core count f19 layout, mainly for testing + + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + @@ -303,34 +414,34 @@ none - -4 - -4 - -4 - -4 - -4 - -4 - -4 - -4 + -4 + -4 + -4 + -4 + -4 + -4 + -4 + -4 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 @@ -372,6 +483,80 @@ + + + + none + + -1 + -14 + -14 + -14 + -14 + -14 + -14 + -14 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + + + + + Much lower core count f09 layout, mainly for testing + + -1 + -5 + -5 + -5 + -5 + -5 + -5 + -5 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + @@ -557,6 +742,80 @@ + + + + none + + -1 + -5 + -5 + -5 + -5 + -5 + -5 + -5 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + + + + + none + + -1 + -20 + -20 + -20 + -20 + -20 + -20 + -20 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + @@ -1225,6 +1484,44 @@ + + + + none + + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + @@ -1374,5 +1671,228 @@ + + + + none + + -1 + -14 + -14 + -14 + -14 + -14 + -14 + -14 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + + + + + Much lower core count nldas2 layout, mainly for testing + + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + + + Need at least 4 nodes to default to normal queue + + -1 + -3 + -3 + -3 + -3 + -3 + -3 + -3 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + + + + + none + + -1 + -7 + -7 + -7 + -7 + -7 + -7 + -7 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + + + + + + Need at least 2 nodes to devel queue + + -2 + -2 + -2 + -2 + -2 + -2 + -2 + -2 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + + + none + + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + -1 + + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/cime_config/config_tests.xml b/cime_config/config_tests.xml index 0307ee7ef5..536f79aeec 100644 --- a/cime_config/config_tests.xml +++ b/cime_config/config_tests.xml @@ -113,6 +113,16 @@ This defines various CTSM-specific system tests $STOP_N + + Generate prescribed maturity requirements, then test with them + 1 + FALSE + FALSE + never + $STOP_OPTION + $STOP_N + + + + + + FAIL + #2268 + + + + + + FAIL + #1887 + + @@ -37,19 +51,201 @@ - + + + FAIL + #1733 + + + + FAIL - #1844 + ESMCI/ccs_config_cesm#131 + + + FAIL + ESMCI/ccs_config_cesm#130 + + + + + + FAIL + ESMCI/ccs_config_cesm#130 + + + + + + FAIL + ESMCI/ccs_config_cesm#130 + + + + + + FAIL + ESMCI/ccs_config_cesm#130 + + + + + + FAIL + #2310 + + + FAIL + #2310 + + + + + + FAIL + #2310 + + + FAIL + #2310 + + + + + + FAIL + #2310 + + + FAIL + #2310 + + + + + + FAIL + #2310 + + + FAIL + #2310 + + + + + + FAIL + #2310 + + + FAIL + #2310 + + + - + + + FAIL + #2321 + + + + + + FAIL + FATES#701 + + + + + + FAIL + FATES#701 + + + + + + FAIL + FATES#701 + + + + + + PEND + #1045 + + + + + + PEND + #1045 + + + + + + FAIL + ESMCI/ccs_config_cesm#130 + + + + + + FAIL + ESMCI/ccs_config_cesm#130 + + + + + + PEND + FATES#983 + This job should time out on izumi, seems to be hanging on history output. + + + + + + FAIL + FATES#1089 + + + + + + FAIL + FATES#1089 + + + + + + FAIL + #2325 + + + + + + FAIL + #2325 + + + + FAIL - #667 + #2325 diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index dedf27350e..f51e2e2d77 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3,6 +3,7 @@ + @@ -12,14 +13,18 @@ + + + + @@ -29,6 +34,7 @@ + @@ -39,23 +45,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -72,6 +157,7 @@ + @@ -81,6 +167,7 @@ + @@ -90,6 +177,7 @@ + @@ -99,6 +187,7 @@ + @@ -108,6 +197,7 @@ + @@ -118,22 +208,14 @@ + + - - - - - - - - - - @@ -159,13 +241,23 @@ - + - + - - + + + + + + + + + + + + @@ -177,6 +269,15 @@ + + + + + + + + + @@ -186,13 +287,13 @@ - + - + - + @@ -203,6 +304,14 @@ + + + + + + + + @@ -211,9 +320,18 @@ + + + + + + + + + @@ -223,6 +341,8 @@ + + @@ -231,6 +351,7 @@ + @@ -239,6 +360,7 @@ + @@ -248,6 +370,7 @@ + @@ -256,6 +379,7 @@ + @@ -264,6 +388,7 @@ + @@ -273,6 +398,8 @@ + + @@ -287,9 +414,19 @@ + + + + + + + + + + @@ -299,6 +436,7 @@ + @@ -308,6 +446,7 @@ + @@ -327,13 +466,23 @@ - + + + + + + + + + + + @@ -343,6 +492,7 @@ + @@ -352,6 +502,7 @@ + @@ -361,6 +512,7 @@ + @@ -370,6 +522,7 @@ + @@ -379,6 +532,8 @@ + + @@ -387,6 +542,7 @@ + @@ -397,6 +553,8 @@ + + @@ -405,6 +563,7 @@ + @@ -413,6 +572,7 @@ + @@ -422,6 +582,8 @@ + + @@ -430,6 +592,7 @@ + @@ -439,6 +602,7 @@ + @@ -448,6 +612,7 @@ + @@ -467,17 +632,45 @@ - - + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + @@ -507,6 +700,17 @@ + + + + + + + + + + + @@ -515,6 +719,14 @@ + + + + + + + + @@ -524,7 +736,16 @@ - + + + + + + + + + + @@ -550,6 +771,14 @@ + + + + + + + + @@ -558,6 +787,14 @@ + + + + + + + + @@ -566,6 +803,14 @@ + + + + + + + + @@ -575,6 +820,15 @@ + + + + + + + + + @@ -584,6 +838,15 @@ + + + + + + + + + @@ -593,6 +856,15 @@ + + + + + + + + + @@ -602,24 +874,25 @@ - + - - + - + - + + + - - + + @@ -634,6 +907,7 @@ + @@ -643,6 +917,8 @@ + + @@ -651,6 +927,7 @@ + @@ -659,6 +936,7 @@ + @@ -675,6 +953,7 @@ + @@ -684,6 +963,7 @@ + @@ -723,9 +1003,19 @@ + + + + + + + + + + @@ -735,6 +1025,7 @@ + @@ -744,6 +1035,7 @@ + @@ -759,6 +1051,15 @@ + + + + + + + + + @@ -767,6 +1068,14 @@ + + + + + + + + @@ -776,6 +1085,15 @@ + + + + + + + + + @@ -785,9 +1103,19 @@ + + + + + + + + + + @@ -797,6 +1125,7 @@ + @@ -806,6 +1135,7 @@ + @@ -815,6 +1145,7 @@ + @@ -824,6 +1155,7 @@ + @@ -832,6 +1164,7 @@ + @@ -841,6 +1174,7 @@ + @@ -857,6 +1191,16 @@ + + + + + + + + + + @@ -867,6 +1211,16 @@ + + + + + + + + + + @@ -877,6 +1231,16 @@ + + + + + + + + + + @@ -886,27 +1250,63 @@ - + - + + - + - + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -921,6 +1321,14 @@ + + + + + + + + @@ -930,6 +1338,15 @@ + + + + + + + + + @@ -940,6 +1357,16 @@ + + + + + + + + + + @@ -950,12 +1377,12 @@ - + - + - + @@ -963,6 +1390,7 @@ + @@ -977,9 +1405,19 @@ + + + + + + + + + + @@ -989,6 +1427,7 @@ + @@ -998,6 +1437,7 @@ + @@ -1007,6 +1447,7 @@ + @@ -1019,7 +1460,16 @@ - + + + + + + + + + + @@ -1028,6 +1478,10 @@ + + + + @@ -1036,6 +1490,7 @@ + @@ -1045,6 +1500,7 @@ + @@ -1053,6 +1509,7 @@ + @@ -1062,6 +1519,8 @@ + + @@ -1079,6 +1538,7 @@ + @@ -1133,6 +1593,7 @@ + @@ -1157,13 +1618,13 @@ - + - + - + @@ -1175,6 +1636,15 @@ + + + + + + + + + @@ -1184,6 +1654,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1207,6 +1718,7 @@ + @@ -1222,9 +1734,19 @@ + + + + + + + + + + @@ -1235,6 +1757,8 @@ + + @@ -1245,6 +1769,8 @@ + + @@ -1254,12 +1780,14 @@ + + - + @@ -1278,46 +1806,53 @@ - + - - - + + + - + + + + - - + - - + + + + - + + + + - + @@ -1326,6 +1861,7 @@ + @@ -1336,6 +1872,7 @@ + @@ -1345,6 +1882,7 @@ + @@ -1354,6 +1892,7 @@ + @@ -1363,6 +1902,7 @@ + @@ -1372,6 +1912,7 @@ + @@ -1381,6 +1922,7 @@ + @@ -1390,18 +1932,42 @@ + + + + + + + + + + + + + + + + + + + + + - + + + + @@ -1419,6 +1985,12 @@ + + + @@ -1429,16 +2001,6 @@ - - - - - - - - - - @@ -1457,21 +2019,22 @@ - + - + + + + + + - - - - + - @@ -1480,6 +2043,10 @@ + + + + @@ -1490,6 +2057,9 @@ + + + @@ -1498,6 +2068,7 @@ + @@ -1507,21 +2078,13 @@ + + - - - - - - - - - - @@ -1535,15 +2098,21 @@ + + - + - + + + @@ -1552,20 +2121,99 @@ - + - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1576,6 +2224,7 @@ + @@ -1593,23 +2242,23 @@ - - - - - - + + + + + + - - - - - - + + + + + + @@ -1624,6 +2273,7 @@ + @@ -1632,9 +2282,11 @@ + + @@ -1645,19 +2297,30 @@ - + + + + + + + + + + + - + + @@ -1684,6 +2347,8 @@ + + @@ -1693,6 +2358,7 @@ + @@ -1702,6 +2368,7 @@ + @@ -1711,6 +2378,7 @@ + @@ -1730,6 +2398,7 @@ + @@ -1740,6 +2409,8 @@ + + @@ -1766,6 +2437,7 @@ + @@ -1775,6 +2447,7 @@ + @@ -1783,6 +2456,7 @@ + @@ -1792,6 +2466,8 @@ + + @@ -1800,76 +2476,150 @@ + - + + - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + - + + + + + + + + + + + + + + + + + + + + + - + + + + - + @@ -1878,72 +2628,83 @@ - + - + + + - + - + + + + - - + - + - - + - + - + - + + + + - + + - + + - + + @@ -1951,9 +2712,10 @@ - + + @@ -1961,9 +2723,10 @@ - + + @@ -1971,16 +2734,17 @@ - + + - + @@ -1990,6 +2754,16 @@ + + + + + + + + + + @@ -2024,15 +2798,24 @@ + + + + + + + + + - - - - - - + + + + + + @@ -2046,40 +2829,47 @@ - + + + + + + + - - - - - - - + + + + + + + + + - - - - - - - - + + + + + + - + + - + @@ -2088,9 +2878,19 @@ - + + + + + + + + + + + @@ -2098,9 +2898,10 @@ - + + @@ -2108,9 +2909,10 @@ - + + @@ -2119,9 +2921,10 @@ - + + @@ -2129,10 +2932,22 @@ - + + + + + + + + + + + + + @@ -2143,6 +2958,7 @@ + @@ -2150,9 +2966,13 @@ - + + + @@ -2161,21 +2981,27 @@ - + + + - + + + @@ -2183,9 +3009,10 @@ - + + @@ -2194,9 +3021,10 @@ - + + @@ -2207,6 +3035,7 @@ + @@ -2214,9 +3043,10 @@ - + + @@ -2224,9 +3054,10 @@ - + + @@ -2234,83 +3065,100 @@ - + + - + + - + + - + + + + + + + + + + + - + + - + + - - + + - + - + + @@ -2318,7 +3166,7 @@ - + @@ -2328,18 +3176,30 @@ - + + + + + + + + + + + + - + + @@ -2350,6 +3210,7 @@ + @@ -2357,10 +3218,12 @@ - + + + @@ -2370,6 +3233,7 @@ + @@ -2381,6 +3245,9 @@ + + + @@ -2390,6 +3257,7 @@ + @@ -2399,97 +3267,83 @@ - - - - - - + + + + + + - - - - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + @@ -2502,6 +3356,15 @@ + + + + + + + + + @@ -2512,6 +3375,15 @@ + + + + + + + + + @@ -2522,36 +3394,65 @@ + + + + + + + + + - - - - - - + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/cime_config/testdefs/testmods_dirs/clm/DA_multidrv/user_nl_clm_0001 b/cime_config/testdefs/testmods_dirs/clm/DA_multidrv/user_nl_clm_0001 index 719785e25d..17e5cd7da9 100644 --- a/cime_config/testdefs/testmods_dirs/clm/DA_multidrv/user_nl_clm_0001 +++ b/cime_config/testdefs/testmods_dirs/clm/DA_multidrv/user_nl_clm_0001 @@ -10,4 +10,4 @@ hist_type1d_pertape = ' ',' ',' ' use_init_interp = .true. - finidat = '/glade/p/cisl/dares/RDA_strawman/CESM_ensembles/CLM/CLM5BGC-Crop/ctsm_2001-01-01-00000/clm5_f09_spinup80.clm2_0001.r.2001-01-01-00000.nc' + finidat = '/glade/campaign/cisl/dares/glade-p-dares-Oct2023/RDA_strawman/CESM_ensembles/CLM/CLM5BGC-Crop/ctsm_2001-01-01-00000/clm5_f09_spinup80.clm2_0001.r.2001-01-01-00000.nc' diff --git a/cime_config/testdefs/testmods_dirs/clm/DA_multidrv/user_nl_clm_0002 b/cime_config/testdefs/testmods_dirs/clm/DA_multidrv/user_nl_clm_0002 index 37d5b2b24e..6ef6ce8df2 100644 --- a/cime_config/testdefs/testmods_dirs/clm/DA_multidrv/user_nl_clm_0002 +++ b/cime_config/testdefs/testmods_dirs/clm/DA_multidrv/user_nl_clm_0002 @@ -10,4 +10,4 @@ hist_type1d_pertape = ' ',' ',' ' use_init_interp = .true. - finidat = '/glade/p/cisl/dares/RDA_strawman/CESM_ensembles/CLM/CLM5BGC-Crop/ctsm_2001-01-01-00000/clm5_f09_spinup80.clm2_0002.r.2001-01-01-00000.nc' + finidat = '/glade/campaign/cisl/dares/glade-p-dares-Oct2023/RDA_strawman/CESM_ensembles/CLM/CLM5BGC-Crop/ctsm_2001-01-01-00000/clm5_f09_spinup80.clm2_0002.r.2001-01-01-00000.nc' diff --git a/cime_config/testdefs/testmods_dirs/clm/ExcessIceStartup_output_sp_exice/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStartup_output_sp_exice/include_user_mods new file mode 100644 index 0000000000..6d8de3732a --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStartup_output_sp_exice/include_user_mods @@ -0,0 +1,2 @@ +../monthly +../../../../usermods_dirs/output_sp_exice diff --git a/cime_config/testdefs/testmods_dirs/clm/ExcessIceStartup_output_sp_exice/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStartup_output_sp_exice/user_nl_clm new file mode 100644 index 0000000000..e479af9449 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStartup_output_sp_exice/user_nl_clm @@ -0,0 +1,3 @@ + use_excess_ice = .true. + finidat = '$DIN_LOC_ROOT/lnd/clm2/initdata_map/clmi.I1850Clm50Sp.0003-01-01.0.9x1.25_gx1v7_exice_simyr1850_c221205.nc' + use_init_interp = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm new file mode 100644 index 0000000000..f61ca32a79 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/ExcessIceStreams/user_nl_clm @@ -0,0 +1,2 @@ + use_excess_ice = .true. + use_excess_ice_streams = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/Fates/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/Fates/user_nl_clm index 59b639fa84..406fb598f6 100644 --- a/cime_config/testdefs/testmods_dirs/clm/Fates/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/Fates/user_nl_clm @@ -2,12 +2,11 @@ hist_mfilt = 365 hist_nhtfrq = -24 hist_empty_htapes = .true. +hist_ndens = 1 fates_spitfire_mode = 1 hist_fincl1 = 'FATES_NCOHORTS', 'FATES_TRIMMING', 'FATES_AREA_PLANTS', -'FATES_AREA_TREES', 'FATES_COLD_STATUS', 'FATES_DROUGHT_STATUS', 'FATES_GDD', -'FATES_NCHILLDAYS', 'FATES_NCOLDDAYS', 'FATES_DAYSINCE_COLDLEAFOFF', -'FATES_DAYSINCE_COLDLEAFON', 'FATES_DAYSINCE_DROUGHTLEAFOFF', -'FATES_DAYSINCE_DROUGHTLEAFON', 'FATES_MEANLIQVOL_DROUGHTPHEN', +'FATES_AREA_TREES', 'FATES_COLD_STATUS', 'FATES_GDD', +'FATES_NCHILLDAYS', 'FATES_NCOLDDAYS', 'FATES_DAYSINCE_COLDLEAFOFF','FATES_DAYSINCE_COLDLEAFON', 'FATES_CANOPY_SPREAD', 'FATES_NESTEROV_INDEX', 'FATES_IGNITIONS', 'FATES_FDI', 'FATES_ROS','FATES_EFFECT_WSPEED', 'FATES_FUELCONSUMED', 'FATES_FIRE_INTENSITY', 'FATES_FIRE_INTENSITY_BURNFRAC', 'FATES_BURNFRAC', 'FATES_FUEL_MEF', @@ -15,12 +14,10 @@ hist_fincl1 = 'FATES_NCOHORTS', 'FATES_TRIMMING', 'FATES_AREA_PLANTS', 'FATES_FUEL_AMOUNT', 'FATES_LITTER_IN', 'FATES_LITTER_OUT', 'FATES_SEED_BANK', 'FATES_SEEDS_IN', 'FATES_STOREC', 'FATES_VEGC', 'FATES_SAPWOODC', 'FATES_LEAFC', 'FATES_FROOTC', 'FATES_REPROC', -'FATES_CEFFLUX', 'FATES_STRUCTC', 'FATES_NONSTRUCTC', 'FATES_VEGC_ABOVEGROUND', +'FATES_STRUCTC', 'FATES_NONSTRUCTC', 'FATES_VEGC_ABOVEGROUND', 'FATES_CANOPY_VEGC', 'FATES_USTORY_VEGC', 'FATES_PRIMARY_PATCHFUSION_ERR', -'FATES_DISTURBANCE_RATE_P2P', 'FATES_DISTURBANCE_RATE_P2S', -'FATES_DISTURBANCE_RATE_S2S', 'FATES_DISTURBANCE_RATE_FIRE', +'FATES_HARVEST_CARBON_FLUX', 'FATES_DISTURBANCE_RATE_FIRE', 'FATES_DISTURBANCE_RATE_LOGGING', 'FATES_DISTURBANCE_RATE_TREEFALL', -'FATES_DISTURBANCE_RATE_POTENTIAL', 'FATES_HARVEST_CARBON_FLUX', 'FATES_STOMATAL_COND', 'FATES_LBLAYER_COND', 'FATES_NPP', 'FATES_GPP', 'FATES_AUTORESP', 'FATES_GROWTH_RESP', 'FATES_MAINT_RESP', 'FATES_GPP_CANOPY', 'FATES_AUTORESP_CANOPY', 'FATES_GPP_USTORY', 'FATES_AUTORESP_USTORY', @@ -29,4 +26,5 @@ hist_fincl1 = 'FATES_NCOHORTS', 'FATES_TRIMMING', 'FATES_AREA_PLANTS', 'FATES_NEP', 'FATES_HET_RESP', 'FATES_FIRE_CLOSS', 'FATES_FIRE_FLUX_EL', 'FATES_CBALANCE_ERROR', 'FATES_ERROR_EL', 'FATES_LEAF_ALLOC', 'FATES_SEED_ALLOC', 'FATES_STEM_ALLOC', 'FATES_FROOT_ALLOC', -'FATES_CROOT_ALLOC', 'FATES_STORE_ALLOC' +'FATES_CROOT_ALLOC', 'FATES_STORE_ALLOC', +'FATES_PATCHAREA_LU', 'FATES_DISTURBANCE_RATE_MATRIX_LULU' diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDef/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesCold/include_user_mods similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDef/include_user_mods rename to cime_config/testdefs/testmods_dirs/clm/FatesCold/include_user_mods diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDef/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesCold/shell_commands similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDef/shell_commands rename to cime_config/testdefs/testmods_dirs/clm/FatesCold/shell_commands diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefAllVars/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdAllVars/shell_commands similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefAllVars/shell_commands rename to cime_config/testdefs/testmods_dirs/clm/FatesColdAllVars/shell_commands diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefAllVars/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdAllVars/user_nl_clm similarity index 94% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefAllVars/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdAllVars/user_nl_clm index b7d33c87d2..7f5ece27c8 100644 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefAllVars/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdAllVars/user_nl_clm @@ -3,6 +3,7 @@ hist_mfilt = 365 hist_nhtfrq = -24 hist_empty_htapes = .false. fates_spitfire_mode = 1 +hist_ndens = 1 hist_fincl1 = 'FATES_CROWNAREA_PF', 'FATES_CANOPYCROWNAREA_PF', 'FATES_NCL_AP', 'FATES_NPATCH_AP', 'FATES_VEGC_AP', 'FATES_SECONDARY_FOREST_FRACTION', 'FATES_WOOD_PRODUCT', @@ -16,7 +17,7 @@ hist_fincl1 = 'FATES_CROWNAREA_PF', 'FATES_CANOPYCROWNAREA_PF', 'FATES_FABI_SUN_CLLLPF', 'FATES_FABI_SHA_CLLLPF', 'FATES_FABD_SUN_CLLL', 'FATES_FABD_SHA_CLLL', 'FATES_FABI_SUN_CLLL', 'FATES_FABI_SHA_CLLL', 'FATES_PARPROF_DIR_CLLLPF', 'FATES_PARPROF_DIF_CLLLPF', -'FATES_PARPROF_DIR_CLLL', 'FATES_PARPROF_DIF_CLLL', 'FATES_FABD_SUN_TOPLF_CL', +'FATES_FABD_SUN_TOPLF_CL', 'FATES_FABD_SHA_TOPLF_CL', 'FATES_FABI_SUN_TOPLF_CL', 'FATES_FABI_SHA_TOPLF_CL', 'FATES_NET_C_UPTAKE_CLLL', 'FATES_CROWNAREA_CLLL', 'FATES_NPLANT_CANOPY_SZAP', 'FATES_NPLANT_USTORY_SZAP', 'FATES_DDBH_CANOPY_SZAP', 'FATES_DDBH_USTORY_SZAP', @@ -70,4 +71,7 @@ hist_fincl1 = 'FATES_CROWNAREA_PF', 'FATES_CANOPYCROWNAREA_PF', 'FATES_CROOTMAINTAR_USTORY_SZ', 'FATES_FROOTMAINTAR_USTORY_SZ', 'FATES_GROWAR_USTORY_SZ', 'FATES_MAINTAR_USTORY_SZ', 'FATES_VEGC_SZPF', 'FATES_LEAFC_SZPF', 'FATES_FROOTC_SZPF', 'FATES_SAPWOODC_SZPF', -'FATES_STOREC_SZPF', 'FATES_REPROC_SZPF', 'FATES_CEFFLUX_SZPF' +'FATES_STOREC_SZPF', 'FATES_REPROC_SZPF', 'FATES_DROUGHT_STATUS_PF', +'FATES_DAYSINCE_DROUGHTLEAFOFF_PF', 'FATES_DAYSINCE_DROUGHTLEAFON_PF', +'FATES_MEANLIQVOL_DROUGHTPHEN_PF', 'FATES_MEANSMP_DROUGHTPHEN_PF', +'FATES_ELONG_FACTOR_PF' diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdBasic/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdBasic/shell_commands new file mode 100644 index 0000000000..586dff9dd5 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdBasic/shell_commands @@ -0,0 +1,2 @@ +./xmlchange CLM_FORCE_COLDSTART="on" +./xmlchange BFBFLAG="TRUE" diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefCH4Off/README b/cime_config/testdefs/testmods_dirs/clm/FatesColdCH4Off/README similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefCH4Off/README rename to cime_config/testdefs/testmods_dirs/clm/FatesColdCH4Off/README diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdCH4Off/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdCH4Off/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdCH4Off/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdCH4Off/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdCH4Off/user_nl_clm new file mode 100644 index 0000000000..9f977ac5ce --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdCH4Off/user_nl_clm @@ -0,0 +1,2 @@ +use_lch4 = .false. +hist_fields_list_file = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefCH4Off/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefCH4Off/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefCH4Off/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefCH4Off/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefCH4Off/user_nl_clm deleted file mode 100644 index 4d7617fed4..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefCH4Off/user_nl_clm +++ /dev/null @@ -1,2 +0,0 @@ -use_lch4 = .false. -hist_master_list_file = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefDryDep/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefDryDep/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefDryDep/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefDryDepReducedComplexSatPhen/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefDryDepReducedComplexSatPhen/include_user_mods deleted file mode 100644 index f754dacdab..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefDryDepReducedComplexSatPhen/include_user_mods +++ /dev/null @@ -1,2 +0,0 @@ -../FatesColdDefDryDep -../FatesColdDefReducedComplexSatPhen diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefHydro/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefHydro/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefHydro/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefLandUse/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefLandUse/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefLandUse/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefLogging/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefLogging/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefLogging/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefMegan/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefMegan/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefMegan/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefMeganReducedComplexSatPhen/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefMeganReducedComplexSatPhen/include_user_mods deleted file mode 100644 index 5dd78011ec..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefMeganReducedComplexSatPhen/include_user_mods +++ /dev/null @@ -1,2 +0,0 @@ -../FatesColdDefMegan -../FatesColdDefReducedComplexSatPhen diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefNoFire/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefNoFire/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefNoFire/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPPhys/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPPhys/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPPhys/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPRT2/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPRT2/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPRT2/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPRT2/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPRT2/user_nl_clm deleted file mode 100644 index bde61cee2e..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPRT2/user_nl_clm +++ /dev/null @@ -1 +0,0 @@ -fates_parteh_mode = 2 \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexFixedBiogeo/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexFixedBiogeo/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexFixedBiogeo/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexNoComp/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexNoComp/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexNoComp/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen/include_user_mods deleted file mode 100644 index a38be35f63..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen/include_user_mods +++ /dev/null @@ -1,2 +0,0 @@ -../FatesColdDef -../../../../usermods_dirs/fates_sp diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen/user_nl_clm deleted file mode 100644 index 5ae85742ed..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen/user_nl_clm +++ /dev/null @@ -1,2 +0,0 @@ -use_fates_sp = .true. -fates_spitfire_mode = 0 diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen_prescribed/README b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen_prescribed/README deleted file mode 100644 index a1c8f04632..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen_prescribed/README +++ /dev/null @@ -1,3 +0,0 @@ -This testmod currently does NOT work, because of issue #1722 - -See https://github.com/ESCOMP/CTSM/issues/1722 diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen_prescribed/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen_prescribed/include_user_mods deleted file mode 100644 index 7fe9b048d5..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexSatPhen_prescribed/include_user_mods +++ /dev/null @@ -1,2 +0,0 @@ -../FatesColdDefReducedComplexSatPhen -../prescribed diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefST3/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefST3/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefST3/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefSizeAgeMort/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefSizeAgeMort/include_user_mods deleted file mode 100644 index 1f2c03aebe..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefSizeAgeMort/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefTreeDamage/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDefTreeDamage/include_user_mods deleted file mode 100644 index e269feea6f..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefTreeDamage/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../FatesColdDef \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDep/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDep/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDep/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefDryDep/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDep/shell_commands similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefDryDep/shell_commands rename to cime_config/testdefs/testmods_dirs/clm/FatesColdDryDep/shell_commands diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDepSatPhen/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDepSatPhen/include_user_mods new file mode 100644 index 0000000000..f0a10ab452 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDepSatPhen/include_user_mods @@ -0,0 +1 @@ +../FatesColdBasic diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDepSatPhen/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDepSatPhen/shell_commands new file mode 100644 index 0000000000..870cd99bf5 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDepSatPhen/shell_commands @@ -0,0 +1,3 @@ +./xmlchange CLM_BLDNML_OPTS='--drydep' --append +# The following would be required if you want to run with DryDep and FATES without Fates-SP mode +#./xmlchange CLM_BLDNML_OPTS="--ignore_warnings" --append diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDepSatPhen/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDepSatPhen/user_nl_clm new file mode 100644 index 0000000000..3749fd1d61 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdDryDepSatPhen/user_nl_clm @@ -0,0 +1,4 @@ + +hist_mfilt = 365 +hist_nhtfrq = -24 + diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdFixedBiogeo/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdFixedBiogeo/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdFixedBiogeo/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexFixedBiogeo/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdFixedBiogeo/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexFixedBiogeo/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdFixedBiogeo/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdHydro/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdHydro/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdHydro/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefHydro/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdHydro/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefHydro/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdHydro/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdLUH2/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdLUH2/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdLUH2/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdLUH2/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdLUH2/user_nl_clm new file mode 100644 index 0000000000..854c21407f --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdLUH2/user_nl_clm @@ -0,0 +1 @@ +use_fates_luh = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdLandUse/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdLandUse/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdLandUse/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefLandUse/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdLandUse/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefLandUse/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdLandUse/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdLogging/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdLogging/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdLogging/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefLogging/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdLogging/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefLogging/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdLogging/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdMegan/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdMegan/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdMegan/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefMegan/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdMegan/shell_commands similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefMegan/shell_commands rename to cime_config/testdefs/testmods_dirs/clm/FatesColdMegan/shell_commands diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdMeganSatPhen/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdMeganSatPhen/include_user_mods new file mode 100644 index 0000000000..f0a10ab452 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdMeganSatPhen/include_user_mods @@ -0,0 +1 @@ +../FatesColdBasic diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdMeganSatPhen/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdMeganSatPhen/shell_commands new file mode 100644 index 0000000000..749af3486f --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdMeganSatPhen/shell_commands @@ -0,0 +1,2 @@ +./xmlchange CLM_BLDNML_OPTS='--megan' --append +./xmlchange CLM_BLDNML_OPTS="--ignore_warnings" --append diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdMeganSatPhen/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdMeganSatPhen/user_nl_clm new file mode 100644 index 0000000000..3749fd1d61 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdMeganSatPhen/user_nl_clm @@ -0,0 +1,4 @@ + +hist_mfilt = 365 +hist_nhtfrq = -24 + diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdNoComp/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoComp/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoComp/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexNoComp/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoComp/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefReducedComplexNoComp/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdNoComp/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompFixedBioGeo/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompFixedBioGeo/include_user_mods new file mode 100644 index 0000000000..ea160c525f --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompFixedBioGeo/include_user_mods @@ -0,0 +1 @@ +../FatesColdNoComp \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompFixedBioGeo/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompFixedBioGeo/user_nl_clm new file mode 100644 index 0000000000..d3349780e0 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompFixedBioGeo/user_nl_clm @@ -0,0 +1 @@ +use_fates_fixed_biogeog=.true. \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompNoFire/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompNoFire/shell_commands new file mode 100644 index 0000000000..0a8098ef7f --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompNoFire/shell_commands @@ -0,0 +1,2 @@ +./xmlchange CLM_FORCE_COLDSTART="on" +./xmlchange CLM_BLDNML_OPTS="-ignore_warnings" --append \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompNoFire/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompNoFire/user_nl_clm new file mode 100644 index 0000000000..4302b4a1d3 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoCompNoFire/user_nl_clm @@ -0,0 +1,2 @@ +use_fates_nocomp = .true. +fates_spitfire_mode = 0 diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdNoFire/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoFire/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoFire/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefNoFire/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdNoFire/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefNoFire/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdNoFire/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdPPhys/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdPPhys/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdPPhys/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefPPhys/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdPPhys/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefPPhys/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdPPhys/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/user_nl_clm new file mode 100644 index 0000000000..679f025b60 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdPRT2/user_nl_clm @@ -0,0 +1,12 @@ +fates_parteh_mode = 2 +hist_fincl1 = 'FATES_L2FR','FATES_L2FR_CANOPY_REC_PF','FATES_L2FR_USTORY_REC_PF', +'FATES_NH4UPTAKE_SZPF','FATES_NO3UPTAKE_SZPF','FATES_NEFFLUX_SZPF', +'FATES_NDEMAND_SZPF','FATES_NFIX_SYM_SZPF','FATES_NH4UPTAKE','FATES_NO3UPTAKE', +'FATES_NEFFLUX','FATES_NDEMAND','FATES_NFIX_SYM','FATES_STOREN','FATES_STOREN_TF', +'FATES_VEGN','FATES_SAPWOODN','FATES_LEAFN','FATES_FROOTN','FATES_REPRON','FATES_VEGN_SZPF', +'FATES_LEAFN_SZPF','FATES_FROOTN_SZPF','FATES_SAPWOODN_SZPF','FATES_STOREN_SZPF','FATES_STOREN_TF_CANOPY_SZPF', +'FATES_STOREN_TF_USTORY_SZPF','FATES_REPRON_SZPF','FATES_STOREP','FATES_STOREP_TF','FATES_VEGP','FATES_SAPWOODP', +'FATES_LEAFP','FATES_FROOTP','FATES_REPROP','FATES_PUPTAKE','FATES_PEFFLUX','FATES_PDEMAND', +'FATES_VEGP_SZPF','FATES_LEAFP_SZPF','FATES_FROOTP_SZPF','FATES_SAPWOODP_SZPF','FATES_STOREP_SZPF', +'FATES_STOREP_TF_CANOPY_SZPF','FATES_STOREP_TF_USTORY_SZPF','FATES_REPROP_SZPF','FATES_PUPTAKE_SZPF', +'FATES_PEFFLUX_SZPF','FATES_PDEMAND_SZPF' diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdST3/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdST3/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdST3/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefST3/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdST3/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefST3/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdST3/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen/include_user_mods new file mode 100644 index 0000000000..f0a10ab452 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen/include_user_mods @@ -0,0 +1 @@ +../FatesColdBasic diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen/user_nl_clm new file mode 100644 index 0000000000..3749fd1d61 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen/user_nl_clm @@ -0,0 +1,4 @@ + +hist_mfilt = 365 +hist_nhtfrq = -24 + diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen_prescribed/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen_prescribed/include_user_mods new file mode 100644 index 0000000000..33ca1de12e --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen_prescribed/include_user_mods @@ -0,0 +1 @@ +../FatesColdSatPhen diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen_prescribed/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen_prescribed/user_nl_clm new file mode 100644 index 0000000000..59a4137be7 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdSatPhen_prescribed/user_nl_clm @@ -0,0 +1,3 @@ +use_lai_streams = .true. +lai_tintalgo = 'lower' ! set time interpolation to use lower value, so can compare more directly to input dataset + \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/README b/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/README new file mode 100644 index 0000000000..484fa67db9 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/README @@ -0,0 +1,26 @@ +Testing FATES cross grid seed dispersal is activated by enabling the +namelist switch option fates_seeddisp_cadence as well as providing reasonable +values to the fates parameter file for the following variables: + +fates_seed_dispersal_fraction +fates_seed_dispersal_max_dist +fates_seed_dispersal_pdf_scale +fates_seed_dispersal_pdf_shape + +Given that the default fates parameter file has the above variables as unset, +a custom fates parameter file must be supplied to appropriately test this mode. +This testmod itself addresses CTSM issue 2151: https://github.com/ESCOMP/CTSM/issues/2151 +Note that to avoid exceeding the filename string length maximu, the parameter +file generated on the fly is placed in the $SRCROOT/src/fates/parameter_files +directory. This may still run into problems is the $SRCROOT string is too long. + +The max_dist value will impact the size of the 'neighborhood' of gridcells +that fates will attempt to distribute seeds to. To limit the neighborhood to +something tractable for a regression test, the user_nl_clm file points to a +specific fates parameter file that was generated to work with a 5x5_amazon +resolution. + +The main downside of this method is that this file will require a custom update +for every fates parameter file API update. Addressing CTSM issue #2126 will alleviate +this issue as it will provide the capability to build the fates parameter file on +the fly which with the appropriate values for this test. diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/shell_commands new file mode 100644 index 0000000000..94a832af25 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/shell_commands @@ -0,0 +1,11 @@ +SRCDIR=`./xmlquery SRCROOT --value` +CASEDIR=`./xmlquery CASEROOT --value` +FATESDIR=$SRCDIR/src/fates/ +FATESPARAMFILE=$SRCDIR/fates_params_seeddisp_4x5.nc + +ncgen -o $FATESPARAMFILE $FATESDIR/parameter_files/fates_params_default.cdl + +$FATESDIR/tools/modify_fates_paramfile.py --O --fin $FATESPARAMFILE --fout $FATESPARAMFILE --var fates_seed_dispersal_fraction --val 0.2 --allpfts +$FATESDIR/tools/modify_fates_paramfile.py --O --fin $FATESPARAMFILE --fout $FATESPARAMFILE --var fates_seed_dispersal_max_dist --val 2500000 --allpfts +$FATESDIR/tools/modify_fates_paramfile.py --O --fin $FATESPARAMFILE --fout $FATESPARAMFILE --var fates_seed_dispersal_pdf_scale --val 1e-05 --allpfts +$FATESDIR/tools/modify_fates_paramfile.py --O --fin $FATESPARAMFILE --fout $FATESPARAMFILE --var fates_seed_dispersal_pdf_shape --val 0.1 --allpfts diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/user_nl_clm new file mode 100644 index 0000000000..e8d24253c1 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdSeedDisp/user_nl_clm @@ -0,0 +1,3 @@ +fates_paramfile = '$SRCROOT/fates_params_seeddisp_4x5.nc' +fates_seeddisp_cadence = 1 +hist_fincl1 = 'FATES_SEEDS_IN_GRIDCELL_PF', 'FATES_SEEDS_OUT_GRIDCELL_PF' diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdSizeAgeMort/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdSizeAgeMort/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdSizeAgeMort/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefSizeAgeMort/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdSizeAgeMort/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefSizeAgeMort/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdSizeAgeMort/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdTreeDamage/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdTreeDamage/include_user_mods new file mode 100644 index 0000000000..5417dbaa1c --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdTreeDamage/include_user_mods @@ -0,0 +1 @@ +../FatesCold \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdDefTreeDamage/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdTreeDamage/user_nl_clm similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/FatesColdDefTreeDamage/user_nl_clm rename to cime_config/testdefs/testmods_dirs/clm/FatesColdTreeDamage/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/README b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/README new file mode 100644 index 0000000000..295f8125f3 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/README @@ -0,0 +1,15 @@ +Testing FATES two-stream radiation scheme is activated by switching the fates_rad_model +parameter from 1 to 2. This is all that is needed, both radiation schemes +1) Norman and 2) two-stream use the same optical parameters. + +fates_rad_model + +Note that to avoid exceeding the filename string length maximum, the parameter +file generated on the fly is placed in the $SRCROOT/src/fates/parameter_files +directory. This may still run into problems is the $SRCROOT string is too long. + +Like the test with seed dispersal activation, the main downside of this method is +that this file will require a custom update for every fates parameter file API update. +Addressing CTSM issue #2126 will alleviate +this issue as it will provide the capability to build the fates parameter file on +the fly which with the appropriate values for this test. diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/include_user_mods new file mode 100644 index 0000000000..14f7591b72 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/include_user_mods @@ -0,0 +1 @@ +../FatesCold diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/shell_commands new file mode 100644 index 0000000000..5d94e5f659 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/shell_commands @@ -0,0 +1,8 @@ +SRCDIR=`./xmlquery SRCROOT --value` +CASEDIR=`./xmlquery CASEROOT --value` +FATESDIR=$SRCDIR/src/fates +FATESPARAMFILE=$CASEDIR/fates_params_twostream.nc + +ncgen -o $FATESPARAMFILE $FATESDIR/parameter_files/fates_params_default.cdl + +$FATESDIR/tools/modify_fates_paramfile.py --O --fin $FATESPARAMFILE --fout $FATESPARAMFILE --var fates_rad_model --val 2 --allpfts diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/user_nl_clm new file mode 100644 index 0000000000..cae5fc2112 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStream/user_nl_clm @@ -0,0 +1 @@ +fates_paramfile = '$CASEROOT/fates_params_twostream.nc' diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/README b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/README new file mode 100644 index 0000000000..d2c2269fae --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/README @@ -0,0 +1,3 @@ +This tests two-stream radiation crossed with fixed biogeography and nocomp, for +a description of how two-stream is turned on for tests see +FatesColdTwoStream/README diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/include_user_mods new file mode 100644 index 0000000000..17d5840e8c --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/include_user_mods @@ -0,0 +1 @@ +../FatesColdNoComp diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/shell_commands new file mode 100644 index 0000000000..5d94e5f659 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/shell_commands @@ -0,0 +1,8 @@ +SRCDIR=`./xmlquery SRCROOT --value` +CASEDIR=`./xmlquery CASEROOT --value` +FATESDIR=$SRCDIR/src/fates +FATESPARAMFILE=$CASEDIR/fates_params_twostream.nc + +ncgen -o $FATESPARAMFILE $FATESDIR/parameter_files/fates_params_default.cdl + +$FATESDIR/tools/modify_fates_paramfile.py --O --fin $FATESPARAMFILE --fout $FATESPARAMFILE --var fates_rad_model --val 2 --allpfts diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/user_nl_clm new file mode 100644 index 0000000000..362dfa4a5e --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdTwoStreamNoCompFixedBioGeo/user_nl_clm @@ -0,0 +1,2 @@ +fates_paramfile = '$CASEROOT/fates_params_twostream.nc' +use_fates_fixed_biogeog=.true. \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesPRISM/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/FatesPRISM/include_user_mods new file mode 100644 index 0000000000..4c7aa0f2b4 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesPRISM/include_user_mods @@ -0,0 +1 @@ +../Fates diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesPRISM/shell_commands b/cime_config/testdefs/testmods_dirs/clm/FatesPRISM/shell_commands new file mode 100644 index 0000000000..39812a8706 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/FatesPRISM/shell_commands @@ -0,0 +1,2 @@ +#!/bin/bash +./xmlchange CLM_USRDAT_NAME=NEON.PRISM diff --git a/cime_config/testdefs/testmods_dirs/clm/PRISM/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/PRISM/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/PRISM/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/PRISM/shell_commands b/cime_config/testdefs/testmods_dirs/clm/PRISM/shell_commands new file mode 100644 index 0000000000..39812a8706 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/PRISM/shell_commands @@ -0,0 +1,2 @@ +#!/bin/bash +./xmlchange CLM_USRDAT_NAME=NEON.PRISM diff --git a/cime_config/testdefs/testmods_dirs/clm/SaveHistFieldList/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/SaveHistFieldList/user_nl_clm new file mode 100644 index 0000000000..4791cd28b2 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/SaveHistFieldList/user_nl_clm @@ -0,0 +1 @@ +hist_fields_list_file = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/USUMB_mct/shell_commands b/cime_config/testdefs/testmods_dirs/clm/USUMB_mct/shell_commands index 446125abf9..e410197c3d 100755 --- a/cime_config/testdefs/testmods_dirs/clm/USUMB_mct/shell_commands +++ b/cime_config/testdefs/testmods_dirs/clm/USUMB_mct/shell_commands @@ -1,12 +1,12 @@ -# shell commands to execute xmlchange commands written by PTCLMmkdata: -# ./PTCLMmkdata --cesm_root ../../../.. -s US-UMB -d /glade/p/cesm/cseg/inputdata --mydatadir=/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024 +#!/bin/bash +# shell commands to execute xmlchange commands written by PTCLMmkdata: which is now unsupported ./xmlchange CLM_USRDAT_NAME=1x1pt_US-UMB ./xmlchange DATM_CLMNCEP_YR_START=1999 ./xmlchange DATM_CLMNCEP_YR_END=2006 # Comment this out if NINST_LND is greater than 1 (see: http://bugs.cgd.ucar.edu/show_bug.cgi?id=2521) ./xmlchange MPILIB=mpi-serial -./xmlchange ATM_DOMAIN_PATH=/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024/1x1pt_US-UMB -./xmlchange LND_DOMAIN_PATH=/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024/1x1pt_US-UMB +./xmlchange ATM_DOMAIN_PATH='$DIN_LOC_ROOT/lnd/clm2/PTCLMmydatafiles.c171024/1x1pt_US-UMB' +./xmlchange LND_DOMAIN_PATH='$DIN_LOC_ROOT/lnd/clm2/PTCLMmydatafiles.c171024/1x1pt_US-UMB' ./xmlchange ATM_DOMAIN_FILE=domain.lnd.1x1pt_US-UMB_navy.171024.nc ./xmlchange LND_DOMAIN_FILE=domain.lnd.1x1pt_US-UMB_navy.171024.nc ./xmlchange --append CLM_BLDNML_OPTS='-mask navy -no-crop' @@ -15,5 +15,4 @@ ./xmlchange ATM_NCPL=24 ./xmlchange RUN_STARTDATE=1999-01-01 ./xmlchange DATM_CLMNCEP_YR_ALIGN=1999 -./xmlchange DIN_LOC_ROOT=/glade/p/cesm/cseg/inputdata -./xmlchange DIN_LOC_ROOT_CLMFORC=/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024 +./xmlchange DIN_LOC_ROOT_CLMFORC='$DIN_LOC_ROOT/lnd/clm2/PTCLMmydatafiles.c171024' diff --git a/cime_config/testdefs/testmods_dirs/clm/USUMB_mct/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/USUMB_mct/user_nl_clm index 38ce400297..8bb7848d49 100644 --- a/cime_config/testdefs/testmods_dirs/clm/USUMB_mct/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/USUMB_mct/user_nl_clm @@ -1,5 +1,4 @@ -! user_nl_clm namelist options written by PTCLMmkdata: -! ./PTCLMmkdata --cesm_root ../../../.. -s US-UMB -d /glade/p/cesm/cseg/inputdata --mydatadir=/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024 - fsurdat = '/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024/1x1pt_US-UMB/surfdata_1x1pt_US-UMB_16pfts_Irrig_CMIP6_simyr2000_c171024.nc' +! user_nl_clm namelist options written by PTCLMmkdata, which is no longer available + fsurdat = '$DIN_LOC_ROOT/lnd/clm2/PTCLMmydatafiles.c171024/1x1pt_US-UMB/surfdata_1x1pt_US-UMB_16pfts_Irrig_CMIP6_simyr2000_c171024.nc' hist_nhtfrq = 0 hist_mfilt = 1200 diff --git a/cime_config/testdefs/testmods_dirs/clm/USUMB_nuopc/shell_commands b/cime_config/testdefs/testmods_dirs/clm/USUMB_nuopc/shell_commands index 08a9014abe..43fe16a192 100755 --- a/cime_config/testdefs/testmods_dirs/clm/USUMB_nuopc/shell_commands +++ b/cime_config/testdefs/testmods_dirs/clm/USUMB_nuopc/shell_commands @@ -1,5 +1,4 @@ -# shell commands to execute xmlchange commands written by PTCLMmkdata: -# ./PTCLMmkdata --cesm_root ../../../.. -s US-UMB -d /glade/p/cesm/cseg/inputdata --mydatadir=/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024 +# shell commands to execute xmlchange commands written by PTCLMmkdata: which is now unsupported ./xmlchange CLM_USRDAT_NAME=1x1pt_US-UMB ./xmlchange DATM_YR_START=1999 ./xmlchange DATM_YR_END=2006 @@ -11,7 +10,6 @@ ./xmlchange ATM_NCPL=24 ./xmlchange RUN_STARTDATE=1999-01-01 ./xmlchange DATM_YR_ALIGN=1999 -./xmlchange DIN_LOC_ROOT=/glade/p/cesm/cseg/inputdata -./xmlchange DIN_LOC_ROOT_CLMFORC=/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024 +./xmlchange DIN_LOC_ROOT_CLMFORC='$DIN_LOC_ROOT/lnd/clm2/PTCLMmydatafiles.c171024' ./xmlchange PTS_LON=275.2862 ./xmlchange PTS_LAT=45.5598 diff --git a/cime_config/testdefs/testmods_dirs/clm/USUMB_nuopc/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/USUMB_nuopc/user_nl_clm index 38ce400297..8bb7848d49 100644 --- a/cime_config/testdefs/testmods_dirs/clm/USUMB_nuopc/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/USUMB_nuopc/user_nl_clm @@ -1,5 +1,4 @@ -! user_nl_clm namelist options written by PTCLMmkdata: -! ./PTCLMmkdata --cesm_root ../../../.. -s US-UMB -d /glade/p/cesm/cseg/inputdata --mydatadir=/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024 - fsurdat = '/glade/p/cesm/cseg/inputdata/lnd/clm2/PTCLMmydatafiles.c171024/1x1pt_US-UMB/surfdata_1x1pt_US-UMB_16pfts_Irrig_CMIP6_simyr2000_c171024.nc' +! user_nl_clm namelist options written by PTCLMmkdata, which is no longer available + fsurdat = '$DIN_LOC_ROOT/lnd/clm2/PTCLMmydatafiles.c171024/1x1pt_US-UMB/surfdata_1x1pt_US-UMB_16pfts_Irrig_CMIP6_simyr2000_c171024.nc' hist_nhtfrq = 0 hist_mfilt = 1200 diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/user_nl_clm index 7ae4a69aad..c235d72df1 100644 --- a/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/user_nl_clm @@ -1,2 +1,2 @@ -paramfile = '/glade/p/cesm/cseg/inputdata/lnd/clm2/paramdata/ctsm51_ciso_cwd_hr_params.c211112.nc' +paramfile = '$DIN_LOC_ROOT/lnd/clm2/paramdata/ctsm51_ciso_cwd_hr_params.c240207b.nc' hist_fincl1 = 'CWDC_HR','C13_CWDC_HR','C14_CWDC_HR','CWD_HR_L2','CWD_HR_L2_vr','CWD_HR_L3','CWD_HR_L3_vr' diff --git a/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningMode/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningMode/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningMode/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningMode/shell_commands b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningMode/shell_commands new file mode 100644 index 0000000000..cf39cca1c0 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningMode/shell_commands @@ -0,0 +1,5 @@ +#!/bin/bash + +./xmlchange LND_TUNING_MODE="clm5_1_cam6.0" +./xmlchange ROF_NCPL='$ATM_NCPL' + diff --git a/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeCiso/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeCiso/include_user_mods new file mode 100644 index 0000000000..aa76c52034 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeCiso/include_user_mods @@ -0,0 +1 @@ +../clm51cam6LndTuningMode diff --git a/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeCiso/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeCiso/user_nl_clm new file mode 100644 index 0000000000..e7627dea50 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeCiso/user_nl_clm @@ -0,0 +1,5 @@ +! Turn on Carbon isotopes +use_c13 = .true. +use_c14 = .true. +use_c13_timeseries = .true. +use_c14_bombspike = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/collapse_pfts_78_to_16_decStart_f10/README b/cime_config/testdefs/testmods_dirs/clm/collapse_pfts_78_to_16_decStart_f10/README index 81fb991ed0..af5d819ffc 100644 --- a/cime_config/testdefs/testmods_dirs/clm/collapse_pfts_78_to_16_decStart_f10/README +++ b/cime_config/testdefs/testmods_dirs/clm/collapse_pfts_78_to_16_decStart_f10/README @@ -9,10 +9,10 @@ According to the file the following two files used in this test are default files for the following options: -fsurdat = '/glade/p/cesmdata/cseg/inputdata/lnd/clm2/surfdata_map/surfdata_10x15_78pfts_CMIP6_simyr1850_c170824.nc' +fsurdat = '$DIN_LOC_ROOT/lnd/clm2/surfdata_map/surfdata_10x15_78pfts_CMIP6_simyr1850_c170824.nc' hgrid="10x15" sim_year="1850" use_crop=".true." -flanduse_timeseries = '/glade/p/cesmdata/cseg/inputdata/lnd/clm2/surfdata_map/landuse.timeseries_10x15_hist_78pfts_CMIP6_simyr1850-2015_c170824.nc' +flanduse_timeseries = '$DIN_LOC_ROOT/lnd/clm2/surfdata_map/landuse.timeseries_10x15_hist_78pfts_CMIP6_simyr1850-2015_c170824.nc' hgrid="10x15" sim_year_range="1850-2000" use_crop=".true." hgrid="10x15" rcp="8.5" sim_year_range="1850-2100" use_crop=".true." hgrid="10x15" rcp="6" sim_year_range="1850-2100" use_crop=".true." diff --git a/cime_config/testdefs/testmods_dirs/clm/collapse_pfts_78_to_16_decStart_f10/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/collapse_pfts_78_to_16_decStart_f10/user_nl_clm index ff78e0122c..8c4fed6873 100644 --- a/cime_config/testdefs/testmods_dirs/clm/collapse_pfts_78_to_16_decStart_f10/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/collapse_pfts_78_to_16_decStart_f10/user_nl_clm @@ -1,2 +1,2 @@ -fsurdat = '/glade/p/cesmdata/cseg/inputdata/lnd/clm2/surfdata_map/surfdata_10x15_78pfts_CMIP6_simyr1850_c170824.nc' -flanduse_timeseries = '/glade/p/cesmdata/cseg/inputdata/lnd/clm2/surfdata_map/landuse.timeseries_10x15_hist_78pfts_CMIP6_simyr1850-2015_c170824.nc' +fsurdat = '$DIN_LOC_ROOT/lnd/clm2/surfdata_map/surfdata_10x15_78pfts_CMIP6_simyr1850_c170824.nc' +flanduse_timeseries = '$DIN_LOC_ROOT/lnd/clm2/surfdata_map/landuse.timeseries_10x15_hist_78pfts_CMIP6_simyr1850-2015_c170824.nc' diff --git a/cime_config/testdefs/testmods_dirs/clm/cplhist/shell_commands b/cime_config/testdefs/testmods_dirs/clm/cplhist/shell_commands index ac079d5334..f0eb85010b 100755 --- a/cime_config/testdefs/testmods_dirs/clm/cplhist/shell_commands +++ b/cime_config/testdefs/testmods_dirs/clm/cplhist/shell_commands @@ -1,12 +1,8 @@ -driver=`./xmlquery --value COMP_INTERFACE` -if [ "$driver" = "nuopc" ]; then - ./xmlchange DATM_YR_ALIGN=1 - ./xmlchange DATM_YR_END=84 - ./xmlchange DATM_YR_START=82 -else - ./xmlchange DATM_CPLHIST_YR_ALIGN=1 - ./xmlchange DATM_CPLHIST_YR_END=84 - ./xmlchange DATM_CPLHIST_YR_START=82 -fi -./xmlchange DATM_CPLHIST_CASE=b.e20.B1850.f09_g17.pi_control.all.221.cplhist -./xmlchange DATM_CPLHIST_DIR=/glade/p/cesm/bgcwg_dev/forcing/b.e20.B1850.f09_g17.pi_control.all.221.cplhist/cpl/hist.mon +./xmlchange DATM_CPLHIST_CASE=cam6ctsm51_cesm23a12c_ne30pg3g17_CPLHIST_1850 +./xmlchange DATM_CPLHIST_DIR='$DIN_LOC_ROOT/atm/datm7/atm_forcing.cdeps_datm.CPLHIST_cam6ctsm51_cesm23a12c_ne30pg3g17_1850' +./xmlchange DATM_YR_START=1 +./xmlchange DATM_YR_END=1 +./xmlchange DATM_YR_ALIGN=1 + +# Needed until we have cplhist forcing with ndep - see https://github.com/escomp/ctsm/issues/1844 +./xmlchange DATM_PRESNDEP=none diff --git a/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm index 4add705be3..67042ea01a 100644 --- a/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm @@ -1,9 +1,17 @@ hist_fincl1 += 'GDD0', 'GDD8', 'GDD10', 'GDD020', 'GDD820', 'GDD1020', 'GDDACCUM', 'GDDTSOI', 'A5TMIN', 'A10TMIN', - 'HUI' + 'HUI', 'GRAINN_TO_FOOD' ! The main point of including this field is to test the SUM history field infrastructure ! This is in the crop testmods because this field is mainly useful in transient crop runs ! This is on history tape 2 because this field is not meaningful at the gridcell level hist_fincl2 += 'DYN_COL_SOIL_ADJUSTMENTS_C' + + +! Annual crop variables on per-sowing/per-harvest axes, per PFT. +hist_fincl3 = 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'GRAINN_TO_FOOD_PERHARV', 'GRAINN_TO_FOOD_ANN', 'GRAINC_TO_SEED_PERHARV', 'GRAINC_TO_SEED_ANN', 'GRAINN_TO_SEED_PERHARV', 'GRAINN_TO_SEED_ANN', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV', 'SWINDOW_STARTS', 'SWINDOW_ENDS' +hist_nhtfrq(3) = 17520 +hist_mfilt(3) = 1 +hist_type1d_pertape(3) = 'PFTS' +hist_dov2xy(3) = .false. diff --git a/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm index dfe718a7b4..ae4284da6b 100644 --- a/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm @@ -1,6 +1,2 @@ - hist_nhtfrq = 0,-240,-8760 + hist_nhtfrq = 0,-240,17520 hist_mfilt = 1,1,1 - - ! Add an annual output file with these crop-specific variables, which exercise some special logic: - hist_fincl3 = 'SDATES', 'HDATES' - hist_dov2xy(3) = .false. diff --git a/cime_config/testdefs/testmods_dirs/clm/datm_bias_correct_cruv7/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/datm_bias_correct_cruv7/user_nl_clm deleted file mode 100644 index c7cfe279ee..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/datm_bias_correct_cruv7/user_nl_clm +++ /dev/null @@ -1 +0,0 @@ -use_lai_streams = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/extra_outputs/README b/cime_config/testdefs/testmods_dirs/clm/extra_outputs/README index 03bc956b6f..574d7cc204 100644 --- a/cime_config/testdefs/testmods_dirs/clm/extra_outputs/README +++ b/cime_config/testdefs/testmods_dirs/clm/extra_outputs/README @@ -1,4 +1,4 @@ This test mod turns on extra diagnostic fields -It also outputs an optional text file containing a table of the -history fields master list +It also outputs an optional text file containing a table of all +the possible history fields diff --git a/cime_config/testdefs/testmods_dirs/clm/extra_outputs/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/extra_outputs/user_nl_clm index 6dc5225f1d..dad8a7e843 100644 --- a/cime_config/testdefs/testmods_dirs/clm/extra_outputs/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/extra_outputs/user_nl_clm @@ -1,5 +1,5 @@ calc_human_stress_indices = 'ALL' -hist_master_list_file = .true. +hist_fields_list_file = .true. hist_fincl1 += 'GSSUN:L43200', 'GSSHA:L43200', 'FSDSND:L43200', 'FSRND:L43200', 'FSRSFND:L43200', 'SSRE_FSRND:L43200', 'FSDSVD:L43200', 'FSDSVI:L43200', diff --git a/cime_config/testdefs/testmods_dirs/clm/f09_dec1990Start_GU_LULCC/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/f09_dec1990Start_GU_LULCC/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/f09_dec1990Start_GU_LULCC/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/f09_dec1990Start_GU_LULCC/shell_commands b/cime_config/testdefs/testmods_dirs/clm/f09_dec1990Start_GU_LULCC/shell_commands new file mode 100755 index 0000000000..3adf0390b1 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/f09_dec1990Start_GU_LULCC/shell_commands @@ -0,0 +1,3 @@ +./xmlchange RUN_STARTDATE=1990-12-30 +# Ignore warnings because we are using crop, but starting from a different date than the initial conditions were for +./xmlchange CLM_BLDNML_OPTS=-ignore_warnings --append diff --git a/cime_config/testdefs/testmods_dirs/clm/f09_dec1990Start_GU_LULCC/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/f09_dec1990Start_GU_LULCC/user_nl_clm new file mode 100644 index 0000000000..efb3212d5f --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/f09_dec1990Start_GU_LULCC/user_nl_clm @@ -0,0 +1,5 @@ + ! Specify a dataset that has non-zero Gross Unrepresented Land Use change fields on it + ! And turn it on + flanduse_timeseries = '$DIN_LOC_ROOT/lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_0.9x1.25_hist_78pfts_CMIP6_simyr1700-2021_c220825.nc' + fsurdat = '$DIN_LOC_ROOT/lnd/clm2/surfdata_map/ctsm5.1.dev052/surfdata_0.9x1.25_hist_78pfts_CMIP6_simyr1700_c220825.nc' + do_grossunrep = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/mimicsFatesColdDef/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/mimicsFatesCold/include_user_mods similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/mimicsFatesColdDef/include_user_mods rename to cime_config/testdefs/testmods_dirs/clm/mimicsFatesCold/include_user_mods diff --git a/cime_config/testdefs/testmods_dirs/clm/mimicsFatesColdDef/shell_commands b/cime_config/testdefs/testmods_dirs/clm/mimicsFatesCold/shell_commands similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/mimicsFatesColdDef/shell_commands rename to cime_config/testdefs/testmods_dirs/clm/mimicsFatesCold/shell_commands diff --git a/cime_config/testdefs/testmods_dirs/clm/remove_residues/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/remove_residues/include_user_mods new file mode 100644 index 0000000000..23ea3745e6 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/remove_residues/include_user_mods @@ -0,0 +1 @@ +../crop diff --git a/cime_config/testdefs/testmods_dirs/clm/remove_residues/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/remove_residues/user_nl_clm new file mode 100644 index 0000000000..cea306136b --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/remove_residues/user_nl_clm @@ -0,0 +1,2 @@ +crop_residue_removal_frac = 1.0 + diff --git a/cime_config/testdefs/testmods_dirs/clm/run_self_tests/shell_commands b/cime_config/testdefs/testmods_dirs/clm/run_self_tests/shell_commands old mode 100644 new mode 100755 index a66f52f6fd..d426269206 --- a/cime_config/testdefs/testmods_dirs/clm/run_self_tests/shell_commands +++ b/cime_config/testdefs/testmods_dirs/clm/run_self_tests/shell_commands @@ -1,3 +1,5 @@ #!/bin/bash ./xmlchange CLM_FORCE_COLDSTART="on" +# We use this testmod in a _Ln1 test; this requires forcing the ROF coupling frequency to every time step +./xmlchange ROF_NCPL=48 diff --git a/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/modify_smallville_with_dynurban.ncl b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/modify_smallville_with_dynurban.ncl index 15ec0469be..5ac651b508 100644 --- a/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/modify_smallville_with_dynurban.ncl +++ b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/modify_smallville_with_dynurban.ncl @@ -18,8 +18,8 @@ begin print ("Start Time: "+systemfunc("date") ) print ("=========================================") - infile = "/glade/p/cgd/tss/people/oleson/modify_surfdata/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_c160127.nc" - outfile = "/glade/p/cgd/tss/people/oleson/modify_surfdata/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_dynUrban_c220223.nc" + infile = "/glade/campaign/cgd/tss/people/oleson/modify_surfdata/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_c160127.nc" + outfile = "/glade/campaign/cgd/tss/people/oleson/modify_surfdata/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_dynUrban_c220223.nc" system("cp " + infile + " " + outfile) @@ -61,7 +61,9 @@ begin outf->PCT_URBAN = pct_urban outf->PCT_CROP = pct_crop - outf@history = "This file was created with the following NCL script: /glade/p/cgd/tss/people/oleson/modify_surfdata/modify_smallville_with_dynurban.ncl. The file used as a template is: /glade/p/cesm/cseg/inputdata/lnd/clm2/surfdata_map/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_c160127.nc. Key points are that urban area starts as 0, increases after the first year, then decreases after the second year. Medium density urban is set to zero to test the memory-saving behavior of PCT_URBAN_MAX. PCT_CROP is also changed so that PCT_URBAN + PCT_CROP <= 100. (Here, PCT_CROP increases and decreases at the same time as PCT_URBAN in order to exercise the simultaneous increase or decrease of two landunits, but that isn't a critical part of this test.). Note that the use of this file means that this testmod can only be used with the 1x1_smallvilleIA grid." + outf@history = "This file was created with the following NCL script: +/glade/campaign/cgd/tss/people/oleson/modify_surfdata/modify_smallville_with_dynurban.ncl. The file used as a template is: +/glade/campaign/cesm/cesmdata/inputdata/lnd/clm2/surfdata_map/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_c160127.nc. Key points are that urban area starts as 0, increases after the first year, then decreases after the second year. Medium density urban is set to zero to test the memory-saving behavior of PCT_URBAN_MAX. PCT_CROP is also changed so that PCT_URBAN + PCT_CROP <= 100. (Here, PCT_CROP increases and decreases at the same time as PCT_URBAN in order to exercise the simultaneous increase or decrease of two landunits, but that isn't a critical part of this test.). Note that the use of this file means that this testmod can only be used with the 1x1_smallvilleIA grid." print ("=========================================") print ("Finish Time: "+systemfunc("date") ) diff --git a/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm index 69a78ee17d..0ba93b1ee2 100644 --- a/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm @@ -1,9 +1,9 @@ do_transient_urban = .true. ! The flanduse_timeseries file was created with the following NCL script (a copy of this script is in cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly): -! /glade/p/cgd/tss/people/oleson/modify_surfdata/modify_smallville_with_dynurban.ncl +! /glade/campaign/cgd/tss/people/oleson/modify_surfdata/modify_smallville_with_dynurban.ncl ! The file used as a template is: -! /glade/p/cesm/cseg/inputdata/lnd/clm2/surfdata_map/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_c160127.nc +! /glade/campaign/cgd/tss/people/oleson/modify_surfdata/lnd/clm2/surfdata_map/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_c160127.nc ! Key points are that urban area starts as 0, increases after the first year, then decreases after the second year. ! Medium density urban is set to zero to test the memory-saving behavior of PCT_URBAN_MAX. ! PCT_CROP is also changed so that PCT_URBAN + PCT_CROP <= 100. (Here, PCT_CROP increases and decreases at the same time as PCT_URBAN in order to exercise the simultaneous increase or decrease of two landunits, but that isn't a critical part of this test.) diff --git a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm new file mode 100644 index 0000000000..03165bb306 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm @@ -0,0 +1,5 @@ +stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' +stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' +stream_meshfile_cropcal = '$DIN_LOC_ROOT/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc' +stream_year_first_cropcal = 2000 +stream_year_last_cropcal = 2000 diff --git a/cime_config/testdefs/testmods_dirs/clm/till/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/till/include_user_mods new file mode 100644 index 0000000000..23ea3745e6 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/till/include_user_mods @@ -0,0 +1 @@ +../crop diff --git a/cime_config/testdefs/testmods_dirs/clm/till/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/till/user_nl_clm new file mode 100644 index 0000000000..f94c6df309 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/till/user_nl_clm @@ -0,0 +1,2 @@ +tillage_mode = 'high' + diff --git a/cime_config/usermods_dirs/NEON/BARR/shell_commands b/cime_config/usermods_dirs/NEON/BARR/shell_commands index 55037c6b37..a5892a146b 100644 --- a/cime_config/usermods_dirs/NEON/BARR/shell_commands +++ b/cime_config/usermods_dirs/NEON/BARR/shell_commands @@ -1,7 +1,13 @@ +#!/bin/bash + ./xmlchange NEONSITE=BARR ./xmlchange PTS_LON=203.349781 ./xmlchange PTS_LAT=71.281711 -./xmlchange RUN_STARTDATE=2019-01-01 -./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019,DATM_YR_END=2022 -./xmlchange STOP_N=39 - +./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019 +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/BLAN/shell_commands b/cime_config/usermods_dirs/NEON/BLAN/shell_commands index cb093d806a..0508481981 100644 --- a/cime_config/usermods_dirs/NEON/BLAN/shell_commands +++ b/cime_config/usermods_dirs/NEON/BLAN/shell_commands @@ -1,3 +1,3 @@ ./xmlchange NEONSITE=BLAN -./xmlchange PTS_LON=281.92885 -./xmlchange PTS_LAT=39.06044 +./xmlchange PTS_LON=281.958212 +./xmlchange PTS_LAT=39.033698 diff --git a/cime_config/usermods_dirs/NEON/CPER/shell_commands b/cime_config/usermods_dirs/NEON/CPER/shell_commands index 169b358a40..b70b1a46a0 100644 --- a/cime_config/usermods_dirs/NEON/CPER/shell_commands +++ b/cime_config/usermods_dirs/NEON/CPER/shell_commands @@ -1,3 +1,10 @@ +#!/bin/bash + ./xmlchange NEONSITE=CPER ./xmlchange PTS_LON=255.25545 ./xmlchange PTS_LAT=40.81297 +if [[ $compset =~ ^HIST ]]; then + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=50 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/ABBY/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/ABBY/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/ABBY/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/ABBY/shell_commands b/cime_config/usermods_dirs/NEON/FATES/ABBY/shell_commands new file mode 100644 index 0000000000..08f6e7cdef --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/ABBY/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=ABBY +./xmlchange PTS_LON=237.67032799999998 +./xmlchange PTS_LAT=45.762378 diff --git a/cime_config/usermods_dirs/NEON/FATES/BARR/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/BARR/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/BARR/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/BARR/shell_commands b/cime_config/usermods_dirs/NEON/FATES/BARR/shell_commands new file mode 100644 index 0000000000..a5892a146b --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/BARR/shell_commands @@ -0,0 +1,13 @@ +#!/bin/bash + +./xmlchange NEONSITE=BARR +./xmlchange PTS_LON=203.349781 +./xmlchange PTS_LAT=71.281711 +./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019 +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/BART/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/BART/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/BART/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/BART/shell_commands b/cime_config/usermods_dirs/NEON/FATES/BART/shell_commands new file mode 100644 index 0000000000..a4e86a1b8c --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/BART/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=BART +./xmlchange PTS_LON=288.71166 +./xmlchange PTS_LAT=44.06516 diff --git a/cime_config/usermods_dirs/NEON/FATES/BLAN/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/BLAN/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/BLAN/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/BLAN/shell_commands b/cime_config/usermods_dirs/NEON/FATES/BLAN/shell_commands new file mode 100644 index 0000000000..0508481981 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/BLAN/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=BLAN +./xmlchange PTS_LON=281.958212 +./xmlchange PTS_LAT=39.033698 diff --git a/cime_config/usermods_dirs/NEON/FATES/BONA/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/BONA/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/BONA/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/BONA/shell_commands b/cime_config/usermods_dirs/NEON/FATES/BONA/shell_commands new file mode 100644 index 0000000000..2a66d148b4 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/BONA/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=BONA +./xmlchange PTS_LON=212.49806 +./xmlchange PTS_LAT=65.15333 diff --git a/cime_config/usermods_dirs/NEON/FATES/CLBJ/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/CLBJ/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/CLBJ/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/CLBJ/shell_commands b/cime_config/usermods_dirs/NEON/FATES/CLBJ/shell_commands new file mode 100644 index 0000000000..c1b9154027 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/CLBJ/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=CLBJ +./xmlchange PTS_LON=262.43275 +./xmlchange PTS_LAT=33.40143 diff --git a/cime_config/usermods_dirs/NEON/FATES/CPER/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/CPER/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/CPER/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/CPER/shell_commands b/cime_config/usermods_dirs/NEON/FATES/CPER/shell_commands new file mode 100644 index 0000000000..b70b1a46a0 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/CPER/shell_commands @@ -0,0 +1,10 @@ +#!/bin/bash + +./xmlchange NEONSITE=CPER +./xmlchange PTS_LON=255.25545 +./xmlchange PTS_LAT=40.81297 +if [[ $compset =~ ^HIST ]]; then + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=50 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/DCFS/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/DCFS/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/DCFS/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/DCFS/shell_commands b/cime_config/usermods_dirs/NEON/FATES/DCFS/shell_commands new file mode 100644 index 0000000000..a6cbed64e1 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/DCFS/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=DCFS +./xmlchange PTS_LON=260.88749 +./xmlchange PTS_LAT=47.15919 diff --git a/cime_config/usermods_dirs/NEON/FATES/DEJU/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/DEJU/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/DEJU/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/DEJU/shell_commands b/cime_config/usermods_dirs/NEON/FATES/DEJU/shell_commands new file mode 100644 index 0000000000..fce519d559 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/DEJU/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=DEJU +./xmlchange PTS_LON=214.25235 +./xmlchange PTS_LAT=63.87983 diff --git a/cime_config/usermods_dirs/NEON/FATES/DELA/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/DELA/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/DELA/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/DELA/shell_commands b/cime_config/usermods_dirs/NEON/FATES/DELA/shell_commands new file mode 100644 index 0000000000..f3acbb8fd3 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/DELA/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=DELA +./xmlchange PTS_LON=272.19659 +./xmlchange PTS_LAT=32.54092 diff --git a/cime_config/usermods_dirs/NEON/FATES/DSNY/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/DSNY/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/DSNY/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/DSNY/shell_commands b/cime_config/usermods_dirs/NEON/FATES/DSNY/shell_commands new file mode 100644 index 0000000000..8304c91d48 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/DSNY/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=DSNY +./xmlchange PTS_LON=278.56606 +./xmlchange PTS_LAT=28.12919 diff --git a/cime_config/usermods_dirs/NEON/FATES/GRSM/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/GRSM/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/GRSM/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/GRSM/shell_commands b/cime_config/usermods_dirs/NEON/FATES/GRSM/shell_commands new file mode 100644 index 0000000000..e52a633408 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/GRSM/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=GRSM +./xmlchange PTS_LON=276.49815 +./xmlchange PTS_LAT=35.68839 diff --git a/cime_config/usermods_dirs/NEON/FATES/GUAN/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/GUAN/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/GUAN/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/GUAN/shell_commands b/cime_config/usermods_dirs/NEON/FATES/GUAN/shell_commands new file mode 100644 index 0000000000..c78bf31f55 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/GUAN/shell_commands @@ -0,0 +1,13 @@ +#!/bin/bash + +./xmlchange NEONSITE=GUAN +./xmlchange PTS_LON=293.13112 +./xmlchange PTS_LAT=17.96882 +./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019 +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/HARV/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/HARV/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/HARV/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/HARV/shell_commands b/cime_config/usermods_dirs/NEON/FATES/HARV/shell_commands new file mode 100644 index 0000000000..839ccf5d8f --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/HARV/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=HARV +./xmlchange PTS_LON=287.82438 +./xmlchange PTS_LAT=42.53562 diff --git a/cime_config/usermods_dirs/NEON/FATES/HEAL/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/HEAL/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/HEAL/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/HEAL/shell_commands b/cime_config/usermods_dirs/NEON/FATES/HEAL/shell_commands new file mode 100644 index 0000000000..21892219e0 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/HEAL/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=HEAL +./xmlchange PTS_LON=210.78461 +./xmlchange PTS_LAT=63.8798 diff --git a/cime_config/usermods_dirs/NEON/FATES/JERC/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/JERC/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/JERC/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/JERC/shell_commands b/cime_config/usermods_dirs/NEON/FATES/JERC/shell_commands new file mode 100644 index 0000000000..80f66d23a2 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/JERC/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=JERC +./xmlchange PTS_LON=275.53353 +./xmlchange PTS_LAT=31.19608 diff --git a/cime_config/usermods_dirs/NEON/FATES/JORN/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/JORN/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/JORN/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/JORN/shell_commands b/cime_config/usermods_dirs/NEON/FATES/JORN/shell_commands new file mode 100644 index 0000000000..87fc3b8c1e --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/JORN/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=JORN +./xmlchange PTS_LON=253.15623 +./xmlchange PTS_LAT=32.59052 diff --git a/cime_config/usermods_dirs/NEON/FATES/KONZ/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/KONZ/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/KONZ/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/KONZ/shell_commands b/cime_config/usermods_dirs/NEON/FATES/KONZ/shell_commands new file mode 100644 index 0000000000..bda370c170 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/KONZ/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=KONZ +./xmlchange PTS_LON=263.43773 +./xmlchange PTS_LAT=39.1007 diff --git a/cime_config/usermods_dirs/NEON/FATES/LAJA/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/LAJA/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/LAJA/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/LAJA/shell_commands b/cime_config/usermods_dirs/NEON/FATES/LAJA/shell_commands new file mode 100644 index 0000000000..217216e3ec --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/LAJA/shell_commands @@ -0,0 +1,14 @@ +#!/bin/bash +./xmlchange NEONSITE=LAJA +./xmlchange PTS_LON=292.92392 +./xmlchange PTS_LAT=18.02184 +./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019 +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi +fi + + diff --git a/cime_config/usermods_dirs/NEON/FATES/LENO/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/LENO/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/LENO/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/LENO/shell_commands b/cime_config/usermods_dirs/NEON/FATES/LENO/shell_commands new file mode 100644 index 0000000000..06af587292 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/LENO/shell_commands @@ -0,0 +1,14 @@ +#!/bin/bash + +./xmlchange NEONSITE=LENO +./xmlchange PTS_LON=271.83897 +./xmlchange PTS_LAT=31.8531 +./xmlchange DATM_YR_ALIGN=2021,DATM_YR_START=2021 +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2021-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=15 + fi +fi + diff --git a/cime_config/usermods_dirs/NEON/FATES/MLBS/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/MLBS/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/MLBS/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/MLBS/shell_commands b/cime_config/usermods_dirs/NEON/FATES/MLBS/shell_commands new file mode 100644 index 0000000000..11ab445450 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/MLBS/shell_commands @@ -0,0 +1,16 @@ +#!/bin/bash +./xmlchange NEONSITE=MLBS +./xmlchange PTS_LON=279.47575 +./xmlchange PTS_LAT=37.37783 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + ./xmlchange DATM_YR_END=2019 + # Different default number of months to run for transient case + if [[ $compset =~ ^HIST ]]; then + ./xmlchange DATM_YR_END=2020 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=24 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/MOAB/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/MOAB/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/MOAB/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/MOAB/shell_commands b/cime_config/usermods_dirs/NEON/FATES/MOAB/shell_commands new file mode 100644 index 0000000000..649fa2eaba --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/MOAB/shell_commands @@ -0,0 +1,16 @@ +#!/bin/bash +./xmlchange NEONSITE=MOAB +./xmlchange PTS_LON=250.61118 +./xmlchange PTS_LAT=38.25136 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + ./xmlchange DATM_YR_END=2020 + # Different default number of months to run for transient case + if [[ $compset =~ ^HIST ]]; then + ./xmlchange DATM_YR_END=2021 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=36 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/NIWO/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/NIWO/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/NIWO/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/NIWO/shell_commands b/cime_config/usermods_dirs/NEON/FATES/NIWO/shell_commands new file mode 100644 index 0000000000..b4f27ea8ed --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/NIWO/shell_commands @@ -0,0 +1,9 @@ +#!/bin/bash +./xmlchange NEONSITE=NIWO +./xmlchange PTS_LON=254.41676 +./xmlchange PTS_LAT=40.05236 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == "NEON" ]]; then + ./xmlchange DATM_YR_END=2018 +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/NOGP/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/NOGP/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/NOGP/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/NOGP/shell_commands b/cime_config/usermods_dirs/NEON/FATES/NOGP/shell_commands new file mode 100644 index 0000000000..ad3ef69cd2 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/NOGP/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=NOGP +./xmlchange PTS_LON=259.08168 +./xmlchange PTS_LAT=46.76846 diff --git a/cime_config/usermods_dirs/NEON/FATES/OAES/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/OAES/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/OAES/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/OAES/shell_commands b/cime_config/usermods_dirs/NEON/FATES/OAES/shell_commands new file mode 100644 index 0000000000..2a5cfb87e4 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/OAES/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=OAES +./xmlchange PTS_LON=260.93956000000003 +./xmlchange PTS_LAT=35.41062 diff --git a/cime_config/usermods_dirs/NEON/FATES/ONAQ/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/ONAQ/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/ONAQ/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/ONAQ/shell_commands b/cime_config/usermods_dirs/NEON/FATES/ONAQ/shell_commands new file mode 100644 index 0000000000..dae5344528 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/ONAQ/shell_commands @@ -0,0 +1,16 @@ +#!/bin/bash +./xmlchange NEONSITE=ONAQ +./xmlchange PTS_LON=247.54755 +./xmlchange PTS_LAT=40.17760 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + ./xmlchange DATM_YR_END=2019 + # Different default number of months to run for transient case + if [[ $compset =~ ^HIST ]]; then + ./xmlchange DATM_YR_END=2020 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=24 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/ORNL/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/ORNL/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/ORNL/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/ORNL/shell_commands b/cime_config/usermods_dirs/NEON/FATES/ORNL/shell_commands new file mode 100644 index 0000000000..5708d3dec5 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/ORNL/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=ORNL +./xmlchange PTS_LON=275.717412 +./xmlchange PTS_LAT=35.964128 diff --git a/cime_config/usermods_dirs/NEON/FATES/OSBS/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/OSBS/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/OSBS/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/OSBS/shell_commands b/cime_config/usermods_dirs/NEON/FATES/OSBS/shell_commands new file mode 100644 index 0000000000..385021f98a --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/OSBS/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=OSBS +./xmlchange PTS_LON=278.00655 +./xmlchange PTS_LAT=29.68819 diff --git a/cime_config/usermods_dirs/NEON/FATES/PUUM/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/PUUM/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/PUUM/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/PUUM/shell_commands b/cime_config/usermods_dirs/NEON/FATES/PUUM/shell_commands new file mode 100644 index 0000000000..07c4331769 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/PUUM/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=PUUM +./xmlchange PTS_LON=204.68269 +./xmlchange PTS_LAT=19.55309 diff --git a/cime_config/usermods_dirs/NEON/FATES/README.md b/cime_config/usermods_dirs/NEON/FATES/README.md new file mode 100644 index 0000000000..dcfcfdf9af --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/README.md @@ -0,0 +1,10 @@ +# NEON user mods directories for FATES runs + +Use these user mods as you would any other user_mods, e.g.: + +`./create_newcase --case FATES_ABBY_test --res CLM_USRDAT --compset I1PtClm51Fates --run-unsupported --user-mods-dir /glade/work/$user/CTSM/cime_config/usermods_dirs/NEON/FATES/ABBY` + +## Note on crop sites KONA and STER + +Currently you cannot run FATES at these sites because FATES does not have crops as of yet. We will add these sites back once this capability is available. + diff --git a/cime_config/usermods_dirs/NEON/FATES/RMNP/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/RMNP/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/RMNP/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/RMNP/shell_commands b/cime_config/usermods_dirs/NEON/FATES/RMNP/shell_commands new file mode 100644 index 0000000000..8dfbf0fa0d --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/RMNP/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=RMNP +./xmlchange PTS_LON=254.45476 +./xmlchange PTS_LAT=40.27707 diff --git a/cime_config/usermods_dirs/NEON/FATES/SCBI/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/SCBI/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SCBI/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/SCBI/shell_commands b/cime_config/usermods_dirs/NEON/FATES/SCBI/shell_commands new file mode 100644 index 0000000000..aa42b8022c --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SCBI/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=SCBI +./xmlchange PTS_LON=281.86235999999997 +./xmlchange PTS_LAT=38.89209 diff --git a/cime_config/usermods_dirs/NEON/FATES/SERC/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/SERC/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SERC/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/SERC/shell_commands b/cime_config/usermods_dirs/NEON/FATES/SERC/shell_commands new file mode 100644 index 0000000000..1053e2dc17 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SERC/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=SERC +./xmlchange PTS_LON=283.44115999999997 +./xmlchange PTS_LAT=38.89124 diff --git a/cime_config/usermods_dirs/NEON/FATES/SJER/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/SJER/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SJER/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/SJER/shell_commands b/cime_config/usermods_dirs/NEON/FATES/SJER/shell_commands new file mode 100644 index 0000000000..ee50f61fa1 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SJER/shell_commands @@ -0,0 +1,20 @@ +#!/bin/bash +./xmlchange NEONSITE=SJER +./xmlchange PTS_LON=240.267 +./xmlchange PTS_LAT=37.107117 +./xmlchange DATM_YR_ALIGN=2019 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [ $CLM_USRDAT_NAME=='NEON' ] +then + ./xmlchange DATM_YR_START=2019 + # Different default start date and number of months to run for transient case + if [[ $compset =~ ^HIST ]] + then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi + fi +fi + diff --git a/cime_config/usermods_dirs/NEON/FATES/SOAP/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/SOAP/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SOAP/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/SOAP/shell_commands b/cime_config/usermods_dirs/NEON/FATES/SOAP/shell_commands new file mode 100644 index 0000000000..c10274c047 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SOAP/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=SOAP +./xmlchange PTS_LON=240.7379 +./xmlchange PTS_LAT=37.03269 diff --git a/cime_config/usermods_dirs/NEON/FATES/SRER/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/SRER/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SRER/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/SRER/shell_commands b/cime_config/usermods_dirs/NEON/FATES/SRER/shell_commands new file mode 100644 index 0000000000..be1bec52d3 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/SRER/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=SRER +./xmlchange PTS_LON=249.16451 +./xmlchange PTS_LAT=31.91068 diff --git a/cime_config/usermods_dirs/NEON/FATES/STEI/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/STEI/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/STEI/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/STEI/shell_commands b/cime_config/usermods_dirs/NEON/FATES/STEI/shell_commands new file mode 100644 index 0000000000..c2aced2c2e --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/STEI/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=STEI +./xmlchange PTS_LON=270.4112 +./xmlchange PTS_LAT=45.5076 diff --git a/cime_config/usermods_dirs/NEON/FATES/TALL/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/TALL/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/TALL/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/TALL/shell_commands b/cime_config/usermods_dirs/NEON/FATES/TALL/shell_commands new file mode 100644 index 0000000000..1a176ae23f --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/TALL/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=TALL +./xmlchange PTS_LON=272.6059 +./xmlchange PTS_LAT=32.95106 diff --git a/cime_config/usermods_dirs/NEON/FATES/TEAK/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/TEAK/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/TEAK/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/TEAK/shell_commands b/cime_config/usermods_dirs/NEON/FATES/TEAK/shell_commands new file mode 100644 index 0000000000..abac8e9263 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/TEAK/shell_commands @@ -0,0 +1,17 @@ +#!/bin/bash +./xmlchange NEONSITE=TEAK +./xmlchange PTS_LON=240.99424199999999 +./xmlchange PTS_LAT=37.006472 +./xmlchange DATM_YR_ALIGN=2019 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + ./xmlchange DATM_YR_START=2019 + # Different default start date and number of months to run for transient case + if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/TOOL/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/TOOL/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/TOOL/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/TOOL/shell_commands b/cime_config/usermods_dirs/NEON/FATES/TOOL/shell_commands new file mode 100644 index 0000000000..3c749cde93 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/TOOL/shell_commands @@ -0,0 +1,12 @@ +#!/bin/bash +./xmlchange NEONSITE=TOOL +./xmlchange PTS_LON=210.629872 +./xmlchange PTS_LAT=68.66045 +./xmlchange DATM_YR_ALIGN=2020,DATM_YR_START=2020 +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2020-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=27 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/TREE/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/TREE/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/TREE/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/TREE/shell_commands b/cime_config/usermods_dirs/NEON/FATES/TREE/shell_commands new file mode 100644 index 0000000000..6d0a4aa1fa --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/TREE/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=TREE +./xmlchange PTS_LON=270.41252 +./xmlchange PTS_LAT=45.49266 diff --git a/cime_config/usermods_dirs/NEON/FATES/UKFS/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/UKFS/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/UKFS/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/UKFS/shell_commands b/cime_config/usermods_dirs/NEON/FATES/UKFS/shell_commands new file mode 100644 index 0000000000..7c8d4f8829 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/UKFS/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=UKFS +./xmlchange PTS_LON=264.79505 +./xmlchange PTS_LAT=39.04168 diff --git a/cime_config/usermods_dirs/NEON/FATES/UNDE/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/UNDE/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/UNDE/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/UNDE/shell_commands b/cime_config/usermods_dirs/NEON/FATES/UNDE/shell_commands new file mode 100644 index 0000000000..f810e4a76b --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/UNDE/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=UNDE +./xmlchange PTS_LON=270.462746 +./xmlchange PTS_LAT=46.23391 diff --git a/cime_config/usermods_dirs/NEON/FATES/WOOD/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/WOOD/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/WOOD/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/WOOD/shell_commands b/cime_config/usermods_dirs/NEON/FATES/WOOD/shell_commands new file mode 100644 index 0000000000..48ff0ef999 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/WOOD/shell_commands @@ -0,0 +1,3 @@ +./xmlchange NEONSITE=WOOD +./xmlchange PTS_LON=260.76093000000003 +./xmlchange PTS_LAT=47.12833 diff --git a/cime_config/usermods_dirs/NEON/FATES/WREF/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/WREF/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/WREF/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/WREF/shell_commands b/cime_config/usermods_dirs/NEON/FATES/WREF/shell_commands new file mode 100644 index 0000000000..77a48ae1c0 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/WREF/shell_commands @@ -0,0 +1,17 @@ +#!/bin/bash +./xmlchange NEONSITE=WREF +./xmlchange PTS_LON=238.04162 +./xmlchange PTS_LAT=45.81637 +./xmlchange DATM_YR_ALIGN=2019 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + # Different default start date and number of months to run for transient case + ./xmlchange DATM_YR_START=2019 + if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/YELL/include_user_mods b/cime_config/usermods_dirs/NEON/FATES/YELL/include_user_mods new file mode 100644 index 0000000000..b152996d95 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/YELL/include_user_mods @@ -0,0 +1 @@ +../defaults diff --git a/cime_config/usermods_dirs/NEON/FATES/YELL/shell_commands b/cime_config/usermods_dirs/NEON/FATES/YELL/shell_commands new file mode 100644 index 0000000000..33915b88a1 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/YELL/shell_commands @@ -0,0 +1,17 @@ +#!/bin/bash +./xmlchange NEONSITE=YELL +./xmlchange PTS_LON=249.45803999999998 +./xmlchange PTS_LAT=44.95597 +./xmlchange DATM_YR_ALIGN=2019 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + # Different default start date and number of months to run for transient case + ./xmlchange DATM_YR_START=2019 + if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/FATES/defaults/shell_commands b/cime_config/usermods_dirs/NEON/FATES/defaults/shell_commands new file mode 100644 index 0000000000..5bec7332b0 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/defaults/shell_commands @@ -0,0 +1,43 @@ +#!/bin/bash +./xmlchange CLM_USRDAT_NAME=NEON +# CLM_USRDAT_NAME can be set to either NEON or NEON.PRISM +./xmlchange CCSM_CO2_PPMV=408.83 +# Set data forcing data to future scenario so will have data from 2018 to present-day +./xmlchange DATM_PRESAERO=SSP3-7.0 +./xmlchange DATM_PRESNDEP=SSP3-7.0 +./xmlchange DATM_PRESO3=SSP3-7.0 +# Explicitly set the MPI library to mpi-serial so won't have the build/run complexity of a full MPI library +./xmlchange MPILIB=mpi-serial +# Set years to run forcing data over +./xmlchange DATM_YR_ALIGN=2018,DATM_YR_END=2021,DATM_YR_START=2018 +# +# Save some variables that may be used later +# +compset=`./xmlquery COMPSET --value` +CLM_USRDAT_NAME=`./xmlquery CLM_USRDAT_NAME --value` +TEST=`./xmlquery TEST --value` + +# For a transient case run the whole length and don't cycle +if [[ $compset =~ ^HIST ]]; then + ./xmlchange DATM_YR_END=2022 + ./xmlchange RUN_STARTDATE=2018-01-01 + # Number of months that can be run for the full transient case + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_OPTION="nmonths" + ./xmlchange STOP_N=51 + fi + ./xmlchange CLM_NML_USE_CASE="2018-PD_transient" +else + ./xmlchange CLM_NML_USE_CASE="2018_control" +fi + +# If needed for SP simulations: +# Does anything need to be set for FATES-SP mode? +#if [[ $compset =~ ".*CLM[0-9]%.*SP.*" ]]; then +#fi + + +# Explicitly set PIO Type to NETCDF since this is a single processor case (should already be set this way) +./xmlchange PIO_TYPENAME=netcdf + +./xmlchange NEONVERSION="v2" diff --git a/cime_config/usermods_dirs/NEON/FATES/defaults/user_nl_clm b/cime_config/usermods_dirs/NEON/FATES/defaults/user_nl_clm new file mode 100644 index 0000000000..2a6b4d76d5 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/defaults/user_nl_clm @@ -0,0 +1,28 @@ +!---------------------------------------------------------------------------------- +! Users should add all user specific namelist changes below in the form of +! namelist_var = new_namelist_value +! +! EXCEPTIONS: +! Set use_cndv by the compset you use and the CLM_BLDNML_OPTS -dynamic_vegetation setting +! Set use_vichydro by the compset you use and the CLM_BLDNML_OPTS -vichydro setting +! Set use_cn by the compset you use and CLM_BLDNML_OPTS -bgc setting +! Set use_crop by the compset you use and CLM_BLDNML_OPTS -crop setting +! Set spinup_state by the CLM_BLDNML_OPTS -bgc_spinup setting +! Set co2_ppmv with CCSM_CO2_PPMV option +! Set fatmlndfrc with LND_DOMAIN_PATH/LND_DOMAIN_FILE options +! Set finidat with RUN_REFCASE/RUN_REFDATE/RUN_REFTOD options for hybrid or branch cases +! (includes $inst_string for multi-ensemble cases) +! or with CLM_FORCE_COLDSTART to do a cold start +! or set it with an explicit filename here. +! Set maxpatch_glcmec with GLC_NEC option +! Set glc_do_dynglacier with GLC_TWO_WAY_COUPLING env variable +!---------------------------------------------------------------------------------- + +flanduse_timeseries = ' ' ! This isn't needed for a non transient case, but will be once we start using transient compsets +fsurdat = "$DIN_LOC_ROOT/lnd/clm2/surfdata_map/NEON/16PFT_mixed/surfdata_1x1_NEON_${NEONSITE}_hist_16pfts_Irrig_CMIP6_simyr2000_c230120.nc" + +! h1 output stream +hist_fincl2 = 'FATES_AUTORESP','FCEV','FCTR','FGEV','FIRA','FSA','FSH','FATES_GPP','FATES_GPP_PF','H2OSOI', + 'SNOW_DEPTH','TBOT','TSOI','SOILC_vr','FATES_NPP','FATES_NPP_PF','FATES_VEGC','FATES_VEGC_PF' +hist_mfilt(2) = 48 +hist_nhtfrq(2) = 1 diff --git a/cime_config/usermods_dirs/NEON/FATES/defaults/user_nl_cpl b/cime_config/usermods_dirs/NEON/FATES/defaults/user_nl_cpl new file mode 100644 index 0000000000..e7f6c90a86 --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/defaults/user_nl_cpl @@ -0,0 +1,20 @@ +!------------------------------------------------------------------------ +! Users should ONLY USE user_nl_cpl to change namelists variables +! for namelist variables in drv_in (except for the ones below) and +! any keyword/values in seq_maps.rc +! Users should add ALL user specific namelist and seq_maps.rc changes below +! using the following syntax +! namelist_var = new_namelist_value +! or +! mapname = new_map_name +! For example to change the default value of ocn2atm_fmapname to 'foo' use +! ocn2atm_fmapname = 'foo' +! +! Note that some namelist variables MAY NOT be changed in user_nl_cpl - +! they are defined in a $CASEROOT xml file and must be changed with +! xmlchange. +! +! For example, rather than set username to 'foo' in user_nl_cpl, call +! ./xmlchange USER=foo +!------------------------------------------------------------------------ +orb_iyear = 2018 diff --git a/cime_config/usermods_dirs/NEON/FATES/defaults/user_nl_datm_streams b/cime_config/usermods_dirs/NEON/FATES/defaults/user_nl_datm_streams new file mode 100644 index 0000000000..36f1e72b3a --- /dev/null +++ b/cime_config/usermods_dirs/NEON/FATES/defaults/user_nl_datm_streams @@ -0,0 +1,39 @@ +!------------------------------------------------------------------------ +! This file is used to modify datm.streams.xml generated in $RUNDIR +! Entries should have the form +! :<= new stream_value> +! The following are accepted values for an assume streamname of foo +! foo:meshfile = character string +! foo:datafiles = comma separated string of full pathnames (e.g. file1,file2,file3...) +! foo:datavars = comma separated string of field pairs (e.g. foo foobar,foo2 foobar2...) +! foo:taxmode = one of [cycle, extend, limit] +! foo:tintalgo = one of [lower,upper,nearest,linear,coszen] +! foo:readmode = single (only suported mode right now) +! foo:mapalgo = one of [bilinear,redist,nn,consf,consd,none] +! foo:dtlimit = real (1.5 is default) +! foo:year_first = integer +! foo:year_last = integer +! foo:year_align = integer +! foo:vectors = one of [none,u:v] +! foo:lev_dimname: = one of [null,name of level dimenion name] +! foo:offset = integer +! As an example: +! foo:year_first = 1950 +! would change the stream year_first stream_entry to 1950 for the foo stream block +!------------------------------------------------------------------------ +presaero.SSP3-7.0:datafiles = $DIN_LOC_ROOT/atm/cam/chem/trop_mozart_aero/aero/aerodep_clm_SSP370_b.e21.BWSSP370cmip6.f09_g17.CMIP6-SSP3-7.0-WACCM.001_2018-2030_monthly_0.9x1.25_c210826.nc +presaero.SSP3-7.0:year_first=2018 +presaero.SSP3-7.0:year_last=2022 +presaero.SSP3-7.0:year_align=2018 +presaero.SSP3-7.0:dtlimit=30 + +presndep.SSP3-7.0:datafiles = $DIN_LOC_ROOT/lnd/clm2/ndepdata/fndep_clm_f09_g17.CMIP6-SSP3-7.0-WACCM_2018-2030_monthly_c210826.nc +presndep.SSP3-7.0:year_first=2018 +presndep.SSP3-7.0:year_last=2022 +presndep.SSP3-7.0:year_align=2018 +presndep.SSP3-7.0:dtlimit=30 + +preso3.SSP3-7.0:year_first=2018 +preso3.SSP3-7.0:year_last=2022 +preso3.SSP3-7.0:year_align=2018 +preso3.SSP3-7.0:dtlimit=30 diff --git a/cime_config/usermods_dirs/NEON/GUAN/shell_commands b/cime_config/usermods_dirs/NEON/GUAN/shell_commands index ee2eca82d9..c78bf31f55 100644 --- a/cime_config/usermods_dirs/NEON/GUAN/shell_commands +++ b/cime_config/usermods_dirs/NEON/GUAN/shell_commands @@ -1,6 +1,13 @@ +#!/bin/bash + ./xmlchange NEONSITE=GUAN ./xmlchange PTS_LON=293.13112 ./xmlchange PTS_LAT=17.96882 -./xmlchange RUN_STARTDATE=2019-01-01 ./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019 -./xmlchange STOP_N=39 +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/KONA/shell_commands b/cime_config/usermods_dirs/NEON/KONA/shell_commands index c00e220e77..66f274dd36 100644 --- a/cime_config/usermods_dirs/NEON/KONA/shell_commands +++ b/cime_config/usermods_dirs/NEON/KONA/shell_commands @@ -1,3 +1,7 @@ ./xmlchange NEONSITE=KONA ./xmlchange PTS_LON=263.38956 ./xmlchange PTS_LAT=39.10828 +# Setup to run with prognostic crops for this site +# If you want to explicitly run in SP mode or add other +# options you'll need to add that after this... +./xmlchange CLM_BLDNML_OPTS="--bgc bgc --crop" diff --git a/cime_config/usermods_dirs/NEON/LAJA/shell_commands b/cime_config/usermods_dirs/NEON/LAJA/shell_commands index 522818a697..217216e3ec 100644 --- a/cime_config/usermods_dirs/NEON/LAJA/shell_commands +++ b/cime_config/usermods_dirs/NEON/LAJA/shell_commands @@ -1,8 +1,14 @@ +#!/bin/bash ./xmlchange NEONSITE=LAJA ./xmlchange PTS_LON=292.92392 ./xmlchange PTS_LAT=18.02184 -./xmlchange RUN_STARTDATE=2019-01-01 ./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019 -./xmlchange STOP_N=39 +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/LENO/shell_commands b/cime_config/usermods_dirs/NEON/LENO/shell_commands index 89583ed158..06af587292 100644 --- a/cime_config/usermods_dirs/NEON/LENO/shell_commands +++ b/cime_config/usermods_dirs/NEON/LENO/shell_commands @@ -1,7 +1,14 @@ +#!/bin/bash + ./xmlchange NEONSITE=LENO ./xmlchange PTS_LON=271.83897 ./xmlchange PTS_LAT=31.8531 -./xmlchange RUN_STARTDATE=2021-01-01 ./xmlchange DATM_YR_ALIGN=2021,DATM_YR_START=2021 -./xmlchange STOP_N=15 +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2021-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=15 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/MLBS/shell_commands b/cime_config/usermods_dirs/NEON/MLBS/shell_commands index 9f70ecd662..11ab445450 100644 --- a/cime_config/usermods_dirs/NEON/MLBS/shell_commands +++ b/cime_config/usermods_dirs/NEON/MLBS/shell_commands @@ -1,6 +1,16 @@ +#!/bin/bash ./xmlchange NEONSITE=MLBS ./xmlchange PTS_LON=279.47575 ./xmlchange PTS_LAT=37.37783 -./xmlchange STOP_N=24 -./xmlchange DATM_YR_END=2019 - +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + ./xmlchange DATM_YR_END=2019 + # Different default number of months to run for transient case + if [[ $compset =~ ^HIST ]]; then + ./xmlchange DATM_YR_END=2020 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=24 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/MOAB/shell_commands b/cime_config/usermods_dirs/NEON/MOAB/shell_commands index d91513a92c..649fa2eaba 100644 --- a/cime_config/usermods_dirs/NEON/MOAB/shell_commands +++ b/cime_config/usermods_dirs/NEON/MOAB/shell_commands @@ -1,8 +1,16 @@ +#!/bin/bash ./xmlchange NEONSITE=MOAB ./xmlchange PTS_LON=250.61118 ./xmlchange PTS_LAT=38.25136 -./xmlchange RUN_STARTDATE=2018-01-01 -./xmlchange DATM_YR_ALIGN=2018,DATM_YR_START=2018,DATM_YR_END=2020 -./xmlchange STOP_N=36 - - +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + ./xmlchange DATM_YR_END=2020 + # Different default number of months to run for transient case + if [[ $compset =~ ^HIST ]]; then + ./xmlchange DATM_YR_END=2021 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=36 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/NIWO/shell_commands b/cime_config/usermods_dirs/NEON/NIWO/shell_commands index a3e73ca343..b4f27ea8ed 100644 --- a/cime_config/usermods_dirs/NEON/NIWO/shell_commands +++ b/cime_config/usermods_dirs/NEON/NIWO/shell_commands @@ -1,4 +1,9 @@ +#!/bin/bash ./xmlchange NEONSITE=NIWO ./xmlchange PTS_LON=254.41676 ./xmlchange PTS_LAT=40.05236 -./xmlchange DATM_YR_END=2018 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == "NEON" ]]; then + ./xmlchange DATM_YR_END=2018 +fi diff --git a/cime_config/usermods_dirs/NEON/ONAQ/shell_commands b/cime_config/usermods_dirs/NEON/ONAQ/shell_commands index f2e1640725..dae5344528 100644 --- a/cime_config/usermods_dirs/NEON/ONAQ/shell_commands +++ b/cime_config/usermods_dirs/NEON/ONAQ/shell_commands @@ -1,8 +1,16 @@ +#!/bin/bash ./xmlchange NEONSITE=ONAQ -./xmlchange PTS_LON=276.49815 -./xmlchange PTS_LAT=35.68839 -./xmlchange RUN_STARTDATE=2018-01-01 -./xmlchange DATM_YR_ALIGN=2018,DATM_YR_START=2018,DATM_YR_END=2019 -./xmlchange STOP_N=24 - - +./xmlchange PTS_LON=247.54755 +./xmlchange PTS_LAT=40.17760 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + ./xmlchange DATM_YR_END=2019 + # Different default number of months to run for transient case + if [[ $compset =~ ^HIST ]]; then + ./xmlchange DATM_YR_END=2020 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=24 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/ORNL/shell_commands b/cime_config/usermods_dirs/NEON/ORNL/shell_commands index 264d451753..5708d3dec5 100644 --- a/cime_config/usermods_dirs/NEON/ORNL/shell_commands +++ b/cime_config/usermods_dirs/NEON/ORNL/shell_commands @@ -1,3 +1,3 @@ ./xmlchange NEONSITE=ORNL -./xmlchange PTS_LON=275.83419000000004 -./xmlchange PTS_LAT=35.57525 +./xmlchange PTS_LON=275.717412 +./xmlchange PTS_LAT=35.964128 diff --git a/cime_config/usermods_dirs/NEON/SJER/shell_commands b/cime_config/usermods_dirs/NEON/SJER/shell_commands index 9d3ee15a81..ee50f61fa1 100644 --- a/cime_config/usermods_dirs/NEON/SJER/shell_commands +++ b/cime_config/usermods_dirs/NEON/SJER/shell_commands @@ -1,8 +1,20 @@ +#!/bin/bash ./xmlchange NEONSITE=SJER ./xmlchange PTS_LON=240.267 ./xmlchange PTS_LAT=37.107117 -./xmlchange RUN_STARTDATE=2019-01-01 -./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019 -./xmlchange STOP_N=39 +./xmlchange DATM_YR_ALIGN=2019 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [ $CLM_USRDAT_NAME=='NEON' ] +then + ./xmlchange DATM_YR_START=2019 + # Different default start date and number of months to run for transient case + if [[ $compset =~ ^HIST ]] + then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi + fi +fi -#./xmlchange RUN_STARTDATE=2018-09-01 diff --git a/cime_config/usermods_dirs/NEON/STER/shell_commands b/cime_config/usermods_dirs/NEON/STER/shell_commands index 2c1699fc9c..38b173c309 100644 --- a/cime_config/usermods_dirs/NEON/STER/shell_commands +++ b/cime_config/usermods_dirs/NEON/STER/shell_commands @@ -1,3 +1,7 @@ ./xmlchange NEONSITE=STER ./xmlchange PTS_LON=256.96992 ./xmlchange PTS_LAT=40.45984 +# Setup to run with prognostic crops for this site +# If you want to explicitly run in SP mode or add other +# # options you'll need to add that after this... +./xmlchange CLM_BLDNML_OPTS="--bgc bgc --crop" diff --git a/cime_config/usermods_dirs/NEON/TEAK/shell_commands b/cime_config/usermods_dirs/NEON/TEAK/shell_commands index 5309888a12..abac8e9263 100644 --- a/cime_config/usermods_dirs/NEON/TEAK/shell_commands +++ b/cime_config/usermods_dirs/NEON/TEAK/shell_commands @@ -1,7 +1,17 @@ +#!/bin/bash ./xmlchange NEONSITE=TEAK ./xmlchange PTS_LON=240.99424199999999 ./xmlchange PTS_LAT=37.006472 -./xmlchange RUN_STARTDATE=2019-01-01 -./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019 -./xmlchange STOP_N=39 - +./xmlchange DATM_YR_ALIGN=2019 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + ./xmlchange DATM_YR_START=2019 + # Different default start date and number of months to run for transient case + if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/TOOL/shell_commands b/cime_config/usermods_dirs/NEON/TOOL/shell_commands index fc2551390b..3c749cde93 100644 --- a/cime_config/usermods_dirs/NEON/TOOL/shell_commands +++ b/cime_config/usermods_dirs/NEON/TOOL/shell_commands @@ -1,8 +1,12 @@ +#!/bin/bash ./xmlchange NEONSITE=TOOL ./xmlchange PTS_LON=210.629872 ./xmlchange PTS_LAT=68.66045 -./xmlchange RUN_STARTDATE=2020-01-01 ./xmlchange DATM_YR_ALIGN=2020,DATM_YR_START=2020 -./xmlchange STOP_N=27 - - +# Different default start date and number of months to run for transient case +if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2020-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=27 + fi +fi diff --git a/cime_config/usermods_dirs/NEON/UNDE/shell_commands b/cime_config/usermods_dirs/NEON/UNDE/shell_commands index 79688e0a8f..f810e4a76b 100644 --- a/cime_config/usermods_dirs/NEON/UNDE/shell_commands +++ b/cime_config/usermods_dirs/NEON/UNDE/shell_commands @@ -1,3 +1,3 @@ ./xmlchange NEONSITE=UNDE -./xmlchange PTS_LON=270.6779 -./xmlchange PTS_LAT=46.14103 +./xmlchange PTS_LON=270.462746 +./xmlchange PTS_LAT=46.23391 diff --git a/cime_config/usermods_dirs/NEON/WREF/shell_commands b/cime_config/usermods_dirs/NEON/WREF/shell_commands index 77a0b750cd..77a48ae1c0 100644 --- a/cime_config/usermods_dirs/NEON/WREF/shell_commands +++ b/cime_config/usermods_dirs/NEON/WREF/shell_commands @@ -1,3 +1,17 @@ +#!/bin/bash ./xmlchange NEONSITE=WREF ./xmlchange PTS_LON=238.04162 ./xmlchange PTS_LAT=45.81637 +./xmlchange DATM_YR_ALIGN=2019 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + # Different default start date and number of months to run for transient case + ./xmlchange DATM_YR_START=2019 + if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/YELL/shell_commands b/cime_config/usermods_dirs/NEON/YELL/shell_commands index c32b11ef7d..33915b88a1 100644 --- a/cime_config/usermods_dirs/NEON/YELL/shell_commands +++ b/cime_config/usermods_dirs/NEON/YELL/shell_commands @@ -1,7 +1,17 @@ +#!/bin/bash ./xmlchange NEONSITE=YELL ./xmlchange PTS_LON=249.45803999999998 ./xmlchange PTS_LAT=44.95597 -./xmlchange RUN_STARTDATE=2019-01-01 -./xmlchange DATM_YR_ALIGN=2019,DATM_YR_START=2019 -./xmlchange STOP_N=39 -# ./xmlchange RUN_STARTDATE=2018-08-01 +./xmlchange DATM_YR_ALIGN=2019 +# NEON precipitation data for this site is missing so end early +# If CLM_USRDAT_NAME is NEON.PRISM you can run to the end of the data +if [[ $CLM_USRDAT_NAME == 'NEON' ]]; then + # Different default start date and number of months to run for transient case + ./xmlchange DATM_YR_START=2019 + if [[ $compset =~ ^HIST ]]; then + ./xmlchange RUN_STARTDATE=2019-01-01 + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_N=39 + fi + fi +fi diff --git a/cime_config/usermods_dirs/NEON/defaults/shell_commands b/cime_config/usermods_dirs/NEON/defaults/shell_commands index 53e445e06a..39810dbc70 100644 --- a/cime_config/usermods_dirs/NEON/defaults/shell_commands +++ b/cime_config/usermods_dirs/NEON/defaults/shell_commands @@ -1,16 +1,47 @@ +#!/bin/bash ./xmlchange CLM_USRDAT_NAME=NEON -./xmlchange RUN_STARTDATE=2018-01-01 -./xmlchange CLM_NML_USE_CASE=1850-2100_SSP3-7.0_transient +# CLM_USRDAT_NAME can be set to either NEON or NEON.PRISM ./xmlchange CCSM_CO2_PPMV=408.83 +# Set data forcing data to future scenario so will have data from 2018 to present-day ./xmlchange DATM_PRESAERO=SSP3-7.0 ./xmlchange DATM_PRESNDEP=SSP3-7.0 ./xmlchange DATM_PRESO3=SSP3-7.0 +# Explicitly set the MPI library to mpi-serial so won't have the build/run complexity of a full MPI library +./xmlchange MPILIB=mpi-serial +# Set years to run forcing data over ./xmlchange DATM_YR_ALIGN=2018,DATM_YR_END=2021,DATM_YR_START=2018 +# +# Save some variables that may be used later +# +compset=`./xmlquery COMPSET --value` +CLM_USRDAT_NAME=`./xmlquery CLM_USRDAT_NAME --value` +TEST=`./xmlquery TEST --value` + +# For a transient case run the whole length and don't cycle +if [[ $compset =~ ^HIST ]]; then + ./xmlchange DATM_YR_END=2022 + ./xmlchange RUN_STARTDATE=2018-01-01 + # Number of months that can be run for the full transient case + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_OPTION="nmonths" + ./xmlchange STOP_N=51 + fi + ./xmlchange CLM_NML_USE_CASE="2018-PD_transient" +else + ./xmlchange CLM_NML_USE_CASE="2018_control" +fi + +# If needed for SP simulations: & set history file variables +if [[ $compset =~ .*CLM[0-9]+%[^_]*SP.* ]]; then + if [[ $TEST != "TRUE" ]]; then + ./xmlchange STOP_OPTION=nyears + fi + ./xmlchange CLM_FORCE_COLDSTART=on + + echo "hist_fincl2 = 'FCEV','FCTR','FGEV','FIRA','FSA','FSH','FPSN','H2OSOI','SNOW_DEPTH','TBOT','TSOI'" >> user_nl_clm +fi # Explicitly set PIO Type to NETCDF since this is a single processor case (should already be set this way) ./xmlchange PIO_TYPENAME=netcdf -# BD:05/06/2022 - The PIO_REARRANGER_LND value - for global runs, PIO_REARRANGER_LND = 1 is ideal -# and a value of 2 results in slow I/O. For point runs like these, a value of 1 results in a crash (PIO bug, probably), -# so we explicitly set a value of 2. -./xmlchange PIO_REARRANGER_LND=2 +./xmlchange NEONVERSION="v2" diff --git a/cime_config/usermods_dirs/NEON/defaults/user_nl_clm b/cime_config/usermods_dirs/NEON/defaults/user_nl_clm index 332060dd99..419ff0314c 100644 --- a/cime_config/usermods_dirs/NEON/defaults/user_nl_clm +++ b/cime_config/usermods_dirs/NEON/defaults/user_nl_clm @@ -19,19 +19,7 @@ !---------------------------------------------------------------------------------- flanduse_timeseries = ' ' ! This isn't needed for a non transient case, but will be once we start using transient compsets -fsurdat = "$DIN_LOC_ROOT/lnd/clm2/surfdata_map/NEON/surfdata_hist_78pfts_CMIP6_simyr2000_${NEONSITE}_c211102.nc" -model_year_align_urbantv = 2018 -stream_year_first_urbantv = 2018 -stream_year_last_urbantv = 2021 -stream_year_first_ndep = 2018 -model_year_align_ndep = 2018 -stream_year_last_ndep = 2021 -model_year_align_popdens = 2018 -stream_year_first_popdens = 2018 -stream_year_last_popdens = 2021 - -stream_fldfilename_lightng = '$DIN_LOC_ROOT/atm/datm7/NASA_LIS/clmforc.Li_2016_climo1995-2013.360x720.lnfm_Total_NEONarea_c210625.nc' -!stream_fldfilename_ndep = '$DIN_LOC_ROOT/lnd/clm2/ndepdata/fndep_clm_f09_g17.CMIP6-SSP3-7.0-WACCM_2018-2030_monthly_c210826.nc' +fsurdat = "$DIN_LOC_ROOT/lnd/clm2/surfdata_map/NEON/surfdata_1x1_NEON_${NEONSITE}_hist_78pfts_CMIP6_simyr2000_c230601.nc" ! h1 output stream hist_fincl2 = 'AR','ELAI','FCEV','FCTR','FGEV','FIRA','FSA','FSH','GPP','H2OSOI', diff --git a/cime_config/usermods_dirs/NEON/defaults/user_nl_datm_streams b/cime_config/usermods_dirs/NEON/defaults/user_nl_datm_streams index 6244eed2fa..bae77db6b5 100644 --- a/cime_config/usermods_dirs/NEON/defaults/user_nl_datm_streams +++ b/cime_config/usermods_dirs/NEON/defaults/user_nl_datm_streams @@ -1,16 +1,40 @@ +!------------------------------------------------------------------------ +! This file is used to modify datm.streams.xml generated in $RUNDIR +! Entries should have the form +! :<= new stream_value> +! The following are accepted values for an assume streamname of foo +! foo:meshfile = character string +! foo:datafiles = comma separated string of full pathnames (e.g. file1,file2,file3...) +! foo:datavars = comma separated string of field pairs (e.g. foo foobar,foo2 foobar2...) +! foo:taxmode = one of [cycle, extend, limit] +! foo:tintalgo = one of [lower,upper,nearest,linear,coszen] +! foo:readmode = single (only suported mode right now) +! foo:mapalgo = one of [bilinear,redist,nn,consf,consd,none] +! foo:dtlimit = real (1.5 is default) +! foo:year_first = integer +! foo:year_last = integer +! foo:year_align = integer +! foo:vectors = one of [none,u:v] +! foo:lev_dimname: = one of [null,name of level dimenion name] +! foo:offset = integer +! As an example: +! foo:year_first = 1950 +! would change the stream year_first stream_entry to 1950 for the foo stream block +!------------------------------------------------------------------------ presaero.SSP3-7.0:datafiles = $DIN_LOC_ROOT/atm/cam/chem/trop_mozart_aero/aero/aerodep_clm_SSP370_b.e21.BWSSP370cmip6.f09_g17.CMIP6-SSP3-7.0-WACCM.001_2018-2030_monthly_0.9x1.25_c210826.nc presaero.SSP3-7.0:year_first=2018 -presaero.SSP3-7.0:year_last=2030 +presaero.SSP3-7.0:year_last=2022 presaero.SSP3-7.0:year_align=2018 presaero.SSP3-7.0:dtlimit=30 presndep.SSP3-7.0:datafiles = $DIN_LOC_ROOT/lnd/clm2/ndepdata/fndep_clm_f09_g17.CMIP6-SSP3-7.0-WACCM_2018-2030_monthly_c210826.nc presndep.SSP3-7.0:year_first=2018 -presndep.SSP3-7.0:year_last=2030 +presndep.SSP3-7.0:year_last=2022 presndep.SSP3-7.0:year_align=2018 presndep.SSP3-7.0:dtlimit=30 preso3.SSP3-7.0:year_first=2018 -preso3.SSP3-7.0:year_last=2030 +preso3.SSP3-7.0:year_last=2022 preso3.SSP3-7.0:year_align=2018 preso3.SSP3-7.0:dtlimit=30 + diff --git a/cime_config/usermods_dirs/fates_nocomp/user_nl_clm b/cime_config/usermods_dirs/fates_nocomp/user_nl_clm new file mode 100644 index 0000000000..2d93aa0ed0 --- /dev/null +++ b/cime_config/usermods_dirs/fates_nocomp/user_nl_clm @@ -0,0 +1,2 @@ +! Turn FATES-NOCOMP mode on +use_fates_nocomp = .true. diff --git a/cime_config/usermods_dirs/fates_sp/user_nl_clm b/cime_config/usermods_dirs/fates_sp/user_nl_clm index cc3d9339e8..093ecd7eda 100644 --- a/cime_config/usermods_dirs/fates_sp/user_nl_clm +++ b/cime_config/usermods_dirs/fates_sp/user_nl_clm @@ -6,10 +6,11 @@ use_lch4 = .false. fates_spitfire_mode = 0 use_fates_fixed_biogeog = .true. use_fates_nocomp = .true. +hist_ndens = 1 ! Turn off a list of fields that are not needed for FATES-SP mode -hist_fexcl1 = 'FATES_TRIMMING', 'FATES_COLD_STATUS', 'FATES_DROUGHT_STATUS', 'FATES_GDD', 'FATES_NCHILLDAYS', - 'FATES_NCOLDDAYS', 'FATES_DAYSINCE_COLDLEAFOFF', 'FATES_DAYSINCE_COLDLEAFON', 'FATES_DAYSINCE_DROUGHTLEAFOFF', - 'FATES_DAYSINCE_DROUGHTLEAFON', 'FATES_MEANLIQVOL_DROUGHTPHEN', 'FATES_CANOPY_SPREAD', 'FATES_VEGC_PF', +hist_fexcl1 = 'FATES_TRIMMING', 'FATES_COLD_STATUS', 'FATES_GDD', 'FATES_NCHILLDAYS', + 'FATES_NCOLDDAYS', 'FATES_DAYSINCE_COLDLEAFOFF', 'FATES_DAYSINCE_COLDLEAFON', + 'FATES_CANOPY_SPREAD', 'FATES_VEGC_PF', 'FATES_STOREC_PF', 'FATES_RECRUITMENT_PF', 'FATES_MORTALITY_PF', 'FATES_PATCHAREA_AP', 'FATES_LAI_AP', 'FATES_CANOPYAREA_AP', 'FATES_NESTEROV_INDEX', 'FATES_IGNITIONS', 'FATES_FDI', 'FATES_ROS', 'FATES_EFFECT_WSPEED', 'FATES_FUELCONSUMED', 'FATES_FIRE_INTENSITY', 'FATES_FIRE_INTENSITY_BURNFRAC', 'FATES_BURNFRAC', 'FATES_FUEL_MEF', @@ -18,11 +19,10 @@ hist_fexcl1 = 'FATES_TRIMMING', 'FATES_COLD_STATUS', 'FATES_DROUGHT_STATUS', 'FA 'FATES_FUEL_AMOUNT_AP', 'FATES_FUEL_BURNT_BURNFRAC_FC', 'FATES_LITTER_IN', 'FATES_LITTER_OUT', 'FATES_SEED_BANK', 'FATES_SEEDS_IN', 'FATES_LITTER_IN_EL', 'FATES_LITTER_OUT_EL', 'FATES_SEED_BANK_EL', 'FATES_SEEDS_IN_LOCAL_EL', 'FATES_SEEDS_IN_EXTERN_EL', 'FATES_SEED_GERM_EL', 'FATES_SEED_DECAY_EL', 'FATES_STOREC', 'FATES_VEGC', - 'FATES_SAPWOODC', 'FATES_FROOTC', 'FATES_REPROC', 'FATES_CEFFLUX', 'FATES_STRUCTC', 'FATES_NONSTRUCTC', + 'FATES_SAPWOODC', 'FATES_FROOTC', 'FATES_REPROC', 'FATES_STRUCTC', 'FATES_NONSTRUCTC', 'FATES_VEGC_ABOVEGROUND', 'FATES_CANOPY_VEGC', 'FATES_USTORY_VEGC', 'FATES_PRIMARY_PATCHFUSION_ERR', - 'FATES_DISTURBANCE_RATE_P2P', 'FATES_DISTURBANCE_RATE_P2S', 'FATES_DISTURBANCE_RATE_S2S', 'FATES_DISTURBANCE_RATE_FIRE', 'FATES_DISTURBANCE_RATE_LOGGING', 'FATES_DISTURBANCE_RATE_TREEFALL', - 'FATES_DISTURBANCE_RATE_POTENTIAL', 'FATES_HARVEST_CARBON_FLUX', 'FATES_GPP_CANOPY', 'FATES_AUTORESP_CANOPY', + 'FATES_HARVEST_CARBON_FLUX', 'FATES_GPP_CANOPY', 'FATES_AUTORESP_CANOPY', 'FATES_GPP_USTORY', 'FATES_AUTORESP_USTORY', 'FATES_CROWNAREA_CL', 'FATES_DEMOTION_CARBONFLUX', 'FATES_PROMOTION_CARBONFLUX', 'FATES_MORTALITY_CFLUX_CANOPY', 'FATES_MORTALITY_CFLUX_USTORY', 'FATES_DDBH_CANOPY_SZ', 'FATES_DDBH_USTORY_SZ', 'FATES_BASALAREA_SZ', diff --git a/cime_config/usermods_dirs/newton_krylov_spinup/user_nl_clm b/cime_config/usermods_dirs/newton_krylov_spinup/user_nl_clm index 318105a043..75513be601 100644 --- a/cime_config/usermods_dirs/newton_krylov_spinup/user_nl_clm +++ b/cime_config/usermods_dirs/newton_krylov_spinup/user_nl_clm @@ -1,8 +1,8 @@ hist_dov2xy = .true.,.false. hist_nhtfrq = 0,-175200 hist_mfilt = 1,1 -hist_fincl2 = 'FPI_vr', 'K_PAS_SOM', 'K_SLO_SOM', 'K_ACT_SOM', - 'K_CWD', 'K_CEL_LIT', 'K_LIG_LIT', 'K_MET_LIT', +hist_fincl2 = 'FPI_vr', 'K_SOM_PAS', 'K_SOM_SLO', 'K_SOM_ACT', + 'K_CWD', 'K_LIT_CEL', 'K_LIT_LIG', 'K_LIT_MET', 'CWD_PATHFRAC_L2_vr', 'CWD_RESP_FRAC_L2_vr', 'CWD_PATHFRAC_L3_vr', 'CWD_RESP_FRAC_L3_vr', 'L1_PATHFRAC_S1_vr', 'L1_RESP_FRAC_S1_vr', diff --git a/cime_config/usermods_dirs/output_bgc/user_nl_clm b/cime_config/usermods_dirs/output_bgc/user_nl_clm index f7aaa09911..a7c5d098db 100644 --- a/cime_config/usermods_dirs/output_bgc/user_nl_clm +++ b/cime_config/usermods_dirs/output_bgc/user_nl_clm @@ -3,8 +3,8 @@ !---------------------------------------------------------------------------------- ! h0 stream (monthly average, gridcell-level) -hist_fexcl1 += 'ACT_SOMC_vr', 'ACT_SOMN_vr', 'SLO_SOMC_vr', 'SLO_SOMN_vr', 'PAS_SOMC_vr', 'PAS_SOMN_vr', 'SOILC_vr','SOILN_vr', 'CWDC_vr', 'MET_LITC_vr', 'CEL_LITC_vr', 'LIG_LITC_vr', 'MET_LITN_vr', 'CEL_LITN_vr', 'LIG_LITN_vr', 'CWDN_vr', 'SMIN_NO3_vr', 'CONC_O2_UNSAT', 'CONC_O2_SAT','SMIN_NH4_vr','SMINN_vr' -hist_fincl1 += 'LEAFC_TO_LITTER', 'FROOTC_TO_LITTER','MET_LITC_TO_ACT_SOMC','MET_LITN_TO_ACT_SOMN','CEL_LITC_TO_ACT_SOMC', 'CEL_LITN_TO_ACT_SOMN','LIG_LITC_TO_SLO_SOMC','LIG_LITN_TO_SLO_SOMN','DWT_WOOD_PRODUCTC_GAIN_PATCH' +hist_fexcl1 += 'SOM_ACT_C_vr', 'SOM_ACT_N_vr', 'SOM_SLO_C_vr', 'SOM_SLO_N_vr', 'SOM_PAS_C_vr', 'SOM_PAS_N_vr', 'SOILC_vr','SOILN_vr', 'CWD_C_vr', 'LIT_MET_C_vr', 'LIT_CEL_C_vr', 'LIT_LIG_C_vr', 'LIT_MET_N_vr', 'LIT_CEL_N_vr', 'LIT_LIG_N_vr', 'CWD_N_vr', 'SMIN_NO3_vr', 'CONC_O2_UNSAT', 'CONC_O2_SAT','SMIN_NH4_vr','SMINN_vr' +hist_fincl1 += 'LEAFC_TO_LITTER', 'FROOTC_TO_LITTER','LIT_MET_C_TO_SOM_ACT_C','LIT_MET_N_TO_SOM_ACT_N','LIT_CEL_C_TO_SOM_ACT_C', 'LIT_CEL_N_TO_SOM_ACT_N','LIT_LIG_C_TO_SOM_SLO_C','LIT_LIG_N_TO_SOM_SLO_N','DWT_WOOD_PRODUCTC_GAIN_PATCH' ! h1 stream (monthly average, finest sub-grid) hist_fincl2 += 'GPP', 'NPP', 'AGNPP', 'TOTVEGC', 'NPP_NUPTAKE', 'AR', 'HR', 'HTOP' @@ -14,7 +14,7 @@ hist_fincl2 += 'GPP', 'NPP', 'AGNPP', 'TOTVEGC', 'NPP_NUPTAKE', 'AR', 'HR', 'HTO hist_fincl3 += 'GPP', 'NPP', 'AR', 'HR', 'DWT_CONV_CFLUX_PATCH', 'WOOD_HARVESTC', 'DWT_WOOD_PRODUCTC_GAIN_PATCH', 'SLASH_HARVESTC', 'COL_FIRE_CLOSS', 'FROOTC:I', 'HTOP' ! h3 stream (yearly average, gridcell-level) -hist_fincl4 += 'SOILC_vr', 'SOILN_vr', 'CWDC_vr', 'MET_LITC_vr', 'CEL_LITC_vr', 'LIG_LITC_vr', 'MET_LITN_vr', 'CEL_LITN_vr', 'LIG_LITN_vr','CWDN_vr', 'TOTLITC:I', 'TOT_WOODPRODC:I', 'TOTSOMC:I','TOTVEGC:I' +hist_fincl4 += 'SOILC_vr', 'SOILN_vr', 'CWD_C_vr', 'LIT_MET_C_vr', 'LIT_CEL_C_vr', 'LIT_LIG_C_vr', 'LIT_MET_N_vr', 'LIT_CEL_N_vr', 'LIT_LIG_N_vr','CWD_N_vr', 'TOTLITC:I', 'TOT_WOODPRODC:I', 'TOTSOMC:I','TOTVEGC:I' ! h4 stream (yearly average, landunit-level) hist_fincl5 += 'TOTSOMC:I', 'TOTSOMC_1m:I', 'TOTECOSYSC:I', 'TOTVEGC:I', 'WOODC:I', 'TOTLITC:I', 'LIVECROOTC:I', 'DEADCROOTC:I', 'FROOTC:I' diff --git a/cime_config/usermods_dirs/output_sp_exice/include_user_mods b/cime_config/usermods_dirs/output_sp_exice/include_user_mods new file mode 100644 index 0000000000..786fa907a9 --- /dev/null +++ b/cime_config/usermods_dirs/output_sp_exice/include_user_mods @@ -0,0 +1,2 @@ +../_includes/output_base +../output_sp diff --git a/cime_config/usermods_dirs/output_sp_exice/user_nl_clm b/cime_config/usermods_dirs/output_sp_exice/user_nl_clm new file mode 100644 index 0000000000..48e680df67 --- /dev/null +++ b/cime_config/usermods_dirs/output_sp_exice/user_nl_clm @@ -0,0 +1,8 @@ +!---------------------------------------------------------------------------------- +! Settings from output_sp_exice +!---------------------------------------------------------------------------------- + +! h3 stream (yearly average, gridcell-level) +! Eyr +hist_fincl4 += 'EXCESS_ICE' + diff --git a/doc/.ChangeLog_template b/doc/.ChangeLog_template index 63f4628bad..a1170a61cf 100644 --- a/doc/.ChangeLog_template +++ b/doc/.ChangeLog_template @@ -27,19 +27,14 @@ Does this tag change answers significantly for any of the following physics conf [ ] clm4_5 -Bugs fixed or introduced ------------------------- +Bugs fixed +---------- [Remove any lines that don't apply. Remove entire section if nothing applies.] CTSM issues fixed (include CTSM Issue #): -Externals issues fixed (include issue #): - Known bugs introduced in this tag (include issue #): -Known bugs found since the previous tag (include issue #): - - Notes of particular relevance for users --------------------------------------- [Remove any lines that don't apply. Remove entire section if nothing applies.] @@ -101,11 +96,11 @@ infrastructure should be run when appropriate, as described below. build-namelist tests (if CLMBuildNamelist.pm has changed): - cheyenne - + derecho - tools-tests (test/tools) (if tools have been changed): - cheyenne - + derecho - python testing (if python code has changed; see instructions in python/README.md; document testing done): @@ -119,15 +114,15 @@ infrastructure should be run when appropriate, as described below. doing their own baseline generation. If you are already running the full aux_clm then you do NOT need to separately run the clm_pymods test suite, and you can remove the following line.] - clm_pymods test suite on cheyenne - + clm_pymods test suite on derecho - regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): - cheyenne ---- + derecho ----- izumi ------- fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) - cheyenne ---- + derecho ----- izumi ------- any other testing (give details below): @@ -150,7 +145,9 @@ Changes answers relative to baseline: - nature of change (roundoff; larger than roundoff/same climate; new climate): If bitwise differences were observed, how did you show they were no worse - than roundoff? + than roundoff? Roundoff differences means one or more lines of code change results + only by roundoff level (because order of operation changes for example). Roundoff + changes to state fields usually grow to greater than roundoff as the simulation progresses. If this tag changes climate describe the run(s) done to evaluate the new climate (put details of the simulations in the experiment database) diff --git a/doc/ChangeLog b/doc/ChangeLog index b6c434f21c..084516e23e 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,4865 @@ =============================================================== +Tag name: ctsm5.1.dev167 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Thu 08 Feb 2024 01:56:05 PM MST +One-line Summary: Delete _FillValue and history from parameter files + +Purpose and description of changes +---------------------------------- + +Updates parameter files to c240207b. These are the same as c240105 except: +- Attribute _FillValue has been removed from all variables +- Global attributes history, history_of_appended_files, and latest_git_log have been removed + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- + +CTSM issues fixed (include CTSM Issue #): +- Fixes #2347 + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +- #2350: Delete _FillValue and history from parameter files (https://github.com/ESCOMP/CTSM/pull/2350) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev166 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310), tking (Teagan King), samrabin (Sam Rabin) +Date: Wed 24 Jan 2024 05:39:41 PM MST +One-line Summary: BFB merge tag + +Purpose and description of changes +---------------------------------- + + #2315 @TeaganKing Refactoring run_neon for PLUMBER2 part1 + #2213 @samsrabin Automatically assign high priority items to project 25 + #2330 @samsrabin Add Izumi version of the aux_clm unit testing + #2326 @samsrabin run_sys_tests: Check Python environment for FatesColdTwoStream tests + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- + +CTSM issues fixed (include CTSM Issue #): + Fixes #2315 + Fixes #2213 + Fixes #2330 + Fixes #2326 + +Known bugs introduced in this tag (include issue #): + - New feature coming in with #2213 where user will receive email from + github when pushing to their remote: + "Run failed: .github/workflows/assign-to-project.yml" + - New feature that also affects older tags: The izumi FatesColdTwoStream + test submitted from ./run_sys_tests will fail at CREATE_NEWCASE unless users + introduce "module load lang/python/3.7.0" in their .bash_profile. + Longterm solution discussed in #2335. The test also works when submitted + manually with ./create_test. + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: + #2315 New unit tests for arg_parse and NeonSite + #2330 New test in aux_clm that does unit testing on izumi because unit + testing does not work on derecho, yet + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - OK, pylint gives long list of warnings (expected) + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + + +Answer changes +-------------- +Changes answers relative to baseline: No + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2334 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev165 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310), oleson (Keith Oleson), samrabin (Sam Rabin) +Date: Fri 19 Jan 2024 06:40:36 PM MST +One-line Summary: Turn Meier2022, tillage, and residue removal on for ctsm5.1, fix #2212 + +Purpose and description of changes +---------------------------------- + +Answer-changing merge-tag: +- Turn Meier2022 on for ctsm5.1. Had turned off temporarily while fixing a bug. +- Bring in Urban answer fix #2212. +- Turn tillage and residue removal on for ctsm5.1. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[x] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +CTSM issues fixed (include CTSM Issue #): +Fixes #2212 + +Notes of particular relevance for users +--------------------------------------- +Changes made to namelist defaults (e.g., changed parameter values): +- Making Meier2022 the default for ctsm5.1 again. +- Making tillage low by default for ctsm5.1. +- Making residue removal 0.5 by default for ctsm5.1. + +Testing summary: +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: YES + + [ If a tag changes answers relative to baseline comparison the + following should be filled in (otherwise remove this section). + And always remove these three lines and parts that don't apply. ] + + Summarize any changes to answers, i.e., + - what code configurations: ALL + - what platforms/compilers: ALL + - nature of change:i + clm45 and clm50: larger than roundoff + clm51: possibly climate changing + Effect of Meier2022 was documented here: https://github.com/NCAR/LMWG_dev/issues/38 + Effect of tillage and residue removal may require an Answer Changing Tag simulation + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2323 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev164 +Originator(s): rgknox (Ryan Knox) +Date: Wed 17 Jan 2024 12:38:18 PM MST +One-line Summary: Compatibility and tests for FATES 2-Stream + +Purpose and description of changes +---------------------------------- + +This set of changes enables compatibility and testing for FATES two-stream radiation scattering. Two stream radiation is selected by setting the FATES parameter file variable fates_rad_mod = 2. This is an alternative to Norman radiation. The FATES default radiation model will continue to be Norman for the time being, but is expected to transition to two-stream in the near future. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- + +CTSM issues fixed (include CTSM Issue #): 2305 + +Known bugs introduced in this tag (include issue #): none, but testing was modified to catch a pre-existing bug via test: SMS_D_Ld3.f09_g17.I2000Clm51FatesSpCruRsGs.derecho_gnu.clm-FatesColdSatPhen_prescribed. This has been documented in CTSM issue #2321 + +Notes of particular relevance for users +--------------------------------------- + +Caveats for users (e.g., need to interpolate initial conditions): none + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): none + +Changes made to namelist defaults (e.g., changed parameter values): none + +Changes to the datasets (e.g., parameter, surface or initial files): none + +Substantial timing or memory changes: +[e.g., check PFS test in the test suite and look at timings, if you +expect possible significant timing changes] + +If a fates user does opt to use two-stream radiation, they should expect changes in simulation time compared with norman radiation. This difference varies and is somewhere between equal or 20% slower at a maximum. Most tests seemed to be about 10-15% slower for regions with high vegetation demographic diversity. + +Notes of particular relevance for developers: +--------------------------------------------- +NOTE: Be sure to review the steps in README.CHECKLIST.master_tags as well as the coding style in the Developers Guide +[Remove any lines that don't apply. Remove entire section if nothing applies.] + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): + +Changes to tests or testing: + +New tests were added to the fates and aux_clm regression suites, with suffix clm-FatesColdTwoStream. One of them uses fixed giogeography and no cross-pft competition. + +Testing summary: +---------------- + + regular tests, baseline: ctsm5.1.dev163 + + derecho ----- OK + izumi ------- OK + + fates tests: baseline: fates-sci.1.70.0_api.32.0.0-ctsm5.1.dev163 + derecho ----- OK + izumi ------- (being run after tag was made) + + any other testing (give details below): + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + +Answer changes +-------------- + +All answers are B4B with baselines mentioned above, except for one fates variable: FATES_RAD_ERROR. This variable was changed to report the maximum of VIS and NIR, instead of just VIS. A follow up set of changes will change the dimension of this variable. This change was expected. + + +Other details +------------- + +This set of changes is synchronized with the new FATES tag: sci.1.71.0_api.33.0.0 +PR: https://github.com/NGEET/fates/pull/1141 + + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev163 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Wed Jan 10 13:03:34 MST 2024 +One-line Summary: Add tillage and residue removal + +Purpose and description of changes +---------------------------------- + +Adds capability for cropland soil tillage and post-harvest residue removal. + +Tillage: This PR brings in the tillage code written by Sam Levis and Michael Graham and used in Graham et al. (2021, ERL, doi:10.1088/1748-9326/abe6c6). Low- and high-intensity tillage here work by increasing the decomposition rate of different soil carbon pools. These "decomposition multipliers" vary based on soil pool and how long it's been since the crop was planted; they are set with new paramfile variables till_decompk_multipliers and mimics_till_decompk_multipliers. Note that tillage is off by default. + +Residue removal: Adds a parameter hat represents what fraction of post-harvest crop residues (stems and leaves) should be removed to the crop product pool rather than being transferred to litter. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- + +CTSM issues fixed (include CTSM Issue #): +- Resolves #112 (https://github.com/ESCOMP/CTSM/issues/112) +- Contributes to #2310 (https://github.com/ESCOMP/CTSM/issues/2310) by adding NEON tests to expected failure list) +- run_sys_tests no longer fails on Izumi + + +Notes of particular relevance for users +--------------------------------------- + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): +- Adds parameter tillage_mode with options off (default), low, and high. +- Adds parameter use_original_tillage_phases (false by default; see Add soil tillage for crops #2040). +- Adds parameter max_tillage_depth (cm). +- Adds parameter crop_residue_removal_frac (default 0) + + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: +- Changes SMS_Ld5.f10_f10_mg37.I1850Clm45BgcCrop.derecho_gnu.clm-crop to use tillage and residue removal; now SMS_Ld5.f10_f10_mg37.I1850Clm45BgcCrop.derecho_gnu.clm-till--clm-remove_residues +- Changes PEM_Ld1.f10_f10_mg37.I2000Clm51BgcCrop.izumi_intel.clm-crop to use tilllage; now PEM_Ld1.f10_f10_mg37.I2000Clm51BgcCrop.izumi_intel.clm-till + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + (any machine) - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- PASS + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +- #2311: Add tillage and residue removal (https://github.com/ESCOMP/CTSM/pull/2311) +which is a combination of: +- #2040: Add soil tillage for crops (https://github.com/ESCOMP/CTSM/pull/2040) +- #2297: Add crop residue removal (https://github.com/ESCOMP/CTSM/pull/2297) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev162 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Fri Jan 5 15:57:23 MST 2024 +One-line Summary: Improvements to processing of crop calendar files + +Purpose and description of changes +---------------------------------- + +In python/ctsm/crop_calendars/, process_ggcmi_shdates.py and regrid_ggcmi_shdates.py are used to convert the raw GGCMI crop calendar files into CTSM-compatible versions. This update fixes some bugs, removes dependencies on the nco utilities, enables the use of surface datasets as template files (in addition to the existing ability to use CTSM output files), and standardizes things for consistency with other CTSM Python tools. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): +* Fixes #2167: run_ctsm_py_tests failures at os.getcwd() (https://github.com/ESCOMP/CTSM/issues/2167) + + +Notes of particular relevance for users +--------------------------------------- + +These scripts can now be called using the wrapper scripts in tools/crop_calendars/. + + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: +* Adds test_sys_regrid_ggcmi_shdates and test_unit_utils_import_coord +* Fixes bugs that were causing failures using `python/run_ctsm_py_tests` even when `make all` was fine. + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + (derecho) - PASS + + clm_pymods test suite on derecho - PASS + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +* #2292: Improvements to processing of crop calendar files (v2) (https://github.com/ESCOMP/CTSM/pull/2292) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev161 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Thu Jan 4 09:00:28 MST 2024 +One-line Summary: Refactor 20-year running means of crop GDD accumulation + +Purpose and description of changes +---------------------------------- + +Three variables track the 20-year running mean of GDD accumulation (base temperatures 0, 8, and 10°C) during the "growing season" (April through September in the Northern Hemisphere, October through March in the Southern Hemisphere). This PR refactors those to use accumulMod, resolving overly-strong weighting of the first few years after a crop becomes active. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[X] clm5_1 + +[X] clm5_0 + +[X] ctsm5_0-nwp + +[X] clm4_5 + + +Bugs fixed +---------- +CTSM issues fixed (include CTSM Issue #): +* Fixes #75. + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- DIFF + izumi ------- DIFF + + +Answer changes +-------------- + +Changes answers relative to baseline: YES + + Summarize any changes to answers, i.e., + - what code configurations: All crop configurations + - what platforms/compilers: All + - nature of change (roundoff; larger than roundoff/same climate; new climate): larger than roundoff/same climate + +GDD020, GDD820, and GDD1020 will differ, most strongly in the first few years after a crop becomes active. This will have downstream effects on lots of variables, since those are used in determining sowing date and maturity requirements. + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +* #2060: Refactor 20-year running means of crop GDD accumulation (https://github.com/ESCOMP/CTSM/pull/2060) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev160 +Originator(s): glemieux (Gregory Lemieux, LBNL, glemieux@lbl.gov) +Date: Sat 30 Dec 2023 11:23:47 PM MST +One-line Summary: FATES landuse version 1 + +Purpose and description of changes +---------------------------------- + +This tag enables FATES to utilize the state and transitions data +from the Land Use Harmonization (https://luh.umd.edu/) data sets. +This data has been preprocessed using tooling provided by FATES via +a separate pull request (FATES#1032). A new module has been added +to the dyn_subgrid directory that largely adapts the dynHarvest +module to import and read this minimially processed data, which is +data is passed to fates. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +CTSM issues fixed (include CTSM Issue #): + #1077 -- Read in full LUH2 dataset for use by FATES + #2283 -- fates wood product flux not being correctly reported during CBalanceCheck endrun diagnostic output + +Notes of particular relevance for users +--------------------------------------- +Changes made to namelist defaults (e.g., changed parameter values): + New namelist item: fluh_timeseries and use_fates_luh + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: + FATES landuse testmod, FatesColdLUH2, added + +IMPORTANT NOTE ON BASELINES: + FATES baseline tests have a change in namelists because the fluh_timeseries file is listed in the baselines + but was removed in a last minute change. Compare to baseline other than that change are exact though. + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - PASS (66 FATES tests differ because of new fates param file) + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- PASS + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + derecho ----- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: yes, for FATES only + + Summarize any changes to answers, i.e., + - what code configurations: FATES + - what platforms/compilers: ALL + - nature of change (roundoff; larger than roundoff/same climate; new climate): larger than roundoff + + The fates tag update incorporates bug fixes and hydraulic mortality fixes, as well as the + restructured disturbance code necessary to accomodate the new landuse transitions capability. + As such, small differences were observed in testmods that engaged specific disturbance modes + or were long enough to trigger other default disturbances (e.g. fire) + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): + fates: sci.1.69.0_api.31.0.0 -> fates-sci.1.70.0_api.32.0.0 + +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2076 + https://github.com/NGEET/fates/pull/1040 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev159 +Originator(s): multiple (slevis (Sam Levis), afoster (Adrianna Foster), erik (Erik Kluzek), wwieder (Will Wieder), glemieux (Greg Lemieux), oleson (Keith Oleson), sacks (Bill Sacks), samrabin (Sam Rabin), santos (Sean Patrick Santos)) +Date: Tue 12 Dec 2023 11:10:26 AM MST +One-line Summary: Various BFB fixes and updates + +Purpose and description of changes +---------------------------------- +#2253 Fix subset_data error (slevis) +#2271 bfb bug-fix allowing us to make Meier2022 the default; making it +the default comes in a later tag (slevis) +#2267 Fix run_neon CIME path import (afoster, erik, wwieder) +#2075 Add xesmf to the standard python env (glemieux) +#2229 Fix misplaced if statement and end model run if no-isotope to isotope run with user_init_interp=.false. (oleson, sacks, wwieder) +#2239 Add comment in LakeHydrology (samrabin, santos) + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +Fixes #2253 +Fixes #2271 +Fixes #2267 +Fixes #2075 +Fixes #2229 +Fixes #2239 + +Testing summary: +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- PASS + +Answer changes +-------------- +Changes answers relative to baseline: NO + +Other details +------------- +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): + +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2253 + https://github.com/ESCOMP/ctsm/pull/2271 + https://github.com/ESCOMP/ctsm/pull/2267 + https://github.com/ESCOMP/ctsm/pull/2075 + https://github.com/ESCOMP/ctsm/pull/2229 + https://github.com/ESCOMP/ctsm/pull/2239 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev158 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Thu 07 Dec 2023 10:22:46 PM MST +One-line Summary: First tag with testing moved to Derecho and working PE-layouts for Derecho + +Purpose and description of changes +---------------------------------- + +First tag for CTSM working and tested on Derecho. Update CDEPS so that we can run with the NAG compiler. +Working PE layouts. Changes from CESM3_dev over to main-dev. Testing added for Derecho. +Do some work to get tools testing working on Derecho, not completed. +Add Derecho to the README files under tools/modify_input_files and tools/site_and_regional +Remove some /glade/p references in the code. This is still an issue in the: doc, lilac, tools/mksurfdata_map, +tools/contrib, tools/mkmapdata directories, and the namelist_defaults_ctsm_tools file. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): + Resolves Derecho transition: Tests and test infrastructure #1995 + Resolves Updating Externals for Derecho causes Izumi nag tests to fail #2280 + Resolves Transient simulation with ne30np4.pg3 fails due to floating point error #2268 + Resolves Need to move location of DA_multidrv finidat files from /glade/p to /glade/campaign #2282 + Works on Add support to test/tools/test_driver.sh for Derecho for NEON tools #2276 + +Notes of particular relevance for users +--------------------------------------- + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + Added clm5_1_cam6 option to LND_TUNING_MODE + This is important in order to enable using latest clm5_1 physics with fully coupled cases + +Changes made to namelist defaults (e.g., changed parameter values): + Make sure there are finidat files for clm5_1 with CAM6 for 1850 and 2000 (from clm5_0 version at f09 + Make sure ne30np4.pg3 is setup + Some adjustments for ne30np4 and ne30np4.pg3 to make sure landuse.timeseries files are correct + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: + Add cesm3_dev test list from the CESM3_dev branch + Make sure ne30np4.pg3 + + Unit tests fail on Derecho because of ESMCI/ccs_config_cesm#131 + Derecho tests with DEBUG=T, intel compiler, and mpi-serial fail because of ESMCI/ccs_config_cesm#130 + + +Testing summary: regular + fates + ctsm_sci + cesm3_dev +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - PASS + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + derecho ----- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + derecho ----- OK + izumi ------- OK + + + +Answer changes +-------------- + +Changes answers relative to baseline: No, bit-for-bit + +Other details +------------- +[Remove any lines that don't apply. Remove entire section if nothing applies.] + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): cdeps + cdeps -> cdeps1.0.24 (allows to run with NAG compiler) + +Pull Requests that document the changes (include PR ids): + #2269 -- First tag with testing moved to Derecho and working PE layouts +(https://github.com/ESCOMP/ctsm/pull) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev157 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Tue Dec 5 09:48:26 MST 2023 +One-line Summary: Update Externals to work on Derecho + +Purpose and description of changes +---------------------------------- + +Updates Externals.cfg to work on Derecho. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): +* Resolves #2217 ("Tags for building CTSM library on Derecho [WRF-CTSM]", https://github.com/ESCOMP/CTSM/issues/2217) +* Resolves #2090 ("Update to cesm2_3_beta16 externals.", https://github.com/ESCOMP/CTSM/issues/2090) + +Known bugs introduced in this tag (include issue #): +* #2280: Updating Externals for Derecho causes Izumi nag tests to fail (https://github.com/ESCOMP/CTSM/issues/2280) + + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: +* All Izumi nag tests fail early in the run phase. This should be fixed in the next tag, which will be a more comprehensive Derecho-focused update. + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- PASS (except nag) + + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): +* cime: cime6.0.125 -> cime6.0.175 +* cmeps: cmeps0.14.21 -> cmeps0.14.43 +* cdeps: cdeps1.0.13 -> cdeps1.0.23 +* cpl7: cpl77.0.5 -> cpl77.0.7 +* parallelio: pio2_5_10 -> pio2_6_2 + +Pull Requests that document the changes (include PR ids): +* #2270: Update Externals.cfg to work on Derecho (https://github.com/ESCOMP/CTSM/pull/2270) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev156 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Thu Nov 30 15:27:18 MST 2023 +One-line Summary: Do not use Meier roughness by default + +Purpose and description of changes +---------------------------------- + +ctsm5.1.dev155 had turned on Meier2022 surface roughness calculation by default for 5.1 compsets. Several bugs have recently emerged that were not caught by pre-merge testing, so this tag reverts that change. Thus, the ZengWang2007 method is default for all compsets again. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[X] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Notes of particular relevance for users +--------------------------------------- + +Changes made to namelist defaults (e.g., changed parameter values): 5.1 compsets now use ZengWang2007 method (instead of Meier2022) for roughness calculation. + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- DIFF + izumi ------- DIFF + + +Answer changes +-------------- + +Changes answers relative to baseline: YES + + Summarize any changes to answers, i.e., + - what code configurations: 5.1 compsets + - what platforms/compilers: All + - nature of change (roundoff; larger than roundoff/same climate; new climate): new climate + + No climate-evaluating run performed, as this change is reverting part of a commit thats barely a week old. + + +Other details +------------- +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) +* #2273: Do not use Meier roughness by default, even with 5.1. (https://github.com/ESCOMP/CTSM/pull/2273) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev155 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Mon Nov 27 21:16:51 MST 2023 +One-line Summary: Use baset_latvary parameters + +Purpose and description of changes +---------------------------------- + +Namelist parameters baset_latvary_slope and baset_latvary_intercept were never actually used, with values of 0.4 and 12 being hard-coded in the relevant subroutine instead. This PR fixes that, and also adds unit testing of a refactored function that uses them. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- PASS + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +* #2240: Use baset_latvary parameters (https://github.com/ESCOMP/CTSM/pull/2240) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev154 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Wed Nov 22 09:53:01 MST 2023 +One-line Summary: New params files: Changes for Meier roughness, MIMICS, and SNICAR, and changes to leafcn and k*_nonmyc + +Purpose and description of changes +---------------------------------- + +This PR (#2258) addresses several issues: +1) Start using existing new params file for Meier roughness: +/glade/campaign/cesm/cesmdata/inputdata/lnd/clm2/paramdata/ctsm51_params.RMz0.c231011.nc +and include bug-fix #2219. +2) Update forcing heights per #2071. +3) Update params file for MIMICS per #1845. +4) Make leafcn for pfts 15 and 16 the same per #2184. +5) Switch the values of params kc_nonmyc and kn_nonmyc per #2120. +6) Move SNICAR parameters to ctsm51, clm50, and clm45 params files per #2247. + +See #2258 and the above issues for a list of contributors. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[x] clm5_1 + +[x] clm5_0 + +[x] ctsm5_0-nwp + +[x] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +Fixes #2219 +Fixes #2071 +Fixes #1845 +Fixes #2184 +Fixes #2120 +Fixes #2247 + + +Notes of particular relevance for users +--------------------------------------- +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + SNICAR namelist variable fresh_snw_rds_max moved to the params file. + +Changes made to namelist defaults (e.g., changed parameter values): + SNICAR namelist variable fresh_snw_rds_max moved to the params file. + Pointing to new params files for clm4_5, clm5_0, clm5_1. + +Changes to the datasets (e.g., parameter, surface or initial files): + New clm5_1 params file with new parameters and with modified values of existing parameters. + New clm5_0 and clm4_5 params files with new parameters for SNICAR. + ./rimport on the new params files fails with "No space left on device" but the 4 files are safe here: + /glade/u/home/slevis/paramfiles/*_params.c231117.nc + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: +- Remove clm50 Meier test. Should be clm51 but the compset I1850Clm51BgcNoAnthro does not exist. +- Remove Meier testmod directory and remove such reference from corresponding tests. +- Change mimics tests from clm50 to clm51. +- For details, see the updated testlist_clm.xml file. + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: YES + + Summarize any changes to answers, i.e., + - what code configurations: ALL + - what platforms/compilers: ALL + - nature of change: + Larger than roundoff/same climate? + I will post this tag on the Answer changing tags wiki page as "SIGNIFICANT" + and will run a simulation and diagnostics to compare against dev145. + + I used the izumi test-suite to perform one bfb sanity test: + I backed up my branch to 6dc1966 (before the snicar mods), then I put back the changes of the + commit right after snicar (71e174f). Comparing to dev154 (this tag's new baseline), + the izumi test-suite passed bfb (12 gnu, 18 intel, and 32 nag tests). + Other mods are quite confined and clear, so I will not pursue other sanity tests. + + Changes to answers commit-by-commit in this PR: + f9978db and b8c71fa: These two change answers for Meier2022 and, therefore, clm51 only + 626f520: Takes out if (z0param_method == 'Meier2022'), so changes answers for all three CLMs + 319d194: Changes answers for mimics and, therefore clm51 only (order of ops, so roundoff) + 2ee6943: Changes clm51 params file, so affects clm51 only (expect more than roundoff) + f185a31: bfb + 6dc1966: This git merge escomp/master probably does change answers from previous commit + 29ca5ad and 71e174f: Puts snicar params on the params files for all three CLMs; sanity test gave bfb + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2258 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev153 +Originator(s): afoster (Adrianna Foster) and johnpaulalex (John Paul Alex) +Date: Fri Nov 17 11:53:14 MST 2023 +One-line Summary: Call new FATES-side FatesReadParameters + +Purpose and description of changes +---------------------------------- + +Have CTSM use the new code path in FATES that allows passing in a `fates_param_reader_type`, which does the actual work reading the parameter files, in lieu of calling CTSM methods. + +Also updated NEON usermods to use version 2 data by default, rather than latest. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + +Bugs fixed or introduced +------------------------ + +Some progress towards CTSM#2006 and FATES#1076 + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- OK + izumi ------- OK + + fates baseline: `fates-sci.1.68.2_api.31.0.0-ctsm5.1.dev153` + + +Answer changes +-------------- + +None + + +Other details +-------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): fates + +Pull Requests that document the changes (include PR ids): +https://github.com/NGEET/fates/pull/1096 +https://github.com/ESCOMP/CTSM/pull/2198 + + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev152 +Originator(s): multiple (tking (Teagan King); slevis (Sam Levis); AdrienDams (Adrien Damseaux); afoster (Adrianna Foster); samrabin (Sam Rabin); ekluzek (Erik Kluzek); wwieder (Will Wieder); sacks (Bill Sacks); a few others listed below) +Date: Tue Nov 14 17:09:43 MST 2023 +One-line Summary: Mv tools to /python and add tests; add snow_thermal_cond_method; a few fixes / refactors + +Purpose and description of changes +---------------------------------- + +#2156 tking, slevis +Move the following scripts to /python/ctsm/site_and_regional +and make wrapper scripts for them in /tools/site_and_regional: +- run_neon.py +- neon_surf_wrapper.py +- modify_singlept_site_neon.py + +Add unit testing for: +- iso_utils +- modify_singlept_site_neon +- neon_surf_wrapper +- run_neon + +Add system testing for: +- modify_singlept_site_neon +- run_neon + +#2148 Adrien Damseaux (AWI, Germany), Victoria Dutch, Leanne Wake +Add namelist option snow_thermal_cond_method to select between Jordan (1991) (default) and +Sturm et al. (1997). Sturm option described for single point runs by Dutch et al. (2022). + +#2233 afoster, sacks +Fix a compiler error (for GNU 13.2) within cropcalStreamMod. +Simple fix was to change whole-array assignments/references for the starts and ends arrays to specifically +reference bounds (begp and endp). + +#2235 srabin, wwieder +Refactor ssp_anomaly_forcing script to make it easier to read and more amenable to future development. +- Adds --output-dir option; default ./anomaly_forcing reproduces previous behavior +- Makes synonyms for options with hyphens replacing underscores + +#2237 srabin +Add the following fields to restart files: +- repr_grainc_to_seed_perharv_patch +- swindow_starts_thisyr_patch +- swindow_ends_thisyr_patch + +#2044 ekluzek +More confined regular expression for NEON and a few simple fixes. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +Closes #2156 Fixes #1441 +Closes #2148 +Closes #2233 Fixes #2232 +Closes #2235 +Closes #2237 Fixes #2236 +Closes #2044 Fixes #2039 Fixes #2103 Fixes #2028 Fixes #1506 Fixes #1499 + +Known Issues: +pylint errors from previous work remain in this tag. + +Notes of particular relevance for users +--------------------------------------- +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): +#2156 New wrapper scripts don't have .py suffixes. +#2148 New namelist option snow_thermal_cond_method as described above. +#2133 None +#2135 New --output-dir option; default ./anomaly_forcing reproduces previous behavior. +Also makes synonyms for options with hyphens replacing underscores. +#2137 None +#2044 None + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: +#2156 Numerous changes were made to include new tests. +README.md for testing was updated to clarify that arguments should be used. + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + (any machine) - cheyenne OK (pylint suggestions from previous work remain) + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK, the following PASS/FAILs are expected: + +PASS ERS_Lm20_Mmpi-serial.1x1_smallvilleIA.I2000Clm50BgcCropQianRs.izumi_gnu.clm-cropMonthlyNoinitial COMPARE_base_rest (UNEXPECTED: expected FAIL) +FAIL ERS_Lm20_Mmpi-serial.1x1_smallvilleIA.I2000Clm50BgcCropQianRs.izumi_gnu.clm-cropMonthlyNoinitial BASELINE ctsm5.1.dev151: DIFF + +FAIL SMS_Ld10_D_Mmpi-serial.CLM_USRDAT.I1PtClm51Bgc.izumi_nag.clm-default--clm-NEON-NIWO BASELINE ctsm5.1.dev151: DIFF +FAIL SMS_Ld10_D_Mmpi-serial.CLM_USRDAT.I1PtClm51Bgc.izumi_nag.clm-NEON-MOAB--clm-PRISM BASELINE ctsm5.1.dev151: DIFF + + +Answer changes +-------------- +Changes answers relative to baseline: +#2156 NO +#2148 NO +#2233 NO +#2235 NO, adds attributes to write_climo files' dimension variables +#2237 ONLY Smallville "no initial" restarts; specifically, this previously +failing (COMPARE_base_rest) aux_clm test +ERS_Lm20_Mmpi-serial.1x1_smallvilleIA.I2000Clm50BgcCropQianRs.izumi_gnu.clm-cropMonthlyNoinitial +now differs from the baseline as follows: + SUMMARY of cprnc: + A total number of 76 fields were compared + and 3 had differences in fill patterns + A total number of 2 fields could not be analyzed + diff_test: the two files seem to be DIFFERENT +#2044 ONLY the NEON tests listed above due to the one-line change in +cime_config/usermods_dirs/NEON/defaults/shell_commands in #2044 + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2156 + https://github.com/ESCOMP/ctsm/pull/2148 + https://github.com/ESCOMP/ctsm/pull/2233 + https://github.com/ESCOMP/ctsm/pull/2235 + https://github.com/ESCOMP/ctsm/pull/2237 + https://github.com/ESCOMP/ctsm/pull/2044 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev151 +Originator(s): rgknox (Ryan Knox,LAWRENCE BERKELEY NATIONAL LABORATORY,510-495-2153) +Date: Sat Nov 11 16:53:01 MST 2023 +One-line Summary: Fixes to FATES long run restarts + +Purpose and description of changes +---------------------------------- + +This is a set of changes that enables exact restart tests to pass with FATES, for + longer periods, particularly those that have elapsed over a year. + We removed calls that were incrementing, uncecessary calls, and added key new + variables to the restart file (such as the leaf layer carbon balance vector). + +Collaborators: @mvdebolskiy, @mvertens, @glemieux, @ekluzek, @ckoven, @rosiealice + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +No changes to scientifically-supported configurations. + +Bugs fixed or introduced +------------------------ + +Fixes: FATES#1051 https://github.com/NGEET/fates/issues/1051 + +Notes of particular relevance for users +--------------------------------------- + +This set of changes is introduced, while there is a known test failure in: +ERS_Lm20_Mmpi-serial.1x1_smallvilleIA.I2000Clm50BgcCropQianRs.izumi_gnu.clm-cropMonthlyNoinitial + +This is documented in issue: 2236 and a fix is slated for integration. The nature of the changes + in this PR where obviously orthogonal to the issue. + +Substantial timing or memory changes: None + + +Notes of particular relevance for developers: +--------------------------------------------- + +A new test was added to aux_clm and fates test suites. The walltime for this test + was 29 minutes. This is an important new test for FATES because it is the + first gridded test that spans significantly over a year (and passes). Various test + configurations were explored to find a gridded test that completed 25 months + with some attempt at expedience in walltime. A walltime + allowance of 60 minutes is set in the test. Here is the test: + ERS_P144x1_Lm25.f10_f10_mg37.I2000Clm51Fates clm-FatesColdNoComp + + +Testing summary: +---------------- + regular tests: + + cheyenne ---- ok + izumi ------- ok + +Answer changes +-------------- + +No answer changes + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): fates API30 + +Pull Requests that document the changes (include PR ids): + +https://github.com/ESCOMP/CTSM/pull/2199 +https://github.com/NGEET/fates/pull/1098 + + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev150 +Originator(s): rgknox (Ryan Knox,LAWRENCE BERKELEY NATIONAL LABORATORY,510-495-2153) +Date: Mon Nov 6 14:12:37 MST 2023 +One-line Summary: FATES API fix to support future fates npp-fixation coupling, and urgent coupling fixes with E3SM. + +Purpose and description of changes +---------------------------------- + +This set of changes accomodates an API change on the FATES side of the code. Those changes are needed +to accomodate a bug-fix in E3SM. These changes will also accomodate correct coupling with free-living nitrogen +fixation when it is enabled in clm-fates. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +none + +Bugs fixed or introduced +------------------------ + +Supports fixes FATES issues: +https://github.com/NGEET/fates/issues/1113 +https://github.com/NGEET/fates/issues/1106 + +CTSM issues fixed (include CTSM Issue #): none + +Known bugs introduced in this tag (include issue #): + +Notes of particular relevance for users +--------------------------------------- + +none + +Notes of particular relevance for developers: +--------------------------------------------- + +This set of changes is introduced, while there is a known test failure in: +ERS_Lm20_Mmpi-serial.1x1_smallvilleIA.I2000Clm50BgcCropQianRs.izumi_gnu.clm-cropMonthlyNoinitial + +This is documented in issue: 2236 and a fix is slated for integration. The nature of the changes +in this PR where obviously orthogonal to the issue. + +Changes to tests or testing: none + + +Testing summary: +---------------- + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- ok + izumi ------- ok + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- ok + + +Answer changes +-------------- + +no answer changes + +Other details +------------- + +none + +Pull Requests that document the changes (include PR ids): + +https://github.com/ESCOMP/CTSM/pull/2231 + + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev149 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Fri Nov 3 19:52:44 MDT 2023 +One-line Summary: Rearrange leaf/stem "harvest" and fix soil gas diffusivity + +Purpose and description of changes +---------------------------------- + +1. Rearranges the calculation of how much leaf and livestem C and N goes to biofuels vs. litter, in anticipation of adding crop residue removal. Also makes the affected subroutine easier to read. +2. Resolves two bugs in the calculation of diffusion in SoilBiogeochemNitrifDenitrif(). Also does some rearranging and renaming to improve clarity. +3. Includes unrelated documentation updates from Documentation Week Oct. 2023. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[X] clm5_1 + +[X] clm5_0 + +[X] ctsm5_0-nwp + +[X] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): +* Resolves #1990: Problems about the soil gas diffusivity in methane code and nitrification-denitrification mod (https://github.com/ESCOMP/CTSM/issues/1990) + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- DIFF + izumi ------- DIFF + + +Answer changes +-------------- + +Changes answers relative to baseline: + + Summarize any changes to answers, i.e., + - what code configurations: virtually all + - what platforms/compilers: cheyenne and izumi; intel, gnu, and nag + - nature of change (roundoff; larger than roundoff/same climate; new climate): larger than roundoff/same climate + + If bitwise differences were observed, how did you show they were no worse + than roundoff? Roundoff differences means one or more lines of code change results + only by roundoff level (because order of operation changes for example). Roundoff + changes to state fields usually grow to greater than roundoff as the simulation progresses. + + * Roundoff-level differences were observed for the rearrangement of leaf/stem "harvest" code. + * Notable differences were observed for the soil gas diffusivity bugfix, but only for output variable diffus. + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +* #2154: Rearrange calculation of leaf/livestem C and N to biofuels/litter (https://github.com/ESCOMP/CTSM/pull/2154) +* #2157: Soil gas diffusivity bugfix (https://github.com/ESCOMP/CTSM/pull/2157) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev148 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Fri Nov 3 07:56:43 MDT 2023 +One-line Summary: Add GRAINN outputs + +Purpose and description of changes +---------------------------------- + +In response to a user request for GRAINN_TO_FOOD outputs, this adds *_N_TO_FOOD(_ANN) and *_N_TO_SEED(_ANN) outputs for reproductive N pools. These are off by default, unlike their C counterparts. Note that the results are not scientifically supported, and tests have revealed unrealistic values. (Also adds GRAINC_TO_SEED_ANN output.) + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK (with some fieldlist diffs) + izumi ------- PASS (with some fieldlist diffs) + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +* #2074 (https://github.com/ESCOMP/CTSM/pull/2074) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev147 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Mon Oct 30 16:53:20 MDT 2023 +One-line Summary: Add sowing window input files + +Purpose and description of changes +---------------------------------- + +Previously, one could run crops with either (a) sowing windows defined by the hemisphere-specific start and end dates on the paramfile or (b) prescribed sowing dates specified by input file stream_fldFileName_sdate. This PR replaces the latter with two new input files, stream_fldFileName_swindow_start and stream_fldFileName_swindow_end. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Notes of particular relevance for users +--------------------------------------- + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): +* Replaces input file stream_fldfilename_sdate (prescribed sowing date) with stream_fldFileName_swindow_start (start of sowing window) and stream_fldFileName_swindow_end (end of sowing window). +* Any gridcell with sowing window start == end will experience prescribed sowing, matching previous behavior with stream_fldfilename_sdate. +* Setting new parameter allow_invalid_swindow_inputs to .true. makes it so that gridcell-crops without values in provided sowing window files will fall back to paramfile sowing windows. Otherwise, such cells will cause an error. + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK (some diffs in field lists) + izumi ------- OK (some diffs in field lists) + + any other testing (give details below): + * RXCROPMATURITY test passes. + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +* #2193 (https://github.com/ESCOMP/CTSM/pull/2193) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev146 +Originator(s): glemieux (Gregory Lemieux, LBNL, glemieux@lbl.gov) +Date: Tue Oct 24 20:13:17 MDT 2023 +One-line Summary: FATES cross-grid seed dispersal + +Purpose and description of changes +---------------------------------- + +This PR enables FATES to disperse seeds across neighboring grid cells using MPI. +The API update includes calls to new fates dispersal procedures. There are +four parameters that are utilized to control the dispersal kernel, although +these were introduced in ctsm5.1.dev130 so no new default FATES parameter file +is necessary with this update. A new namelist parameter has been added to +enable the use of the seed dispersal mode. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +Known bugs introduced in this tag (include issue #): +- ESCOMP/CTSM#1089 (Cross-grid seed dispersal mechanism is not b4b for PE layout changes) + +Notes of particular relevance for users +--------------------------------------- +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): +- New namelist option, fates_seeddisp_cadence, added. This option Setting the switch +value to zero turns off dispersal. Setting the switch to 1, 2, or 3 sets the dispersal +cadence to daily, monthly or yearly. + +Substantial timing or memory changes: +- Users should be careful to limit the maximum dispersal distance parameter, +fates_seed_dispersal_max_dist, to a reasonable value based on the gridcell +resolution used. Using a very large value will increase the memory requirements +to store the gridcell neighborhood information. + +Notes of particular relevance for developers: +--------------------------------------------- +Caveats for developers (e.g., code that is duplicated that requires double maintenance): +- This PR introduces MPI calls to subroutines that are called by clm_driver. +Comments have been provided in code to make future developers aware of these +calls so as to avoid moving them into OpenMP threaded regions. + +Changes to tests or testing: +- Two new testmods have been added into the fates suite to test seed +dispersal. One of the tests is to track issue CTSM#1089 which was introduced +with this PR. + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- PASS + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: Only fates testmods in aux_clm are +answer changing due to science updates associated with externals update. + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): +- fates: sci.1.67.2_api.27.0.0 -> sci.1.68.0_api.28.0.0 + +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/2077 +https://github.com/NGEET/fates/pull/1005 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev145 +Originator(s): @cenlinhe (Cenlin He,UCAR/RAL), slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Thu Oct 19 14:34:25 MDT 2023 +One-line Summary: SNICAR snow albedo scheme updates + +Purpose and description of changes +---------------------------------- + + Notes copied here from the PR #1861: + + A few substantial changes in the SNICAR module for the following updated snow + albedo calculation features: + - Updated ice optical properties from Flanner et al. (2021), with multiple types + for ice refractive indices. + - Updated aerosol optical properties from Flanner et al. (2021) with multiple + dust types & new BC and OC optics. + - Updated downward solar spectra from Flanner et al. (2021) for multiple + condition types. + - More accurate radiative transfer solver (adding-doubling) from Dang et al. (2019). + - Nonspherical snow grain scheme from He et al. (2017). + - BC-snow internal mixing scheme from He et al. (2017). + - Dust-snow internal mixing scheme from He et al. (2019). + - Hyperspectral (480-band, 10-nm spectral res) capability with all the above features. + - New namelist controls for aerosol in snow and additional snow albedo + diagnostic output variables. + + Specific notes + - Code contributors: Cenlin He (NCAR/RAL) with advice from + Dave Lawrence (NCAR/CGD) and Mark Flanner (UMich). + - The manuscript to report this update is + Cenlin He, Mark Flanner, David M Lawrence, Yu Gu: New features and + enhancements in Community Land Model (CLM5) snow albedo modeling: description, + sensitivity, and evaluation. Authorea. June 08, 2023. + DOI:10.22541/essoar.168626390.01530324/v1 + - These updates will change the snow and surface albedo results along with + other surface fluxes changes. + - There are a few new namelist options related to SNICAR scheme added to the + namelist control. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[X] clm5_1 + +[X] clm5_0 + +[X] ctsm5_0-nwp + +[X] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (all three unrelated to this PR): +Fixes #2173 +Fixes #2107 +Fixes #2129 + +Notes of particular relevance for users +--------------------------------------- +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + New namelist variables: + snicar_numrad_snw = 5 or 480 wavelength bands, default 5 + snicar_solarspec, default "mid_latitude_winter" among six available options + snicar_dust_optics, default "sahara" among three avail. options + snicar_snw_shape, default "hexagonal_plate" among fourn avail. options + snicar_use_aerosol, default .true. + snicar_snobc_intmix and snicar_snodst_intmix, default .false. means do not + activate bc-snow and dust-snow internal mixing + + do_sno_oc, default .false., already appeared in previous code but in caps + use_snicar_frc, default .false., existed before + fsnowoptics now points to an updated 5-band file and gives the option for a + 480-band file + +Substantial timing or memory changes: +[e.g., check PFS test in the test suite and look at timings, if you +expect possible significant timing changes] + + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: + YES + + Summarize any changes to answers, i.e., + - what code configurations: All + - what platforms/compilers: All + - nature of change: larger than roundoff; new climate? + + Namelist defaults are such that phys="clm5.0" and phys="clm4.5" give different + answers only due to the changed fsnowoptics file. + Namelist defaults are such that phys="clm5.1" changes answers as a result of + new parameterizations. + + If this tag changes climate, I will document such information on + https://github.com/ESCOMP/CTSM/wiki/Answer-changing-tags + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/1861 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev144 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Thu Oct 19 13:30:02 MDT 2023 +One-line Summary: Remove a deprecated shr_mpi_bcast call + +Purpose and description of changes +---------------------------------- + +Removes a use of the deprecated shr_mpi_mod, replacing with ESMF_VMBroadcast. In addition, since the last tag, some minor documentation changes have been made. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + +If the tag used for baseline comparisons was NOT the previous tag, note that here: Compared ctsm5.1.dev142 to a version of this branch (1f39800e1) with ctsm5.1.dev142 merged in. + + +Answer changes +-------------- + +Changes answers relative to baseline: No + +Other details +------------- +[Remove any lines that don't apply. Remove entire section if nothing applies.] + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): + +Pull Requests that document the changes (include PR ids): +* #1991 (https://github.com/ESCOMP/CTSM/pull/1991) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev143 +Originator(s): rgknox (Ryan Knox,LAWRENCE BERKELEY NATIONAL LABORATORY,510-495-) +Date: Fri Oct 13 08:53:38 MDT 2023 +One-line Summary: Zeroing of wood product fluxes on fates columns + +Purpose and description of changes +---------------------------------- + +This is a small change that initializes wood product fluxes on fates columns to zero. These +products are otherwise zero'd in a p2c() routine that is incompatible with fates. When +wood product fluxes become available via fates, these routines will be updated. These fluxes +were previously left as uninitialized, which was causing math issues on some compilers. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +There are no changes to scientifically-supported configurations. + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +Fixes CTSM issue 2165 + + +Notes of particular relevance for users +--------------------------------------- + + +No caveats, no bugs, no issues of relevance. + +No noticable timing changes. + + +Notes of particular relevance for developers: +--------------------------------------------- + +None + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- PASS + + Compared against baseline: ctsm5.1.dev142 + + +Answer changes +-------------- + +Answer changes only on FATES tests, and only on the specific wood product fluxes modified. These values +are now zeros, instead of being uninitialized. Everything else is b4b. + + +Other details +------------- + +No other details. + +Pull Requests that document the changes (include PR ids): + #2168 -- GRU update for FATES + #2134 -- Update to documentation for Meier et al. (2022) roughness length parameterization + https://github.com/ESCOMP/CTSM/pull + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev142 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Tue Sep 19 11:30:22 MDT 2023 +One-line Summary: Merge 5 bit-for-bit pull requests + +Purpose and description of changes +---------------------------------- + +Merge 5 bit-for-bit pull requests; see "Other details." + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +* Add unit test for making fsurdat with all crops everywhere (#2079) +* Rework master_list_(no)?fates.rst? (#2083) +* conda run -n can fail if a conda environment is already active (#2109) +* conda fails to load for SystemTests (#2111) + + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: +* FSURDATMODIFYCTSM system test should now work for everyone. + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - PASS + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + cheyenne - PASS + clm_pymods test suite on cheyenne - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +* Add system and unit tests for making fsurdat with all crops everywhere (#2081) +* Rework master_list* files etc. (#2087) +* Fixes to methane Tech Note (#2091) +* Add is_doy_in_interval() function (#2158) +* Avoid using subprocess.run() in FSURDATMODIFYCTSM (#2125) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev141 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Wed Sep 13 13:58:04 MDT 2023 +One-line Summary: Change small snocan to zero + +Purpose and description of changes +---------------------------------- + + Issues #2041 and #2048 discuss and resolve a test failure in the ctsm5.2 + branch. The failure goes away when we reset small snocan to zero. + + Bill Sacks recommended merging this change in ctsm5.1 and then updating + the ctsm5.2 branch to the latest ctsm5.1. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): + Fixes #2041 + Fixes #2048 + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: + Yes + + Summarize any changes to answers, i.e., + - what code configurations: All + - what platforms/compilers: All + - nature of change: roundoff + + The answer changes are expected to be roundoff-level because the code change + just truncates roundoff-level greater-than-zero states to exactly zero for + snocan that most likely needed to be zero anyway. + + The answer changes grow to greater than roundoff, but the + cprnc.out file from a 20-year izumi test-suite case does not contain + differences of concerning magnitude. + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2053 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev140 +Originator(s): afoster (Adrianna Foster) +Date: Tue Sep 12 14:47:06 MDT 2023 +One-line Summary: add lai_streams capability for FATES + +Purpose and description of changes +---------------------------------- + +Removed checks in clm_driver and CLMBuildNamelist.pm so that now FATES can run when use_lai_streams=.true. + +I also had to modify the init in cpl/share_esmf/laiStreamMod to allocate the g_to_ig array in the lai_init method (rather than in the lai_advance method. This was required because SatellitePhenology is called in clim_initializedMod in FATES cases (here). This happens before lai_advance is ever called so at that point the g_to_ig array was not yet allocated. Moving the allocation/initialization to the lai_init method fixes this. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): #1722 - Should be able to use lai streams with FATES-SP mode + +Notes of particular relevance for developers: +--------------------------------------------- + +build-namelist tests (if CLMBuildNamelist.pm has changed): added tests to make sure use_lai_streams failed correctly + +Changes to tests or testing: Added a test for lai_streams with FATES + +Testing summary: +---------------- + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: None + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): fates + +Pull Requests that document the changes (include PR ids): #2054 +(https://github.com/ESCOMP/ctsm/pull) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev139 +Originator(s): slevis (Samuel Levis) +Date: Fri Aug 25 16:47:45 MDT 2023 +One-line Summary: Fix problems uncovered by nag -nan tests + +Purpose and description of changes +---------------------------------- + + Fix problems uncovered by adding the -nan compilation flag for the Nag + compiler. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): + Makes progress on issue #1994 (same title) + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- PASS + + +Answer changes +-------------- + +Changes answers relative to baseline: No + + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2051 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev138 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Fri Aug 25 14:44:22 MDT 2023 +One-line Summary: Refactor max_patch_per_col and maxsoil_patches loops + +Purpose and description of changes +---------------------------------- + +Refactor such loops for clearer and more efficient code, as recommended in +issue #2025. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +Fixes #2025 "Refactor loops that use max_patch_per_col?" + +Testing summary: +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + +Answer changes +-------------- +Changes answers relative to baseline: No + + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2056 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev137 +Originator(s): Ronny Meier, slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Tue Aug 22 14:34:53 MDT 2023 +One-line Summary: Surface roughness modifications + +Purpose and description of changes +---------------------------------- + +Surface roughness (z0) modifications that appear in this publication: +https://doi.org/10.5194/gmd-15-2365-2022 + +When changing the namelist input z0param_method from ZengWang2007 (default) +to Meier2022 the following modifications are activated: + +- A new parameterization of the vegetation surface roughness based on +Raupach (1992) with optimized parameters to match the data collected in +Hu et al. (2020) for different types of vegetation. This requires several new +PFT-specific input parameters in the parameter file. +- A spatially explicit z0m input field for bare soil based on the data of +Prigent et al. (2005). This may be activated specifically by the user through +the namelist input use_z0mg_2d. This requires a new input variable in the +fsurdat file. +- The parameterization of z0m for snow based on accumulated snow melt as +proposed in Brock et al. (2006). This may be activated specifically by the +user through the namelist input use_z0m_snowmelt. +- The parameterization of Yang et al. (2008) for z0h and z0q over bare soil, +snow, and glaciers. +- The study in GMD also proposes new globally constant values for the +z0m of bare soil, snow, and ice. To "activate" those the parameter file needs +to be changed at the moment. The original and modified parameter files and +fsurdat files will be shared by ftp. + +Open issues/questions (discussed with @ekluzek, @dlawrenncar, @olyson): + +- How to incorporate the data of Prigent et al. (2005) in the surfdata +generation. I will write an email about this to Catherine Prigent. +- One statement marked in CanopyFluxesMod should probably be changed when +using Meier2022 (i.e., Yang et al. (2008) formulation should be used instead +of Zeng and Dickinson (1998)). +- At the moment one needs to change the parameter file to switch between the +original and proposed globally constant z0m values for bare soil, snow, and +ice. This is obviously not very user friendly and prone to mistakes. +- The introduction of Yang et al. (2008) frequently results in z0h and z0q +larger than z0m. This is only rarely observed in the field and in contradiction +to the theory that z0h and z0q should be smaller because heat and water vapor +need to be transported through molecular diffusion in the surface sublayer. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +Fixes #1316 +Fixes #1596 + +Notes of particular relevance for users +--------------------------------------- +Details already discussed in the description above. + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: +New tests are in place for this new code. + +Testing summary: +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + +Answer changes +-------------- +Changes answers relative to baseline: + No, unless user chooses to run in non-default Meier2022 mode. + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2045 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev136 +Originator(s): jedwards (Jim Edwards), sacks (Bill Sacks) +Date: Tue Aug 22 13:10:28 MDT 2023 +One-line Summary: Change order of history fields to improve performance on derecho + +Purpose and description of changes +---------------------------------- + +Instead of just ordering history fields alphabetically, order them first +by the name of their level dimension (with fields without a level +dimension appearing first), then alphabetically within a given level +dimension. This changed ordering gives a significant performance +improvement especially noticeable on lustre file systems such as on +derecho. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Notes of particular relevance for users +--------------------------------------- +Caveats for users (e.g., need to interpolate initial conditions): +- History fields will now appear in a different order from tools like + ncdump, etc. + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + In addition to expected fails, the test + FSURDATMODIFYCTSM_D_Mmpi-serial_Ld1.5x5_amazon.I2000Clm50SpRs.cheyenne_intel + also failed as in https://github.com/ESCOMP/CTSM/issues/2111 + +Answer changes +-------------- + +Changes answers relative to baseline: NO + +Other details +------------- +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/2114 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev135 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Mon Aug 21 15:06:35 MDT 2023 +One-line Summary: Rename hist fields to track them down more easily + +Purpose and description of changes +---------------------------------- + + Renaming history fields to make easier to find in lists, e.g. when + using ncview. For example, litter fields like MET_LIT and STR_LIT + will be LIT_MET and LIT_STR. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): + Fixes #2095 + + +Testing summary: +---------------- +[Remove any lines that don't apply.] + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + any other testing (give details below): + Sam L. ran the LMWG diag. pkg and found only one plot affected by this + PR's changes. In particular, set 6 CWD_C, which was CWDC + + +Answer changes +-------------- + +Changes answers relative to baseline: + No. Field lists differ. In some tests, the namelists differ. + + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2106 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev134 +Originator(s): rgknox (Ryan Knox,LBNL EESA), erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Wed Aug 16 17:20:27 MDT 2023 +One-line Summary: Migration of FATES to share normal soil BGC call sequence and functionality + +Purpose and description of changes +---------------------------------- + +This set of changes enables the normal soil biogeochemistry that is used for CN, to be used for FATES as well. FATES had been using a simplified subset of soil biogeochemistry in its own module. This change required coordination of litter flux and methane boundary conditions from FATES to CLM. CNVEG datastructures were given trivial allocation (of size one on index zero) to prevent inappropriate use of CNVEG datastructures while FATES is active. Note that now the carbon balance checking for the soil is now active when FATES is active. Various accomodations have also been put in place to enable nitrogen cycling between the two models. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +Surprisingly, nvhpc tests are now working, but it may just be coincidental. All existing aux_clm tests are passing. Tests without FATES are b4b, with roundoff differences in just TOTCOLC and TOTCOLN. + +CTSM issues fixed (include CTSM Issue #): + We think #1879 -- "AD spinup issues for FATES", is fixed but haven't proved it + #2112 -- black check on SystemTest file + +Notes of particular relevance for users +--------------------------------------- +A CLM-FATES simulation will turn on nitrogen supplementation, this enables sufficient immobilization and decomposition. Until FATES and CLM can handle fully coupled nitrogen exchange, which would include root uptake of the mineralized aqueous forms (NH4 and NO3), N limitations in the soil are meaningless when FATES is on. + +Caveats for users (e.g., need to interpolate initial conditions): + FATES MUST have suplnitro='ALL' now (was NONE). When fates_parteh_mode>=1 other settings are allowed. + More checking for use_luna and suplnitro is added for FATES in the build-namelist + +Changes made to namelist defaults (e.g., changed parameter values): FATES runs now supplement N + suplnitro set to ALL for FATES + use_luna set to .false. for FATES and clm4_5 physics + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): + We should update defaults for suplnitro, when Nitrogen nutrients are allowed in FATES + The black checdk github action has to duplicate actions for each source file or directory + We should move to using the Makefile in the python directory when we figure it out + +Testing summary: +---------------- + +aux_clm test run on cheyenne and izumi. See: + +izumi: OK /scratch/cluster/rgknox/tests_0814-095624iz +cheyenne: OK /glade/scratch/rgknox/tests_0814-134713ch + + +Answer changes +-------------- + +Changes answers relative to baseline: Two diganostic fields (TOTCOLC and TOTCOLN) + +Baseline changes will be reported for many tests, all tests were combed to identify RMS diffs, all non-FATES tests had at most, roundoff level (-) + cheyenne ---- OK + izumi ------- OK + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + Note that the previous ctsm5.1.dev132 tag was generated using FATES tagname + sci.1.66.1_api.25.5.0. This api update includes intermediate FATES science + tags that are non-b4b, so aux_clm FATES tests result in DIFFs as expected. + +Answer changes +-------------- + +Changes answers relative to baseline: Yes, but only for aux_clm fates tests. + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): + + sci.1.66.1_api.25.5.0 -> sci.1.67.1_api.26.0.0 + ccs_config_cesm0.0.64 -> ccs_config_cesm0.0.65 + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + + https://github.com/ESCOMP/CTSM/pull/2000 -- refactor + https://github.com/ESCOMP/CTSM/pull/2089 -- Change template update + https://github.com/NGEET/fates/pull/1024 -- FATES refactor + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev132 +Originator(s): mvdebolskiy (NORCE, Bergen, Norway), slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Fri Aug 4 17:52:45 MDT 2023 +One-line Summary: Add parameterization to allow excess ice in soil and subsidence + +Purpose and description of changes +---------------------------------- + +As described in PR #1787: + +Parameterization for excess ice described in Lee et al. (2014): +http://dx.doi.org/10.1088/1748-9326/9/12/124006 + +This code is a modified version of code provided by Lei Cai: +https://github.com/lca041/ctsm/tree/clm5.0.dev92_exice + +Works only for the nuopc driver. + +Files changed: +bld/CLMBuildNamelist.pm, bld/namelist_files/namelist_defaults_ctsm.xml, bld/namelist_files/namelist_definitionss_ctsm.xml -- added namelist options; +src/main/clm_varctl.F90, src/main/controlMod.F90 -- added option to switch excess ice physics and read namelist option; +src/biogeophys/WaterStateType.F90 -- added prognostic excess ice variable and a history field; +src/biogeophys/WaterStateBulkType.F90, src/main/clm_instMod.F90, -- added arguments to soubrutiens for proper initialization; +src/biogeophys/TemperatureType.F90 -- initial soil temperature set to 268.15 K at the cold start (possibly redundant because #1460 is closed) +src/biogeophys/WaterDiagnosticBulkType.F90 -- added two diagnostic excess ice variables and two history fields; +src/biogeophys/SoilTemperatureMod.F90 -- added main excess ice calculations; +src/biogeophys/TotalWaterAndHeatMod.F90 -- added excess ice to total water for balance checks; +src/biogeophys/SoilHydrologyMod.F90 -- added excess ice for ice fraction calculation; + +New files: +src/cpl/share_esmf/ExcessIceStreamType.F90 -- routines to read dataset with initial excess ice concentration + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): + Fixes #1229 -- excess ice + + +Notes of particular relevance for users +--------------------------------------- + +Caveats for users (e.g., need to interpolate initial conditions): + Excess ice can EITHER be turned on by using the stream file, OR a restart file that has excess ice on it. + Since, excess ice is expected to melt as time goes on, the use of a restart file is preferred. + But, use of a restart file requires a simulation that was spun up to that point. + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + New namelist options added: + - use_excess_ice (logical, in clm_inparm) default = .false.; turns on excess ice physics + - stream_meshfile_exice, stream_fldfilename_exice, stream_mapalgo_exice (char, in exice_streams) + meshfile, stream file, spatial interpolation algorithm for initial values of excess ice + defaults - lnd/clm2/paramdata/exice_init_0.125x0.125_ESMFmesh_c20220516.nc, + lnd/clm2/paramdata/exice_init_0.125x0.125_c20220516.nc + and bilinear + Dataset interpolated to 0.125x0.125 degrees grid from Brown et al. (1997) can be found here: + https://drive.google.com/file/d/1mA457Oa52zG_MtLGB7KHuUYQvsS2-P5o/view?usp=sharing + Dataset used only in cold start or hybrid runs (or starting with finidat) that do not have excess ice + + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: + New tests in place for this new code + +Testing summary: +---------------- +[Remove any lines that don't apply.] + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + heyenne ---- OK + izumi ------- OK + + any other testing (give details below): + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + +Answer changes +-------------- + +Changes answers relative to baseline: No + + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/1787 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev131 +Originator(s): samrabin (Sam Rabin,UCAR/TSS) +Date: Thu Jul 27 14:24:07 MDT 2023 +One-line Summary: Enable prescribed crop calendars. + +Purpose and description of changes +---------------------------------- + +This branch enables CLM to read in externally-prescribed crop sowing dates and "cultivar" maturity requirements (growing +degree-days, GDDs). This has so far only been tested with static values, and the results indicate that yield performance is +worsened. However, this capability is required by the GGCMI phase 3 / ISIMIP3 Agriculture protocol. + +Briefly, the way this works is that an offline run is first performed with prescribed sowing dates and 364-day seasons. +Instantaneous GDD accumulation is saved daily. A Python script then cross-references those daily outputs with a map of mean sowing +dates to determine the mean accumulated GDDs in the growing season, saving the result as a file for use as prescribed maturity +requirements. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): + Resolves #281 -- Clean up CropPhenology logic + Resolves #519 -- Read in crop planting and harvest dates + Fixes #2042 -- Issue running SystemTests due to "conda activate" error + + Some on #1649 -- Additional "annual" (per growing season) crop outputs + +Notes of particular relevance for users +--------------------------------------- + +Caveats for users: + Untested but theoretically possible: + * Time-varying inputs + * Running at any resolution other than one matching the crop calendar inputs + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + Adds optional namelist variables: + * stream_fldfilename_sdate: Filename of input stream data for sowing dates + * stream_fldfilename_cultivar_gdds: Filename of input stream data for cultivar growing degree-day targets + * stream_meshfile_cropcal: Filename of input stream data for crop calendar inputs + * stream_year_first_cropcal: First year to loop over for crop calendar data + * stream_year_last_cropcal: Last year to loop over for crop calendar data + * model_year_align_cropcal: Simulation year that aligns with stream_year_first_cropcal value + * generate_crop_gdds: Set to .true. in order to override crop harvesting logic and to instead harvest the day before the next sowing date. Used to generate growing-degree day outputs that can be used with an external script to generate new GDD requirement ("cultivar") files. + * use_mxmat: Set to .false. in order to ignore crop PFT parameter for maximum growing season length (mxmat). Must be set to .false. when generate_crop_gdds is .true. + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: + * Adds RXCROPMATURITY SystemTest, with an example added to ctsm_sci test suite. + * Removes 12 MCT tests from testlist_clm.xml, as discussed in CTSM SE standup 2023-06-26. + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + cheyenne - PASS + +Answer changes +-------------- + +Changes answers relative to baseline: YES for Clm45BgcCrop compsets only + + Summarize any changes to answers, i.e., + - what code configurations: Clm45BgcCrop + - what platforms/compilers: Cheyenne intel and gnu, Izumi intel and nag + - nature of change (roundoff; larger than roundoff/same climate; new climate): roundoff + + 5 tests in aux_clm showed true DIFFs (i.e., not just field list differences / new output files): + - SMS_D_Ly6_Mmpi-serial.1x1_smallvilleIA.IHistClm45BgcCropQianRs.izumi_intel.clm-cropMonthOutput + - ERP_D_P36x2_Ld3.f10_f10_mg37.I2000Clm45BgcCrop.cheyenne_gnu.clm-no_subgrid_fluxes + - LGRAIN2_Ly2_P72x1.f10_f10_mg37.I1850Clm45BgcCrop.cheyenne_gnu.clm-ciso--clm-cropMonthOutput + - ERS_Ly5_P72x1.f10_f10_mg37.IHistClm45BgcCrop.cheyenne_intel.clm-cropMonthOutput + - SMS_D_Ld1_P48x1.f10_f10_mg37.I2000Clm45BgcCrop.izumi_nag.clm-oldhyd + + The first four were likely due to an order-of-operations change in CNOffsetLitterfall(), as they resolve with the patch at + https://github.com/samsrabin/CTSM/commit/c30320cbd6583bccbcc290ffe536e8500e6ec358 + + The last is resolved with an additional patch that removes all my changes to CNOffsetLitterfall()---changes which *should* only + affect new diagnostic variables: + https://github.com/samsrabin/CTSM/commit/e025f555e74584c63d50f27c4df38326fa64bc4f + +Other details +------------- +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): cime + cime: cime6.0.108 -> cime6.0.125 + +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/CTSM/pull/1863 + +=============================================================== + +=============================================================== +Tag name: ctsm5.1.dev130 +Originator(s): glemieux (Greg Lemieux,LBL/NGEET,510-486-5049) +Date: Sun Jul 9 23:24:29 MDT 2023 +One-line Summary: FATES parameter file and test definition update + +Purpose and description of changes +---------------------------------- + +This tag incorporates updates to the FATES parameter file and test +definitions to be consistent with updates to the drought deciduous +phenology model in FATES. This also updates the external FATES +pointer to the tag associated with the drought deciduous phenology +update. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): + Fixes #2043 -- Five izumi NEON tests fail (for me) because the testnames include L10d instead of Ld10 + +Known bugs found since the previous tag (include issue #): + #2049 -- Use of 0.01_r8 as a magic number + #2042 -- Issue running SystemTests due to "conda activate" error + #2039 -- Conditional for NEON usermods is too broad + +Notes of particular relevance for users +--------------------------------------- +Changes made to namelist defaults (e.g., changed parameter values): + + FATES parameter file default updated to fates_params_api.25.5.0_12pft_c230628.nc + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: YES for FATES tests only + + Summarize any changes to answers, i.e., + - what code configurations: FATES + - what platforms/compilers: ALL + - nature of change (roundoff; larger than roundoff/same climate; new climate): larger than roundoff + + If this tag changes climate describe the run(s) done to evaluate the new + climate (put details of the simulations in the experiment database) + + See FATES #958 for discussion. The update increased the dimensions of + some FATES history output from site-level to pft-level. As such, these + have been removed from some test cases to keep the test light weight and + added to the AllVars test to maintain coverage. + + Note that this FATES update also incorporates a number of additional science updates + since the previous tag. + +Other details +------------- +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): fates + fates: sci.1.65.3_api.25.4.0 -> fates-sci.1.66.0_api.25.5.0 + +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2009 + https://github.com/NGEET/fates/pull/958 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev129 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Thu Jun 22 01:21:56 MDT 2023 +One-line Summary: NEON fixes for TOOL and user-mods, add SP for NEON, some history file updates, black refactor for buildlib/buildnml + +Purpose and description of changes +---------------------------------- + +Merge the NEON fixes for TOOL and allowing SP mode, as well as a few simple history PR's, and a black reformat. + +Fixes NEON bug identified at the NCAR-NEON workshop. Corrects the dominant PFT at TOOL site & usermods_dirs +Some small changes for quality of life improvements for the run_neon script. Some documentation and code +cleanup type changes regarding history code (delete a unused subroutine). Do a black reformat of python files +buildlib/buildnml (and CTSM SystemTests) for consistency across CESM. Also add running them through black in the python + +Specific notes: + + - fixed a couple lines so that python will stop complaining about deprecated things + - added print statements about warning messages being not an issue, and that building/running may take a while + - added a "success" print statement - I'm not sure this will actually only print if "successful" + - Add more documentation to history tape code + - Add more breadcrumbs between related variables and methods + - Put related history namelist flags/methods together + - Update some out of date comments in history code + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): + Fixes #2013 -- Incorrect PFT at TOOL + Fixes #2014 -- NEON usermods not working as intended + Fixes #2029 -- fire_method is used before it's set and hence fire_res isn't set to none + Fixes #2030 -- Logic in build-namelist not functioning correctly for FATES with light_res + Updates conda environment so that #1974 works + +Known bugs found since the previous tag (include issue #): + #2037 -- shell_commands for tests with two testmods listed don't concatenate both together + #2036 -- Remove setting of STOP options in user-mod directories + #2017 -- subset_data does not function for regional grids that span across Greenwich longitude zero + #2024 -- snow fraction is uninitialized when passed to fates during cold-starts + + +Notes of particular relevance for users +--------------------------------------- + +Caveats for users (e.g., need to interpolate initial conditions): + Ability to run for SP sites was added for NEON, but run-neon.py doesn't have an option for it + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + New --light_res option for 106x74 for NEON lightning data added (can be set in CLM_BLDNML_OPTS) + This is the default for NEON sites (with $CLM_USRDAT_NAME == NEON or NEON.PRISM) + It fails for non CLM_USRDAT resolutions and gives a warning for non-NEON CLM_USRDAT resolutions) + +Changes made to namelist defaults (e.g., changed parameter values): + Add NEON 106x74 lightning dataset + +Changes to the datasets (e.g., parameter, surface or initial files): New NEON surface datasets + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: + Add a NEON case that runs in SP mode + Change one of the FATES tests to turn fire on and require lightning data + +Testing summary: regular NEON-tools +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - PASS (57 tests different from baseline) + + tools-tests (test/tools) (if tools have been changed): + + cheyenne - PASS (NEON test list passes) + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + cheyenne - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + other testing: + izumi -- PASS (tests of changed NEON sites, see list in #2031) + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + +Answer changes +-------------- + +Changes answers relative to baseline: bit-for-bit (except some NEON sites) + + Summarize any changes to answers, i.e., + - what code configurations: NEON TOOL site and other NEON sites with namelist changes + - what platforms/compilers: all + - nature of change: new climate + +Other details +------------- +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + #2031 -- Merge of below... + #2015 -- Address NEON bugs + #2021 -- Small changes to fix warnings, add print statements for clarity + #2023 -- Document side effects of htapes_fieldlist + #2022 -- Delete unued hist_add_subscript + #2020 -- Add more documentation to history tape code + #2007 -- black reformat python files for consistancy across cesm + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev128 +Originator(s): glemieux (Gregory Lemieux,LBL/NGEET,510-486-5049) +Date: Thu Jun 1 15:31:52 MDT 2023 +One-line Summary: Update FATES tests to double precision + +Purpose and description of changes +---------------------------------- + +This pull request updates the fates tests to set the output +precision to double precision. The usermod fates_sp is similarly +updated. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): +- Resolves https://github.com/ESCOMP/CTSM/issues/1986 + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- OK + izumi ------- OK + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + FATES tests run against fates-sci.1.65.6_api.25.4.0-ctsm5.1.dev127 baseline + +Answer changes +-------------- + +Changes answers relative to baseline: Yes, but only for fates tests and compsets + + Summarize any changes to answers, i.e., + - Differences are due to changing hist_ndens to 1 (double precision) + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/2010 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev127 +Originator(s): sacks (Bill Sacks) +Date: Fri May 19 04:48:30 MDT 2023 +One-line Summary: Fix nuopc cplhist test + +Purpose and description of changes +---------------------------------- + +Make some changes to the cplhist testmod that fix the cplhist test, +based on testing done by Keith Oleson: +- Point to new cplhist forcing data generated and used by Adam + Herrington and Keith Oleson +- Use DATM_PRESNDEP=none until + https://github.com/escomp/ctsm/issues/1844 is resolved + +Also, remove mct cplhist test. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +- Takes steps towards addressing ESCOMP/CTSM#1844 (Create new auxiliary + history file for cplhist test with ndep data) + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: +- Changes cplhist test; new test is + SMS_D_Ld1.ne30pg3_t061.I1850Clm50BgcSpinup.cheyenne_intel.clm-cplhist +- Removes mct cplhist test + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- PASS + izumi ------- PASS + + Note that there were no baselines for the new test + (SMS_D_Ld1.ne30pg3_t061.I1850Clm50BgcSpinup.cheyenne_intel.clm-cplhist) + +Answer changes +-------------- + +Changes answers relative to baseline: NO + +Other details +------------- +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): +- cdeps: cdeps1.0.12 -> cdeps1.0.13 + +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/1999 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev126 +Originator(s): jpalex (John Alex) +Date: Thu May 18 17:21:59 MDT 2023 +One-line Summary: Clean up some loops in UrbanTimeVarType + +Purpose and description of changes +---------------------------------- + +Refactor some inefficient and confusing looping structures in +UrbanTimeVarType.F90 + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +- Resolves ESCOMP/CTSM#1514 (Inefficient and confusing looping structures in UrbanTimeVarType.F90) + +Testing summary: +---------------- + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- PASS + izumi ------- PASS + +Answer changes +-------------- + +Changes answers relative to baseline: NO + +Other details +------------- +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/2005 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev125 +Originator(s): jpalex (John Alex) +Date: Sun Jul 9 21:04:13 MDT 2023 +One-line Summary: Added cache for clock step_size in clm_time_manager.F90 + +Purpose and description of changes +---------------------------------- + +Added cache for clock step_size in clm_time_manager.F90 to improve +performance. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +- Resolves ESCOMP/CTSM#207 (Improve performance of get_step_size) + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- PASS + izumi ------- PASS + +Answer changes +-------------- + +Changes answers relative to baseline: NO + +Other details +------------- +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/2004 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev124 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) / mvertens / adamrher / MiCurry / jtrusdal / TeaganKing +Date: Tue May 9 16:52:05 MDT 2023 +One-line Summary: Initialization memory update, new surface datasets for new grids, add option for running NEON with PRISM data + +Purpose and description of changes +---------------------------------- + +Lower memory usage at initialization: + + Some work by Mariana Vertenstein to lower the memory usage at initiatlization. + +New Low Resolutions for SE grids: + + ne3np4.pg3, ne5np4.pg3, ne16np4.pg3 + +New MPASA Resolutions: + + mpasa480 --------- Course resolution + mpasa120 --------- Near 1-degree + mpasa60, mpasa30 - High resolution + mpasa15 ---------- Very high resolution + + This merge adds the surface data and landuse.timeseries files for the CAM-MPAS dycore. At present, these grids are: + + mpasa480 - 480 km quasi-uniform, global with 2,462 horizontal grid columns + mpasa120 - 120 km quasi-uniform, global with 40,962 horizontal grid columns + mpasa60 - 60 km quasi-uniform, global with 163,842 horizontal grid columns + mpasa30 - 30 km quasi-uniform, global with 655,362 horizontal grid columns + mpasa15 - 15 km quasi-uniform, global with 2,621,442 horizontal grid columns + + +Option to Run NEON with PRISM preciption data: + + Some NEON sites have bad or incomplete precitation data. By setting CLM_USRDAT_NAME="NEON.PRISM" + the PRISM 4km CONUS ReAnayslis data is used in place of the NEON precipitation data. This + is helpful for several NEON sites: + MLBS, MOAB, ONAQ, SJER, NIWO, TEAK, WREF, YELL + + Allow PRISM precipitation to be used as a new datm stream. + + Updates to cime_config/config_component.xml include additional valid values for PRECIP data stream names. Changes in + cime_config/usermods_dirs/NEON/defaults/user_nl_datm_streams specify which input variables are gathered from which streams and + specifies file location for PRISM data. + + Using PRISM precipitation instead of NEON precipitation does have a substantial impact on CTSM output (eg. latent heat flux + biases). + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): + Fixes #1969 -- Move dynGrossUnrepMod.F90 from biogeochem to dyn_subgrid subdirectory + Fixes #1904 -- Other precipitation streams for NEON + Fixes #1927 -- Course SE resolutions support + Fixes #1313 -- MPASA resolution support + +Notes of particular relevance for users +--------------------------------------- + +Caveats for users (e.g., need to interpolate initial conditions): + New resolutions do NOT have specific initial conditions for them, they use the general ones + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + NEON cases will give a warning about spinup data for non default transient cases + if you set a NEON case with CLM_USRDAT_NAME=NEON.PRISM, PRISM data will be used for precip + + Two new options to run_neon.py "--prism" and "--experiment" + +Changes made to namelist defaults (e.g., changed parameter values): + "v3" data option for NEONVERSION + +Changes to the datasets (e.g., parameter, surface or initial files): + New surface and landuse.timeseries datasets for: + ne3np4.pg3, ne5np4.pg3, ne16np4.pg3 + mpasa480, mpasa120, mpasa60, mpasa30, mpasa15 + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: Add new tests for new resolutions + +Testing summary: regular, tools +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - PASS (81 new tests) + + tools-tests (test/tools) (if tools have been changed): + + cheyenne - OK + cheyenne - PASS (tests_pretag_nompi_neon) + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + cheyenne - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + any other testing (give details below): + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + +Answer changes +-------------- + +Changes answers relative to baseline: No bit-for-bit + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): cdeps + Update CDEPS to cdeps1.0.12 + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + + #1998 -- combination of below PR's... + + #1954 -- PRISM + #1973 -- SE + #1501 -- MPASA + #1899 -- memory scaling + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev123 +Originator(s): sacks (Bill Sacks) +Date: Mon May 1 11:37:51 MDT 2023 +One-line Summary: Updates needed for pFUnit 4 and other externals updates + +Purpose and description of changes +---------------------------------- + +(1) Lots of small changes needed for the update to pFUnit4. Note that + this is a backwards-incompatible update, so we will require pFUnit 4 + moving forward. + +(2) Externals updates: some of these are needed for the update to pFUnit + 4; others are included to update externals to those in a recent CESM + alpha tag. + +Notes of particular relevance for developers: +--------------------------------------------- +Caveats for developers (e.g., code that is duplicated that requires double maintenance): +- Running the Fortran unit tests now requires pFUnit 4 +- I didn't run the mksurfdata_map unit tests... these will be removed + soon anyway with the replacement of mksurfdata_map with mksurfdata_esmf + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- PASS + izumi ------- PASS + + Note: most testing run on 46968da7b; reran just izumi-nag testing on + the latest version (the only difference was in the version of the + ccs_config external, and the only diff there was for nag). + + any other testing (give details below): + - Fortran unit tests (under src) on izumi and my Mac + +Answer changes +-------------- + +Changes answers relative to baseline: NO + +Other details +------------- +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): +- ccs_config: ccs_config_cesm0.0.58 -> ccs_config_cesm0.0.64 +- cime: cime6.0.100 -> cime6.0.108 +- cmeps: cmeps0.14.17 -> cmeps0.14.21 +- cdeps: cdeps1.0.7 -> cdeps1.0.9 +- cpl7: cpl7.0.14 -> cpl77.0.5 +- share: share1.0.16 -> share1.0.17 + +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/1989 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev122 +Originator(s): sacks (Bill Sacks) +Date: Sun Apr 23 19:36:37 MDT 2023 +One-line Summary: Rework handling of evaporation constraint in SoilFluxes + +Purpose and description of changes +---------------------------------- + +Occasionally, h2osoi_ice was going significantly negative in +UpdateState_TopLayerFluxes - see +https://github.com/ESCOMP/CTSM/issues/1979. As noted in that issue, this +seems to be due to h2osoi_ice having a very different magnitude from +h2osoi_liq, leading to greater-than-roundoff-level differences from zero +final state in a relative sense (i.e., relative to the magnitude of +h2osoi_ice) - I think because of the appearance of the sum (h2osoi_ice + +h2osoi_liq) in the equations that limit fluxes. + +To try to deal with this, I have reworked the handling of the +evaporation constraint to directly limit both the liqevap and solidevap, +so that both of them should result in the equivalent liq or ice states +going to 0 within roundoff. + +To do that, I needed to move the partitioning of the total flux into +liquid and solid to earlier in the subroutine and then recalculate those +partitioning fluxes in conditions where we're applying an evaporation +constraint. + +Note that I applied a max of 0 to the new fluxes because many initial +conditions files have roundoff-level negative H2OSOI_LIQ, so without +this limit, we were getting roundoff-level negative fluxes. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +- Resolves ESCOMP/CTSM#1979 (Need some changes to avoid negative h2osoi_ice in UpdateState_TopLayerFluxes) + + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + Tests passed, some baseline differences as expected. + +Answer changes +-------------- + +Changes answers relative to baseline: YES + + Summarize any changes to answers, i.e., + - what code configurations: potentially all + - what platforms/compilers: potentially all + - nature of change (roundoff; larger than roundoff/same climate; new climate): + roundoff + + Differences were only observed in a few tests: + - ERP_P36x2_Ld30.f45_f45_mg37.I2000Clm51FatesSpCruRsGs.cheyenne_intel.clm-FatesColdSatPhen + - ERI_D_Ld9_P48x1.T31_g37.I2000Clm50Sp.izumi_nag.clm-reduceOutput + - SMS_D_Ln9_P36x3.f19_g17.IHistClm50Sp.cheyenne_intel.clm-waccmx_offline + - SMS_D_Ln9_P36x3_Vmct.f19_g17.IHistClm50Sp.cheyenne_intel.clm-waccmx_offline + + If bitwise differences were observed, how did you show they were no worse + than roundoff? + + Only two tests had greater-than-roundoff-level differences in the + cprnc output: + SMS_D_Ln9_P36x3.f19_g17.IHistClm50Sp.cheyenne_intel.clm-waccmx_offline + and the mct equivalent, + SMS_D_Ln9_P36x3_Vmct.f19_g17.IHistClm50Sp.cheyenne_intel.clm-waccmx_offline. + To verify that differences were fundamentally no greater than + roundoff-level, I introduced temporary code; this minimal diff + ended up being enough to give just roundoff-level differences from baseline: + + diff --git a/src/biogeophys/SoilFluxesMod.F90 b/src/biogeophys/SoilFluxesMod.F90 + index c316d30fe..6a958c0ee 100644 + --- a/src/biogeophys/SoilFluxesMod.F90 + +++ b/src/biogeophys/SoilFluxesMod.F90 + @@ -45,7 +45,7 @@ subroutine SoilFluxes (bounds, num_urbanl, filter_urbanl, & + ! Update surface fluxes based on the new ground temperature + ! + ! !USES: + - use clm_time_manager , only : get_step_size_real + + use clm_time_manager , only : get_step_size_real, get_nstep + use clm_varcon , only : hvap, cpair, grav, vkc, tfrz, sb + use landunit_varcon , only : istsoil, istcrop + use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall, icol_road_perv + @@ -79,7 +79,9 @@ subroutine SoilFluxes (bounds, num_urbanl, filter_urbanl, & + real(r8) :: t_grnd0(bounds%begc:bounds%endc) ! t_grnd of previous time step + real(r8) :: lw_grnd + real(r8) :: evaporation_limit ! top layer moisture available for evaporation + - real(r8) :: evaporation_demand ! evaporative demand + + real(r8) :: evaporation_demand ! evaporative demand + + real(r8) :: qflx_liqevap_orig + + real(r8) :: qflx_solidevap_orig + !----------------------------------------------------------------------- + + associate( & + @@ -291,6 +293,7 @@ subroutine SoilFluxes (bounds, num_urbanl, filter_urbanl, & + qflx_evap_soi(p) = qflx_evap_soi(p) - frac_sno_eff(c)*(evaporation_demand - evaporation_limit) + qflx_liqevap_from_top_layer(p) = max(h2osoi_liq(c,j)/(frac_sno_eff(c)*dtime), 0._r8) + qflx_solidevap_from_top_layer(p) = max(h2osoi_ice(c,j)/(frac_sno_eff(c)*dtime), 0._r8) + + + ! conserve total energy flux + eflx_sh_grnd(p) = eflx_sh_grnd(p) + frac_sno_eff(c)*(evaporation_demand - evaporation_limit)*htvp(c) + endif + @@ -307,6 +310,24 @@ subroutine SoilFluxes (bounds, num_urbanl, filter_urbanl, & + qflx_ev_snow(p) = qflx_evap_soi(p) + qflx_liqevap_from_top_layer(p) = max(h2osoi_liq(c,j)/dtime, 0._r8) + qflx_solidevap_from_top_layer(p) = max(h2osoi_ice(c,j)/dtime, 0._r8) + + + + if (h2osoi_liq(c,j) + h2osoi_ice(c,j) > 0._r8) then + + qflx_liqevap_orig = max(qflx_evap_soi(p)*(h2osoi_liq(c,j)/ & + + (h2osoi_liq(c,j)+h2osoi_ice(c,j))), 0._r8) + + else + + qflx_liqevap_orig = 0._r8 + + end if + + qflx_solidevap_orig = qflx_evap_soi(p) - qflx_liqevap_orig + + if (qflx_solidevap_from_top_layer(p) == 0._r8 .and. & + + qflx_solidevap_orig < 0._r8 .and. & + + qflx_solidevap_orig > -1.e-16_r8) then + + write(iulog,'(a, i0, 1x, i0, 1x, 5e24.17)') & + + 'WJS adj urb: solid orig le 0, new 0: nstep, p, orig, new, qflx_evap_soi, h2osoi_liq, h2osoi_ice = ', & + + get_nstep(), p, qflx_solidevap_orig, qflx_solidevap_from_top_layer(p), & + + qflx_evap_soi(p), h2osoi_liq(c,j), h2osoi_ice(c,j) + + qflx_solidevap_from_top_layer(p) = qflx_solidevap_orig + + end if + + + ! conserve total energy flux + eflx_sh_grnd(p) = eflx_sh_grnd(p) +(evaporation_demand -evaporation_limit)*htvp(c) + endif + + (Note that the diffs in + ERP_P36x2_Ld30.f45_f45_mg37.I2000Clm51FatesSpCruRsGs.cheyenne_intel.clm-FatesColdSatPhen + were ambiguous as to whether they were roundoff-level due to the + single-precision output in that test; I reran with double precision + for the baseline and the branch and was able to verify that the + diffs were only double-precision roundoff-level.) + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/1987 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev121 +Originator(s): glemieux (Gregory Lemieux,LBL/NGEET,510-486-5049) +Date: Wed Apr 5 13:34:09 MDT 2023 +One-line Summary: Changes soil moisture initialization logic for FATES + +Purpose and description of changes +---------------------------------- + +This PR changes the logic for soil moisture initialization to initialize +with wetter soils (75% of saturated water content, as opposed to 15% of +absolute water content) for all FATES configurations. The rationale for +this is that in FATES-nocomp simulations, Jessica Needham was finding very +high initial mortality rates in some seasonal tropical forest regions which +she traced it back to the initial soil moisture killing off plants before the +ecosystem could get established. + +This also updates the fates externals pointer to the latest tag which includes +a number of science updates since the last tag update and updates the the +default parameter file. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +Externals issues fixed (include issue #): + Partially addresses FATES#994 -- Bare ground establishment problem and increased soil moisture + +Known bugs found since the previous tag (include issue #): + #1979 -- Need to loosen tolerance on near-zero truncation of h2osoi_ice in UpdateState_TopLayerFluxes + +Notes of particular relevance for users +--------------------------------------- + +Changes made to namelist defaults (e.g., changed parameter values): + fates_paramfile updated to fates_params_api.25.4.0_12pft_c230327.nc + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): + This changes the starting level for a COLD start of soil moisture for ALL FATES cases to a much + higher value than for non-FATES. In the long run we'd like to have these the same and/or + have the value changable on the namelist. + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- OK + izumi ------- OK + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + FATES tests run against fates-sci.1.65.3_api.25.4.0-ctsm5.1.dev120 baseline + + +Answer changes +-------------- + +Changes answers relative to baseline: + + Changes answers in fates suite for all non-hydro fates tests since the soil + moisture initialization matches that of fates hydro now. Changes answer + for all fates testmods in the aux_clm suite as the science tag has iterated + forward by 4 minor version updates. All diffs accounted for with prior fates + suite tests. + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): + +- FATES: sci.1.61.0_api.25.0.0 -> sci.1.65.3_api.25.4.0 + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + + https://github.com/ESCOMP/CTSM/pull/1962 -- Cold start moisture for FATES increased + https://github.com/ESCOMP/CTSM/pull/1978 -- revert some commits now that FUNITCTSM works again + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev120 +Originator(s): sacks (Bill Sacks) +Date: Sat Mar 25 17:49:27 MDT 2023 +One-line Summary: Update externals and minor fixes + +Purpose and description of changes +---------------------------------- + +Main change is to update externals to cesm2_3_alpha12c-ish. + +Doing this exposed a few issues that are also fixed here. + +Also, reduce GU_LULCC tests down to a single test. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +- Resolves ESCOMP/CTSM#1968 (Reduce the GULU tests down to one) +- Resolves ESCOMP/CTSM#1971 (fsurdatmodifyctsm test should abort if it has trouble running the python script) + +Known bugs introduced in this tag (include issue #): +- ESCOMP/CTSM#1972 (FUNITCTSM test fails when run through run_sys_tests in upcoming ctsm5.1.dev120) + + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: +- Fortran unit tests now need to be run manually, since FUNITCTSM is + failing when run through run_sys_tests +- Reduced GU_LULCC tests down to a single test + + +Testing summary: +---------------- + + regular tests: + - aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing + - Fortran unit tests on cheyenne (until https://github.com/ESCOMP/CTSM/issues/1972 is resolved): from src, run: + ../cime/scripts/fortran_unit_testing/run_tests.py --build-dir `mktemp -d --tmpdir=. unit_tests.XXXXXXXX` + + aux_clm on cheyenne ------------ OK + aux_clm on izumi --------------- OK + Fortran unit tests on cheyenne - PASS + + For the two new tests (with BFAILs), ran them from dev119 with + comparison against this branch: + - ERP_D_Ld10_P36x2.f10_f10_mg37.IHistClm51BgcCrop.cheyenne_intel.clm-ciso_decStart + - SMS_Ld3_PS.f09_g17.IHistClm50BgcCrop.cheyenne_intel.clm-f09_dec1990Start_GU_LULCC + (with start date in the test mod changed to match the new version) + +Answer changes +-------------- + +Changes answers relative to baseline: YES, but just for certain compilers + + Summarize any changes to answers, i.e., + - what code configurations: all on certain compilers + - what platforms/compilers: + - nvhpc on cheyenne in non-debug cases (can be explained from + differences in compilation flags for non-debug cases, and also + some module differences) + - intel on izumi in debug cases (there were updates in ESMF + modules, though that's the same for other izumi compilers; I'm not + seeing other relevant diffs in ccs_config, so I'm not sure why we're + getting diffs here. I tried investigating, but ran into trouble trying + to get things to compile with the old ccs_config, so gave up on + tracking down the source of this difference) + + - nature of change (roundoff; larger than roundoff/same climate; new climate): + not investigated + +Other details +------------- +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): +- ccs_config: ccs_config_cesm0.0.38 -> ccs_config_cesm0.0.58 +- cime: cime6.0.45 -> cime6.0.100 +- cmeps: cmeps0.13.71 -> cmeps0.14.17 +- cdeps: cdeps0.12.65 -> cdeps1.0.7 +- share: share1.0.13 -> share1.0.16 +- pio: pio2_5_7 -> pio2_5_10 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev119 +Originator(s): slevis (Samuel Levis,SLevis Consulting,303-665-1310), ekluzek Erik Kluzek), lawrencepj1 (Peter Lawrence) +Date: Thu Mar 16 14:13:37 MDT 2023 +One-line Summary: Allow gross unrepresented land use transitions (PR #309) + +Purpose and description of changes +---------------------------------- + + Get gross unrepresented land use transitions working in CLM5.1. This is additional optional + data added to the landuse.timeseries files for transient simulations. The current landuse.timseries + files have this data, but it's set to zero. This data will be part of the CTSM5.2 surface dataset + (that is upcoming) and be on by default for clm5_3 physics. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics +onfigurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer +hanges.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + Known bugs introduced in this tag (include issue #): #1968 + #1968 -- Reduce the GULU tests down to one + +Notes of particular relevance for users +--------------------------------------- +Changes made to namelist defaults (e.g., changed parameter values): + New namelist variable: do_grossunrep + +Changes to the datasets (e.g., parameter, surface or initial files): + Surface datasets may now contain non-zero gross unrepresented land use + transitions. + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: + Erik introduced new tests that can be identified by the GU_LULCC in + their names. I ran these with the test-suites and generated baselines + for them. + +Testing summary: +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: NO (unless see next) + + Code configurations: do_grossunrep = .true. and surface dataset + includes non-zero gross unrepresented land use + transitions. I (slevis) have not investigated the + nature of the changes. + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/309 -- Added new files to allow Gross Unrepresented Land Use transition + https://github.com/ESCOMP/ctsm/pull/1965 -- update README + (NOT a PR) Update manage externals + + + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev118 +Originator(s): slevis (Samuel Levis,SLevis Consulting,303-665-1310) +Date: Sun Feb 5 18:31:29 MST 2023 +One-line Summary: Use conda environment rather than ncar_pylib with the fsurdat_modifier system test + +Purpose and description of changes +---------------------------------- + + Reason: ncar_pylib is going away soon. + + The fsurdat_modifier system test that we're discussing in this tag and + corresponding pull request (PR #1798) + FSURDATMODIFYCTSM_D_Mmpi-serial_Ld1.5x5_amazon.I2000Clm50SpRs.cheyenne_intel + stopped working when I updated the PR to dev117. + The test worked in the PR when I was still in dev115. + The test worked in vanilla dev117. + I fixed the failure by removing a restriction added in dev116 and the + corresponding override --allow_ideal_and_include_non_veg. + + I am removing another restriction added in dev116 and the corresponding + override --allow_dom_pft_and_idealized. This one didn't cause a test to fail + but unnecessarily restricted usage of the fsurdat_modifier tool. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): +Fixes #1786 -- ncar_pylib +Fixes #1925 -- replace ncar_pylib + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + clm_pymods test suite on cheyenne - PASS + (softlinks created for baselines to previous tag since this is bit-for-bit) + + any other testing (give details below): + + make all (in /python directory) - PASS + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + +Answer changes +-------------- + +Changes answers relative to baseline: NO + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/1798 -- Have fsurdat_modifier system test use conda environment rather than ncar_pylib + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev117 +Originator(s): afoster (Adrianna Foster) +Date: Thu Feb 2 10:34:23 MST 2023 +One-line Summary: Updates to facilitate running FATES at NEON sites + +Purpose and description of changes +---------------------------------- + +Small updates to facilitate creation, modification, and +use of FATES-usable (i.e. 16-PFT) NEON surface data files and +user-mods for FATES NEON cases. + +Updated neon_surf_wrapper.py and modify_singlept_site_neon.py to include a +--16pft argument that will create and/or modify the 16-PFT versions of the +surface datasets, as well as a --mixed flag to the neon_surf_wrapper.py +which tells subset_data to not overwrite the surfae dataset to be just 100% +one PFT. + +Also corrects lat-lon being used for ONAQ NEON site and updates the surface +datasets for all NEON sites. + +Also adds a check to ensure that fire emission (-fire_emis) is not on if FATES +is being run. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): +- Partially addresses ESCOMP/CTSM#1609(Get FATES working for NEON) + +Known bugs introduced in this tag (include issue #): +#1949 - Duplication problems in user_mods + +Known bugs found since the previous tag (include issue #): +#1948 - FATES and 78PFT surface datasets +FATES#983 - PRT2 test failing on izumi + + +Notes of particular relevance for users +--------------------------------------- + +Changes to the datasets (e.g., parameter, surface or initial files): +updated surface datasets for all NEON sites for big-leaf (78-PFT) and FATES (16-PFT) + + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): +Files changed in cime_config/usermods_dirs/NEON for big-leaf CLM must also +be changed in similar files in cime_config/usermods_dirs/NEON/FATES, as these are +currently duplicated. This duplication should be fixed at a later date. + +Changes to tests or testing: +Added a test in bld/unit_testers/build-namelist_test.pl to check that FATES and +fire emission cannot be on at the same time. + + +Testing summary: +---------------- + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- OK + izumi ------- OK + + +Answer changes +-------------- + +Changes answers relative to baseline: + +Only for NEON sites. + + + Summarize any changes to answers, i.e., + - what code configurations: All configurations at NEON sites + - what platforms/compilers: All platforms when running NEON sites + - nature of change: updated surface datasets + + +Other details +------------- +#1933 - Update neon_sites_dompft.csv +#1932 - NEON FATES capabilities + + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev116 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Thu Jan 26 02:17:27 MST 2023 +One-line Summary: Small answer changes with bug fixes, zetamaxstable=2 for BHS, new single point fsurdat files + +Purpose and description of changes +---------------------------------- + +Change zetamaxstable to 2 when biomass-heat-storage is on. This changes simulation answers once they +run long enough to exceed that threshold. + +Also fix an issue with maintence respiration (MR) for BGC simulations. This changes answers for most BGC cases once +they run long enough. Live course MR wasn't included. + +Make the default for MOSART to send negative flow to river outlets. Also fix an issue with this mode. + +Bring in new surface datasets for the single point sites. We now make these sites using subset_data rather +than mksurfdata. + +Some new capability to the subset_data and modify_fsurdat tools. + +subset_data add options: +--out-surface -- To name the surface dataset on the command line rather than based on the current date +--cfg-file ----- Enter the default configure file to use rather than assume a fixed one + +modify_fsurdat add options: +--fsurdat_in -- to input on command line rather than config file +--fsurdat_out -- to input on command line rather than config file +--allow_ideal_and_include_non_veg -- to allow idealized and include_non_veg at the same time +--allow_dom_pft_and_idealized -- to allow dom_pft and idealized at the same time +--overwrite -- allow output file to be overwritten +config file options: +process_subgrid_section -- Read in an optional section to set the PCT_* fractions +process_var_list_section - Read in an optional section to set any variable on the file + +Add --silent option to python tools. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[X] clm5_1 + +[x] clm5_0 + +[ ] ctsm5_0-nwp + +[x] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): + Fixes #1676 -- live coarse maintenance respiration is not included in the root respiration + Fixes #1674 -- Change mksurfdata_map/mksurfdata_esmf Makefile to build single-point datasets using subset_data + Fixes #1809 -- Add ability to name a different default config file for subset_data + Fixes #1941 -- Add --silent option to ctsm_logging python infrastructure + Fixes #1942 -- Move py_env_create outside of tools test driver, as fails on compute nodes on cheyenne + Fixes #1924 -- Some updates to fsurdat_modifier script + Fixes #1690 -- Set and use zetamaxstable for BHS cases + Fixes #1689 -- Set zetamaxstable to 2 consistently for BHS + +Externals issues fixed (include issue #): + MOSART #58 Make negative and direct_to_outlet the default option + MOSART #56 Some issues with direct_to_outlet + +Notes of particular relevance for users +--------------------------------------- + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): tools + Just to the subset_data and fsurdat_modifier tools as outlined above + +Changes made to namelist defaults (e.g., changed parameter values): + zetamaxstable now 2.0 is use_biomass_heat_storage is on + +Changes to the datasets (e.g., parameter, surface or initial files): Single point fsurdat + 1x1_brazil, 1x1_numaIA, 1x1_smallvilleIA, 1x1_vancouverCAN, 1x1_mexicocityMEX, 1x1_urbanc_alpha + surface datasets and 1x1_brazil landuse.timeseries + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): + tools test now just activates the ctsm_py conda environment rather than creating it + There are seperate configure files for each urban surface dataset for modify_fsurdat + There is a 1850 configure file for subset_data + +Changes to tests or testing: + fsurdatmodifyctsm.py script adjusted to compensate for changes + python directory changes include unit and system tests to support updates + Single point mksurdata_map tests were removed + + +Testing summary: regular tools +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - PASS + + tools-tests (test/tools) (if tools have been changed): + + cheyenne - PASS + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + cheyenne - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: + + Summarize any changes to answers, i.e., + - what code configurations: clm5_1, supported single point resolutions + - what platforms/compilers: All + - nature of change: adjusted climate + clm5_1 changes because zetmaxstable set to 2.0. If a simulation runs long enough + this max will be hit and it will change answers once it does. But if stability + doesn't hit the max answers can be identical. + clm5_0 and clm4_5 also change if biomass heat storage is turned on + single point resolutions (i.e. 1x1_smallvilleIA, 1x1_brazil, 1x1_mexicocityMEX) have differences + maintenence respiration + + If this tag changes climate describe the run(s) done to evaluate the new + climate (put details of the simulations in the experiment database) + Keith Oleson ran experiments with changing zetamaxstable some slides showing this are here: + https://docs.google.com/presentation/d/1u6ycr7F97QYYRcRfEdD9yIxH75diUx2r + + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): mosart + mosart updated to mosart1_0_48 + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + #1812 -- Get single point surface datasets from subset_data rather than mksurfdata + #1802 -- Make zetamaxstable consistently 2.0 when BHS on + #1915 -- Fix issue #1864 in release documentation + Update manage_externals (direct push to main-dev) + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev115 +Originator(s): rgknox (Ryan Knox) +Date: Fri Dec 2 15:45:32 MST 2022 +One-line Summary: API compatability with FATES V2 nutrient dynamics + +Purpose and description of changes +---------------------------------- + +This set of changes allows CTSM to continue API compatability with changes to the FATES API. FATES has updated its nutrient dynamics routine, and required a modification to the test environment, some minor updates to variable dimensions in the history, and a call to a new FATES history routine. Implicitly, the updating of the FATES tag introduces new content in the FATES model since the last API update (mostly bug fixes). + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + + +The changes here will only affect FATES simulations. Any FATES simulation will be affected. Carbon-only FATES simulations will not have qualitatively different results since the last API update (but will have bit-for-bit differences). Nutrient enabled FATES simulations (not fully coupled to CLM, only via prognosed plant N,P boundaries), and FATES-Hydro simulations (bug fix) will be different. + +Bugs fixed or introduced +------------------------ + +See the descriptions in FATES tags between sci.1.57.4_api.24.0.0 to sci.1.60.0_api.25.0.0 for details on FATES changes in this tag. + +CTSM issues fixed (include CTSM Issue #): None + +Externals issues fixed (include issue #): None + +Known bugs introduced in this tag (include issue #): None + +Known bugs found since the previous tag (include issue #): None + + +Notes of particular relevance for users +--------------------------------------- + +This set of changes comes with an updated FATES parameter file. This includes format changes only. No changes to variable values were introduced. Format changes are relegated to new parameters and/or name changes only. These changes are encapsulated in: fates/parameter_files/archive/apichange_24.2to25.xml + + +Notes of particular relevance for developers: +--------------------------------------------- + +Nothing of note regarding changes for developers. + +Changes to tests or testing: New history variables were added to the FATES PRT2 user_nl_clm. + + +Testing summary: +---------------- + +regular + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- nominal: /glade/scratch/rgknox/tests_1201-121507ch + izumi ------- nominal: /scratch/cluster/rgknox/tests_1201-122133iz + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- nominal against ctsm5.1_dev112 /glade/scratch/rgknox/tests_1130-082657ch + izumi ------- NA + +Answer changes +-------------- + +Changes answers relative to baseline: FATES answers changed relative to base. Explanation already provided (nutrient and hydro changes are qualitative). + + +Other details +------------- + +FATES external was updated. + +Pull Requests that document the changes (include PR ids): + +https://github.com/ESCOMP/CTSM/pull/1874 +https://github.com/NGEET/fates/pull/880 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev114 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326)/@wwieder/@olyson/@ka7eh +Date: Sat Nov 19 18:11:15 MST 2022 +One-line Summary: Some NEON updates fixing AG sites, update MOSART, small fixes + +Purpose and description of changes +---------------------------------- + +Minor changes to python scripts and usermod_dirs for NEON cases. Also update the lightning mesh file so that it goes with the +smaller lightning file. Have NEON use new use-cases for 2018 and 2018-PD conditions for CLM. Have NEON +Agricultural sites run with prognostic crop. Simple fix for warning about NaN's in import/export data from/to coupler. + +Get NEON tests working on izumi, add --inputdata-dir to subset_data and modify_singlept_site_neon.py so they aren't tied +to only running on cheyenne. + +Also update MOSART with fixed for direct_to_outlet option. + +Add error checking in ParitionWoodFluxes. Fix value of albgrd_col in SurfaceAlbefdoType.F90. +Previously, the wrong value (albgri_col) was being set in InitHistory. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): + Fixes #1871 -- Error in NEON surface datasets for AG sites + Fixes #1876 -- NEON data in container + Fixes #1889 -- NEON AG sites are running without prognostic crop + Fixes #1363 -- 2018_control and 2018-PD_transient use-cases for NEON + Fixes #1896 -- Improve misleading error message in check_for_nans + Fixes #1263 -- Fix partitionWood fluxes + Fixes #1788 -- Fix albgrd_col + Fixes #1901 -- Fix NEONSITE YELL + + Some on #1910 -- add pandas version check to modify_singlept_site_neon.py so will abort cleanly if version not updated + + Known bugs found since the previous tag (include issue #): + #1910 -- modify_singlept_site_neon.py has trouble on izumi + +Notes of particular relevance for users +--------------------------------------- + +Caveats for users (e.g., need to interpolate initial conditions): + NEON users: use neon_gcs_upload now. Filenames for NEON surface + datasets are changed. Start and end of simulations is different + for some sites, and managed by the user-mod-directories. The NEON + user-mod assumes transient cases will run with a transient compset + and the settings are slightly different for transient vs control + including pointing to 2018_control or 2018-PD_transient use-cases. + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + Add notes to python tools to run using conda environment setup in py_env_create + +Changes made to namelist defaults (e.g., changed parameter values): + New use cases: 2018_control and 2018-PD_transient + +Changes to the datasets (e.g., parameter, surface or initial files): + New updated NEON surface datasets + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers (e.g., code that is duplicated that requires double maintenance): + Remove toolchain python scripts as this work was moved over to the ctsm5.2 development + +Changes to tests or testing: + Add a run_black target to the python directory Makefile to run black and not just do a black check + Add python modules needed for neon scripts to conda py_create_env conda environment + +Testing summary: regular, tools +---------------- + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - OK (141 NEON tests are different than baseline) + + tools-tests (test/tools) (if tools have been changed): + + cheyenne - OK + cheyenne (NEON) - PASS + izumi (NEON) -- OK (modify_singlept_site_neon.py test fails due to #1910) + izumi -- OK + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + Acheyenne -- PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + any other testing (give details below): + run_neon.py ran for all NEON sites ad, post-ad, and transient + +Answer changes +-------------- + +Changes answers relative to baseline: No (other than NEON sites, and if direct_to_outlet turned on in MOSART) + + Summarize any changes to answers, i.e., + - what code configurations: NEON or if bypass_routing_option==direct_to_outlet in MOSART + - what platforms/compilers: all + - nature of change: + NEON AG sites are significantly different + + NEON sites reran and reevaluated + MOSART direct_to_outlet option evaluated by @swensosc and @olyson + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): mosart + mosart updated from mosart1_0_45 to to mosart1_0_47 (asynchronous changes, and direct_to_outlet fixes) + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + #1872 -- NEON updates + #1814 -- Add error checking in partitionWoodFluxes + #1810 -- Fix albdgrd_col value + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev113 +Originator(s): sacks (Bill Sacks), ekluzek (Erik Kluzek), jedwards (Jim Edwards) +Date: Fri Oct 28 11:00:26 MDT 2022 +One-line Summary: Fix some compsets; add only clauses for ESMF use statements + +Purpose and description of changes +---------------------------------- + +(1) Fix I1850Clm51BgcCrop compset (was using CLM50 instead of CLM51) +- Resolves https://github.com/ESCOMP/CTSM/issues/1882 + +(2) Change LND_TUNING_MODE for DATM%CPLHIST compsets to use CAM tunings + since these cases typically use atmosphere forcings from CAM. +- Resolves https://github.com/ESCOMP/CTSM/issues/1885 + +(3) Add "only" clauses to ESMF use statements +- Resolves https://github.com/ESCOMP/CTSM/issues/1846 + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ +CTSM issues fixed (include CTSM Issue #): +- Resolves https://github.com/ESCOMP/CTSM/issues/1882 (I1850Clm51BgcCrop actually uses CLM50) +- Resolves https://github.com/ESCOMP/CTSM/issues/1885 (CPLHIST compsets should use same land tunings as for CAM compsets) +- Resolves https://github.com/ESCOMP/CTSM/issues/1846 (Add "only" clause to a problematic use statement in lnd_comp_nuopc for cce compiler) + +Known bugs introduced in this tag (include issue #): +- https://github.com/ESCOMP/CTSM/issues/1887 (Gnu MCT builds will fail starting in ctsm5.1.dev113) + + +Testing summary: +---------------- + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + +Answer changes +-------------- + +Changes answers relative to baseline: YES, but just for limited cases + + Summarize any changes to answers, i.e., + - what code configurations: + - Cases with I1850Clm51BgcCrop compset + - Cases with DATM%CPLHIST + - what platforms/compilers: all + - nature of change (roundoff; larger than roundoff/same climate; new climate): + Larger than roundoff; may be new climate + +Other details +------------- +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/CTSM/pull/1870 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev112 +Originator(s): adrifoster (Adrianna Foster), glemieux (Gregory Lemieux, LBL/NGEET) +Date: Sat Oct 15 16:26:28 MDT 2022 +One-line Summary: Rework fates test definitions and add new fates tests + +Purpose and description of changes +---------------------------------- + +This tag includes a number of updates to the fates test definitions and test list to gain more coverage: + +(1) Reorder and update the fates test definitions so that fates satellite phenology mode +can be configured for cases using a compset. + +(2) Add a long-term exact restart test to catch issues that may arise due to updates to +fates procedure calls during end of year simulation dates. + +(3) Add a no-competition + fixed biogeography, non-satellite phenology test definition to provide +additional mode combination configuration. + +(4) Update the fates externals tag to incorporate a fix a vegetation temperature exact restart +issue discovered while implementing (1) above. + +(5) Truncate all testmods starting with "Fates" to mitigate running over the limits on the length +of testnames, typically when specifying custom `test_id` using `run_sys_test`. + +(6) Updates the expected failures list. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): + Fixes #1839 - Add more FATES tests that are longer than one year + Fixes #1817 - Make sure at least one test running FatesSp just uses the compset and not a test-mod directory + Fixes #1551 - Add FATES NoComp + FixedBiogeog regression test to the fates category of tests + +Externals issues fixed (include issue #): + FATES#908 - Bareground area_pft not being carried over during restarts + FATES#911 - 24-hr running mean vegetation temperature is not b4b on threaded exact restart SatPhen test + +Notes of particular relevance for developers: +--------------------------------------------- + +Changes to tests or testing: + FATES#897 is still unresolved, but is now covered by this updated test list. + FATES#701 was reopened + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + cheyenne - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + cheyenne ---- OK + izumi ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + cheyenne ---- OK + izumi ------- OK + + any other testing (give details below): + +If the tag used for baseline comparisons was NOT the previous tag, note that here: + + FATES tests run against both fates-sci.1.58.1_api.24.1.0-ctsm5.1.dev111 to check the + fates update against the previous baseline is in line with expected DIFFs and + fates-sci.1.59.7_api.24.1.0-ctsm5.1.dev111. + +Answer changes +-------------- + +Changes answers relative to baseline: + Yes, for fates run modes only due non-b4b updates in multiple fates tags. These + include both software bug fixes and answer changing science updates. + +Other details +------------- + +List any externals directories updated (cime, rtm, mosart, cism, fates, etc.): fates + +Pull Requests that document the changes (include PR ids): + + https://github.com/ESCOMP/CTSM/pull/1827 + https://github.com/ESCOMP/CTSM/pull/1849 + + +=============================================================== +=============================================================== Tag name: ctsm5.1.dev111 Originator(s): jedwards (Jim Edwards), wwieder (Will Wieder), sacks (Bill Sacks) Date: Wed Oct 5 13:05:52 MDT 2022 diff --git a/doc/ChangeSum b/doc/ChangeSum index cdbc339758..d644cff144 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,61 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.1.dev167 samrabin 02/08/2024 Delete _FillValue and history from parameter files + ctsm5.1.dev166 multiple 01/24/2024 BFB merge tag + ctsm5.1.dev165 slevis 01/19/2024 Turn Meier2022, tillage, residue removal on for ctsm5.1, fix #2212 + ctsm5.1.dev164 rgknox 01/17/2024 Compatibility and tests for FATES 2-Stream + ctsm5.1.dev163 samrabin 01/10/2024 Add tillage and residue removal + ctsm5.1.dev162 samrabin 01/05/2024 Improvements to processing of crop calendar files + ctsm5.1.dev161 samrabin 01/04/2024 Refactor 20-year running means of crop GDD accumulation + ctsm5.1.dev160 glemieux 12/30/2023 FATES landuse version 1 + ctsm5.1.dev159 multiple 12/12/2023 Various BFB fixes and updates + ctsm5.1.dev158 erik 12/07/2023 First tag with testing moved to Derecho and working PE-layouts for Derecho + ctsm5.1.dev157 samrabin 12/05/2023 Update Externals to work on Derecho + ctsm5.1.dev156 samrabin 11/30/2023 Do not use Meier roughness by default. + ctsm5.1.dev155 samrabin 11/27/2023 Use baset_latvary parameters + ctsm5.1.dev154 slevis 11/22/2023 New params files with changes for Meier roughness, MIMICS, and SNICAR, and changes to leafcn and k*_nonmyc + ctsm5.1.dev153 afoster 11/17/2023 Call new FATES-side FatesReadParameters + ctsm5.1.dev152 multiple 11/14/2023 Mv tools to /python and add tests; add snow_thermal_cond_method; a few fixes / refactors + ctsm5.1.dev151 rgknox 11/11/2023 Fixes to FATES long run restarts + ctsm5.1.dev150 rgknox 11/06/2023 FATES API fix to support future fates npp-fixation coupling, and urgent coupling fixes with E3SM. + ctsm5.1.dev149 samrabin 11/03/2023 Rearrange leaf/stem "harvest" and fix soil gas diffusivity + ctsm5.1.dev148 samrabin 11/03/2023 Add GRAINN outputs + ctsm5.1.dev147 samrabin 10/30/2023 Add sowing window input files + ctsm5.1.dev146 glemieux 10/24/2023 FATES cross-grid seed dispersal + ctsm5.1.dev145 slevis 10/19/2023 SNICAR snow albedo scheme updates + ctsm5.1.dev144 samrabin 10/19/2023 Remove a deprecated shr_mpi_bcast call + ctsm5.1.dev143 rgknox 10/13/2023 Zeroing of wood product fluxes on fates columns + ctsm5.1.dev142 samrabin 09/19/2023 Merge 5 bit-for-bit pull requests + ctsm5.1.dev141 slevis 09/13/2023 Change small snocan to zero + ctsm5.1.dev140 afoster 09/12/2023 add lai_streams capability for FATES + ctsm5.1.dev139 slevis 08/28/2023 Fix problems uncovered by nag -nan tests + ctsm5.1.dev138 slevis 08/25/2023 Refactor max_patch_per_col and maxsoil_patches loops + ctsm5.1.dev137 slevis 08/23/2023 Surface roughness modifications + ctsm5.1.dev136 multiple 08/22/2023 Change order of history fields to improve performance on derecho + ctsm5.1.dev135 slevis 08/21/2023 Rename hist fields to track them down more easily + ctsm5.1.dev134 rgknox 08/16/2023 Migration of FATES to share normal soil BGC call sequence and functionality + ctsm5.1.dev133 glemieux 08/09/2023 FATES API update to facilitate fates refactor + ctsm5.1.dev132 slevis 08/04/2023 Add parameterization to allow excess ice in soil and subsidence + ctsm5.1.dev131 samrabin 07/27/2023 Enable prescribed crop calendars + ctsm5.1.dev130 glemieux 07/09/2023 FATES parameter file and test definition update + ctsm5.1.dev129 erik 06/22/2023 NEON fixes for TOOL and user-mods, add SP for NEON, some history file updates, black refactor for buildlib/buildnml + ctsm5.1.dev128 glemieux 06/01/2023 Update FATES tests to double precision + ctsm5.1.dev127 sacks 05/19/2023 Fix nuopc cplhist test + ctsm5.1.dev126 jpalex 05/18/2023 Clean up some loops in UrbanTimeVarType + ctsm5.1.dev125 jpalex 05/17/2023 Added cache for clock step_size in clm_time_manager.F90 + ctsm5.1.dev124 erik 05/09/2023 Initialization memory update, new surface datasets for new grids, add option for running NEON with PRISM data + ctsm5.1.dev123 sacks 05/01/2023 Updates needed for pFUnit 4 and other externals updates + ctsm5.1.dev122 sacks 04/23/2023 Rework handling of evaporation constraint in SoilFluxes + ctsm5.1.dev121 glemieux 04/05/2023 Changes soil moisture initialization logic for FATES + ctsm5.1.dev120 sacks 03/25/2023 Update externals and minor fixes + ctsm5.1.dev119 slevis 03/16/2023 Allow gross unrepresented land use transition (PR #309) + ctsm5.1.dev118 slevis 02/05/2023 Use conda environment rather than ncar_pylib with the fsurdat_modifier system test + ctsm5.1.dev117 afoster 02/02/2023 Updates to facilitate running FATES at NEON sites + ctsm5.1.dev116 erik 01/26/2023 Small answer changes with bug fixes, zetamaxstable=2 for BHS, new single point fsurdat files + ctsm5.1.dev115 rgknox 12/02/2022 API compatability with FATES V2 nutrient dynamics + ctsm5.1.dev114 multiple 11/19/2022 Some NEON updates fixing AG sites, update MOSART, small fixes + ctsm5.1.dev113 multiple 10/28/2022 Fix some compsets; add only clauses for ESMF use statements + ctsm5.1.dev112 multiple 10/15/2022 Rework fates test definitions and add new fates tests ctsm5.1.dev111 multiple 10/05/2022 Fixes for NEON cases ctsm5.1.dev110 slevis 09/26/2022 Introduction of modify_meshes tool for use in I-cases and F-cases ctsm5.1.dev109 slevis 09/26/2022 If not MIMICS, do not output certain MIMICS history fields diff --git a/doc/README.CHECKLIST.master_tags b/doc/README.CHECKLIST.master_tags index 31c09895be..ed7794130b 100644 --- a/doc/README.CHECKLIST.master_tags +++ b/doc/README.CHECKLIST.master_tags @@ -55,6 +55,14 @@ https://github.com/ESCOMP/ctsm/wiki/CTSM-development-workflow ---- THE FOLLOWING CAN ONLY BE DONE BY INTEGRATORS ---- +NOTE (especially for new integrators): Be sure to follow the recommended +git setup in +. +Especially note that you should never use something like `git merge +escomp/master` to merge the upstream master branch into your local copy: +instead, you should always use `git pull` with the recommended +configuration settings (or `git merge --ff-only`) for that scenario. + (7) Merge the PR to master when review is approved (8) Compare master to branch show that they are identical @@ -65,7 +73,10 @@ This should show no diffs (9) Make an annotated tag on master -(10) Push master and tag to ESCOMP/ctsm +(10) Push tag to ESCOMP/ctsm + +(10a) Push to master (if needed because you changed something in master after PR was merged, or +if you did step 7 above using git commands that require this step) (11) Update the CTSM upcoming tags project, if necessary (https://github.com/ESCOMP/ctsm/projects/6) diff --git a/doc/design/dynamic_urban.rst b/doc/design/dynamic_urban.rst index 0ca5d488f4..fa7a499725 100644 --- a/doc/design/dynamic_urban.rst +++ b/doc/design/dynamic_urban.rst @@ -6,18 +6,18 @@ Overview of this design document ================================== -This documents some of the high-level design decisions made during implementation of +This documents some of the high-level design decisions made during implementation of dynamic urban landunits. ============================================================================ The use of dzsoi_decomp for urban landunits to calculate totcolch4 in ch4Mod.F90 ============================================================================ -During the first test simulation for dynamic urban, we encountered a methane conservation -error the first time PCT_URBAN changed. The dynamic adjustments for conc_ch4_sat_col and +During the first test simulation for dynamic urban, we encountered a methane conservation +error the first time PCT_URBAN changed. The dynamic adjustments for conc_ch4_sat_col and conc_ch4_unsat_col (the column_state_updater in subroutine DynamicColumnAdjustments within ch4Mod.F90) were distributing non-zero values for roof and walls for layers 1,nlevsoi. -When the total column ch4 is summed over the soil layers (or in this case, urban layers), the -summation is done over nlevsoi, not nlevurb, using dz. dz is 1.e36 for roof/wall layers +When the total column ch4 is summed over the soil layers (or in this case, urban layers), the +summation is done over nlevsoi, not nlevurb, using dz. dz is 1.e36 for roof/wall layers that are greater than nlevurb, thus creating an imbalance. Rather than trying to keep the BGC variables physically meaningful in urban landunits, diff --git a/doc/design/python_script_user_interface.rst b/doc/design/python_script_user_interface.rst index 4dd0c9c546..3ad6a4d2cf 100644 --- a/doc/design/python_script_user_interface.rst +++ b/doc/design/python_script_user_interface.rst @@ -40,8 +40,8 @@ Options that are more than a single character should be formatted as ``--some-va 2. Standard options: verbose, silent, help and debug: -Scripts should support ``--verbose`` / ``-v`` and ``--debug`` options. See comments at the top of ``ctsm_logging.py`` for details. -Also the options silent and help are recommended as well. +Scripts should support ``--verbose`` / ``-v``, ''--silent'', and ``--debug`` options. See comments at the top of ``ctsm_logging.py`` for details. +Also the help option is highly recommended as well. 3. Value flags: diff --git a/doc/design/water_tracers.rst b/doc/design/water_tracers.rst index b52d653393..c17b14a43d 100644 --- a/doc/design/water_tracers.rst +++ b/doc/design/water_tracers.rst @@ -30,7 +30,7 @@ Broadly speaking, we considered three ways to store information for each tracer: ``h2osoi_liq_col_tracer(c,j,m)`` for tracers). 3. Multiple instances of ``waterstate_type`` (etc.), with no extra dimension required for - individual variables. + individual variables. We decided to go with option (3) for a number of reasons: diff --git a/doc/source/conf.py b/doc/source/conf.py index bee524164a..dcd0b2eae6 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -37,7 +37,6 @@ 'sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', - 'sphinx.ext.imgmath', 'sphinx.ext.githubpages'] # Add any paths that contain templates here, relative to this directory. @@ -94,8 +93,6 @@ # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True -imgmath_image_format = 'svg' - # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for diff --git a/doc/source/lilac/obtaining-building-and-running/index.rst b/doc/source/lilac/obtaining-building-and-running/index.rst index 14dc6f40be..0739800137 100644 --- a/doc/source/lilac/obtaining-building-and-running/index.rst +++ b/doc/source/lilac/obtaining-building-and-running/index.rst @@ -9,4 +9,5 @@ obtaining-and-building-ctsm.rst setting-ctsm-runtime-options.rst + notes-on-running-ctsm.rst restarting.rst diff --git a/doc/source/lilac/obtaining-building-and-running/notes-on-running-ctsm.rst b/doc/source/lilac/obtaining-building-and-running/notes-on-running-ctsm.rst new file mode 100644 index 0000000000..1e3d36cdf7 --- /dev/null +++ b/doc/source/lilac/obtaining-building-and-running/notes-on-running-ctsm.rst @@ -0,0 +1,26 @@ +.. highlight:: shell + +.. _notes-on-running-ctsm: + +======================= + Notes on running CTSM +======================= + +.. _runtime-environment-variables: + +Environment variables that may need to be set at runtime +======================================================== + +With the MPT MPI library (which is the default MPI library on NCAR's cheyenne machine), it is important to set the environment variable ``MPI_TYPE_DEPTH`` to 16 when running CTSM (this setting is required by the Parallel IO library). Typically you should set this variable in your job submission script, using either: + +.. code-block:: Bash + + export MPI_TYPE_DEPTH=16 + +or: + +.. code-block:: Tcsh + + setenv MPI_TYPE_DEPTH 16 + +prior to running the model. diff --git a/doc/source/lilac/obtaining-building-and-running/obtaining-and-building-ctsm.rst b/doc/source/lilac/obtaining-building-and-running/obtaining-and-building-ctsm.rst index 99cb908d28..c0e510c017 100644 --- a/doc/source/lilac/obtaining-building-and-running/obtaining-and-building-ctsm.rst +++ b/doc/source/lilac/obtaining-building-and-running/obtaining-and-building-ctsm.rst @@ -250,7 +250,6 @@ Makefile-formatted file gives variables that should be set in the atmosphere mod build. :ref:`See below for information on how to use this file`. - Rebuilding after changing CTSM source code ------------------------------------------ diff --git a/doc/source/lilac/specific-atm-models/index.rst b/doc/source/lilac/specific-atm-models/index.rst index b5c3d2bc08..317732fb58 100644 --- a/doc/source/lilac/specific-atm-models/index.rst +++ b/doc/source/lilac/specific-atm-models/index.rst @@ -1,7 +1,7 @@ .. _specific-atm-models: ============================================================== - Instructions on using CTSM with specific atmosphere models + Instructions on using CTSM with specific atmosphere models ============================================================== .. toctree:: diff --git a/doc/source/lilac/specific-atm-models/wrf-nesting.rst b/doc/source/lilac/specific-atm-models/wrf-nesting.rst index b21593029b..f4c4570f2f 100644 --- a/doc/source/lilac/specific-atm-models/wrf-nesting.rst +++ b/doc/source/lilac/specific-atm-models/wrf-nesting.rst @@ -11,15 +11,15 @@ nested domain. A nested domain is usually used to have a finer-resolution domain within the coarser model domain. A nested simulation enables running at a higher -resolution over a smaller domain +resolution over a smaller domain .. note:: A nest should cover a portion of the parent domain and is fully contained by the parent domain, so that it is driven along its lateral boundaries by the - parent domain. + parent domain. .. todo:: - Negin wants to add a flowchart showing the workflow of a nested case. + Negin wants to add a flowchart showing the workflow of a nested case. There are currently two types of nesting available within WRF: @@ -33,10 +33,10 @@ There are currently two types of nesting available within WRF: - Also, the averaged values from the inner domain are being sent back to the outer domain at the corresponding grid points. .. important:: - Currently, the WRF-CTSM coupling infrastructure only support one-way nesting. - This example clarifies the workflow for running a nested WRF-CTSM case using one-way nesting with ``ndown.exe``. + Currently, the WRF-CTSM coupling infrastructure only support one-way nesting. + This example clarifies the workflow for running a nested WRF-CTSM case using one-way nesting with ``ndown.exe``. -The procedure for running a nested simulation for WRF with CTSM is +The procedure for running a nested simulation for WRF with CTSM is similar to the workflow for running WRF real cases, except that it requires additional steps to (1) clone the CTSM repository, (2) build CTSM and LILAC, and (3) define namelist options reuired for CTSM. @@ -46,14 +46,12 @@ A full description of all steps for a WRF-CTSM run are included here. .. important:: This section assumes the user has completed all the steps required for - WRF-CTSM simulation single domain. + WRF-CTSM simulation single domain. Therefore, we are not repeating the steps necessary for building WRF and - CTSM. - + CTSM. In this example we use a nested domain over the CONUS as shows below: - .. _Figure ctsm-ndown: .. figure:: ndown_ctsm_diagram.svg @@ -105,7 +103,7 @@ Nested Simulations : Pre-processing (ungrib.exe) ------------------------------------------------- As mentioned previously, the purpose of the ungrib script is to unpack GRIB meteorological data and pack it into an intermediate file format. -This step is exactly identical to a non-nested simulation. +This step is exactly identical to a non-nested simulation. Run ungrib to get gribbed data into usable format to be ingested by WRF. @@ -128,12 +126,11 @@ Check ungrib log for the following message showing successful completion of ungr At this point, you should see ungrib output (intermediate files) in your WPS directory. - Nested Simulations : Pre-processing (metgrid.exe) ------------------------------------------------- Ensure that the `start_date` and `end_date` for domain two is set correctly for your simulation. -Next, run ``metgrid.exe``:: +Next, run ``metgrid.exe``:: ./metgrid.exe >& log.metgrid @@ -144,14 +141,12 @@ metgrid step:: ! Successful completion of metgrid. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - Running metgrid for two domains will create files like below:: + met_em.d01.* met_em.d02.* - - Nested Simulations : real.exe ------------------------------ @@ -160,31 +155,30 @@ both domains. In summary, complete the following steps: -Move or link WPS output files (``met_em.d01*`` and ``met_em.d02`` files) to your WRF test directory. +Move or link WPS output files (``met_em.d01*`` and ``met_em.d02`` files) to your WRF test directory. Edit namelist.input for your WRF domain and desirable configurations. This should be the same domain as WPS namelist. Make sure you set ``max_dom = -2,`` in the namelist. +2,`` in the namelist. To run WRF-CTSM, in your namelist change land-surface option to 6 for both domains:: - sf_surface_physics = 6, 6, - + sf_surface_physics = 6, 6, Run real.exe (if compiled parallel submit a batch job) to generate -initail and boundary condition files for both domain. +initail and boundary condition files for both domain. Make sure the following three files have been created in your directory:: wrfinput_d01 wrfinput_d02 wrfbdy_d01 -The boundary condition file is only created for the outer domain. +The boundary condition file is only created for the outer domain. -Check the last line of the real log file for the following message:: +Check the last line of the real log file for the following message: -Rename wrfinput_d02 +Rename wrfinput_d02 ------------------- Next, rename the ``wrfinput_d02`` file to ``wrfndi_d02``:: @@ -199,24 +193,24 @@ Add the following into your namelist.input file under ``&time_control``:: io_form_auxinput2 = 2 -Run ndown.exe to create ``wrfinput_d02`` and ``wrfbdy_d02``. +Run ndown.exe to create ``wrfinput_d02`` and ``wrfbdy_d02``. -Run WRF for coarser domain +Run WRF for coarser domain --------------------------- In this step, run WRF for the outer domain. -Make sure that ``max_dom = 1`` to run only for the coarser domain. +Make sure that ``max_dom = 1`` to run only for the coarser domain. This step is exactly identical as the previous example and only creates the -``wrfout*`` files for the coarser domain. +``wrfout*`` files for the coarser domain. Please make sure to copy ``lnd_in`` , ``lilac_in``, and ``lnd_modelio`` for the -coarser domain in this directory. +coarser domain in this directory. Create CTSM runtime files for the fine domain --------------------------------------------- This step is in addition creating CTSM runtime files for coarser domain which was explained here. For succesfully completing the previous step you should -have already created these files for the coarser domain. +have already created these files for the coarser domain. .. seealso:: @@ -225,7 +219,6 @@ have already created these files for the coarser domain. files for the finer domain you should follow the steps in section :numref:`setting-ctsm-runtime-options`. - Again, the goal here is to create files that determine CTSM runtime options which are defined within these three files: @@ -235,20 +228,18 @@ are defined within these three files: - ``lilac_in``: This namelist controls the operation of LILAC - -Run WRF for the finer domain +Run WRF for the finer domain ----------------------------- First, save (rename or move) the data from the coarser domain simulation (``wrfout_d01_*`` files). Next, rename ``wrfinput_d02`` and ``wrfbdy_d02`` to ``wrfinput_d01`` and ``wrfbdy_d01``, respectively. - Edit namelist.input, moving all of the fine-grid domain data from column 2 to column 1 so that this run will be for the fine-grid domain only. Make sure you set `max_dom=1` and set your `time_step` based on the finer domain. -.. note:: - It may be beneficial to save namelist.input to something else prior to this step in case you need to repeat this +.. note:: + It may be beneficial to save namelist.input to something else prior to this step in case you need to repeat this process in the future. Save the newly-edited namelist as namelist.input . Now run wrf.exe by submitting a job similar to a not-nested case. @@ -256,7 +247,5 @@ Now run wrf.exe by submitting a job similar to a not-nested case. .. important:: The output for the finer domain is wrfout_d01_* not wrfout_d02_* and although - in the name it is saying d01 it is technically d02 domain. - - + in the name it is saying d01 it is technically d02 domain. diff --git a/doc/source/lilac/specific-atm-models/wrf-tools.rst b/doc/source/lilac/specific-atm-models/wrf-tools.rst index 8b3c423a58..0366bc1582 100644 --- a/doc/source/lilac/specific-atm-models/wrf-tools.rst +++ b/doc/source/lilac/specific-atm-models/wrf-tools.rst @@ -9,8 +9,6 @@ This section includes instructions on tools and utilities developed for WRF-CTSM simulations. - - Generate CTSM surface dataset for a WRF domain ---------------------------------------------- @@ -18,7 +16,6 @@ Before this step, make sure you have successfully created geo_em* files for your specific WRF domain using WPS. Instructions on how to run ``geogrid.exe`` is described in here. - 1. Create SCRIP grid file from WRF ``geo_em*`` files, using the following ncl script:: @@ -39,7 +36,6 @@ is described in here. ncl mkunitymap.ncl - .. warning:: This will throw some git errors if not run in a repository. @@ -52,7 +48,6 @@ is described in here. ../../../configure --macros-format Makefile --mpilib mpi-serial - 5. Generate CTSM domain files using ``get_domain`` tool:: ./gen_domain -m /glade/work/$USER/ctsm/nldas_grid/scrip/wrf2clm_mapping_noneg.nc -o wrf2clm_ocn_noneg -l wrf2clm_lnd_noneg @@ -61,20 +56,15 @@ is described in here. ./mksurfdata.pl -res usrspec -usr_gname "nldas" -usr_gdate "190124" -usr_mapdir "/glade/work/$USER/ctsm/nldas_grid/map" -y 2000 -exedir "/glade/u/home/$USER/src/ctsm/ctsm_surfdata/tools/mksurfdata_map" -no-crop - - Merge WRF initial conditions into an existing CTSM initial condition file -------------------------------------------------------------------------- The following procedure is if you'd wish to merget WRF inital conditions from ``wrfinput`` file into CTSM initial condition file :: - ncl transfer_wrfinput_to_ctsm_with_snow.ncl 'finidat="the_existing_finidat_file.nc"' 'wrfinput="your_wrfinput_file"' 'merged="the_merged_finidat_file.nc"' - .. todo:: Sam, can you please make the above ncl script available. - diff --git a/doc/source/lilac/specific-atm-models/wrf.rst b/doc/source/lilac/specific-atm-models/wrf.rst index d34dd66d0b..5d104778ec 100644 --- a/doc/source/lilac/specific-atm-models/wrf.rst +++ b/doc/source/lilac/specific-atm-models/wrf.rst @@ -18,7 +18,7 @@ A full description of all steps for a WRF-CTSM run are included here. Specific new steps that would not be completed in a standard WRF real case are described in sections :numref:`clone-WRF-CTSM-repositories`, -:numref:`build-CTSM-and-dependencies` , +:numref:`build-CTSM-and-dependencies` , and :numref:`wrf-set-ctsm-runtime-options`. .. important:: @@ -27,8 +27,7 @@ and :numref:`wrf-set-ctsm-runtime-options`. If CIME is not ported to your machine, please see `instructions on porting CIME `_. - In this example we assume NCAR’s ``Cheyenne`` HPC system in particular. - + In this example we assume NCAR's ``Cheyenne`` HPC system in particular. .. _clone-WRF-CTSM-repositories: @@ -41,20 +40,18 @@ Clone the WRF repository and checkout ``develop`` branch:: cd WRF-CTSM git checkout develop - Clone the CTSM repository:: git clone https://github.com/ESCOMP/CTSM.git cd CTSM ./manage_externals/checkout_externals - .. _build-CTSM-and-dependencies: Build CTSM and its dependencies ------------------------------- -In your CTSM directory, build CTSM and its dependencies based on the +In your CTSM directory, build CTSM and its dependencies based on the instructions from section :numref:`obtaining-and-building-ctsm`:: ./lilac/build_ctsm /PATH/TO/CTSM/BUILD --machine MACHINE --compiler COMPILER @@ -63,7 +60,6 @@ For example on ``Cheyenne`` and for ``Intel`` compiler:: ./lilac/build_ctsm ctsm_build_dir --compiler intel --machine cheyenne - .. warning:: The directory you provided for the build script (``/PATH/TO/CTSM/BUILD``) must *not* exist. @@ -74,20 +70,19 @@ For example on ``Cheyenne`` and for ``Intel`` compiler:: Run ``./lilac/build_ctsm -h`` to see all options available. For example if you would like to run with threading support you can use ``--build-with-openmp``. - Building WRF with CTSM ---------------------- First, load the same modules and set the same environments as used for CTSM build by sourcing ``ctsm_build_environment.sh`` for Bash:: - source ctsm_build_dir/ctsm_build_environment.sh + source ctsm_build_dir/ctsm_build_environment.sh or sourcing ``ctsm_build_environment.csh`` for Cshell: .. code-block:: Tcsh - source ctsm_build_dir/ctsm_build_environment.csh + source ctsm_build_dir/ctsm_build_environment.csh Set makefile variables from CTSM needed for the WRF build by setting the following environment. For example for Bash:: @@ -108,29 +103,27 @@ Some of these are not required, but might help if you face any compilation error For more information check `WRF Users' Guide `_. - Explicitly define the model core to build by (Bash):: export WRF_EM_CORE=1 -or (Cshell): +or (Cshell): -.. code-block:: Tcsh +.. code-block:: Tcsh setenv WRF_EM_CORE 1 - Explicilty turn off data assimilation by (Bash):: export WRF_DA_CORE=0 -or (Cshell): +or (Cshell): -.. code-block:: Tcsh +.. code-block:: Tcsh setenv WRF_DA_CORE 0 -Now in your WRF directory configure and build WRF for your machine +Now in your WRF directory configure and build WRF for your machine and intended compiler:: ./clean -a @@ -138,7 +131,7 @@ and intended compiler:: At the prompt choose one of the options, based on the compiler used for building CTSM. Then you should choose if you'd like to build serially or -in parallel. For example, you can choose to build with ``intel`` compiler with +in parallel. For example, you can choose to build with ``intel`` compiler with distributed memory parallelization (``dmpar``). .. tip:: @@ -149,12 +142,10 @@ distributed memory parallelization (``dmpar``). The next prompt requests an option for nesting. Currently nesting is not available for WRF-CTSM so select option ``1 (basic)``. - Now compile em_real and save the log:: ./compile em_real >& compile.log - Check the bottom of your log file for a successful compilation message. .. note:: @@ -178,7 +169,6 @@ skip to section :numref:`wrf-set-ctsm-runtime-options`. Building WPS requires that WRF be already built successfully. - Get WPS from this website:: https://www2.mmm.ucar.edu/wrf/users/download/wrf-regist_or_download.php @@ -202,7 +192,6 @@ Then, compile WPS:: If wps builds succesfully you should see ``geogrid.exe``, ``ungrib.exe``, and ``metgrid.exe``. Alternatively, you can check the log for successful build messages. - Run WPS Programs ---------------- @@ -253,37 +242,32 @@ metgrid step:: ! Successful completion of metgrid. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - Run real.exe ------------ Run ``real.exe`` to generate initial and boundary conditions. -Follow WRF instructions for creating initial and boundary conditions. +Follow WRF instructions for creating initial and boundary conditions. In summary, complete the following steps: -Move or link WPS output files (``met_em.d01*`` files) to your WRF test directory. +Move or link WPS output files (``met_em.d01*`` files) to your WRF test directory. Edit namelist.input for your WRF domain and desirable configurations. This should be the same domain as WPS namelist. - To run WRF-CTSM, in your namelist change land-surface option to 6:: sf_surface_physics = 6 - Run real.exe (if compiled parallel submit a batch job) to generate ``wrfinput`` and ``wrfbdy`` files. - Check the last line of the real log file for the following message:: SUCCESS COMPLETE REAL_EM INIT .. _wrf-set-ctsm-runtime-options: - Set CTSM runtime options ------------------------ @@ -301,7 +285,6 @@ are defined within these three files: - ``lilac_in``: This namelist controls the operation of LILAC - The basic process for creating the necessary input files are summarized as follows: @@ -342,8 +325,7 @@ Run the script ``make_runtime_inputs`` to create ``lnd_in`` and Modify ``lilac_in`` as needed. For this example, you can use the following options:: atm_mesh_filename = '/glade/scratch/negins/wrf_ctsm_files/wrf2ctsm_land_conus_ESMFMesh_c20201110.nc' - lnd_mesh_filename = '/glade/scratch/negins/wrf_ctsm_files/wrf2ctsm_land_conus_ESMFMesh_c20201110.nc' - + lnd_mesh_filename = '/glade/scratch/negins/wrf_ctsm_files/wrf2ctsm_land_conus_ESMFMesh_c20201110.nc' Run ``download_input_data`` script to download any of CTSM's standard input files that are needed based on settings in ``lnd_in`` and ``lilac_in``:: @@ -361,17 +343,17 @@ Run wrf.exe ----------- If real.exe completed successfully, we should have ``wrfinput`` and ``wrfbdy`` files -in our directory. +in our directory. If you plan to use this example's preexisting files, copy the following files to your WRF run directory:: - cp /glade/scratch/negins/wrf_ctsm_files/namelist.input . + cp /glade/scratch/negins/wrf_ctsm_files/namelist.input . cp /glade/scratch/negins/wrf_ctsm_files/wrfinput_d01 . cp /glade/scratch/negins/wrf_ctsm_files/wrfbdy_d01 . Now run WRF-CTSM. On Cheyenne this means submitting a batch job to PBS (Pro workload management system). -Please check NCAR CISL's `instructions on running a batch job on Cheyenne. +Please check NCAR CISL's `instructions on running a batch job on Cheyenne. `__ A simple PBS script to run WRF-CTSM on ``Cheyenne`` looks like this: @@ -390,12 +372,14 @@ A simple PBS script to run WRF-CTSM on ``Cheyenne`` looks like this: #PBS -l select=2:ncpus=36:mpiprocs=36 ### Run the executable + setenv MPI_TYPE_DEPTH 16 mpiexec_mpt ./wrf.exe +(See :numref:`runtime-environment-variables` for a description of the need to set ``MPI_TYPE_DEPTH`` on ``Cheyenne``.) + To submit a batch job to the ``Cheyenne`` queues, use ``qsub`` command followed -by the PBS script name. +by the PBS script name. For example, if you named this script ``run_wrf_ctsm.csh``, submit the job like this:: qsub run_wrf_ctsm.csh - diff --git a/doc/source/tech_note/BVOCs/CLM50_Tech_Note_BVOCs.rst b/doc/source/tech_note/BVOCs/CLM50_Tech_Note_BVOCs.rst index e7e8637649..5d34fdce64 100644 --- a/doc/source/tech_note/BVOCs/CLM50_Tech_Note_BVOCs.rst +++ b/doc/source/tech_note/BVOCs/CLM50_Tech_Note_BVOCs.rst @@ -3,61 +3,24 @@ Biogenic Volatile Organic Compounds (BVOCs) =============================================== -This chapter briefly describes the biogenic volatile organic compound -(BVOC) emissions model implemented in CLM. The CLM3 version (Levis et -al. 2003; Oleson et al. 2004) was based on Guenther et al. (1995). Heald -et al. (2008) updated this scheme in CLM4 based on Guenther et al. -(2006). The current version was implemented in CLM4.5 and is based on MEGAN2.1 discussed in -detail in Guenther et al. (2012). This update of MEGAN incorporates four -main features: 1) expansion to 147 chemical compounds, 2) the treatment of the -light-dependent fraction (LDF) for each compound, 3) inclusion of the -inhibition of isoprene emission by atmospheric CO\ :sub:`2` and -4) emission factors mapped to the specific PFTs of the CLM. - -MEGAN2.1 now describes the emissions of speciated monoterpenes, -sesquiterpenes, oxygenated VOCs as well as isoprene. A flexible scheme -has been implemented in the CLM to specify a subset of emissions. This -allows for additional flexibility in grouping chemical compounds to form -the lumped species frequently used in atmospheric chemistry. The mapping -or grouping is therefore defined through a namelist parameter in -drv\_flds\_in, e.g. megan\_specifier = ’ISOP = isoprene’, ’BIGALK = -pentane + hexane + heptane + tricyclene’. - -Terrestrial BVOC emissions from plants to the atmosphere are expressed -as a flux, :math:`F_{i}` (:math:`\mu` \ g C m\ :sup:`-2` ground area h\ :sup:`-1`), for emission of chemical compound -:math:`i` +This chapter briefly describes the biogenic volatile organic compound (BVOC) emissions model implemented in CLM. The CLM3 version (Levis et al. 2003; Oleson et al. 2004) was based on Guenther et al. (1995). Heald et al. (2008) updated this scheme in CLM4 based on Guenther et al (2006). The current version was implemented in CLM4.5 and is based on MEGAN2.1 discussed in detail in Guenther et al. (2012). This update of MEGAN incorporates four main features: 1) expansion to 147 chemical compounds, 2) the treatment of the light-dependent fraction (LDF) for each compound, 3) inclusion of the inhibition of isoprene emission by atmospheric CO\ :sub:`2` and 4) emission factors mapped to the specific PFTs of the CLM. + +MEGAN2.1 now describes the emissions of speciated monoterpenes, sesquiterpenes, oxygenated VOCs as well as isoprene. A flexible scheme has been implemented in the CLM to specify a subset of emissions. This allows for additional flexibility in grouping chemical compounds to form the lumped species frequently used in atmospheric chemistry. The mapping or grouping is therefore defined through a namelist parameter in drv\_flds\_in, e.g. megan\_specifier = 'ISOP = isoprene', 'BIGALK pentane + hexane + heptane + tricyclene'. + +Terrestrial BVOC emissions from plants to the atmosphere are expressed as a flux, :math:`F_{i}` (:math:`\mu` \ g C m\ :sup:`-2` ground area h\ :sup:`-1`), for emission of chemical compound :math:`i` .. math:: - :label: ZEqnNum964222 + :label: ZEqnNum964222 F_{i} =\gamma _{i} \rho \sum _{j}\varepsilon _{i,j} \left(wt\right)_{j} -where :math:`\gamma _{i}` is the emission activity factor accounting -for responses to meteorological and phenological conditions, -:math:`\rho` is the canopy loss and production factor also known as -escape efficiency (set to 1), and :math:`\varepsilon _{i,\, j}` -(:math:`\mu` \ g C m\ :sup:`-2` ground area h\ :sup:`-1`) is -the emission factor at standard conditions of light, temperature, and -leaf area for plant functional type *j* with fractional coverage -:math:`\left(wt\right)_{j}` (Guenther et al. 2012). The emission -activity factor :math:`\gamma _{i}` depends on plant functional type, -temperature, LAI, leaf age, and soil moisture (Guenther et al. 2012). -For isoprene only, the effect of CO\ :sub:`2` inhibition is now -included as described by Heald et al. (2009). Previously, only isoprene -was treated as a light-dependent emission. In MEGAN2.1, each chemical -compound is assigned a LDF (ranging from 1.0 for isoprene to 0.2 for -some monoterpenes, VOCs and acetone). The activity factor for the light -response of emissions is therefore estimated as: +where :math:`\gamma _{i}` is the emission activity factor accounting for responses to meteorological and phenological conditions, :math:`\rho` is the canopy loss and production factor also known as escape efficiency (set to 1), and :math:`\varepsilon _{i,\, j}` (:math:`\mu` \ g C m\ :sup:`-2` ground area h\ :sup:`-1`) is the emission factor at standard conditions of light, temperature, and leaf area for plant functional type *j* with fractional coverage :math:`\left(wt\right)_{j}` (Guenther et al. 2012). The emission activity factor :math:`\gamma _{i}` depends on plant functional type, temperature, LAI, leaf age, and soil moisture (Guenther et al. 2012) For isoprene only, the effect of CO\ :sub:`2` inhibition is now included as described by Heald et al. (2009). Previously, only isoprene was treated as a light-dependent emission. In MEGAN2.1, each chemical compound is assigned a LDF (ranging from 1.0 for isoprene to 0.2 for some monoterpenes, VOCs and acetone). The activity factor for the light response of emissions is therefore estimated as: .. math:: - :label: 28.2) + :label: 28.2) \gamma _{P,\, i} =\left(1-LDF_{i} \right)+\gamma _{P\_ LDF} LDF_{i} -where the LDF activity factor (:math:`\gamma _{P\_ LDF}` ) is specified -as a function of PAR as in previous versions of MEGAN. +where the LDF activity factor (:math:`\gamma _{P\_ LDF}` ) is specified as a function of PAR as in previous versions of MEGAN. -The values for each emission factor :math:`\epsilon _{i,\, j}` are -now available for each of the plant functional types in the CLM and -each chemical compound. This information is distributed through an -external file, allowing for more frequent and easier updates. +The values for each emission factor :math:`\epsilon _{i,\, j}` are now available for each of the plant functional types in the CLM and each chemical compound. This information is distributed through an external file, allowing for more frequent and easier updates. diff --git a/doc/source/tech_note/CN_Allocation/CLM50_Tech_Note_CN_Allocation.rst b/doc/source/tech_note/CN_Allocation/CLM50_Tech_Note_CN_Allocation.rst index e85a59439f..1c0e5dee57 100644 --- a/doc/source/tech_note/CN_Allocation/CLM50_Tech_Note_CN_Allocation.rst +++ b/doc/source/tech_note/CN_Allocation/CLM50_Tech_Note_CN_Allocation.rst @@ -6,36 +6,14 @@ Carbon and Nitrogen Allocation Introduction ----------------- - -The carbon and nitrogen allocation routines in CLM determine the fate of -newly assimilated carbon, coming from the calculation of photosynthesis, -and available mineral nitrogen, coming from plant uptake of mineral -nitrogen in the soil or being drawn out of plant reserves. A significant change to CLM5 relative to prior versions is that allocation of carbon and nitrogen proceed independently rather than in a sequential manner. - +The carbon and nitrogen allocation routines in CLM determine the fate of newly assimilated carbon, coming from the calculation of photosynthesis, and available mineral nitrogen, coming from plant uptake of mineral nitrogen in the soil or being drawn out of plant reserves. A significant change to CLM5 relative to prior versions is that allocation of carbon and nitrogen proceed independently rather than in a sequential manner. Carbon Allocation for Maintenance Respiration Costs -------------------------------------------------------- -Allocation of available carbon on each time step is prioritized, with -first priority given to the demand for carbon to support maintenance -respiration of live tissues (section 13.7). Second priority is to -replenish the internal plant carbon pool that supports maintenance -respiration during times when maintenance respiration exceeds -photosynthesis (e.g. at night, during winter for perennial vegetation, -or during periods of drought stress) (Sprugel et al., 1995). Third -priority is to support growth of new tissues, including allocation to -storage pools from which new growth will be displayed in subsequent time -steps. - -The total maintenance respiration demand (:math:`CF_{mr}`, gC -m\ :sup:`-2` s\ :sup:`-1`) is calculated as a function of -tissue mass and nitrogen concentration, and temperature (section 13.7). -The carbon supply to support this demand is composed of fluxes allocated -from carbon assimilated in the current timestep -(:math:`CF_{GPP,mr}`, gC m\ :sup:`-2` s\ :sup:`-1`) -and from a storage pool that is drawn down when total demand exceeds -photosynthesis ( :math:`CF_{xs,mr}`, gC m\ :sup:`-2` -s\ :sup:`-1`): +Allocation of available carbon on each time step is prioritized, with first priority given to the demand for carbon to support maintenance respiration of live tissues (section 13.7). Second priority is to replenish the internal plant carbon pool that supports maintenance respiration during times when maintenance respiration exceeds photosynthesis (e.g. at night, during winter for perennial vegetation, or during periods of drought stress) (Sprugel et al., 1995). Third priority is to support growth of new tissues, including allocation to storage pools from which new growth will be displayed in subsequent time steps. + +The total maintenance respiration demand (:math:`CF_{mr}`, gC m\ :sup:`-2` s\ :sup:`-1`) is calculated as a function of tissue mass and nitrogen concentration, and temperature (section 13.7) The carbon supply to support this demand is composed of fluxes allocated from carbon assimilated in the current timestep (:math:`CF_{GPP,mr}`, gC m\ :sup:`-2` s\ :sup:`-1` and from a storage pool that is drawn down when total demand exceeds photosynthesis ( :math:`CF_{xs,mr}`, gC m\ :sup:`-2` s\ :sup:`-1`): .. math:: :label: 19.1 @@ -52,24 +30,7 @@ s\ :sup:`-1`): CF_{xs,mr} =\_ \left\{\begin{array}{l} {0\qquad \qquad \qquad {\rm for\; }CF_{mr} \le CF_{GPP} } \\ {CF_{mr} -CF_{GPP} \qquad {\rm for\; }CF_{mr} >CF_{GPP} } \end{array}\right. -The storage pool that supplies carbon for maintenance respiration in -excess of current :math:`CF_{GPP}` ( :math:`CS_{xs}`, gC -m\ :sup:`-2`) is permitted to run a deficit (negative state), and -the magnitude of this deficit determines an allocation demand which -gradually replenishes :math:`CS_{xs}`. The logic for allowing a -negative state for this pool is to eliminate the need to know in advance -what the total maintenance respiration demand will be for a particular -combination of climate and plant type. Using the deficit approach, the -allocation to alleviate the deficit increases as the deficit increases, -until the supply of carbon into the pool balances the demand for carbon -leaving the pool in a quasi-steady state, with variability driven by the -seasonal cycle, climate variation, disturbance, and internal dynamics of -the plant-litter-soil system. In cases where the combination of climate -and plant type are not suitable to sustained growth, the deficit in this -pool increases until the available carbon is being allocated mostly to -alleviate the deficit, and new growth approaches zero. The allocation -flux to :math:`CS_{xs}` (:math:`CF_{GPP,xs}`, gC -m\ :sup:`-2` s\ :sup:`-1`) is given as +The storage pool that supplies carbon for maintenance respiration in excess of current :math:`CF_{GPP}` ( :math:`CS_{xs}`, gC m\ :sup:`-2`) is permitted to run a deficit (negative state), and the magnitude of this deficit determines an allocation demand which gradually replenishes :math:`CS_{xs}`. The logic for allowing a negative state for this pool is to eliminate the need to know in advance what the total maintenance respiration demand will be for a particular combination of climate and plant type. Using the deficit approach, the allocation to alleviate the deficit increases as the deficit increases, until the supply of carbon into the pool balances the demand for carbon leaving the pool in a quasi-steady state, with variability driven by the seasonal cycle, climate variation, disturbance, and internal dynamics of the plant-litter-soil system. In cases where the combination of climate and plant type are not suitable to sustained growth, the deficit in this pool increases until the available carbon is being allocated mostly to alleviate the deficit, and new growth approaches zero. The allocation flux to :math:`CS_{xs}` (:math:`CF_{GPP,xs}`, gC m\ :sup:`-2` s\ :sup:`-1`) is given as .. math:: :label: 19.4 @@ -81,56 +42,37 @@ m\ :sup:`-2` s\ :sup:`-1`) is given as CF_{GPP,xs} =\left\{\begin{array}{l} {CF_{GPP,xs,pot} \qquad \qquad \qquad {\rm for\; }CF_{GPP,xs,pot} \le CF_{GPP} -CF_{GPP,mr} } \\ {\max (CF_{GPP} -CF_{GPP,mr} ,0)\qquad {\rm for\; }CF_{GPP,xs,pot} >CF_{GPP} -CF_{GPP,mr} } \end{array}\right. -where :math:`\tau_{xs}` is the time constant (currently -set to 30 days) controlling the rate of replenishment of :math:`CS_{xs}`. +where :math:`\tau_{xs}` is the time constant (currently set to 30 days) controlling the rate of replenishment of :math:`CS_{xs}`. -Note that these two top-priority carbon allocation fluxes -(:math:`CF_{GPP,mr}` and :math:`CF_{GPP,xs}`) are not -stoichiometrically associated with any nitrogen fluxes. +Note that these two top-priority carbon allocation fluxes (:math:`CF_{GPP,mr}` and :math:`CF_{GPP,xs}`) are not stoichiometrically associated with any nitrogen fluxes. Carbon and Nitrogen Stoichiometry of New Growth ---------------------------------------------------- -After accounting for the carbon cost of maintenance respiration, the -remaining carbon flux from photosynthesis which can be allocated to new -growth (:math:`CF_{avail}`, gC m\ :sup:`-2` s\ :sup:`-1`) is +After accounting for the carbon cost of maintenance respiration, the remaining carbon flux from photosynthesis which can be allocated to new growth (:math:`CF_{avail}`, gC m\ :sup:`-2` s\ :sup:`-1`) is .. math:: :label: 19.6 CF_{avail\_ alloc} =CF_{GPP} -CF_{GPP,mr} -CF_{GPP,xs} . -Potential allocation to new growth is calculated for all of the plant -carbon and nitrogen state variables based on specified C:N ratios for -each tissue type and allometric parameters that relate allocation -between various tissue types. The allometric parameters are defined as -follows: +Potential allocation to new growth is calculated for all of the plant carbon and nitrogen state variables based on specified C:N ratios for each tissue type and allometric parameters that relate allocation between various tissue types. The allometric parameters are defined as follows: .. math:: :label: 19.7 \begin{array}{l} {a_{1} ={\rm \; ratio\; of\; new\; fine\; root\; :\; new\; leaf\; carbon\; allocation}} \\ {a_{2} ={\rm \; ratio\; of\; new\; coarse\; root\; :\; new\; stem\; carbon\; allocation}} \\ {a_{3} ={\rm \; ratio\; of\; new\; stem\; :\; new\; leaf\; carbon\; allocation}} \\ {a_{4} ={\rm \; ratio\; new\; live\; wood\; :\; new\; total\; wood\; allocation}} \\ {g_{1} ={\rm ratio\; of\; growth\; respiration\; carbon\; :\; new\; growth\; carbon.\; }} \end{array} -Parameters :math:`a_{1}`, :math:`a_{2}`, and :math:`a_{4}` are defined as constants for a given PFT (Table -13.1), while :math:`g_{l }` = 0.3 (unitless) is prescribed as a -constant for all PFTs, based on construction costs for a range of woody -and non-woody tissues (Larcher, 1995). +Parameters :math:`a_{1}`, :math:`a_{2}`, and :math:`a_{4}` are defined as constants for a given PFT (Table 13.1), while :math:`g_{l }` = 0.3 (unitless) is prescribed as a constant for all PFTs, based on construction costs for a range of woody and non-woody tissues (Larcher, 1995). -The model includes a dynamic allocation scheme for woody vegetation -(parameter :math:`a_{3}` = -1, :numref:`Table Allocation and CN ratio parameters`), in which case the -ratio for carbon allocation between new stem and new leaf increases with -increasing net primary production (NPP), as +The model includes a dynamic allocation scheme for woody vegetation (parameter :math:`a_{3}` = -1, :numref:`Table Allocation and CN ratio parameters`), in which case the ratio for carbon allocation between new stem and new leaf increases with increasing net primary production (NPP), as .. math:: :label: 19.8 a_{3} =\frac{2.7}{1+e^{-0.004NPP_{ann} -300} } -0.4 -where :math:`NPP_{ann}` is the annual sum of NPP from the previous -year. This mechanism has the effect of increasing woody allocation in -favorable growth environments (Allen et al., 2005; Vanninen and Makela, -2005) and during the phase of stand growth prior to canopy closure -(Axelsson and Axelsson, 1986). +where :math:`NPP_{ann}` is the annual sum of NPP from the previous year. This mechanism has the effect of increasing woody allocation in favorable growth environments (Allen et al., 2005; Vanninen and Makela, 2005) and during the phase of stand growth prior to canopy closure (Axelsson and Axelsson, 1986). .. _Table Allocation and CN ratio parameters: @@ -194,24 +136,17 @@ favorable growth environments (Allen et al., 2005; Vanninen and Makela, +----------------------------------+-----------------------+-----------------------+-----------------------+-----------------------+---------------------------+-------------------------+-------------------------+-------------------------+ | Switchgrass I | 2 | 0 | 0 | 1 | 25 | 42 | 50 | 500 | +----------------------------------+-----------------------+-----------------------+-----------------------+-----------------------+---------------------------+-------------------------+-------------------------+-------------------------+ - -Carbon to nitrogen ratios are defined for different tissue types as -follows: + +Carbon to nitrogen ratios are defined for different tissue types as follows: .. math:: :label: 19.9 \begin{array}{l} {CN_{leaf} =\_ {\rm \; C:N\; for\; leaf}} \\ {CN_{fr} =\_ {\rm \; C:N\; for\; fine\; root}} \\ {CN_{lw} =\_ {\rm \; C:N\; for\; live\; wood\; (in\; stem\; and\; coarse\; root)}} \\ {CN_{dw} =\_ {\rm \; C:N\; for\; dead\; wood\; (in\; stem\; and\; coarse\; root)}} \end{array} -where all C:N parameters are defined as constants for a given PFT -(:numref:`Table Allocation and CN ratio parameters`). +where all C:N parameters are defined as constants for a given PFT (:numref:`Table Allocation and CN ratio parameters`). -Given values for the parameters in and , total carbon and nitrogen -allocation to new growth ( :math:`CF_{alloc}`, gC -m\ :sup:`-2` s\ :sup:`-1`, and :math:`NF_{alloc}`, gN -m\ :sup:`-2` s\ :sup:`-1`, respectively) can be expressed as -functions of new leaf carbon allocation (:math:`CF_{GPP,leaf}`, gC -m\ :sup:`-2` s\ :sup:`-1`): +Given values for the parameters in and, total carbon and nitrogen allocation to new growth ( :math:`CF_{alloc}`, gC m\ :sup:`-2` s\ :sup:`-1`, and :math:`NF_{alloc}`, gN m\ :sup:`-2` s\ :sup:`-1`, respectively) can be expressed as functions of new leaf carbon allocation (:math:`CF_{GPP,leaf}`, gC m\ :sup:`-2` s\ :sup:`-1`): .. math:: :label: 19.10 @@ -230,30 +165,19 @@ where N_{allom} =\left\{\begin{array}{l} {\frac{1}{CN_{leaf} } +\frac{a_{1} }{CN_{fr} } +\frac{a_{3} a_{4} \left(1+a_{2} \right)}{CN_{lw} } +} \\ {\qquad \frac{a_{3} \left(1-a_{4} \right)\left(1+a_{2} \right)}{CN_{dw} } \qquad {\rm for\; woody\; PFT}} \\ {\frac{1}{CN_{leaf} } +\frac{a_{1} }{CN_{fr} } \qquad \qquad \qquad {\rm for\; non-woody\; PFT.}} \end{array}\right. -Since the C:N stoichiometry for new growth allocation is defined, from -Eq. , as :math:`C_{allom}`/ :math:`N_{allom}`, the total carbon available for new growth allocation -(:math:`CF_{avail\_alloc}`) can be used to calculate the total -plant nitrogen demand for new growth ( :math:`NF_{plant\_demand}`, -gN m\ :sup:`-2` s\ :sup:`-1`) as: +Since the C:N stoichiometry for new growth allocation is defined, from Eq., as :math:`C_{allom}`/ :math:`N_{allom}`, the total carbon available for new growth allocation (:math:`CF_{avail\_alloc}`) can be used to calculate the total plant nitrogen demand for new growth ( :math:`NF_{plant\_demand}`, gN m\ :sup:`-2` s\ :sup:`-1`) as: .. math:: :label: 19.13 NF_{plant\_ demand} =CF_{avail\_ alloc} \frac{N_{allom} }{C_{allom} } . +.. _Carbon Allocation to New Growth: + Carbon Allocation to New Growth ----------------------------------------- -There are two carbon pools associated with each plant tissue – one which -represents the currently displayed tissue, and another which represents -carbon stored for display in a subsequent growth period. The nitrogen -pools follow this same organization. The model keeps track of stored -carbon according to which tissue type it will eventually be displayed -as, and the separation between display in the current timestep and -storage for later display depends on the parameter :math:`f_{cur}` -(values 0 to 1). Given :math:`CF_{alloc,leaf}` and :math:`f_{cur}`, the allocation fluxes of carbon to display and -storage pools (where storage is indicated with *\_stor*) for the various -tissue types are given as: +There are two carbon pools associated with each plant tissue – one which represents the currently displayed tissue, and another which represents carbon stored for display in a subsequent growth period. The nitrogen pools follow this same organization. The model keeps track of stored carbon according to which tissue type it will eventually be displayed as, and the separation between display in the current timestep and storage for later display depends on the parameter :math:`f_{cur}` (values 0 to 1). Given :math:`CF_{alloc,leaf}` and :math:`f_{cur}`, the allocation fluxes of carbon to display and storage pools (where storage is indicated with *\_stor*) for the various tissue types are given as: .. math:: :label: 19.14 @@ -315,12 +239,10 @@ tissue types are given as: CF_{alloc,deadcroot\_ stor} \_ =CF_{alloc,leaf\_ tot} a_{2} a_{3} \left(1-a_{4} \right)\left(1-f_{cur} \right). - - Nitrogen allocation ----------------------------------------- -The total flux of nitrogen to be allocated is given by the FUN model (Chapter :numref:`rst_FUN`). This gives a total N to be allocated within a given timestep, :math:`N_{supply}`. The total N allocated for a given tissue :math:`i` is the minimum between the supply and the demand: +The total flux of nitrogen to be allocated is given by the FUN model (Chapter :numref:`rst_FUN`). This gives a total N to be allocated within a given timestep, :math:`N_{supply}`. The total N allocated for a given tissue :math:`i` is the minimum between the supply and the demand: .. math:: :label: 19.26 @@ -389,14 +311,14 @@ The demand for each tissue, calculated for the tissue to remain on stoichiometry NF_{demand,deadcroot\_ stor} \_ =\frac{CF_{alloc,leaf} a_{2} a_{3} \left(1-a_{4} \right)}{CN_{dw} } \left(1-f_{cur} \right). -After each pool's demand is calculated, the total plant N demand is then the sum of each individual pool :math: `i` corresponding to each tissue: +After each pool's demand is calculated, the total plant N demand is then the sum of each individual pool :math:`i` corresponding to each tissue: .. math:: :label: 19.39 NF_{demand,tot} = \sum _{i=tissues} NF_{demand,i} -and the total supply for each tissue :math: `i` is the product of the fractional demand and the total available N, calculated as the term :math: `N_{uptake}` equal to the sum of the eight N uptake streams described in the FUN model (Chapter :numref:`rst_FUN`). +and the total supply for each tissue :math:`i` is the product of the fractional demand and the total available N, calculated as the term :math:`N_{uptake}` equal to the sum of the eight N uptake streams described in the FUN model (Chapter :numref:`rst_FUN`). .. math:: :label: 19.40 diff --git a/doc/source/tech_note/CN_Pools/CLM50_Tech_Note_CN_Pools.rst b/doc/source/tech_note/CN_Pools/CLM50_Tech_Note_CN_Pools.rst index 77bd7af415..ebff41577a 100644 --- a/doc/source/tech_note/CN_Pools/CLM50_Tech_Note_CN_Pools.rst +++ b/doc/source/tech_note/CN_Pools/CLM50_Tech_Note_CN_Pools.rst @@ -6,32 +6,9 @@ CN Pools Introduction ----------------- -CLM includes a prognostic treatment of the terrestrial carbon and -nitrogen cycles including natural vegetation, crops, and soil biogeochemistry. The model is -fully prognostic with respect to all carbon and nitrogen state variables -in the vegetation, litter, and soil organic matter. The seasonal timing -of new vegetation growth and litterfall is also prognostic, responding -to soil and air temperature, soil water availability, daylength, and -crop management practices in -varying degrees depending on a specified phenology type or management for each PFT -(Chapter -:numref:`rst_Vegetation Phenology and Turnover`). The -prognostic LAI, SAI, -tissue stoichiometry, and vegetation heights are -utilized by the biophysical model that couples carbon, water, and -energy cycles. +CLM includes a prognostic treatment of the terrestrial carbon and nitrogen cycles including natural vegetation, crops, and soil biogeochemistry. The model is fully prognostic with respect to all carbon and nitrogen state variables in the vegetation, litter, and soil organic matter. The seasonal timing of new vegetation growth and litterfall is also prognostic, responding to soil and air temperature, soil water availability, daylength, and crop management practices in varying degrees depending on a specified phenology type or management for each PFT (Chapter :numref:`rst_Vegetation Phenology and Turnover`). The prognostic LAI, SAI, tissue stoichiometry, and vegetation heights are utilized by the biophysical model that couples carbon, water, and energy cycles. -Separate state variables for C and N are tracked for leaf, live stem, -dead stem, live coarse root, dead coarse root, fine root, and grain pools -(:numref:`Figure Vegetation fluxes and pools`). Each of these pools has two corresponding -storage pools representing, respectively, short-term and long-term -storage of non-structural carbohydrates and labile nitrogen. There are -two additional carbon pools, one for the storage of growth respiration -reserves, and another used to meet excess demand for maintenance -respiration during periods with low photosynthesis. One additional -nitrogen pool tracks retranslocated nitrogen, mobilized from leaf tissue -prior to abscission and litterfall. Altogether there are 23 state -variables for vegetation carbon, and 22 for vegetation nitrogen. +Separate state variables for C and N are tracked for leaf, live stem, dead stem, live coarse root, dead coarse root, fine root, and grain pools (:numref:`Figure Vegetation fluxes and pools`). Each of these pools has two corresponding storage pools representing, respectively, short-term and long-term storage of non-structural carbohydrates and labile nitrogen. There are two additional carbon pools, one for the storage of growth respiration reserves, and another used to meet excess demand for maintenance respiration during periods with low photosynthesis. One additional nitrogen pool tracks retranslocated nitrogen, mobilized from leaf tissue prior to abscission and litterfall. Altogether there are 23 state variables for vegetation carbon, and 22 for vegetation nitrogen. .. _Figure Vegetation fluxes and pools: @@ -41,33 +18,16 @@ variables for vegetation carbon, and 22 for vegetation nitrogen. Vegetation fluxes and pools for carbon cycle in CLM5. -In addition to the vegetation pools, CLM includes a series of -decomposing carbon and nitrogen pools as vegetation successively -breaks down to CWD, and/or litter, and subsequently to soil organic -matter. Discussion of the decomposition model, alternate -specifications of decomposition rates, and methods to rapidly -equilibrate the decomposition model, is in Chapter -:numref:`rst_Decomposition`. +In addition to the vegetation pools, CLM includes a series of decomposing carbon and nitrogen pools as vegetation successively breaks down to CWD, and/or litter, and subsequently to soil organic matter. Discussion of the decomposition model, alternate specifications of decomposition rates, and methods to rapidly equilibrate the decomposition model, is in Chapter :numref:`rst_Decomposition`. Tissue Stoichiometry ----------------------- -As of CLM5, vegetation tissues have a flexible stoichiometry, as -described in :ref:`Ghimire et al. (2016) `. Each -tissue has a target C\:N ratio, with the target leaf C\:N varying by plant functional type -(see :numref:`Table Plant functional type (PFT) target CN parameters`), and nitrogen is allocated at each -timestep in order to allow the plant to best match the target -stoichiometry. Nitrogen downregulation of productivity acts by -increasing the C\:N ratio of leaves when insufficient nitrogen is -available to meet stoichiometric demands of leaf growth, thereby -reducing the N available for photosynthesis and reducing the :math:`V_{\text{c,max25}}` and -:math:`J_{\text{max25}}` terms, as described in Chapter -:numref:`rst_Photosynthetic Capacity`. Details of the flexible tissue -stoichiometry are described in Chapter :numref:`rst_CN Allocation`. +As of CLM5, vegetation tissues have a flexible stoichiometry, as described in :ref:`Ghimire et al. (2016) `. Each tissue has a target C\:N ratio, with the target leaf C\:N varying by plant functional type (see :numref:`Table Plant functional type (PFT) target CN parameters`), and nitrogen is allocated at each timestep in order to allow the plant to best match the target stoichiometry. Nitrogen downregulation of productivity acts by increasing the C\:N ratio of leaves when insufficient nitrogen is available to meet stoichiometric demands of leaf growth, thereby reducing the N available for photosynthesis and reducing the :math:`V_{\text{c,max25}}` and :math:`J_{\text{max25}}` terms, as described in Chapter :numref:`rst_Photosynthetic Capacity`. Details of the flexible tissue stoichiometry are described in Chapter :numref:`rst_CN Allocation`. .. _Table Plant functional type (PFT) target CN parameters: -.. table:: Plant functional type (PFT) target C:N parameters. +.. table:: Plant functional type (PFT) target C:N parameters. +----------------------------------+-------------------+ | PFT | target leaf C:N | diff --git a/doc/source/tech_note/Crop_Irrigation/CLM50_Tech_Note_Crop_Irrigation.rst b/doc/source/tech_note/Crop_Irrigation/CLM50_Tech_Note_Crop_Irrigation.rst index a03142e92e..42238c4273 100755 --- a/doc/source/tech_note/Crop_Irrigation/CLM50_Tech_Note_Crop_Irrigation.rst +++ b/doc/source/tech_note/Crop_Irrigation/CLM50_Tech_Note_Crop_Irrigation.rst @@ -8,12 +8,9 @@ Crops and Irrigation Summary of CLM5.0 updates relative to the CLM4.5 ------------------------------------------------ -We describe here the complete crop and irrigation parameterizations that -appear in CLM5.0. Corresponding information for CLM4.5 appeared in the -CLM4.5 Technical Note (:ref:`Oleson et al. 2013 `). +We describe here the complete crop and irrigation parameterizations that appear in CLM5.0. Corresponding information for CLM4.5 appeared in the CLM4.5 Technical Note (:ref:`Oleson et al. 2013 `). -CLM5.0 includes the following new updates to the CROP option, where CROP -refers to the interactive crop management model and is included as an option with the BGC configuration: +CLM5.0 includes the following new updates to the CROP option, where CROP refers to the interactive crop management model and is included as an option with the BGC configuration: - New crop functional types @@ -35,18 +32,15 @@ refers to the interactive crop management model and is included as an option wit - C for annual crop seeding comes from the grain C pool -- Initial seed C for planting is increased from 1 to 3 g C/m^2 - -These updates appear in detail in the sections below. Many also appear in -:ref:`Levis et al. (2016) `. +- Initial seed C for planting is increased from 1 to 3 g C/m^2 +These updates appear in detail in the sections below. Many also appear in :ref:`Levis et al. (2016) `. Available new features since the CLM5 release ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Addition of bioenergy crops - - - +- Ability to customize crop calendars (sowing windows/dates, maturity requirements) using stream files +- Cropland soil tillage .. _The crop model: @@ -56,394 +50,398 @@ The crop model: cash and bioenergy crops Introduction ^^^^^^^^^^^^ -Groups developing Earth System Models generally account for the human -footprint on the landscape in simulations of historical and future -climates. Traditionally we have represented this footprint with natural -vegetation types and particularly grasses because they resemble many -common crops. Most modeling efforts have not incorporated more explicit -representations of land management such as crop type, planting, -harvesting, tillage, fertilization, and irrigation, because global scale -datasets of these factors have lagged behind vegetation mapping. As this -begins to change, we increasingly find models that will simulate the -biogeophysical and biogeochemical effects not only of natural but also -human-managed land cover. - -AgroIBIS is a state-of-the-art land surface model with options to -simulate dynamic vegetation (:ref:`Kucharik et al. 2000 `) and interactive -crop management (:ref:`Kucharik and Brye 2003 `). The interactive crop -management parameterizations from AgroIBIS (March 2003 version) were -coupled as a proof-of-concept to the Community Land Model version 3 -[CLM3.0, :ref:`Oleson et al. (2004) ` ] (not published), then coupled to the -CLM3.5 (:ref:`Levis et al. 2009 `) and later released to the community with -CLM4CN (:ref:`Levis et al. 2012 `), and CLM4.5BGC. Additional updates after the -release of CLM4.5 were available by request (:ref:`Levis et al. 2016 `), -and those are now incorporated into CLM5. - -With interactive crop management and, therefore, a more accurate -representation of agricultural landscapes, we hope to improve the CLM’s -simulated biogeophysics and biogeochemistry. These advances may improve -fully coupled simulations with the Community Earth System Model (CESM), -while helping human societies answer questions about changing food, -energy, and water resources in response to climate, environmental, land -use, and land management change (e.g., :ref:`Kucharik and Brye 2003 `; :ref:`Lobell et al. 2006 `). -As implemented here, the crop model uses the same physiology as the -natural vegetation, though uses different crop-specific parameter values, -phenology, and allocation, as well as fertilizer and irrigation management. +Groups developing Earth System Models generally account for the human footprint on the landscape in simulations of historical and future climates. Traditionally we have represented this footprint with natural vegetation types and particularly grasses because they resemble many common crops. Most modeling efforts have not incorporated more explicit representations of land management such as crop type, planting, harvesting, tillage, fertilization, and irrigation, because global scale datasets of these factors have lagged behind vegetation mapping. As this begins to change, we increasingly find models that will simulate the biogeophysical and biogeochemical effects not only of natural but also human-managed land cover. + +AgroIBIS is a state-of-the-art land surface model with options to simulate dynamic vegetation (:ref:`Kucharik et al. 2000 `) and interactive crop management (:ref:`Kucharik and Brye 2003 `). The interactive crop management parameterizations from AgroIBIS (March 2003 version) were coupled as a proof-of-concept to the Community Land Model version 3 [CLM3.0, :ref:`Oleson et al. (2004) ` ] (not published), then coupled to the CLM3.5 (:ref:`Levis et al. 2009 `) and later released to the community with CLM4CN (:ref:`Levis et al. 2012 `), and CLM4.5BGC. Additional updates after the release of CLM4.5 were available by request (:ref:`Levis et al. 2016 `), and those are now incorporated into CLM5. + +With interactive crop management and, therefore, a more accurate representation of agricultural landscapes, we hope to improve the CLM's simulated biogeophysics and biogeochemistry. These advances may improve fully coupled simulations with the Community Earth System Model (CESM), while helping human societies answer questions about changing food, energy, and water resources in response to climate, environmental, land use, and land management change (e.g., :ref:`Kucharik and Brye 2003 `; :ref:`Lobell et al. 2006 `). As implemented here, the crop model uses the same physiology as the natural vegetation but with uses different crop-specific parameter values, phenology, and allocation, as well as fertilizer and irrigation management. .. _Crop plant functional types: Crop plant functional types ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To allow crops to coexist with natural vegetation in a grid cell, the -vegetated land unit is separated into a naturally vegetated land unit and -a managed crop land unit. Unlike the plant functional types (pfts) in the -naturally vegetated land unit, the managed crop pfts in the managed crop -land unit do not share soil columns and thus permit for differences in the -land management between crops. Each crop type has a rainfed and an irrigated -pft that are on independent soil columns. Crop grid cell coverage is assigned from -satellite data (similar to all natural pfts), and the managed crop type -proportions within the crop area is based on the dataset created by -:ref:`Portmann et al. (2010)` for present day. New in CLM5, crop area is -extrapolated through time using the dataset provided by Land Use Model -Intercomparison Project (LUMIP), which is part of CMIP6 Land use timeseries -(:ref:`Lawrence et al. 2016 `). For more details about how -crop distributions are determined, see Chapter :numref:`rst_Transient Landcover Change`. - -CLM5 includes ten actively managed crop types -(temperate soybean, tropical soybean, temperate corn, tropical -corn, spring wheat, cotton, rice, sugarcane, miscanthus, and switchgrass) that are chosen -based on the availability of corresponding algorithms in AgroIBIS and as -developed by :ref:`Badger and Dirmeyer (2015)` and -described by :ref:`Levis et al. (2016)`, or from available observations -as described by :ref:`Cheng et al. (2019)`. -The representations of sugarcane, rice, cotton, tropical corn, and tropical soy are new in CLM5. -Miscanthus and switchgrass are added after the CLM5 release. -Sugarcane and tropical corn are both C4 plants and are therefore represented -using the temperate corn functional form. Tropical soybean uses the temperate -soybean functional form, while rice and cotton use the wheat functional form. -In tropical regions, parameter values were developed for the Amazon Basin, and planting -date window is shifted by six months relative to the Northern Hemisphere. -Plantation areas of bioenergy crops are projected to expand throughout the 21st century as a major energy source to -replace fossil fuels and mitigate climate change. Miscanthus and switchgrass are perennial bioenergy crops and -have quite different physiological traits and land management practices than annual crops, -such as longer growing seasons, higher productivity, and lower demands for nutrients and water. -About 70% of biofuel aboveground biomass (leaf & livestem) is removed at harvest. Parameter values were developed by using -observation data collected at the University of Illinois Energy Farm -located in Central Midwestern United States (:ref:`Cheng et al., 2019`). - - -In addition, CLM’s default list of plant functional types (pfts) includes an -irrigated and unirrigated unmanaged C3 crop (:numref:`Table Crop plant functional types`) treated as a second C3 grass. -The unmanaged C3 crop is only used when the crop model is not active and -has grid cell coverage assigned from satellite data, and -the unmanaged C3 irrigated crop type is currently not used -since irrigation requires the crop model to be active. -The default list of pfts also includes twenty-one inactive crop pfts -that do not yet have associated parameters required for active management. -Each of the inactive crop types is simulated using the parameters of the -spatially closest associated crop type that is most similar to the functional type (e.g., C3 or C4), -which is required to maintain similar phenological parameters based on temperature thresholds. -Information detailing which parameters are used for each crop type is -included in :numref:`Table Crop plant functional types`. It should be noted that pft-level history output merges -all crop types into the actively managed crop type, so analysis -of crop-specific output will require use of the land surface dataset to -remap the yields of each actively and inactively managed crop type. Otherwise, the -actively managed crop type will include yields for that crop type and all inactively -managed crop types that are using the same parameter set. +To allow crops to coexist with natural vegetation in a grid cell, the vegetated land unit is separated into a naturally vegetated land unit and a managed crop land unit. Unlike the plant functional types (PFTs) in the naturally vegetated land unit, the managed crop PFTs in the managed crop land unit do not share soil columns and thus permit for differences in the land management between crops. Each crop type has a rainfed and an irrigated PFT that are on independent soil columns. Crop grid cell coverage is assigned from satellite data (similar to all natural PFTs), and the managed crop type proportions within the crop area is based on the dataset created by :ref:`Portmann et al. (2010)` for present day. New in CLM5, crop area is extrapolated through time using the dataset provided by Land Use Model Intercomparison Project (LUMIP), which is part of CMIP6 Land use timeseries (:ref:`Lawrence et al. 2016 `). For more details about how crop distributions are determined, see Chapter :numref:`rst_Transient Landcover Change`. + +CLM5 includes ten actively managed crop types (temperate soybean, tropical soybean, temperate corn, tropical corn, spring wheat, cotton, rice, sugarcane, miscanthus, and switchgrass) that are chosen based on the availability of corresponding algorithms in AgroIBIS and as developed by :ref:`Badger and Dirmeyer (2015)` and described by :ref:`Levis et al. (2016)`, or from available observations as described by :ref:`Cheng et al. (2019)`. The representations of sugarcane, rice, cotton, tropical corn, and tropical soy were new in CLM5; miscanthus and switchgrass were added after the CLM5 release. Sugarcane and tropical corn are both C4 plants and are therefore represented using the temperate corn functional form. Tropical soybean uses the temperate soybean functional form, while rice and cotton use the wheat functional form. In tropical regions, parameter values were developed for the Amazon Basin, and planting date window is shifted by six months relative to the Northern Hemisphere. Plantation areas of bioenergy crops are projected to expand throughout the 21st century as a major energy source to replace fossil fuels and mitigate climate change. Miscanthus and switchgrass are perennial bioenergy crops and have quite different physiological traits and land management practices than annual crops, such as longer growing seasons, higher productivity, and lower demands for nutrients and water. About 70% of biofuel aboveground biomass (leaf & livestem) is removed at harvest. Parameter values were developed by using observation data collected at the University of Illinois Energy Farm located in Central Midwestern United States (:ref:`Cheng et al., 2019`). + +In addition, CLM's default list of plant functional types (PFTs) includes an irrigated and unirrigated unmanaged C3 crop (:numref:`Table Crop plant functional types`) treated as a second C3 grass. The unmanaged C3 crop is only used when the crop model is not active and has grid cell coverage assigned from satellite data, and the unmanaged C3 irrigated crop type is currently not used since irrigation requires the crop model to be active. The default list of PFTs also includes twenty-one inactive crop PFTs that do not yet have associated parameters required for active management. Each of the inactive crop types is simulated using the parameters of the spatially closest associated crop type that is most similar to the functional type (e.g., C3 or C4), which is required to maintain similar phenological parameters based on temperature thresholds. Information detailing which parameters are used for each crop type is included in :numref:`Table Crop plant functional types`. It should be noted that PFT-level history output merges all crop types into the actively managed crop type, so analysis of crop-specific output will require use of the land surface dataset to remap the yields of each actively and inactively managed crop type. Otherwise, the actively managed crop type will include yields for that crop type and all inactively managed crop types that are using the same parameter set. .. _Table Crop plant functional types: -.. table:: Crop plant functional types (pfts) included in CLM5BGCCROP. +.. table:: Crop plant functional types (PFTs) included in CLM5BGCCROP. === =========================== ================ =========================== IVT Plant function types (PFTs) Management Class Crop Parameters Used === =========================== ================ =========================== - 15 c3 unmanaged rainfed crop none not applicable - 16 c3 unmanaged irrigated crop none not applicable - 17 rainfed temperate corn active rainfed temperate corn - 18 irrigated temperate corn active irrigated temperate corn - 19 rainfed spring wheat active rainfed spring wheat - 20 irrigated spring wheat active irrigated spring wheat - 21 rainfed winter wheat inactive rainfed spring wheat - 22 irrigated winter wheat inactive irrigated spring wheat - 23 rainfed temperate soybean active rainfed temperate soybean + 15 c3 unmanaged rainfed crop none not applicable + 16 c3 unmanaged irrigated crop none not applicable + 17 rainfed temperate corn active rainfed temperate corn + 18 irrigated temperate corn active irrigated temperate corn + 19 rainfed spring wheat active rainfed spring wheat + 20 irrigated spring wheat active irrigated spring wheat + 21 rainfed winter wheat inactive rainfed spring wheat + 22 irrigated winter wheat inactive irrigated spring wheat + 23 rainfed temperate soybean active rainfed temperate soybean 24 irrigated temperate soybean active irrigated temperate soybean - 25 rainfed barley inactive rainfed spring wheat - 26 irrigated barley inactive irrigated spring wheat - 27 rainfed winter barley inactive rainfed spring wheat - 28 irrigated winter barley inactive irrigated spring wheat - 29 rainfed rye inactive rainfed spring wheat - 30 irrigated rye inactive irrigated spring wheat - 31 rainfed winter rye inactive rainfed spring wheat - 32 irrigated winter rye inactive irrigated spring wheat - 33 rainfed cassava inactive rainfed rice - 34 irrigated cassava inactive irrigated rice - 35 rainfed citrus inactive rainfed spring wheat - 36 irrigated citrus inactive irrigated spring wheat - 37 rainfed cocoa inactive rainfed rice - 38 irrigated cocoa inactive irrigated rice - 39 rainfed coffee inactive rainfed rice - 40 irrigated coffee inactive irrigated rice - 41 rainfed cotton active rainfed cotton - 42 irrigated cotton active irrigated cotton - 43 rainfed datepalm inactive rainfed cotton - 44 irrigated datepalm inactive irrigated cotton - 45 rainfed foddergrass inactive rainfed spring wheat - 46 irrigated foddergrass inactive irrigated spring wheat - 47 rainfed grapes inactive rainfed spring wheat - 48 irrigated grapes inactive irrigated spring wheat - 49 rainfed groundnuts inactive rainfed rice - 50 irrigated groundnuts inactive irrigated rice - 51 rainfed millet inactive rainfed tropical corn - 52 irrigated millet inactive irrigated tropical corn - 53 rainfed oilpalm inactive rainfed rice - 54 irrigated oilpalm inactive irrigated rice - 55 rainfed potatoes inactive rainfed spring wheat - 56 irrigated potatoes inactive irrigated spring wheat - 57 rainfed pulses inactive rainfed spring wheat - 58 irrigated pulses inactive irrigated spring wheat - 59 rainfed rapeseed inactive rainfed spring wheat - 60 irrigated rapeseed inactive irrigated spring wheat - 61 rainfed rice active rainfed rice - 62 irrigated rice active irrigated rice - 63 rainfed sorghum inactive rainfed tropical corn - 64 irrigated sorghum inactive irrigated tropical corn - 65 rainfed sugarbeet inactive rainfed spring wheat - 66 irrigated sugarbeet inactive irrigated spring wheat - 67 rainfed sugarcane active rainfed sugarcane - 68 irrigated sugarcane active irrigated sugarcane - 69 rainfed sunflower inactive rainfed spring wheat - 70 irrigated sunflower inactive irrigated spring wheat - 71 rainfed miscanthus active rainfed miscanthus - 72 irrigated miscanthus active irrigated miscanthus - 73 rainfed switchgrass active rainfed switchgrass - 74 irrigated switchgrass active irrigated switchgrass - 75 rainfed tropical corn active rainfed tropical corn - 76 irrigated tropical corn active irrigated tropical corn - 77 rainfed tropical soybean active rainfed tropical soybean - 78 irrigated tropical soybean active irrigated tropical soybean + 25 rainfed barley inactive rainfed spring wheat + 26 irrigated barley inactive irrigated spring wheat + 27 rainfed winter barley inactive rainfed spring wheat + 28 irrigated winter barley inactive irrigated spring wheat + 29 rainfed rye inactive rainfed spring wheat + 30 irrigated rye inactive irrigated spring wheat + 31 rainfed winter rye inactive rainfed spring wheat + 32 irrigated winter rye inactive irrigated spring wheat + 33 rainfed cassava inactive rainfed rice + 34 irrigated cassava inactive irrigated rice + 35 rainfed citrus inactive rainfed spring wheat + 36 irrigated citrus inactive irrigated spring wheat + 37 rainfed cocoa inactive rainfed rice + 38 irrigated cocoa inactive irrigated rice + 39 rainfed coffee inactive rainfed rice + 40 irrigated coffee inactive irrigated rice + 41 rainfed cotton active rainfed cotton + 42 irrigated cotton active irrigated cotton + 43 rainfed datepalm inactive rainfed cotton + 44 irrigated datepalm inactive irrigated cotton + 45 rainfed foddergrass inactive rainfed spring wheat + 46 irrigated foddergrass inactive irrigated spring wheat + 47 rainfed grapes inactive rainfed spring wheat + 48 irrigated grapes inactive irrigated spring wheat + 49 rainfed groundnuts inactive rainfed rice + 50 irrigated groundnuts inactive irrigated rice + 51 rainfed millet inactive rainfed tropical corn + 52 irrigated millet inactive irrigated tropical corn + 53 rainfed oilpalm inactive rainfed rice + 54 irrigated oilpalm inactive irrigated rice + 55 rainfed potatoes inactive rainfed spring wheat + 56 irrigated potatoes inactive irrigated spring wheat + 57 rainfed pulses inactive rainfed spring wheat + 58 irrigated pulses inactive irrigated spring wheat + 59 rainfed rapeseed inactive rainfed spring wheat + 60 irrigated rapeseed inactive irrigated spring wheat + 61 rainfed rice active rainfed rice + 62 irrigated rice active irrigated rice + 63 rainfed sorghum inactive rainfed tropical corn + 64 irrigated sorghum inactive irrigated tropical corn + 65 rainfed sugarbeet inactive rainfed spring wheat + 66 irrigated sugarbeet inactive irrigated spring wheat + 67 rainfed sugarcane active rainfed sugarcane + 68 irrigated sugarcane active irrigated sugarcane + 69 rainfed sunflower inactive rainfed spring wheat + 70 irrigated sunflower inactive irrigated spring wheat + 71 rainfed miscanthus active rainfed miscanthus + 72 irrigated miscanthus active irrigated miscanthus + 73 rainfed switchgrass active rainfed switchgrass + 74 irrigated switchgrass active irrigated switchgrass + 75 rainfed tropical corn active rainfed tropical corn + 76 irrigated tropical corn active irrigated tropical corn + 77 rainfed tropical soybean active rainfed tropical soybean + 78 irrigated tropical soybean active irrigated tropical soybean === =========================== ================ =========================== - - .. _Phenology: Phenology ^^^^^^^^^ -CLM5-BGC includes evergreen, seasonally deciduous (responding to changes -in day length), and stress deciduous (responding to changes in -temperature and/or soil moisture) phenology algorithms (Chapter :numref:`rst_Vegetation Phenology and Turnover`). -CLM5-BGC-crop uses the AgroIBIS crop phenology algorithm, -consisting of three distinct phases. +CLM5-BGC includes evergreen, seasonally deciduous (responding to changes in day length), and stress deciduous (responding to changes in temperature and/or soil moisture) phenology algorithms (Chapter :numref:`rst_Vegetation Phenology and Turnover`). CLM5-BGC-crop uses the AgroIBIS crop phenology algorithm, consisting of three distinct phases. -Phase 1 starts at planting and ends with leaf emergence, phase 2 -continues from leaf emergence to the beginning of grain fill, and phase -3 starts from the beginning of grain fill and ends with physiological -maturity and harvest. +Phase 1 starts at planting and ends with leaf emergence, phase 2 continues from leaf emergence to the beginning of grain fill, and phase 3 starts from the beginning of grain fill and ends with physiological maturity and harvest. .. _Planting: Planting '''''''' -All crops must meet the following requirements between the minimum planting date and the maximum -planting date (for the northern hemisphere) in :numref:`Table Crop phenology parameters`: +All crops must meet the following requirements between the minimum planting date and the maximum planting date (for the northern hemisphere) in :numref:`Table Crop phenology parameters`: .. math:: :label: 25.1 - \begin{array}{c} - {T_{10d} >T_{p} } \\ - {T_{10d}^{\min } >T_{p}^{\min } } \\ - {GDD_{8} \ge GDD_{\min } } + \begin{array}{c} + {T_{10d} >T_{p} } \\ + {T_{10d}^{\min } >T_{p}^{\min } } \\ + {GDD_{8} \ge GDD_{\min } } \end{array} -where :math:`{T}_{10d}` is the 10-day running mean of :math:`{T}_{2m}`, (the simulated 2-m air -temperature during each model time step) and :math:`T_{10d}^{\min}` is -the 10-day running mean of :math:`T_{2m}^{\min }` (the daily minimum of -:math:`{T}_{2m}`). :math:`{T}_{p}` and :math:`T_{p}^{\min }` are crop-specific coldest planting temperatures -(:numref:`Table Crop phenology parameters`), :math:`{GDD}_{8}` is the 20-year running mean growing -degree-days (units are degree-days or :sup:`o` days) tracked -from April through September (NH) above 8\ :sup:`o` C with -maximum daily increments of 30\ :sup:`o` days (see equation :eq:`25.3`), and -:math:`{GDD}_{min }`\ is the minimum growing degree day requirement -(:numref:`Table Crop phenology parameters`). :math:`{GDD}_{8}` does not change as quickly as :math:`{T}_{10d}` and :math:`T_{10d}^{\min }`, so -it determines whether it is warm enough for the crop to be planted in a grid cell, while the -2-m air temperature variables determine the day when the crop may be planted if the :math:`{GDD}_{8}` threshold is met. -If the requirements in equation :eq:`25.1` are not met by the maximum planting date, -crops are still planted on the maximum planting date as long as :math:`{GDD}_{8} > 0`. In -the southern hemisphere (SH) the NH requirements apply 6 months later. - -At planting, each crop seed pool is assigned 3 gC m\ :sup:`-2` from its -grain product pool. The seed carbon is transferred to the leaves upon leaf emergence. An -equivalent amount of seed leaf N is assigned given the pft’s C to N -ratio for leaves (:math:`{CN}_{leaf}` in :numref:`Table Crop allocation parameters`; this differs from AgroIBIS, -which uses a seed leaf area index instead of seed C). The model updates the average growing degree-days necessary -for the crop to reach vegetative and physiological maturity, -:math:`{GDD}_{mat}`, according to the following AgroIBIS rules: +where :math:`{T}_{10d}` is the 10-day running mean of :math:`{T}_{2m}`, (the simulated 2-m air temperature during each model time step) and :math:`T_{10d}^{\min}` is the 10-day running mean of :math:`T_{2m}^{\min }` (the daily minimum of :math:`{T}_{2m}`). :math:`{T}_{p}` and :math:`T_{p}^{\min }` are crop-specific coldest planting temperatures (:numref:`Table Crop phenology parameters`), :math:`{GDD}_{8}` is the 20-year running mean growing degree-days (units are °C day) tracked from April through September (NH) above 8°C with maximum daily increments of 30 degree-days (see equation :eq:`25.3`), and :math:`{GDD}_{min }`\ is the minimum growing degree day requirement (:numref:`Table Crop phenology parameters`). :math:`{GDD}_{8}` does not change as quickly as :math:`{T}_{10d}` and :math:`T_{10d}^{\min }`, so it determines whether it is warm enough for the crop to be planted in a grid cell, while the 2-m air temperature variables determine the day when the crop may be planted if the :math:`{GDD}_{8}` threshold is met. If the requirements in equation :eq:`25.1` are not met by the maximum planting date, crops are still planted on the maximum planting date as long as :math:`{GDD}_{8} > 0`. In the southern hemisphere (SH) the NH requirements apply 6 months later. + +At planting, each crop seed pool is assigned 3 gC m\ :sup:`-2` from its grain product pool. The seed carbon is transferred to the leaves upon leaf emergence. An equivalent amount of seed leaf N is assigned given the PFT's C to N ratio for leaves (:math:`{CN}_{leaf}` in :numref:`Table Crop allocation parameters`; this differs from AgroIBIS, which uses a seed leaf area index instead of seed C). The model updates the average growing degree-days necessary for the crop to reach vegetative and physiological maturity, :math:`{GDD}_{mat}`, according to the following AgroIBIS rules: .. math:: :label: 25.2 - \begin{array}{lll} - GDD_{{\rm mat}}^{{\rm corn,sugarcane}} =0.85 GDD_{{\rm 8}} & {\rm \; \; \; and\; \; \; }& 950 `, :ref:`Crawford et al. 1982 `, :ref:`Simpson et al. 1983 -`, :ref:`Ta and Weiland 1992 `, :ref:`Barbottin et al. 2005 `, -:ref:`Gallais et al. 2006 `, :ref:`Gallais et al. 2007 `). Nitrogen allocation -for crops follows that of natural vegetation, is supplied in CLM by the -soil mineral nitrogen pool, and depends on C:N ratios for leaves, stems, -roots, and organs. Nitrogen demand during organ development is fulfilled -through retranslocation from leaves, stems, and roots. Nitrogen -retranslocation is initiated at the beginning of the grain fill stage -for all crops except soybean, for which retranslocation is after LAI decline. -Nitrogen stored in the leaf and stem is moved into a storage -retranslocation pool for all crops, and for wheat and rice, nitrogen in roots is also -released into the retranslocation storage pool. The quantity of nitrogen -mobilized depends on the C:N ratio of the plant tissue, and is -calculated as +Nitrogen retranslocation in crops occurs when nitrogen that was used for tissue growth of leaves, stems, and fine roots during the early growth season is remobilized and used for grain development (:ref:`Pollmer et al. 1979 `, :ref:`Crawford et al. 1982 `, :ref:`Simpson et al. 1983 `, :ref:`Ta and Weiland 1992 `, :ref:`Barbottin et al. 2005 `, :ref:`Gallais et al. 2006 `, :ref:`Gallais et al. 2007 `). Nitrogen allocation for crops follows that of natural vegetation, is supplied in CLM by the soil mineral nitrogen pool, and depends on C:N ratios for leaves, stems, roots, and organs. Nitrogen demand during organ development is fulfilled through retranslocation from leaves, stems, and roots. Nitrogen retranslocation is initiated at the beginning of the grain fill stage for all crops except soybean, for which retranslocation is after LAI decline. Nitrogen stored in the leaf and stem is moved into a storage retranslocation pool for all crops, and for wheat and rice, nitrogen in roots is also released into the retranslocation storage pool. The quantity of nitrogen mobilized depends on the C:N ratio of the plant tissue and is calculated as .. math:: :label: 25.6 @@ -532,77 +502,56 @@ calculated as frootn\_ to\_ retransn=N_{froot} -\frac{C_{froot} }{CN_{froot}^{f} } -where :math:`{C}_{leaf}`, :math:`{C}_{stem}`, and :math:`{C}_{froot}` is the carbon in the plant leaf, stem, and fine -root, respectively, :math:`{N}_{leaf}`, :math:`{N}_{stem}`, and :math:`{N}_{froot}` -is the nitrogen in the plant leaf, stem, and fine root, respectively, and :math:`CN^f_{leaf}`, -:math:`CN^f_{stem}`, and :math:`CN^f_{froot}` is the post-grain fill C:N -ratio of the leaf, stem, and fine root respectively (:numref:`Table Crop allocation parameters`). Since -C:N measurements are often taken from mature crops, pre-grain development C:N -ratios for leaves, stems, and roots in the model are optimized to allow maximum -nitrogen accumulation for later use during organ development, and post-grain -fill C:N ratios are assigned the same as crop residue. After -nitrogen is moved into the retranslocated pool, -the nitrogen in this pool is used to meet plant -nitrogen demand by assigning the available nitrogen from the -retranslocated pool equal to the plant nitrogen demand for each organ (:math:`{CN_{[organ]}^{f} }` in :numref:`Table Crop allocation parameters`). Once the -retranslocation pool is depleted, soil mineral nitrogen pool is used to -fulfill plant nitrogen demands. +where :math:`{C}_{leaf}`, :math:`{C}_{stem}`, and :math:`{C}_{froot}` is the carbon in the plant leaf, stem, and fine root, respectively, :math:`{N}_{leaf}`, :math:`{N}_{stem}`, and :math:`{N}_{froot}` is the nitrogen in the plant leaf, stem, and fine root, respectively, and :math:`CN^f_{leaf}`, :math:`CN^f_{stem}`, and :math:`CN^f_{froot}` is the post-grain fill C:N ratio of the leaf, stem, and fine root respectively (:numref:`Table Crop allocation parameters`). Since C:N measurements are often taken from mature crops, pre-grain development C:N ratios for leaves, stems, and roots in the model are optimized to allow maximum nitrogen accumulation for later use during organ development, and post-grain fill C:N ratios are assigned the same as crop residue. After nitrogen is moved into the retranslocated pool, the nitrogen in this pool is used to meet plant nitrogen demand by assigning the available nitrogen from the retranslocated pool equal to the plant nitrogen demand for each organ (:math:`{CN_{[organ]}^{f} }` in :numref:`Table Crop allocation parameters`). Once the retranslocation pool is depleted, soil mineral nitrogen pool is used to fulfill plant nitrogen demands. .. _Harvest to food and seed: Harvest ''''''' -Variables track the flow of grain C and N to food and of all other plant pools, including live stem C and N, to litter, and to biofuel feedstock. -A fraction (determined by the :math:`biofuel\_harvfrac`, defined in -:numref:`Table Plant functional type (PFT) parameters for harvested fraction of leaf/livestem for bioenergy production`) of leaf/livestem C and N from bioenergy crops is removed at harvest for biofuels -(Equations :eq:`25.9`, :eq:`25.10`, :eq:`25.12`, and :eq:`25.13`), -with the remaining portions going to the litter pools (Equations :eq:`20.14)`, :eq:`25.11`, and :eq:`25.14`). -Putting live stem C and N into the litter and biofuel pools is in contrast to the approach for unmanaged PFTs which -puts live stem C and N into dead stem pools first. -Biofuel crop leaf C and N pools are routed to the litter and biofuel pools, in contrast to that of unmanaged PFTs and non-biofuel crops, which put leaf C and N into litter pools only. -Root C and N pools are routed to the litter pools in the same manner as natural vegetation. - +Variables track the flow of grain C and N to food and of all other plant pools, including live stem C and N, to litter, and to biofuel feedstock. A fraction (determined by the :math:`biofuel\_harvfrac`, defined in :numref:`Table Plant functional type (PFT) parameters for harvested fraction of leaf/livestem for bioenergy production`) of leaf/livestem C and N from bioenergy crops is removed at harvest for biofuels (Equations :eq:`25.9`. :eq:`harv_c_to_removed_residue`, :eq:`25.12`, and :eq:`harv_n_to_removed_residue`), with the remaining portions going to the litter pools (Equations :eq:`20.14)`, :eq:`25.11`, and :eq:`25.14`). Putting live stem C and N into the litter and biofuel pools is in contrast to the approach for unmanaged PFTs which puts live stem C and N into dead stem pools first. Biofuel crop leaf and stem C and N pools are routed to the litter and biofuel pools, in contrast to that of unmanaged PFTs and non-biofuel crops, which under default settings put leaf C and N into litter pools only. All crops can have their leaf and stem pools routed to a "removed residue" pool by setting namelist parameter :math:`crop\_residue\_removal\_frac` to something greater than its default zero. Root C and N pools are routed to the litter pools in the same manner as natural vegetation. + +In the equations below, subscript :math:`p` refers to either the leaf or live stem biomass pool. + .. math:: :label: 25.9 - CF_{leaf,biofuel} = \left({CS_{leaf} \mathord{\left/ {\vphantom {CS_{leaf} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{p,biofuel} = \left({CS_{p} \mathord{\left/ {\vphantom {CS_{p} \Delta t}} \right.} \Delta t} \right) * biofuel\_harvfrac - + .. math:: - :label: 25.10 + :label: harv_c_to_removed_residue + + CF_{p,removed\_residue} = \left({CS_{p} \mathord{\left/ {\vphantom {CS_{p} \Delta t}} \right.} \Delta t} + \right) * (1 - biofuel\_harvfrac) * crop\_residue\_removal\_frac - CF_{livestem,biofuel} = \left({CS_{livestem} \mathord{\left/ {\vphantom {CS_{leaf} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} - \right) * biofuel\_harvfrac - .. math:: :label: 25.11 - CF_{livestem,litter} = \left({CS_{livestem} \mathord{\left/ {\vphantom {CS_{livestem} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} - \right) * \left( 1-biofuel\_harvfrac \right) +CF_{alloc,livestem} + CF_{p,litter} = \left({CS_{p} \mathord{\left/ {\vphantom {CS_{p} \Delta t}} \right.} \Delta t} + \right) * \left( 1-biofuel\_harvfrac \right) * \left( 1-crop\_residue\_removal\_frac \right) +CF_{p,alloc} with corresponding nitrogen fluxes: .. math:: :label: 25.12 - NF_{leaf,biofuel} = \left({NS_{leaf} \mathord{\left/ {\vphantom {NS_{leaf} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{p,biofuel} = \left({NS_{p} \mathord{\left/ {\vphantom {NS_{p} \Delta t}} \right.} \Delta t} \right) * biofuel\_harvfrac - + .. math:: - :label: 25.13 + :label: harv_n_to_removed_residue + + NF_{p,removed\_residue} = \left({NS_{p} \mathord{\left/ {\vphantom {NS_{p} \Delta t}} \right.} \Delta t} + \right) * \left( 1 - biofuel\_harvfrac \right) * crop\_residue\_removal\_frac - NF_{livestem,biofuel} = \left({NS_{livestem} \mathord{\left/ {\vphantom {NS_{livestem} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} - \right) * biofuel\_harvfrac - .. math:: :label: 25.14 - NF_{livestem,litter} = \left({NS_{livestem} \mathord{\left/ {\vphantom {NS_{livestem} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} - \right) * \left( 1-biofuel\_harvfrac \right) + NF_{p,litter} = \left({NS_{p} \mathord{\left/ {\vphantom {NS_{p} \Delta t}} \right.} \Delta t} + \right) * \left( 1-biofuel\_harvfrac \right) * \left( 1-crop\_residue\_removal\_frac \right) -where CF is the carbon flux, CS is stored carbon, NF is the nitrogen flux, -NS is stored nitrogen, and :math:`biofuel\_harvfrac` is the harvested fraction of leaf/livestem for biofuel feedstocks. +where CF is the carbon flux, CS is stored carbon, NF is the nitrogen flux, NS is stored nitrogen, and :math:`biofuel\_harvfrac` is the harvested fraction of leaf/livestem for biofuel feedstocks. .. _Table Plant functional type (PFT) parameters for harvested fraction of leaf/livestem for bioenergy production: @@ -660,54 +609,42 @@ NS is stored nitrogen, and :math:`biofuel\_harvfrac` is the harvested fraction o | Switchgrass | 0.70 | +----------------------------------+----------------------------+ -Whereas food C and N was formerly transferred to the litter pool, CLM5 routes food C and N -to a grain product pool where the C and N decay to the atmosphere over one year, -similar in structure to the wood product pools. -The biofuel C and N is also routed to the grain product pool and decays to the atmosphere over one year. -Additionally, CLM5 accounts for the C and N required for crop seeding by removing the seed C and N from the grain -product pool during harvest. The crop seed pool is then used to seed crops in the subsequent year. -Calcuating the crop yields (Equation :eq:`25.15`) requires that you sum the GRAINC_TO_FOOD variable -for each year, and must account for the proportion of C in the dry crop weight. -Here, we assume that grain C is 45% of the total dry weight. Additionally, harvest is not typically 100% efficient, so -analysis needs to assume that harvest efficiency is less. We assume a harvest -efficiency of 85%. +Whereas food C and N was formerly transferred to the litter pool, CLM5 routes food C and N to a grain product pool where the C and N decay to the atmosphere over one year, similar in structure to the wood product pools. Biofuel and removed-residue C and N is also routed to the grain product pool and decays to the atmosphere over one year. Additionally, CLM5 accounts for the C and N required for crop seeding by removing the seed C and N from the grain product pool during harvest. The crop seed pool is then used to seed crops in the subsequent year. + +Annual food crop yields (g dry matter m\ :sup:`-2`) can be calculated by saving the GRAINC_TO_FOOD_ANN variable once per year, then postprocessing with Equation :eq:`25.15`. This calculation assumes that grain C is 45% of the total dry weight. Additionally, harvest is not typically 100% efficient, so analysis needs to assume that harvest efficiency is less---we use 85%. .. math:: :label: 25.15 - Grain\ yield(g.m^{-2})=\frac{\sum(GRAINC\_ TO\_ FOOD)*0.85}{0.45} - + \text{Grain yield} = \frac{GRAINC\_TO\_FOOD\_ANN)*0.85}{0.45} .. _Table Crop allocation parameters: -.. table:: Crop allocation parameters for the active crop plant functional types (pfts) in CLM5BGCCROP. Numbers in the first row correspond to the list of pfts in :numref:`Table Crop plant functional types`. +.. table:: Crop allocation parameters for the active crop plant functional types (PFTs) in CLM5BGCCROP. Numbers in the first row correspond to the list of PFTs in :numref:`Table Crop plant functional types`. =========================================== ============== ============ ================== ====== ====== ========= ============= ================ ================ ================ - \ temperate corn spring wheat temperate soybean cotton rice sugarcane tropical corn tropical soybean miscanthus switchgrass + \ temperate corn spring wheat temperate soybean cotton rice sugarcane tropical corn tropical soybean miscanthus switchgrass =========================================== ============== ============ ================== ====== ====== ========= ============= ================ ================ ================ - IVT 17, 18 19, 20 23, 24 41, 42 61, 62 67, 68 75, 76 77, 78 71, 72 73, 74 - :math:`a_{leaf}^{i}` 0.6 0.9 0.85 0.85 0.75 0.6 0.6 0.85 0.9 0.7 - :math:`{L}_{max}` (m :sup:`2` m :sup:`-2`) 5 7 6 6 7 5 5 6 10 6.5 - :math:`a_{froot}^{i}` 0.1 0.05 0.2 0.2 0.1 0.1 0.1 0.2 0.11 0.14 - :math:`a_{froot}^{f}` 0.05 0 0.2 0.2 0 0.05 0.05 0.2 0.09 0.09 - :math:`a_{leaf}^{f}` 0 0 0 0 0 0 0 0 0 0 - :math:`a_{livestem}^{f}` 0 0.05 0.3 0.3 0.05 0 0 0.3 0 0 - :math:`d_{L}` 1.05 1.05 1.05 1.05 1.05 1.05 1.05 1.05 1.05 1.05 - :math:`d_{alloc}^{stem}` 2 1 5 5 1 2 2 5 2 2 - :math:`d_{alloc}^{leaf}` 5 3 2 2 3 5 5 2 5 5 - :math:`{CN}_{leaf}` 25 20 20 20 20 25 25 20 25 25 - :math:`{CN}_{stem}` 50 50 50 50 50 50 50 50 50 50 - :math:`{CN}_{froot}` 42 42 42 42 42 42 42 42 42 42 - :math:`CN^f_{leaf}` 65 65 65 65 65 65 65 65 65 65 - :math:`CN^f_{stem}` 120 100 130 130 100 120 120 130 120 120 - :math:`CN^f_{froot}` 0 40 0 0 40 0 0 0 0 0 - :math:`{CN}_{grain}` 50 50 50 50 50 50 50 50 50 50 + IVT 17, 18 19, 20 23, 24 41, 42 61, 62 67, 68 75, 76 77, 78 71, 72 73, 74 + :math:`a_{leaf}^{i}` 0.6 0.9 0.85 0.85 0.75 0.6 0.6 0.85 0.9 0.7 + :math:`{L}_{max}` (m :sup:`2` m :sup:`-2`) 5 7 6 6 7 5 5 6 10 6.5 + :math:`a_{froot}^{i}` 0.1 0.05 0.2 0.2 0.1 0.1 0.1 0.2 0.11 0.14 + :math:`a_{froot}^{f}` 0.05 0 0.2 0.2 0 0.05 0.05 0.2 0.09 0.09 + :math:`a_{leaf}^{f}` 0 0 0 0 0 0 0 0 0 0 + :math:`a_{livestem}^{f}` 0 0.05 0.3 0.3 0.05 0 0 0.3 0 0 + :math:`d_{L}` 1.05 1.05 1.05 1.05 1.05 1.05 1.05 1.05 1.05 1.05 + :math:`d_{alloc}^{stem}` 2 1 5 5 1 2 2 5 2 2 + :math:`d_{alloc}^{leaf}` 5 3 2 2 3 5 5 2 5 5 + :math:`{CN}_{leaf}` 25 20 20 20 20 25 25 20 25 25 + :math:`{CN}_{stem}` 50 50 50 50 50 50 50 50 50 50 + :math:`{CN}_{froot}` 42 42 42 42 42 42 42 42 42 42 + :math:`CN^f_{leaf}` 65 65 65 65 65 65 65 65 65 65 + :math:`CN^f_{stem}` 120 100 130 130 100 120 120 130 120 120 + :math:`CN^f_{froot}` 0 40 0 0 40 0 0 0 0 0 + :math:`{CN}_{grain}` 50 50 50 50 50 50 50 50 50 50 =========================================== ============== ============ ================== ====== ====== ========= ============= ================ ================ ================ -Notes: Crop growth phases and corresponding variables are described throughout -the text. :math:`{CN}_{leaf}`, :math:`{CN}_{stem}`, and :math:`{CN}_{froot}` are -the target C:N ratios used during the leaf emergence phase (phase 2). - +Notes: Crop growth phases and corresponding variables are described throughout the text. :math:`{CN}_{leaf}`, :math:`{CN}_{stem}`, and :math:`{CN}_{froot}` are the target C:N ratios used during the leaf emergence phase (phase 2). .. _Other Features: @@ -718,211 +655,123 @@ Other Features Physical Crop Characteristics ''''''''''''''''''''''''''''' -Leaf area index (*L*) is calculated as a function of specific leaf area -(SLA, :numref:`Table Crop phenology parameters`) and leaf C. -Stem area index (*S*) is equal to 0.1\ *L* for temperate and tropical corn, sugarcane, switchgrass, and miscanthus and 0.2\ *L* for -other crops, as in AgroIBIS. All live -C and N pools go to 0 after crop harvest, but the *S* is kept at 0.25 to -simulate a post-harvest “stubble” on the ground. - -Crop heights at the top and bottom of the canopy, :math:`{z}_{top}` -and :math:`{z}_{bot}` (m), come from the AgroIBIS formulation: +Leaf area index (*L*) is calculated as a function of specific leaf area (SLA, :numref:`Table Crop phenology parameters`) and leaf C. Stem area index (*S*) is equal to 0.1\ *L* for temperate and tropical corn, sugarcane, switchgrass, and miscanthus and 0.2\ *L* for other crops, as in AgroIBIS. All live C and N pools go to 0 after crop harvest, but the *S* is kept at 0.25 to simulate a post-harvest "stubble" on the ground. +Crop heights at the top and bottom of the canopy, :math:`{z}_{top}` and :math:`{z}_{bot}` (m), come from the AgroIBIS formulation: .. math:: :label: 25.16 - \begin{array}{l} - {z_{top} =z_{top}^{\max } \left(\frac{L}{L_{\max } -1} \right)^{2} \ge 0.05{\rm \; where\; }\frac{L}{L_{\max } -1} \le 1} \\ - {z_{bot} =0.02{\rm m}} + \begin{array}{l} + {z_{top} =z_{top}^{\max } \left(\frac{L}{L_{\max } -1} \right)^{2} \ge 0.05{\rm \; where\; }\frac{L}{L_{\max } -1} \le 1} \\ + {z_{bot} =0.02{\rm m}} \end{array} -where :math:`z_{top}^{\max }` is the maximum top-of-canopy height of the crop (:numref:`Table Crop phenology parameters`) -and :math:`L_{\max }` is the maximum leaf area index (:numref:`Table Crop allocation parameters`). +where :math:`z_{top}^{\max }` is the maximum top-of-canopy height of the crop (:numref:`Table Crop phenology parameters`) and :math:`L_{\max }` is the maximum leaf area index (:numref:`Table Crop allocation parameters`). .. _Interactive fertilization: Interactive Fertilization ''''''''''''''''''''''''' -CLM simulates fertilization by adding nitrogen directly to the soil mineral nitrogen pool to meet -crop nitrogen demands using both industrial fertilizer and manure application. CLM’s separate crop land unit ensures that -natural vegetation will not access the fertilizer applied to crops. -Fertilizer in CLM5BGCCROP is prescribed by crop functional types and varies spatially -for each year based on the LUMIP land use and land cover change -time series (LUH2 for historical and SSPs for future) (:ref:`Lawrence et al. 2016 `). -One of two fields is used to prescribe industrial fertilizer based on the type of simulation. -For non-transient simulations, annual fertilizer application in g N/m\ :sup:`2`/yr -is specified on the land surface data set by the field CONST_FERTNITRO_CFT. -In transient simulations, annual fertilizer application is specified on the land use time series -file by the field FERTNITRO_CFT, which is also in g N/m\ :sup:`2`/yr. -The values for both of these fields come from the LUMIP time series for each year. -In addition to the industrial fertilizer, background manure fertilizer is specified -on the parameter file by the field 'manunitro'. For perennial bioenergy crops, -little fertilizer (56kg/ha/yr) is applied to switchgrass, no fertilizer is applied to Miscanthus. -Note these rates are only based on local land management practices at the University of Illinois Energy Farm -located in Central Midwestern United States :ref:`(Cheng et al., 2019)` rather than the LUMIP timeseries. For the current CLM5BGCCROP, -manure N is applied at a rate of 0.002 kg N/m\ :sup:`2`/yr. Because previous versions -of CLM (e.g., CLM4) had rapid denitrification rates, fertilizer is applied slowly -to minimize N loss (primarily through denitrification) and maximize plant uptake. -The current implementation of CLM5 inherits this legacy, although denitrification rates -are slower in the current version of the model (:ref:`Koven et al. 2013 `). As such, -fertilizer application begins during the leaf emergence phase of crop -development (phase 2) and continues for 20 days, which helps reduce large losses -of nitrogen from leaching and denitrification during the early stage of -crop development. The 20-day period is chosen as an optimization to -limit fertilizer application to the emergence stage. A fertilizer -counter in seconds, *f*, is set as soon as the leaf emergence phase for crops -initiates: +CLM simulates fertilization by adding nitrogen directly to the soil mineral nitrogen pool to meet crop nitrogen demands using both industrial fertilizer and manure application. CLM's separate crop land unit ensures that natural vegetation will not access the fertilizer applied to crops. Fertilizer in CLM5BGCCROP is prescribed by crop functional types and varies spatially for each year based on the LUMIP land use and land cover change time series (LUH2 for historical and SSPs for future) (:ref:`Lawrence et al. 2016 `). One of two fields is used to prescribe industrial fertilizer based on the type of simulation. For non-transient simulations, annual fertilizer application in g N/m\ :sup:`2`/yr is specified on the land surface data set by the field CONST_FERTNITRO_CFT. In transient simulations, annual fertilizer application is specified on the land use time series file by the field FERTNITRO_CFT, which is also in g N/m\ :sup:`2`/yr. The values for both of these fields come from the LUMIP time series for each year. In addition to the industrial fertilizer, background manure fertilizer is specified on the parameter file by the field ``manunitro``. For perennial bioenergy crops, little fertilizer (56kg/ha/yr) is applied to switchgrass and no fertilizer is applied to Miscanthus. Note these rates are only based on local land management practices at the University of Illinois Energy Farm located in Central Midwestern United States :ref:`(Cheng et al., 2019)` rather than the LUMIP timeseries. For the current CLM5BGCCROP, manure N is applied at a rate of 0.002 kg N/m\ :sup:`2`/yr. Because previous versions of CLM (e.g., CLM4) had rapid denitrification rates, fertilizer is applied slowly to minimize N loss (primarily through denitrification) and maximize plant uptake. The current implementation of CLM5 inherits this legacy, although denitrification rates are slower in the current version of the model (:ref:`Koven et al. 2013 `). As such, fertilizer application begins during the leaf emergence phase of crop development (phase 2) and continues for 20 days, which helps reduce large losses of nitrogen from leaching and denitrification during the early stage of crop development. The 20-day period is chosen as an optimization to limit fertilizer application to the emergence stage. A fertilizer counter in seconds, *f*, is set as soon as the leaf emergence phase for crops initiates: .. math:: :label: 25.17 - f = n \times 86400 - -where *n* is set to 20 fertilizer application days and 86400 is the number of seconds per day. When the crop enters -phase 2 (leaf emergence) of its growth -cycle, fertilizer application begins by initializing fertilizer amount -to the total fertilizer at each column within the grid cell divided by the initialized *f*. -Fertilizer is applied and *f* is decremented each time step until a zero balance on -the counter is reached. + f = n \times 86400 +where *n* is set to 20 fertilizer application days and 86400 is the number of seconds per day. When the crop enters phase 2 (leaf emergence) of its growth cycle, fertilizer application begins by initializing fertilizer amount to the total fertilizer at each column within the grid cell divided by the initialized *f*. Fertilizer is applied and *f* is decremented each time step until a zero balance on the counter is reached. .. _Biological nitrogen fixation for soybeans: Biological nitrogen fixation for soybeans ''''''''''''''''''''''''''''''''''''''''' -Biological N fixation for soybeans is calculated by the fixation and uptake of -nitrogen module (Chapter :numref:`rst_FUN`) and is the same as N fixation in natural vegetation. Unlike natural -vegetation, where a fraction of each pft are N fixers, all soybeans -are treated as N fixers. +Biological N fixation for soybeans is calculated by the fixation and uptake of nitrogen module (Chapter :numref:`rst_FUN`) and is the same as N fixation in natural vegetation. Unlike natural vegetation, where a fraction of each PFT are N fixers, all soybeans are treated as N fixers. .. _Latitude vary base tempereature for growing degree days: Latitudinal variation in base growth tempereature ''''''''''''''''''''''''''''''''''''''''''''''''' -For most crops, :math:`GDD_{T_{{\rm 2m}} }` (growing degree days since planting) -is the same in all locations. However, -the for both rainfed and irrigated spring wheat and sugarcane, the calculation of -:math:`GDD_{T_{{\rm 2m}} }` allows for latitudinal variation: +For most crops, :math:`GDD_{T_{{\rm 2m}} }` (growing degree days since planting) is the same in all locations. However, for both rainfed and irrigated spring wheat and sugarcane, the calculation of :math:`GDD_{T_{{\rm 2m}} }` allows for latitudinal variation: .. math:: :label: 25.18 latitudinal\ variation\ in\ base\ T = \left\{ - \begin{array}{lr} + \begin{array}{lr} baset +12 - 0.4 \times latitude &\qquad 0 \le latitude \le 30 \\ - baset +12 + 0.4 \times latitude &\qquad -30 \le latitude \le 0 + baset +12 + 0.4 \times latitude &\qquad -30 \le latitude \le 0 \end{array} \right\} -where :math:`baset` is the *base temperature for GDD* (7\ :sup:`th` row) in :numref:`Table Crop phenology parameters`. -Such latitudinal variation in base growth temperature could increase the base temperature, slow down :math:`GDD_{T_{{\rm 2m}} }` -accumulation, and extend the growing season for regions within 30ºS to 30ºN for spring wheat -and sugarcane. +where :math:`baset` is the *base temperature for GDD* (7\ :sup:`th` row) in :numref:`Table Crop phenology parameters`. Such latitudinal variation in base temperature could slow :math:`GDD_{T_{{\rm 2m}} }` accumulation extend the growing season for regions within 30°S to 30°N for spring wheat and sugarcane. .. _Separate reproductive pool: Separate reproductive pool '''''''''''''''''''''''''' -One notable difference between natural vegetation and crops is the -presence of reproductive carbon and nitrogen pools. Accounting -for the reproductive pools helps determine whether crops are performing -reasonably through yield calculations. -The reproductive pool is maintained similarly to the leaf, stem, -and fine root pools, but allocation of carbon and nitrogen does not -begin until the grain fill stage of crop development. Equation :eq:`25.5` describes the -carbon and nitrogen allocation coefficients to the reproductive pool. -In CLM5BGCCROP, as allocation declines in stem, leaf, and root pools (see section :numref:`Grain fill to harvest`) -during the grain fill stage of growth, increasing amounts of carbon and -nitrogen are available for grain development. +One notable difference between natural vegetation and crops is the presence of reproductive carbon and nitrogen pools. Accounting for the reproductive pools helps determine whether crops are performing reasonably through yield calculations. The reproductive pool is maintained similarly to the leaf, stem, and fine root pools, but allocation of carbon and nitrogen does not begin until the grain fill stage of crop development. Equation :eq:`25.5` describes the carbon and nitrogen allocation coefficients to the reproductive pool. In CLM5BGCCROP, as allocation declines in stem, leaf, and root pools (see section :numref:`Grain fill to harvest`) during the grain fill stage of growth, increasing amounts of carbon and nitrogen are available for grain development. + +.. _Tillage: +Tillage +''''''' +Tillage is represented as an enhancement of the decomposition rate coefficient; see section :numref:`decomp_mgmt_modifiers`. .. _The irrigation model: The irrigation model -------------------- -The CLM includes the option to irrigate cropland areas that are equipped -for irrigation. The application of irrigation responds dynamically to -the soil moisture conditions simulated by the CLM. This irrigation -algorithm is based loosely on the implementation of -:ref:`Ozdogan et al. (2010) `. +The CLM includes the option to irrigate cropland areas that are equipped for irrigation. The application of irrigation responds dynamically to the soil moisture conditions simulated by the CLM. This irrigation algorithm is based loosely on the implementation of :ref:`Ozdogan et al. (2010) `. -When irrigation is enabled, the crop areas of each grid cell are divided -into irrigated and rainfed fractions according to a dataset of areas -equipped for irrigation (:ref:`Portmann et al. 2010 `). -Irrigated and rainfed crops are placed on separate soil columns, so that -irrigation is only applied to the soil beneath irrigated crops. +When irrigation is enabled, the crop areas of each grid cell are divided into irrigated and rainfed fractions according to a dataset of areas equipped for irrigation (:ref:`Portmann et al. 2010 `). Irrigated and rainfed crops are placed on separate soil columns, so that irrigation is only applied to the soil beneath irrigated crops. -In irrigated croplands, a check is made once per day to determine -whether irrigation is required on that day. This check is made in the -first time step after 6 AM local time. Irrigation is required if crop -leaf area :math:`>` 0, and the available soil water is below a specified -threshold. +In irrigated croplands, a check is made once per day to determine whether irrigation is required on that day. This check is made in the first time step after 6 AM local time. Irrigation is required if crop leaf area :math:`>` 0, and the available soil water is below a specified threshold. -The soil moisture deficit :math:`D_{irrig}` is +The soil moisture deficit :math:`D_{irrig}` is .. math:: :label: 25.61 D_{irrig} = \left\{ - \begin{array}{lr} - w_{thresh} - w_{avail} &\qquad w_{thresh} > w_{avail} \\ - 0 &\qquad w_{thresh} \le w_{avail} + \begin{array}{lr} + w_{target} - w_{avail} &\qquad w_{thresh} > w_{avail} \\ + 0 &\qquad w_{thresh} \le w_{avail} \end{array} \right\} -where :math:`w_{thresh}` is the irrigation moisture threshold (mm) and -:math:`w_{avail}` is the available moisture (mm). The moisture threshold -is +where :math:`w_{target}` is the irrigation target soil moisture (mm) .. math:: :label: 25.62 - w_{thresh} = f_{thresh} \left(w_{target} - w_{wilt}\right) + w_{wilt} + w_{target} = \sum_{j=1}^{N_{irr}} \theta_{target} \Delta z_{j} \ . -where :math:`w_{target}` is the irrigation target soil moisture (mm) +The irrigation moisture threshold (mm) is .. math:: :label: 25.63 - w_{target} = \sum_{j=1}^{N_{irr}} \theta_{target} \Delta z_{j} \ , + w_{thresh} = f_{thresh} \left(w_{target} - w_{wilt}\right) + w_{wilt} -:math:`w_{wilt}` is the wilting point soil moisture (mm) +where :math:`w_{wilt}` is the wilting point soil moisture (mm) .. math:: :label: 25.64 w_{wilt} = \sum_{j=1}^{N_{irr}} \theta_{wilt} \Delta z_{j} \ , -and :math:`f_{thresh}` is a tuning parameter. The available moisture in -the soil is +and :math:`f_{thresh}` is a tuning parameter. The available moisture in the soil (mm) is .. math:: :label: 25.65 w_{avail} = \sum_{j=1}^{N_{irr}} \theta_{j} \Delta z_{j} \ , -:math:`N_{irr}` is the index of the soil layer corresponding to a specified -depth :math:`z_{irrig}` (:numref:`Table Irrigation parameters`) and -:math:`\Delta z_{j}` is the thickness of the soil layer in layer :math:`j` (section -:numref:`Vertical Discretization`). :math:`\theta_{j}` is the -volumetric soil moisture in layer :math:`j` (section :numref:`Soil Water`). -:math:`\theta_{target}` and -:math:`\theta_{wilt}` are the target and wilting point volumetric -soil moisture values, respectively, and are determined by inverting -:eq:`7.94` using soil matric -potential parameters :math:`\Psi_{target}` and :math:`\Psi_{wilt}` -(:numref:`Table Irrigation parameters`). After the soil moisture deficit -:math:`D_{irrig}` is calculated, irrigation in an amount equal to -:math:`\frac{D_{irrig}}{T_{irrig}}` (mm/s) is applied uniformly over -the irrigation period :math:`T_{irrig}` (s). Irrigation water is applied -directly to the ground surface, bypassing canopy interception (i.e., -added to :math:`{q}_{grnd,liq}`: section :numref:`Canopy Water`). - -To conserve mass, irrigation is removed from river water storage (Chapter :numref:`rst_River Transport Model (RTM)`). -When river water storage is inadequate to meet irrigation demand, -there are two options: 1) the additional water can be removed from the -ocean model, or 2) the irrigation demand can be reduced such that -river water storage is maintained above a specified threshold. +Note that :math:`w_{target}` is truly supposed to give the target soil moisture value that we're shooting for whenever irrigation happens; then the soil moisture deficit :math:`D_{irrig}` gives the difference between this target value and the current soil moisture. The irrigation moisture threshold :math:`w_{thresh}`, on the other hand, gives a threshold at which we decide to do any irrigation at all. The way this is written allows for the possibility that one may not want to irrigate every time there becomes even a tiny soil moisture deficit. Instead, one may want to wait until the deficit is larger before initiating irrigation; at that point, one doesn't want to just irrigate up to the "threshold" but instead up to the higher "target". The target should always be greater than or equal to the threshold. + +:math:`N_{irr}` is the index of the soil layer corresponding to a specified depth :math:`z_{irrig}` (:numref:`Table Irrigation parameters`) and :math:`\Delta z_{j}` is the thickness of the soil layer in layer :math:`j` (section :numref:`Vertical Discretization`). :math:`\theta_{j}` is the volumetric soil moisture in layer :math:`j` (section :numref:`Soil Water`). :math:`\theta_{target}` and :math:`\theta_{wilt}` are the target and wilting point volumetric soil moisture values, respectively, and are determined by inverting :eq:`7.94` using soil matric potential parameters :math:`\Psi_{target}` and :math:`\Psi_{wilt}` (:numref:`Table Irrigation parameters`). After the soil moisture deficit :math:`D_{irrig}` is calculated, irrigation in an amount equal to :math:`\frac{D_{irrig}}{T_{irrig}}` (mm/s) is applied uniformly over the irrigation period :math:`T_{irrig}` (s). Irrigation water is applied directly to the ground surface, bypassing canopy interception (i.e., added to :math:`{q}_{grnd,liq}`: section :numref:`Canopy Water`). + +To conserve mass, irrigation is removed from river water storage (Chapter :numref:`rst_River Transport Model (RTM)`). When river water storage is inadequate to meet irrigation demand, there are two options: 1) the additional water can be removed from the ocean model, or 2) the irrigation demand can be reduced such that river water storage is maintained above a specified threshold. .. _Table Irrigation parameters: @@ -941,8 +790,4 @@ river water storage is maintained above a specified threshold. +--------------------------------------+-------------+ .. add a reference to surface data in chapter2 - To accomplish this we downloaded - data of percent irrigated and percent rainfed corn, soybean, and - temperate cereals (wheat, barley, and rye) (:ref:`Portmann et al. 2010 `), - available online from - *ftp://ftp.rz.uni-frankfurt.de/pub/uni-frankfurt/physische\_geographie/hydrologie/public/data/MIRCA2000/harvested\_area\_grids.* + To accomplish this we downloaded data of percent irrigated and percent rainfed corn, soybean, and temperate cereals (wheat, barley, and rye) (:ref:`Portmann et al. 2010 `), available online from *ftp://ftp.rz.uni-frankfurt.de/pub/uni-frankfurt/physische\_geographie/hydrologie/public/data/MIRCA2000/harvested\_area\_grids.* diff --git a/doc/source/tech_note/DGVM/CLM50_Tech_Note_DGVM.rst b/doc/source/tech_note/DGVM/CLM50_Tech_Note_DGVM.rst index c62591b70c..4874ca9943 100644 --- a/doc/source/tech_note/DGVM/CLM50_Tech_Note_DGVM.rst +++ b/doc/source/tech_note/DGVM/CLM50_Tech_Note_DGVM.rst @@ -10,7 +10,6 @@ What has changed - Introduction of FATES: The Functionally Assembled Terrestrial Ecosystem Simulator (FATES) is the actively developed DGVM for the CLM5. - FATES ^^^^^^^^^^^^^^^^^^^^ @@ -22,19 +21,7 @@ Fisher, R. A., Muszala, S., Verteinstein, M., Lawrence, P., Xu, C., McDowell, N. The Ecosystem Demography ('ED'), concept within FATES is derived from the work of :ref:`Moorcroft et al. (2001)` and is a cohort model of vegetation competition and co-existence, allowing a representation of the biosphere which accounts for the division of the land surface into successional stages, and for competition for light between height structured cohorts of representative trees of various plant functional types. -The implementation of the Ecosystem Demography concept within FATES links the surface flux and canopy physiology concepts in CLM -with numerous additional developments necessary to accommodate the new model. These include a version of the SPITFIRE -(Spread and InTensity of Fire) model of :ref:`Thonicke et al. (2010)`, and an adoption of the concept of -`Perfect Plasticity Approximation` approach of -:ref:`Purves et al. 2008`, :ref:`Lichstein et al. 2011` and :ref:`Weng et al. 2014`, in accounting -for the spatial arrangement of crowns. Novel algorithms accounting for -the fragmentation of coarse woody debris into chemical litter streams, -for the physiological optimization of canopy thickness, for the -accumulation of seeds in the seed bank, for multi-layer multi-PFT -radiation transfer, for drought-deciduous and cold-deciduous phenology, -for carbon storage allocation, and for tree mortality under carbon -stress, are also included. - +The implementation of the Ecosystem Demography concept within FATES links the surface flux and canopy physiology concepts in CLM with numerous additional developments necessary to accommodate the new model. These include a version of the SPITFIRE (Spread and InTensity of Fire) model of :ref:`Thonicke et al. (2010)`, and an adoption of the concept of `Perfect Plasticity Approximation` approach of :ref:`Purves et al. 2008`, :ref:`Lichstein et al. 2011` and :ref:`Weng et al. 2014`, in accounting for the spatial arrangement of crowns. Novel algorithms accounting for the fragmentation of coarse woody debris into chemical litter streams, for the physiological optimization of canopy thickness, for the accumulation of seeds in the seed bank, for multi-layer multi-PFT radiation transfer, for drought-deciduous and cold-deciduous phenology, for carbon storage allocation, and for tree mortality under carbon stress, are also included. Further reading ^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/source/tech_note/Decomposition/CLM50_Tech_Note_Decomposition.rst b/doc/source/tech_note/Decomposition/CLM50_Tech_Note_Decomposition.rst index 0d2ad98687..bf6d52ee45 100644 --- a/doc/source/tech_note/Decomposition/CLM50_Tech_Note_Decomposition.rst +++ b/doc/source/tech_note/Decomposition/CLM50_Tech_Note_Decomposition.rst @@ -3,20 +3,7 @@ Decomposition ================= -Decomposition of fresh litter material into progressively more -recalcitrant forms of soil organic matter is represented in CLM is -defined as a cascade of :math:`{k}_{tras}` transformations between -:math:`{m}_{pool}` decomposing coarse woody debris (CWD), litter, -and soil organic matter (SOM) pools, each defined at -:math:`{n}_{lev}` vertical levels. CLM allows the user to define, at -compile time, between 2 contrasting hypotheses of decomposition as -embodied by two separate decomposition submodels: the CLM-CN pool -structure used in CLM4.0, or a second pool structure, characterized by -slower decomposition rates, based on the fCentury model (Parton et al. -1988). In addition, the user can choose, at compile time, whether to -allow :math:`{n}_{lev}` to equal 1, as in CLM4.0, or to equal the -number of soil levels used for the soil hydrological and thermal -calculations (see Section :numref:`Soil Layers` for soil layering). +Decomposition of fresh litter material into progressively more recalcitrant forms of soil organic matter is represented in CLM is defined as a cascade of :math:`{k}_{tras}` transformations between :math:`{m}_{pool}` decomposing coarse woody debris (CWD), litter, and soil organic matter (SOM) pools, each defined at :math:`{n}_{lev}` vertical levels. CLM allows the user to define, at compile time, between 2 contrasting hypotheses of decomposition as embodied by two separate decomposition submodels: the CLM-CN pool structure used in CLM4.0, or a second pool structure, characterized by slower decomposition rates, based on the fCentury model (Parton et al 1988). In addition, the user can choose, at compile time, whether to allow :math:`{n}_{lev}` to equal 1, as in CLM4.0, or to equal the number of soil levels used for the soil hydrological and thermal calculations (see Section :numref:`Soil Layers` for soil layering). .. _Figure Schematic of decomposition model in CLM: @@ -24,80 +11,40 @@ calculations (see Section :numref:`Soil Layers` for soil layering). Schematic of decomposition model in CLM. -Model is structured to allow different representations of the soil C and -N decomposition cascade, as well as a vertically-explicit treatment of -soil biogeochemistry. +Model is structured to allow different representations of the soil C and N decomposition cascade, as well as a vertically-explicit treatment of soil biogeochemistry. -For the single-level model structure, the fundamental equation for -carbon balance of the decomposing pools is: +For the single-level model structure, the fundamental equation for carbon balance of the decomposing pools is: .. math:: - :label: 21.1) + :label: 21.1) \frac{\partial C_{i} }{\partial t} =R_{i} +\sum _{j\ne i}\left(i-r_{j} \right)T_{ji} k_{j} C_{j} -k_{i} C_{i} -where :math:`{C}_{i}` is the carbon content of pool *i*, -:math:`{R}_{i}` are the carbon inputs from plant tissues directly to -pool *i* (only non-zero for CWD and litter pools), :math:`{k}_{i}` -is the decay constant of pool *i*; :math:`{T}_{ji}` is the fraction -of carbon directed from pool *j* to pool *i* with fraction -:math:`{r}_{j}` lost as a respiration flux along the way. +where :math:`{C}_{i}` is the carbon content of pool *i*, :math:`{R}_{i}` are the carbon inputs from plant tissues directly to pool *i* (only non-zero for CWD and litter pools), :math:`{k}_{i}` is the decay constant of pool *i*; :math:`{T}_{ji}` is the fraction of carbon directed from pool *j* to pool *i* with fraction :math:`{r}_{j}` lost as a respiration flux along the way. -Adding the vertical dimension to the decomposing pools changes the -balance equation to the following: +Adding the vertical dimension to the decomposing pools changes the balance equation to the following: .. math:: - :label: 21.2) + :label: 21.2) \begin{array}{l} {\frac{\partial C_{i} (z)}{\partial t} =R_{i} (z)+\sum _{i\ne j}\left(1-r_{j} \right)T_{ji} k_{j} (z)C_{j} (z) -k_{i} (z)C_{i} (z)} \\ {+\frac{\partial }{\partial z} \left(D(z)\frac{\partial C_{i} }{\partial z} \right)+\frac{\partial }{\partial z} \left(A(z)C_{i} \right)} \end{array} -where :math:`{C}_{i}`\ (z) is now defined at each model level, and -in volumetric (gC m\ :sup:`-3`) rather than areal (gC m\ :sup:`-2`) units, along with :math:`{R}_{i}`\ (z) and -:math:`{k}_{j}`\ (z). In addition, vertical transport is handled by -the last two terms, for diffusive and advective transport. In the base -model, advective transport is set to zero, leaving only a diffusive flux -with diffusivity *D(z)* defined for all decomposing carbon and nitrogen -pools. Further discussion of the vertical distribution of carbon inputs -:math:`{R}_{i}`\ (z), vertical turnover times -:math:`{k}_{j}`\ (z), and vertical transport *D(z)* is below. -Discussion of the vertical model and analysis of both decomposition -structures is in :ref:`Koven et al. (2013) `. +where :math:`{C}_{i}`\ (z) is now defined at each model level, and in volumetric (gC m\ :sup:`-3`) rather than areal (gC m\ :sup:`-2`) units, along with :math:`{R}_{i}`\ (z) and :math:`{k}_{j}`\ (z). In addition, vertical transport is handled by the last two terms, for diffusive and advective transport. In the base model, advective transport is set to zero, leaving only a diffusive flux with diffusivity *D(z)* defined for all decomposing carbon and nitrogen pools. Further discussion of the vertical distribution of carbon inputs :math:`{R}_{i}`\ (z), vertical turnover times :math:`{k}_{j}`\ (z), and vertical transport *D(z)* is below Discussion of the vertical model and analysis of both decomposition structures is in :ref:`Koven et al. (2013) `. .. _Figure Pool structure: .. figure:: soil_C_pools_CN_century.png - Pool structure, transitions, respired fractions (numbers at + Pool structure, transitions, respired fractions (numbers at end of arrows), and turnover times (numbers in boxes) for the 2 alternate soil decomposition models included in CLM. CLM-CN Pool Structure, Rate Constants and Parameters --------------------------------------------------------- -The CLM-CN structure in CLM45 uses three state variables for fresh -litter and four state variables for soil organic matter (SOM). The -masses of carbon and nitrogen in the live microbial community are not -modeled explicitly, but the activity of these organisms is represented -by decomposition fluxes transferring mass between the litter and SOM -pools, and heterotrophic respiration losses associated with these -transformations. The litter and SOM pools in CLM-CN are arranged as a -converging cascade (Figure 15.2), derived directly from the -implementation in Biome-BGC v4.1.2 (Thornton et al. 2002; Thornton and -Rosenbloom, 2005). - -Model parameters are estimated based on a synthesis of microcosm -decomposition studies using radio-labeled substrates (Degens and -Sparling, 1996; Ladd et al. 1992; Martin et al. 1980; Mary et al. 1993; -Saggar et al. 1994; Sørensen, 1981; van Veen et al. 1984). Multiple -exponential models are fitted to data from the microcosm studies to -estimate exponential decay rates and respiration fractions (Thornton, -1998). The microcosm experiments used for parameterization were all -conducted at constant temperature and under moist conditions with -relatively high mineral nitrogen concentrations, and so the resulting -rate constants are assumed not limited by the availability of water or -mineral nitrogen. :numref:`Table Decomposition rate constants` lists the base decomposition rates for each -litter and SOM pool, as well as a base rate for physical fragmentation -for the coarse woody debris pool (CWD). +The CLM-CN structure in CLM45 uses three state variables for fresh litter and four state variables for soil organic matter (SOM). The masses of carbon and nitrogen in the live microbial community are not modeled explicitly, but the activity of these organisms is represented by decomposition fluxes transferring mass between the litter and SOM pools, and heterotrophic respiration losses associated with these transformations. The litter and SOM pools in CLM-CN are arranged as a converging cascade (Figure 15.2), derived directly from the implementation in Biome-BGC v4.1.2 (Thornton et al. 2002; Thornton and Rosenbloom, 2005). + +Model parameters are estimated based on a synthesis of microcosm decomposition studies using radio-labeled substrates (Degens and Sparling, 1996; Ladd et al. 1992; Martin et al. 1980; Mary et al. 1993 Saggar et al. 1994; Sørensen, 1981; van Veen et al. 1984). Multiple exponential models are fitted to data from the microcosm studies to estimate exponential decay rates and respiration fractions (Thornton, 1998). The microcosm experiments used for parameterization were all conducted at constant temperature and under moist conditions with relatively high mineral nitrogen concentrations, and so the resulting rate constants are assumed not limited by the availability of water or mineral nitrogen. :numref:`Table Decomposition rate constants` lists the base decomposition rates for each litter and SOM pool, as well as a base rate for physical fragmentation for the coarse woody debris pool (CWD). .. _Table Decomposition rate constants: @@ -125,37 +72,21 @@ for the coarse woody debris pool (CWD). | :math:`{k}_{CWD}` | 0.001 | 0.00004 | - | 1 | +--------------------------+------------------------------------------------+-----------------------------------------------+---------------+-----------------------------------------+ -The first column of :numref:`Table Decomposition rate constants` gives the rates as used for the Biome-BGC -model, which uses a discrete-time model with a daily timestep. The -second column of :numref:`Table Decomposition rate constants` shows the rates transformed for a one-hour -discrete timestep typical of CLM-CN. The transformation is based on the -conversion of the initial discrete-time value (:math:`{k}_{disc1}`) -first to a continuous time value (:math:`{k}_{cont}`), then to the -new discrete-time value with a different timestep -(:math:`{k}_{disc2}`) , following Olson (1963): +The first column of :numref:`Table Decomposition rate constants` gives the rates as used for the Biome-BGC model, which uses a discrete-time model with a daily timestep. The second column of :numref:`Table Decomposition rate constants` shows the rates transformed for a one-hour discrete timestep typical of CLM-CN. The transformation is based on the conversion of the initial discrete-time value (:math:`{k}_{disc1}` first to a continuous time value (:math:`{k}_{cont}`), then to the new discrete-time value with a different timestep (:math:`{k}_{disc2}`), following Olson (1963): .. math:: - :label: ZEqnNum608251 + :label: ZEqnNum608251 k_{cont} =-\log \left(1-k_{disc1} \right) .. math:: - :label: ZEqnNum772630 + :label: ZEqnNum772630 k_{disc2} =1-\exp \left(-k_{cont} \frac{\Delta t_{2} }{\Delta t_{1} } \right) -where :math:`\Delta`\ :math:`{t}_{1}` (s) and -:math:`\Delta`\ t\ :sub:`2` (s) are the time steps of the -initial and new discrete-time models, respectively. +where :math:`\Delta`\ :math:`{t}_{1}` (s) and :math:`\Delta`\ t\ :sub:`2` (s) are the time steps of the initial and new discrete-time models, respectively. -Respiration fractions are parameterized for decomposition fluxes out of -each litter and SOM pool. The respiration fraction (*rf*, unitless) is -the fraction of the decomposition carbon flux leaving one of the litter -or SOM pools that is released as CO\ :sub:`2` due to heterotrophic -respiration. Respiration fractions and exponential decay rates are -estimated simultaneously from the results of microcosm decomposition -experiments (Thornton, 1998). The same values are used in CLM-CN and -Biome-BGC (:numref:`Table Respiration fractions for litter and SOM pools`). +Respiration fractions are parameterized for decomposition fluxes out of each litter and SOM pool. The respiration fraction (*rf*, unitless) is the fraction of the decomposition carbon flux leaving one of the litter or SOM pools that is released as CO\ :sub:`2` due to heterotrophic respiration. Respiration fractions and exponential decay rates are estimated simultaneously from the results of microcosm decomposition experiments (Thornton, 1998). The same values are used in CLM-CN and Biome-BGC (:numref:`Table Respiration fractions for litter and SOM pools`). .. _Table Respiration fractions for litter and SOM pools: @@ -175,23 +106,16 @@ Biome-BGC (:numref:`Table Respiration fractions for litter and SOM pools`). | :math:`{rf}_{SOM2}` | 0.46 | +---------------------------+-----------------------+ | :math:`{rf}_{SOM3}` | 0.55 | - +---------------------------+-----------------------+ + +---------------------------+-----------------------+ | :math:`{rf}_{SOM4}` | :math:`{1.0}^{a}` | +---------------------------+-----------------------+ -:sup:`a`:math:`{}^{a}` The respiration fraction for pool SOM4 is 1.0 by -definition: since there is no pool downstream of SOM4, the entire carbon -flux leaving this pool is assumed to be respired as CO\ :sub:`2`. +:sup:`a`:math:`{}^{a}` The respiration fraction for pool SOM4 is 1.0 by definition: since there is no pool downstream of SOM4, the entire carbon flux leaving this pool is assumed to be respired as CO\ :sub:`2`. Century-based Pool Structure, Rate Constants and Parameters ---------------------------------------------------------------- -The Century-based decomposition cascade is, like CLM-CN, a first-order -decay model; the two structures differ in the number of pools, the -connections between those pools, the turnover times of the pools, and -the respired fraction during each transition (Figure 15.2). The turnover -times are different for the Century-based pool structure, following -those described in Parton et al. (1988) (:numref:`Table Turnover times`). +The Century-based decomposition cascade is, like CLM-CN, a first-order decay model; the two structures differ in the number of pools, the connections between those pools, the turnover times of the pools, and the respired fraction during each transition (Figure 15.2). The turnover times are different for the Century-based pool structure, following those described in Parton et al. (1988) (:numref:`Table Turnover times`). .. _Table Turnover times: @@ -240,591 +164,421 @@ Likewise, values for the respiration fraction of Century-based structure are in Environmental modifiers on decomposition rate -------------------------------------------------- -These base rates are modified on each timestep by functions of the -current soil environment. For the single-level model, there are two rate -modifiers, temperature (:math:`{r}_{tsoil}`, unitless) and moisture -(:math:`{r}_{water}`, unitless), both of which are calculated using -the average environmental conditions of the top five model levels (top -29 cm of soil column). For the vertically-resolved model, two additional -environmental modifiers are calculated beyond the temperature and -moisture limitations: an oxygen scalar (:math:`{r}_{oxygen}`, -unitless), and a depth scalar (:math:`{r}_{depth}`, unitless). +These base rates are modified on each timestep by functions of the current soil environment. For the single-level model, there are two rate modifiers, temperature (:math:`{r}_{tsoil}`, unitless) and moisture (:math:`{r}_{water}`, unitless), both of which are calculated using the average environmental conditions of the top five model levels (top 29 cm of soil column). For the vertically-resolved model, two additional environmental modifiers are calculated beyond the temperature and moisture limitations: an oxygen scalar (:math:`{r}_{oxygen}`, unitless), and a depth scalar (:math:`{r}_{depth}`, unitless). -The Temperature scalar :math:`{r}_{tsoil}` is calculated in CLM -using a :math:`{Q}_{10}` approach, with :math:`{Q}_{10} = 1.5`. +The Temperature scalar :math:`{r}_{tsoil}` is calculated in CLM using a :math:`{Q}_{10}` approach, with :math:`{Q}_{10} = 1.5`. .. math:: - :label: 21.5) + :label: 21.5) r_{tsoil} =Q_{10} ^{\left(\frac{T_{soil,\, j} -T_{ref} }{10} \right)} -where *j* is the soil layer index, :math:`{T}_{soil,j}` (K) is the -temperature of soil level *j*. The reference temperature :math:`{T}_{ref}` = 25C. +where *j* is the soil layer index, :math:`{T}_{soil,j}` (K) is the temperature of soil level *j*. The reference temperature :math:`{T}_{ref}` = 25C. -The rate scalar for soil water potential (:math:`{r}_{water}`, -unitless) is calculated using a relationship from Andrén and Paustian -(1987) and supported by additional data in Orchard and Cook (1983): +The rate scalar for soil water potential (:math:`{r}_{water}`, unitless) is calculated using a relationship from Andrén and Paustian (1987) and supported by additional data in Orchard and Cook (1983): .. math:: - :label: 21.6) + :label: 21.6) - r_{water} =\sum _{j=1}^{5}\left\{\begin{array}{l} {0\qquad {\rm for\; }\Psi _{j} <\Psi _{\min } } \\ {\frac{\log \left({\Psi _{\min } \mathord{\left/ {\vphantom {\Psi _{\min } \Psi _{j} }} \right. \kern-\nulldelimiterspace} \Psi _{j} } \right)}{\log \left({\Psi _{\min } \mathord{\left/ {\vphantom {\Psi _{\min } \Psi _{\max } }} \right. \kern-\nulldelimiterspace} \Psi _{\max } } \right)} w_{soil,\, j} \qquad {\rm for\; }\Psi _{\min } \le \Psi _{j} \le \Psi _{\max } } \\ {1\qquad {\rm for\; }\Psi _{j} >\Psi _{\max } \qquad \qquad } \end{array}\right\} + r_{water} =\sum _{j=1}^{5}\left\{\begin{array}{l} {0\qquad {\rm for\; }\Psi _{j} <\Psi _{\min } } \\ {\frac{\log \left({\Psi _{\min } \mathord{\left/ {\vphantom {\Psi _{\min } \Psi _{j} }} \right.} \Psi _{j} } \right)}{\log \left({\Psi _{\min } \mathord{\left/ {\vphantom {\Psi _{\min } \Psi _{\max } }} \right.} \Psi _{\max } } \right)} w_{soil,\, j} \qquad {\rm for\; }\Psi _{\min } \le \Psi _{j} \le \Psi _{\max } } \\ {1\qquad {\rm for\; }\Psi _{j} >\Psi _{\max } \qquad \qquad } \end{array}\right\} -where :math:`{\Psi}_{j}` is the soil water potential in -layer *j*, :math:`{\Psi}_{min}` is a lower limit for soil -water potential control on decomposition rate (in CLM5, this was -changed from a default value of -10 MPa used in CLM4.5 and earlier to a -default value of -2.5 MPa). :math:`{\Psi}_{max,j}` (MPa) is the soil -moisture at which decomposition proceeds at a moisture-unlimited -rate. The default value of :math:`{\Psi}_{max,j}` for CLM5 is updated -from a saturated value used in CLM4.5 and earlier, to a value -nominally at field capacity, with a value of -0.002 MPa - -For frozen soils, the bulk of the rapid dropoff in decomposition with -decreasing temperature is due to the moisture limitation, since matric -potential is limited by temperature in the supercooled water formulation -of Niu and Yang (2006), +where :math:`{\Psi}_{j}` is the soil water potential in layer *j*, :math:`{\Psi}_{min}` is a lower limit for soil water potential control on decomposition rate (in CLM5, this was changed from a default value of -10 MPa used in CLM4.5 and earlier to a default value of -2.5 MPa). :math:`{\Psi}_{max,j}` (MPa) is the soil moisture at which decomposition proceeds at a moisture-unlimited rate. The default value of :math:`{\Psi}_{max,j}` for CLM5 is updated from a saturated value used in CLM4.5 and earlier, to a value nominally at field capacity, with a value of -0.002 MPa For frozen soils, the bulk of the rapid dropoff in decomposition with decreasing temperature is due to the moisture limitation, since matric potential is limited by temperature in the supercooled water formulation of Niu and Yang (2006), .. math:: - :label: 21.8) + :label: 21.8) \psi \left(T\right)=-\frac{L_{f} \left(T-T_{f} \right)}{10^{3} T} -An additional frozen decomposition limitation can be specified using a -‘frozen Q\ :sub:`10`' following :ref:`Koven et al. (2011) `, however the -default value of this is the same as the unfrozen Q\ :sub:`10` -value, and therefore the basic hypothesis is that frozen respiration is -limited by liquid water availability, and can be modeled following the -same approach as thawed but dry soils. - -An additional rate scalar, :math:`{r}_{oxygen}` is enabled when the -CH\ :sub:`4` submodel is used (set equal to 1 for the single layer -model or when the CH\ :sub:`4` submodel is disabled). This limits -decomposition when there is insufficient molecular oxygen to satisfy -stoichiometric demand (1 mol O\ :sub:`2` consumed per mol -CO\ :sub:`2` produced) from heterotrophic decomposers, and supply -from diffusion through soil layers (unsaturated and saturated) or -aerenchyma (Chapter 19). A minimum value of :math:`{r}_{oxygen}` is -set at 0.2, with the assumption that oxygen within organic tissues can -supply the necessary stoichiometric demand at this rate. This value lies -between estimates of 0.025–0.1 (Frolking et al. 2001), and 0.35 (Wania -et al. 2009); the large range of these estimates poses a large -unresolved uncertainty. - -Lastly, a possible explicit depth dependence, :math:`{r}_{depth}`, -(set equal to 1 for the single layer model) can be applied to soil C -decomposition rates to account for processes other than temperature, -moisture, and anoxia that can limit decomposition. This depth dependence -of decomposition was shown by Jenkinson and Coleman (2008) to be an -important term in fitting total C and 14C profiles, and implies that -unresolved processes, such as priming effects, microscale anoxia, soil -mineral surface and/or aggregate stabilization may be important in -controlling the fate of carbon at depth :ref:`Koven et al. (2013) `. CLM -includes these unresolved depth controls via an exponential decrease in -the soil turnover time with depth: - -.. math:: - :label: 21.9) +An additional frozen decomposition limitation can be specified using a ‘frozen Q\ :sub:`10`' following :ref:`Koven et al. (2011) `, however the default value of this is the same as the unfrozen Q\ :sub:`10` value, and therefore the basic hypothesis is that frozen respiration is limited by liquid water availability, and can be modeled following the same approach as thawed but dry soils. + +An additional rate scalar, :math:`{r}_{oxygen}` is enabled when the CH\ :sub:`4` submodel is used (set equal to 1 for the single layer model or when the CH\ :sub:`4` submodel is disabled). This limits decomposition when there is insufficient molecular oxygen to satisfy stoichiometric demand (1 mol O\ :sub:`2` consumed per mol CO\ :sub:`2` produced) from heterotrophic decomposers, and supply from diffusion through soil layers (unsaturated and saturated) or aerenchyma (Chapter 19). A minimum value of :math:`{r}_{oxygen}` is set at 0.2, with the assumption that oxygen within organic tissues can supply the necessary stoichiometric demand at this rate. This value lies between estimates of 0.025–0.1 (Frolking et al. 2001), and 0.35 (Wania et al. 2009); the large range of these estimates poses a large unresolved uncertainty. + +Lastly, a possible explicit depth dependence, :math:`{r}_{depth}`, (set equal to 1 for the single layer model) can be applied to soil C decomposition rates to account for processes other than temperature, moisture, and anoxia that can limit decomposition. This depth dependence of decomposition was shown by Jenkinson and Coleman (2008) to be an important term in fitting total C and 14C profiles, and implies that unresolved processes, such as priming effects, microscale anoxia, soil mineral surface and/or aggregate stabilization may be important in controlling the fate of carbon at depth :ref:`Koven et al. (2013) `. CLM includes these unresolved depth controls via an exponential decrease in the soil turnover time with depth: + +.. math:: + :label: 21.9) r_{depth} =\exp \left(-\frac{z}{z_{\tau } } \right) -where :math:`{z}_{\tau}` is the e-folding depth for decomposition. For -CLM4.5, the default value of this was 0.5m. For CLM5, this has been -changed to a default value of 10m, which effectively means that -intrinsic decomposition rates may proceed as quickly at depth as at the surface. +where :math:`{z}_{\tau}` is the e-folding depth for decomposition. For CLM4.5, the default value of this was 0.5m. For CLM5, this has been changed to a default value of 10m, which effectively means that intrinsic decomposition rates may proceed as quickly at depth as at the surface. The combined decomposition rate scalar (:math:`{r}_{total}`,unitless) is: .. math:: - :label: 21.10) + :label: 21.10) r_{total} =r_{tsoil} r_{water} r_{oxygen} r_{depth} . +.. _decomp_mgmt_modifiers: + +Management modifiers on decomposition rate +-------------------------------------------------- + +Tillage of cropland soil is represented as an additional rate scalar that depends on tillage intensity (default off), soil pool, and time since planting :ref:`(Graham et al., 2021) `. The tillage enhancement is strongest in the first 14 days after planting (idpp < 15), weaker in the next 30 days (15 ≤ idpp < 45), weaker still in the next 30 days (45 ≤ idpp < 75), and nonexistent after that (idpp ≥ 75). + +.. list-table:: Tillage decomposition rate scalars. Values in each cell represent enhancement in different periods of days past planting: [0, 14], [15, 44], [45, 74]. + :header-rows: 1 + + * - \ + - low + - high + * - Litter 2 (cel_lit) + - 1.5, 1.5, 1.1 + - 1.8, 1.5, 1.1 + * - Litter 3 (lig_lit) + - 1.5, 1.5, 1.1 + - 1.8, 1.5, 1.1 + * - SOM 1 (act_som) + - 1.0, 1.0, 1.0 + - 1.2, 1.0, 1.0 + * - SOM 2 (slo_som) + - 3.0, 1.6, 1.3 + - 4.8, 3.5, 2.5 + * - SOM 3 (pas_som) + - 3.0, 1.6, 1.3 + - 4.8, 3.5, 2.5 + N-limitation of Decomposition Fluxes ----------------------------------------- -Decomposition rates can also be limited by the availability of mineral -nitrogen, but calculation of this limitation depends on first estimating -the potential rates of decomposition, assuming an unlimited mineral -nitrogen supply. The general case is described here first, referring to -a generic decomposition flux from an “upstream” pool (*u*) to a -“downstream” pool (*d*), with an intervening loss due to respiration. -The potential carbon flux out of the upstream pool -(:math:`{CF}_{pot,u}`, gC m\ :sup:`-2` s\ :sup:`-1`) is: +Decomposition rates can also be limited by the availability of mineral nitrogen, but calculation of this limitation depends on first estimating the potential rates of decomposition, assuming an unlimited mineral nitrogen supply. The general case is described here first, referring to a generic decomposition flux from an "upstream" pool (*u*) to a "downstream" pool (*d*), with an intervening loss due to respiration The potential carbon flux out of the upstream pool (:math:`{CF}_{pot,u}`, gC m\ :sup:`-2` s\ :sup:`-1`) is: .. math:: - :label: 21.11) + :label: 21.11) CF_{pot,\, u} =CS_{u} k_{u} -where :math:`{CS}_{u}` (gC m\ :sup:`-2`) is the initial mass -in the upstream pool and :math:`{k}_{u}` is the decay rate constant -(s:sup:`-1`) for the upstream pool, adjusted for temperature and -moisture conditions. Depending on the C:N ratios of the upstream and -downstream pools and the amount of carbon lost in the transformation due -to respiration (the respiration fraction), the execution of this -potential carbon flux can generate either a source or a sink of new -mineral nitrogen -(:math:`{NF}_{pot\_min,u}`\ :math:`{}_{\rightarrow}`\ :math:`{}_{d}`, gN m\ :sup:`-2` s\ :sup:`-1`). The governing equation -(Thornton and Rosenbloom, 2005) is: +where :math:`{CS}_{u}` (gC m\ :sup:`-2`) is the initial mass in the upstream pool and :math:`{k}_{u}` is the decay rate constant (s\ :sup:`-1`) for the upstream pool, adjusted for temperature and moisture conditions. Depending on the C:N ratios of the upstream and downstream pools and the amount of carbon lost in the transformation due to respiration (the respiration fraction), the execution of this potential carbon flux can generate either a source or a sink of new mineral nitrogen (:math:`{NF}_{pot\_min,u}`\ :math:`{}_{\rightarrow}`\ :math:`{}_{d}`, gN m\ :sup:`-2` s\ :sup:`-1`). The governing equation (Thornton and Rosenbloom, 2005) is: .. math:: - :label: 21.12) + :label: 21.12) NF_{pot\_ min,\, u\to d} =\frac{CF_{pot,\, u} \left(1-rf_{u} -\frac{CN_{d} }{CN_{u} } \right)}{CN_{d} } -where :math:`{rf}_{u}` is the respiration fraction for fluxes -leaving the upstream pool, :math:`{CN}_{u}` and :math:`{CN}_{d}` -are the C:N ratios for upstream and downstream pools, respectively. -Negative values of -:math:`{NF}_{pot\_min,u}`\ :math:`{}_{\rightarrow}`\ :math:`{}_{d}` -indicate that the decomposition flux results in a source of new mineral -nitrogen, while positive values indicate that the potential -decomposition flux results in a sink (demand) for mineral nitrogen. +where :math:`{rf}_{u}` is the respiration fraction for fluxes leaving the upstream pool, :math:`{CN}_{u}` and :math:`{CN}_{d}` are the C:N ratios for upstream and downstream pools, respectively Negative values of :math:`{NF}_{pot\_min,u}`\ :math:`{}_{\rightarrow}`\ :math:`{}_{d}` indicate that the decomposition flux results in a source of new mineral nitrogen, while positive values indicate that the potential decomposition flux results in a sink (demand) for mineral nitrogen. -Following from the general case, potential carbon fluxes leaving -individual pools in the decomposition cascade, for the example of the -CLM-CN pool structure, are given as: +Following from the general case, potential carbon fluxes leaving individual pools in the decomposition cascade, for the example of the CLM-CN pool structure, are given as: .. math:: - :label: 21.13) + :label: 21.13) - CF_{pot,\, Lit1} ={CS_{Lit1} k_{Lit1} r_{total} \mathord{\left/ {\vphantom {CS_{Lit1} k_{Lit1} r_{total} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{pot,\, Lit1} ={CS_{Lit1} k_{Lit1} r_{total} \mathord{\left/ {\vphantom {CS_{Lit1} k_{Lit1} r_{total} \Delta t}} \right.} \Delta t} .. math:: - :label: 21.14) + :label: 21.14) - CF_{pot,\, Lit2} ={CS_{Lit2} k_{Lit2} r_{total} \mathord{\left/ {\vphantom {CS_{Lit2} k_{Lit2} r_{total} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{pot,\, Lit2} ={CS_{Lit2} k_{Lit2} r_{total} \mathord{\left/ {\vphantom {CS_{Lit2} k_{Lit2} r_{total} \Delta t}} \right.} \Delta t} .. math:: - :label: 21.15) + :label: 21.15) - CF_{pot,\, Lit3} ={CS_{Lit3} k_{Lit3} r_{total} \mathord{\left/ {\vphantom {CS_{Lit3} k_{Lit3} r_{total} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{pot,\, Lit3} ={CS_{Lit3} k_{Lit3} r_{total} \mathord{\left/ {\vphantom {CS_{Lit3} k_{Lit3} r_{total} \Delta t}} \right.} \Delta t} .. math:: - :label: 21.16) + :label: 21.16) - CF_{pot,\, SOM1} ={CS_{SOM1} k_{SOM1} r_{total} \mathord{\left/ {\vphantom {CS_{SOM1} k_{SOM1} r_{total} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{pot,\, SOM1} ={CS_{SOM1} k_{SOM1} r_{total} \mathord{\left/ {\vphantom {CS_{SOM1} k_{SOM1} r_{total} \Delta t}} \right.} \Delta t} .. math:: - :label: 21.17) + :label: 21.17) - CF_{pot,\, SOM2} ={CS_{SOM2} k_{SOM2} r_{total} \mathord{\left/ {\vphantom {CS_{SOM2} k_{SOM2} r_{total} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{pot,\, SOM2} ={CS_{SOM2} k_{SOM2} r_{total} \mathord{\left/ {\vphantom {CS_{SOM2} k_{SOM2} r_{total} \Delta t}} \right.} \Delta t} .. math:: - :label: 21.18) + :label: 21.18) - CF_{pot,\, SOM3} ={CS_{SOM3} k_{SOM3} r_{total} \mathord{\left/ {\vphantom {CS_{SOM3} k_{SOM3} r_{total} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{pot,\, SOM3} ={CS_{SOM3} k_{SOM3} r_{total} \mathord{\left/ {\vphantom {CS_{SOM3} k_{SOM3} r_{total} \Delta t}} \right.} \Delta t} .. math:: - :label: 21.19) + :label: 21.19) - CF_{pot,\, SOM4} ={CS_{SOM4} k_{SOM4} r_{total} \mathord{\left/ {\vphantom {CS_{SOM4} k_{SOM4} r_{total} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{pot,\, SOM4} ={CS_{SOM4} k_{SOM4} r_{total} \mathord{\left/ {\vphantom {CS_{SOM4} k_{SOM4} r_{total} \Delta t}} \right.} \Delta t} -where the factor (1/:math:`\Delta`\ *t*) is included because the rate -constant is calculated for the entire timestep (Eqs. and ), but the -convention is to express all fluxes on a per-second basis. Potential -mineral nitrogen fluxes associated with these decomposition steps are, -again for the example of the CLM-CN pool structure (the CENTURY -structure will be similar but without the different terminal step): +where the factor (1/:math:`\Delta`\ *t*) is included because the rate constant is calculated for the entire timestep (Eqs. and ), but the convention is to express all fluxes on a per-second basis. Potential mineral nitrogen fluxes associated with these decomposition steps are, again for the example of the CLM-CN pool structure (the CENTURY structure will be similar but without the different terminal step): .. math:: - :label: ZEqnNum934998 + :label: ZEqnNum934998 - NF_{pot\_ min,\, Lit1\to SOM1} ={CF_{pot,\, Lit1} \left(1-rf_{Lit1} -\frac{CN_{SOM1} }{CN_{Lit1} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, Lit1} \left(1-rf_{Lit1} -\frac{CN_{SOM1} }{CN_{Lit1} } \right) CN_{SOM1} }} \right. \kern-\nulldelimiterspace} CN_{SOM1} } + NF_{pot\_ min,\, Lit1\to SOM1} ={CF_{pot,\, Lit1} \left(1-rf_{Lit1} -\frac{CN_{SOM1} }{CN_{Lit1} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, Lit1} \left(1-rf_{Lit1} -\frac{CN_{SOM1} }{CN_{Lit1} } \right) CN_{SOM1} }} \right.} CN_{SOM1} } .. math:: - :label: 21.21) + :label: 21.21) - NF_{pot\_ min,\, Lit2\to SOM2} ={CF_{pot,\, Lit2} \left(1-rf_{Lit2} -\frac{CN_{SOM2} }{CN_{Lit2} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, Lit2} \left(1-rf_{Lit2} -\frac{CN_{SOM2} }{CN_{Lit2} } \right) CN_{SOM2} }} \right. \kern-\nulldelimiterspace} CN_{SOM2} } + NF_{pot\_ min,\, Lit2\to SOM2} ={CF_{pot,\, Lit2} \left(1-rf_{Lit2} -\frac{CN_{SOM2} }{CN_{Lit2} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, Lit2} \left(1-rf_{Lit2} -\frac{CN_{SOM2} }{CN_{Lit2} } \right) CN_{SOM2} }} \right.} CN_{SOM2} } .. math:: - :label: 21.22) + :label: 21.22) - NF_{pot\_ min,\, Lit3\to SOM3} ={CF_{pot,\, Lit3} \left(1-rf_{Lit3} -\frac{CN_{SOM3} }{CN_{Lit3} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, Lit3} \left(1-rf_{Lit3} -\frac{CN_{SOM3} }{CN_{Lit3} } \right) CN_{SOM3} }} \right. \kern-\nulldelimiterspace} CN_{SOM3} } + NF_{pot\_ min,\, Lit3\to SOM3} ={CF_{pot,\, Lit3} \left(1-rf_{Lit3} -\frac{CN_{SOM3} }{CN_{Lit3} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, Lit3} \left(1-rf_{Lit3} -\frac{CN_{SOM3} }{CN_{Lit3} } \right) CN_{SOM3} }} \right.} CN_{SOM3} } .. math:: - :label: 21.23) + :label: 21.23) - NF_{pot\_ min,\, SOM1\to SOM2} ={CF_{pot,\, SOM1} \left(1-rf_{SOM1} -\frac{CN_{SOM2} }{CN_{SOM1} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, SOM1} \left(1-rf_{SOM1} -\frac{CN_{SOM2} }{CN_{SOM1} } \right) CN_{SOM2} }} \right. \kern-\nulldelimiterspace} CN_{SOM2} } + NF_{pot\_ min,\, SOM1\to SOM2} ={CF_{pot,\, SOM1} \left(1-rf_{SOM1} -\frac{CN_{SOM2} }{CN_{SOM1} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, SOM1} \left(1-rf_{SOM1} -\frac{CN_{SOM2} }{CN_{SOM1} } \right) CN_{SOM2} }} \right.} CN_{SOM2} } .. math:: - :label: 21.24) + :label: 21.24) - NF_{pot\_ min,\, SOM2\to SOM3} ={CF_{pot,\, SOM2} \left(1-rf_{SOM2} -\frac{CN_{SOM3} }{CN_{SOM2} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, SOM2} \left(1-rf_{SOM2} -\frac{CN_{SOM3} }{CN_{SOM2} } \right) CN_{SOM3} }} \right. \kern-\nulldelimiterspace} CN_{SOM3} } + NF_{pot\_ min,\, SOM2\to SOM3} ={CF_{pot,\, SOM2} \left(1-rf_{SOM2} -\frac{CN_{SOM3} }{CN_{SOM2} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, SOM2} \left(1-rf_{SOM2} -\frac{CN_{SOM3} }{CN_{SOM2} } \right) CN_{SOM3} }} \right.} CN_{SOM3} } .. math:: - :label: 21.25) + :label: 21.25) - NF_{pot\_ min,\, SOM3\to SOM4} ={CF_{pot,\, SOM3} \left(1-rf_{SOM3} -\frac{CN_{SOM4} }{CN_{SOM3} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, SOM3} \left(1-rf_{SOM3} -\frac{CN_{SOM4} }{CN_{SOM3} } \right) CN_{SOM4} }} \right. \kern-\nulldelimiterspace} CN_{SOM4} } + NF_{pot\_ min,\, SOM3\to SOM4} ={CF_{pot,\, SOM3} \left(1-rf_{SOM3} -\frac{CN_{SOM4} }{CN_{SOM3} } \right)\mathord{\left/ {\vphantom {CF_{pot,\, SOM3} \left(1-rf_{SOM3} -\frac{CN_{SOM4} }{CN_{SOM3} } \right) CN_{SOM4} }} \right.} CN_{SOM4} } .. math:: - :label: ZEqnNum473594 + :label: ZEqnNum473594 - NF_{pot\_ min,\, SOM4} =-{CF_{pot,\, SOM4} \mathord{\left/ {\vphantom {CF_{pot,\, SOM4} CN_{SOM4} }} \right. \kern-\nulldelimiterspace} CN_{SOM4} } + NF_{pot\_ min,\, SOM4} =-{CF_{pot,\, SOM4} \mathord{\left/ {\vphantom {CF_{pot,\, SOM4} CN_{SOM4} }} \right.} CN_{SOM4} } -where the special form of Eq. arises because there is no SOM pool -downstream of SOM4 in the converging cascade: all carbon fluxes leaving -that pool are assumed to be in the form of respired CO\ :sub:`2`, -and all nitrogen fluxes leaving that pool are assumed to be sources of -new mineral nitrogen. +where the special form of Eq. arises because there is no SOM pool downstream of SOM4 in the converging cascade: all carbon fluxes leaving that pool are assumed to be in the form of respired CO\ :sub:`2`, and all nitrogen fluxes leaving that pool are assumed to be sources of new mineral nitrogen. -Steps in the decomposition cascade that result in release of new mineral -nitrogen (mineralization fluxes) are allowed to proceed at their -potential rates, without modification for nitrogen availability. Steps -that result in an uptake of mineral nitrogen (immobilization fluxes) are -subject to rate limitation, depending on the availability of mineral -nitrogen, the total immobilization demand, and the total demand for soil -mineral nitrogen to support new plant growth. The potential mineral -nitrogen fluxes from Eqs. - are evaluated, summing all the positive -fluxes to generate the total potential nitrogen immobilization flux -(:math:`{NF}_{immob\_demand}`, gN m\ :sup:`-2` s\ :sup:`-1`), and summing absolute values of all the negative -fluxes to generate the total nitrogen mineralization flux -(:math:`{NF}_{gross\_nmin}`, gN m\ :sup:`-2` s\ :sup:`-1`). Since :math:`{NF}_{griss\_nmin}` is a source of -new mineral nitrogen to the soil mineral nitrogen pool it is not limited -by the availability of soil mineral nitrogen, and is therefore an actual -as opposed to a potential flux. +Steps in the decomposition cascade that result in release of new mineral nitrogen (mineralization fluxes) are allowed to proceed at their potential rates, without modification for nitrogen availability. Steps that result in an uptake of mineral nitrogen (immobilization fluxes) are subject to rate limitation, depending on the availability of mineral nitrogen, the total immobilization demand, and the total demand for soil mineral nitrogen to support new plant growth. The potential mineral nitrogen fluxes from Eqs. - are evaluated, summing all the positive fluxes to generate the total potential nitrogen immobilization flux (:math:`{NF}_{immob\_demand}`, gN m\ :sup:`-2` s\ :sup:`-1`), and summing absolute values of all the negative fluxes to generate the total nitrogen mineralization flux (:math:`{NF}_{gross\_nmin}`, gN m\ :sup:`-2` s\ :sup:`-1`). Since :math:`{NF}_{griss\_nmin}` is a source of new mineral nitrogen to the soil mineral nitrogen pool it is not limited by the availability of soil mineral nitrogen, and is therefore an actual as opposed to a potential flux. N Competition between plant uptake and soil immobilization fluxes ---------------------------------------------------------------------- -Once :math:`{NF}_{immob\_demand }` and :math:`{NF}_{nit\_demand }` for each layer *j* are known, the competition between plant and microbial nitrogen demand can be resolved. Mineral nitrogen in -the soil pool (:math:`{NS}_{sminn}`, gN m\ :sup:`-2`) at the -beginning of the timestep is considered the available supply. +Once :math:`{NF}_{immob\_demand }` and :math:`{NF}_{nit\_demand }` for each layer *j* are known, the competition between plant and microbial nitrogen demand can be resolved. Mineral nitrogen in the soil pool (:math:`{NS}_{sminn}`, gN m\ :sup:`-2`) at the beginning of the timestep is considered the available supply. -Here, the :math:`{NF}_{plant\_demand}` is the theoretical maximum demand for nitrogen by plants to meet the entire carbon uptake given an N cost of zero (and therefore represents the upper bound on N requirements). N uptake costs that are -:math:`>` 0 imply that the plant will take up less N that it demands, ultimately. However, given the heuristic nature of the N competition algorithm, this discrepancy is not explicitly resolved here. +Here, the :math:`{NF}_{plant\_demand}` is the theoretical maximum demand for nitrogen by plants to meet the entire carbon uptake given an N cost of zero (and therefore represents the upper bound on N requirements). N uptake costs that are :math:`>` 0 imply that the plant will take up less N that it demands, ultimately. However, given the heuristic nature of the N competition algorithm, this discrepancy is not explicitly resolved here. The hypothetical plant nitrogen demand from the soil mineral pool is distributed between layers in proportion to the profile of available mineral N: .. math:: :label: 21.291 - - NF_{plant\_ demand,j} = NF_{plant\_ demand} NS_{sminn\_ j} / \sum _{j=1}^{nj}NS_{sminn,j} -Plants first compete for ammonia (NH4). For each soil layer (*j*), we calculate the total NH4 demand as: + NF_{plant\_ demand,j} = NF_{plant\_ demand} NS_{sminn\_ j} / \sum _{j=1}^{nj}NS_{sminn,j} + +Plants first compete for ammonia (NH4). For each soil layer (*j*), we calculate the total NH4 demand as: .. math:: :label: 21.292 - NF_{total\_ demand_nh4,j} = NF_{immob\_ demand,j} + NF_{immob\_ demand,j} + NF_{nit\_ demand,j} + NF_{total\_ demand_nh4,j} = NF_{immob\_ demand,j} + NF_{immob\_ demand,j} + NF_{nit\_ demand,j} -where -If :math:`{NF}_{total\_demand,j}`\ :math:`\Delta`\ *t* :math:`<` -:math:`{NS}_{sminn,j}`, then the available pool is large enough to -meet both the maximum plant and microbial demand, then immobilization proceeds at the maximum rate. +where If :math:`{NF}_{total\_demand,j}`\ :math:`\Delta`\ *t* :math:`<` :math:`{NS}_{sminn,j}`, then the available pool is large enough to meet both the maximum plant and microbial demand, then immobilization proceeds at the maximum rate. .. math:: - :label: 21.29) + :label: 21.29) f_{immob\_demand,j} = 1.0 -where :math:`{f}_{immob\_demand,j}` is the fraction of potential immobilization demand that can be met given current supply of mineral nitrogen in this layer. We also set the actual nitrification flux to be the same as the potential flux (:math:`NF_{nit}` = :math:`NF_{nit\_ demand}`). +where :math:`{f}_{immob\_demand,j}` is the fraction of potential immobilization demand that can be met given current supply of mineral nitrogen in this layer. We also set the actual nitrification flux to be the same as the potential flux (:math:`NF_{nit}` = :math:`NF_{nit\_ demand}`). -If :math:`{NF}_{total\_demand,j}`\ :math:`\Delta`\ *t* -:math:`\mathrm{\ge}` :math:`{NS}_{sminn,j}`, then there is not enough -mineral nitrogen to meet the combined demands for plant growth and -heterotrophic immobilization, immobilization is reduced proportional to the discrepancy, by :math:`f_{immob\_ demand,j}`, where +If :math:`{NF}_{total\_demand,j} \Delta t \mathrm{\ge} {NS}_{sminn,j}`, then there is not enough mineral nitrogen to meet the combined demands for plant growth and heterotrophic immobilization, immobilization is reduced proportional to the discrepancy, by :math:`f_{immob\_ demand,j}`, where .. math:: - :label: 21.30) + :label: 21.30) f_{immob\_ demand,j} = \frac{NS_{sminn,j} }{\Delta t\, NF_{total\_ demand,j} } -The N available to the FUN model for plant uptake (:math:`{NF}_ {plant\_ avail\_ sminn}` (gN m\ :sup:`-2`), which determines both the cost of N uptake, and the absolute limit on the N which is available for acquisition, is calculated as the total mineralized pool minus the actual immobilized flux: +The N available to the FUN model for plant uptake (:math:`{NF}_ {plant\_ avail\_ sminn}` (gN m\ :sup:`-2`), which determines both the cost of N uptake, and the absolute limit on the N which is available for acquisition, is calculated as the total mineralized pool minus the actual immobilized flux: .. math:: - :label: 21.311) + :label: 21.311) NF_{plant\_ avail\_ sminn,j} = NS_{sminn,j} - f_{immob\_demand} NF_{immob\_ demand,j} - -This treatment of competition for nitrogen as a limiting resource is -referred to a demand-based competition, where the fraction of the -available resource that eventually flows to a particular process depends -on the demand from that process in comparison to the total demand from -all processes. Processes expressing a greater demand acquire a larger -vfraction of the available resource. - +This treatment of competition for nitrogen as a limiting resource is referred to a demand-based competition, where the fraction of the available resource that eventually flows to a particular process depends on the demand from that process in comparison to the total demand from all processes. Processes expressing a greater demand acquire a larger vfraction of the available resource. Final Decomposition Fluxes ------------------------------- -With :math:`{f}_{immob\_demand}` known, final decomposition fluxes -can be calculated. Actual carbon fluxes leaving the individual litter -and SOM pools, again for the example of the CLM-CN pool structure (the -CENTURY structure will be similar but, again without the different -terminal step), are calculated as: +With :math:`{f}_{immob\_demand}` known, final decomposition fluxes can be calculated. Actual carbon fluxes leaving the individual litter and SOM pools, again for the example of the CLM-CN pool structure (the CENTURY structure will be similar but, again without the different terminal step), are calculated as: .. math:: - :label: 21.32) + :label: 21.32) CF_{Lit1} =\left\{\begin{array}{l} {CF_{pot,\, Lit1} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, Lit1\to SOM1} >0} \\ {CF_{pot,\, Lit1} \qquad {\rm for\; }NF_{pot\_ min,\, Lit1\to SOM1} \le 0} \end{array}\right\} .. math:: - :label: 21.33) + :label: 21.33) CF_{Lit2} =\left\{\begin{array}{l} {CF_{pot,\, Lit2} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, Lit2\to SOM2} >0} \\ {CF_{pot,\, Lit2} \qquad {\rm for\; }NF_{pot\_ min,\, Lit2\to SOM2} \le 0} \end{array}\right\} .. math:: - :label: 21.34) + :label: 21.34) CF_{Lit3} =\left\{\begin{array}{l} {CF_{pot,\, Lit3} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, Lit3\to SOM3} >0} \\ {CF_{pot,\, Lit3} \qquad {\rm for\; }NF_{pot\_ min,\, Lit3\to SOM3} \le 0} \end{array}\right\} .. math:: - :label: 21.35) + :label: 21.35) CF_{SOM1} =\left\{\begin{array}{l} {CF_{pot,\, SOM1} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, SOM1\to SOM2} >0} \\ {CF_{pot,\, SOM1} \qquad {\rm for\; }NF_{pot\_ min,\, SOM1\to SOM2} \le 0} \end{array}\right\} .. math:: - :label: 21.36) + :label: 21.36) CF_{SOM2} =\left\{\begin{array}{l} {CF_{pot,\, SOM2} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, SOM2\to SOM3} >0} \\ {CF_{pot,\, SOM2} \qquad {\rm for\; }NF_{pot\_ min,\, SOM2\to SOM3} \le 0} \end{array}\right\} .. math:: - :label: 21.37) + :label: 21.37) CF_{SOM3} =\left\{\begin{array}{l} {CF_{pot,\, SOM3} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, SOM3\to SOM4} >0} \\ {CF_{pot,\, SOM3} \qquad {\rm for\; }NF_{pot\_ min,\, SOM3\to SOM4} \le 0} \end{array}\right\} .. math:: - :label: 21.38) + :label: 21.38) CF_{SOM4} =CF_{pot,\, SOM4} -Heterotrophic respiration fluxes (losses of carbon as -CO\ :sub:`2` to the atmosphere) are: +Heterotrophic respiration fluxes (losses of carbon as CO\ :sub:`2` to the atmosphere) are: .. math:: - :label: 21.39) + :label: 21.39) CF_{Lit1,\, HR} =CF_{Lit1} rf_{Lit1} .. math:: - :label: 21.40) + :label: 21.40) CF_{Lit2,\, HR} =CF_{Lit2} rf_{Lit2} .. math:: - :label: 21.41) + :label: 21.41) CF_{Lit3,\, HR} =CF_{Lit3} rf_{Lit3} .. math:: - :label: 21.42) + :label: 21.42) CF_{SOM1,\, HR} =CF_{SOM1} rf_{SOM1} .. math:: - :label: 21.43) + :label: 21.43) CF_{SOM2,\, HR} =CF_{SOM2} rf_{SOM2} .. math:: - :label: 21.44) + :label: 21.44) CF_{SOM3,\, HR} =CF_{SOM3} rf_{SOM3} .. math:: - :label: 21.45) + :label: 21.45) CF_{SOM4,\, HR} =CF_{SOM4} rf_{SOM4} -Transfers of carbon from upstream to downstream pools in the -decomposition cascade are given as: +Transfers of carbon from upstream to downstream pools in the decomposition cascade are given as: .. math:: - :label: 21.46) + :label: 21.46) CF_{Lit1,\, SOM1} =CF_{Lit1} \left(1-rf_{Lit1} \right) .. math:: - :label: 21.47) + :label: 21.47) CF_{Lit2,\, SOM2} =CF_{Lit2} \left(1-rf_{Lit2} \right) .. math:: - :label: 21.48) + :label: 21.48) CF_{Lit3,\, SOM3} =CF_{Lit3} \left(1-rf_{Lit3} \right) .. math:: - :label: 21.49) + :label: 21.49) CF_{SOM1,\, SOM2} =CF_{SOM1} \left(1-rf_{SOM1} \right) .. math:: - :label: 21.50) + :label: 21.50) CF_{SOM2,\, SOM3} =CF_{SOM2} \left(1-rf_{SOM2} \right) .. math:: - :label: 21.51) + :label: 21.51) CF_{SOM3,\, SOM4} =CF_{SOM3} \left(1-rf_{SOM3} \right) -In accounting for the fluxes of nitrogen between pools in the -decomposition cascade and associated fluxes to or from the soil mineral -nitrogen pool, the model first calculates a flux of nitrogen from an -upstream pool to a downstream pool, then calculates a flux either from -the soil mineral nitrogen pool to the downstream pool (immobilization) -or from the downstream pool to the soil mineral nitrogen pool -(mineralization). Transfers of nitrogen from upstream to downstream -pools in the decomposition cascade are given as: +In accounting for the fluxes of nitrogen between pools in the decomposition cascade and associated fluxes to or from the soil mineral nitrogen pool, the model first calculates a flux of nitrogen from an upstream pool to a downstream pool, then calculates a flux either from the soil mineral nitrogen pool to the downstream pool (immobilization or from the downstream pool to the soil mineral nitrogen pool (mineralization). Transfers of nitrogen from upstream to downstream pools in the decomposition cascade are given as: .. math:: - :label: 21.52) + :label: 21.52) - NF_{Lit1,\, SOM1} ={CF_{Lit1} \mathord{\left/ {\vphantom {CF_{Lit1} CN_{Lit1} }} \right. \kern-\nulldelimiterspace} CN_{Lit1} } + NF_{Lit1,\, SOM1} ={CF_{Lit1} \mathord{\left/ {\vphantom {CF_{Lit1} CN_{Lit1} }} \right.} CN_{Lit1} } .. math:: - :label: 21.53) + :label: 21.53) - NF_{Lit2,\, SOM2} ={CF_{Lit2} \mathord{\left/ {\vphantom {CF_{Lit2} CN_{Lit2} }} \right. \kern-\nulldelimiterspace} CN_{Lit2} } + NF_{Lit2,\, SOM2} ={CF_{Lit2} \mathord{\left/ {\vphantom {CF_{Lit2} CN_{Lit2} }} \right.} CN_{Lit2} } .. math:: - :label: 21.54) + :label: 21.54) - NF_{Lit3,\, SOM3} ={CF_{Lit3} \mathord{\left/ {\vphantom {CF_{Lit3} CN_{Lit3} }} \right. \kern-\nulldelimiterspace} CN_{Lit3} } + NF_{Lit3,\, SOM3} ={CF_{Lit3} \mathord{\left/ {\vphantom {CF_{Lit3} CN_{Lit3} }} \right.} CN_{Lit3} } .. math:: - :label: 21.55) + :label: 21.55) - NF_{SOM1,\, SOM2} ={CF_{SOM1} \mathord{\left/ {\vphantom {CF_{SOM1} CN_{SOM1} }} \right. \kern-\nulldelimiterspace} CN_{SOM1} } + NF_{SOM1,\, SOM2} ={CF_{SOM1} \mathord{\left/ {\vphantom {CF_{SOM1} CN_{SOM1} }} \right.} CN_{SOM1} } .. math:: - :label: 21.56) + :label: 21.56) - NF_{SOM2,\, SOM3} ={CF_{SOM2} \mathord{\left/ {\vphantom {CF_{SOM2} CN_{SOM2} }} \right. \kern-\nulldelimiterspace} CN_{SOM2} } + NF_{SOM2,\, SOM3} ={CF_{SOM2} \mathord{\left/ {\vphantom {CF_{SOM2} CN_{SOM2} }} \right.} CN_{SOM2} } .. math:: - :label: 21.57) + :label: 21.57) - NF_{SOM3,\, SOM4} ={CF_{SOM3} \mathord{\left/ {\vphantom {CF_{SOM3} CN_{SOM3} }} \right. \kern-\nulldelimiterspace} CN_{SOM3} } + NF_{SOM3,\, SOM4} ={CF_{SOM3} \mathord{\left/ {\vphantom {CF_{SOM3} CN_{SOM3} }} \right.} CN_{SOM3} } -Corresponding fluxes to or from the soil mineral nitrogen pool depend on -whether the decomposition step is an immobilization flux or a -mineralization flux: +Corresponding fluxes to or from the soil mineral nitrogen pool depend on whether the decomposition step is an immobilization flux or a mineralization flux: .. math:: - :label: 21.58) + :label: 21.58) NF_{sminn,\, Lit1\to SOM1} =\left\{\begin{array}{l} {NF_{pot\_ min,\, Lit1\to SOM1} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, Lit1\to SOM1} >0} \\ {NF_{pot\_ min,\, Lit1\to SOM1} \qquad {\rm for\; }NF_{pot\_ min,\, Lit1\to SOM1} \le 0} \end{array}\right\} .. math:: - :label: 21.59) + :label: 21.59) NF_{sminn,\, Lit2\to SOM2} =\left\{\begin{array}{l} {NF_{pot\_ min,\, Lit2\to SOM2} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, Lit2\to SOM2} >0} \\ {NF_{pot\_ min,\, Lit2\to SOM2} \qquad {\rm for\; }NF_{pot\_ min,\, Lit2\to SOM2} \le 0} \end{array}\right\} .. math:: - :label: 21.60) + :label: 21.60) NF_{sminn,\, Lit3\to SOM3} =\left\{\begin{array}{l} {NF_{pot\_ min,\, Lit3\to SOM3} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, Lit3\to SOM3} >0} \\ {NF_{pot\_ min,\, Lit3\to SOM3} \qquad {\rm for\; }NF_{pot\_ min,\, Lit3\to SOM3} \le 0} \end{array}\right\} .. math:: - :label: 21.61) + :label: 21.61) NF_{sminn,SOM1\to SOM2} =\left\{\begin{array}{l} {NF_{pot\_ min,\, SOM1\to SOM2} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, SOM1\to SOM2} >0} \\ {NF_{pot\_ min,\, SOM1\to SOM2} \qquad {\rm for\; }NF_{pot\_ min,\, SOM1\to SOM2} \le 0} \end{array}\right\} .. math:: - :label: 21.62) + :label: 21.62) NF_{sminn,SOM2\to SOM3} =\left\{\begin{array}{l} {NF_{pot\_ min,\, SOM2\to SOM3} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, SOM2\to SOM3} >0} \\ {NF_{pot\_ min,\, SOM2\to SOM3} \qquad {\rm for\; }NF_{pot\_ min,\, SOM2\to SOM3} \le 0} \end{array}\right\} .. math:: - :label: 21.63) + :label: 21.63) NF_{sminn,SOM3\to SOM4} =\left\{\begin{array}{l} {NF_{pot\_ min,\, SOM3\to SOM4} f_{immob\_ demand} \qquad {\rm for\; }NF_{pot\_ min,\, SOM3\to SOM4} >0} \\ {NF_{pot\_ min,\, SOM3\to SOM4} \qquad {\rm for\; }NF_{pot\_ min,\, SOM3\to SOM4} \le 0} \end{array}\right\} .. math:: - :label: 21.64) + :label: 21.64) NF_{sminn,\, SOM4} =NF_{pot\_ min,\, SOM4} Vertical Distribution and Transport of Decomposing C and N pools --------------------------------------------------------------------- -Additional terms are needed to calculate the vertically-resolved soil C -and N budget: the initial vertical distribution of C and N from PFTs -delivered to the litter and CWD pools, and the vertical transport of C -and N pools. - -For initial vertical inputs, CLM uses separate profiles for aboveground -(leaf, stem) and belowground (root) inputs. Aboveground inputs are given -a single exponential with default e-folding depth = 0.1m. Belowground -inputs are distributed according to rooting profiles with default values -based on the Jackson et al. (1996) exponential parameterization. - -Vertical mixing is accomplished by an advection-diffusion equation. The -goal of this is to consider slow, soild- and adsorbed-phase transport -due to bioturbation, cryoturbation, and erosion. Faster aqueous-phase -transport is not included in CLM, but has been developed as part of the -CLM-BeTR suite of parameterizations (Tang and Riley 2013). The default -value of the advection term is 0 cm/yr, such that transport is purely -diffusive. Diffusive transport differs in rate between permafrost soils -(where cryoturbation is the dominant transport term) and non-permafrost -soils (where bioturbation dominates). For permafrost soils, a -parameterization based on that of :ref:`Koven et al. (2009) ` is used: the -diffusivity parameter is constant through the active layer, and -decreases linearly from the base of the active layer to zero at a set -depth (default 3m); the default permafrost diffusivity is 5 -cm\ :sup:`2`/yr. For non-permafrost soils, the default diffusivity -is 1 cm\ :sup:`2`/yr. +Additional terms are needed to calculate the vertically-resolved soil C and N budget: the initial vertical distribution of C and N from PFTs delivered to the litter and CWD pools, and the vertical transport of C and N pools. + +For initial vertical inputs, CLM uses separate profiles for aboveground (leaf, stem) and belowground (root) inputs. Aboveground inputs are given a single exponential with default e-folding depth = 0.1m. Belowground inputs are distributed according to rooting profiles with default values based on the Jackson et al. (1996) exponential parameterization. + +Vertical mixing is accomplished by an advection-diffusion equation. The goal of this is to consider slow, soild- and adsorbed-phase transport due to bioturbation, cryoturbation, and erosion. Faster aqueous-phase transport is not included in CLM, but has been developed as part of the CLM-BeTR suite of parameterizations (Tang and Riley 2013). The default value of the advection term is 0 cm/yr, such that transport is purely diffusive. Diffusive transport differs in rate between permafrost soils (where cryoturbation is the dominant transport term) and non-permafrost soils (where bioturbation dominates). For permafrost soils, a parameterization based on that of :ref:`Koven et al. (2009) ` is used: the diffusivity parameter is constant through the active layer, and decreases linearly from the base of the active layer to zero at a set depth (default 3m); the default permafrost diffusivity is 5 cm\ :sup:`2`/yr. For non-permafrost soils, the default diffusivity is 1 cm\ :sup:`2`/yr. Model Equilibration and its Acceleration ----------------------------------------- -For transient experiments, it is usually assumed that the carbon cycle -is starting from a point of relatively close equilibrium, i.e. that -productivity is balanced by ecosystem carbon losses through -respiratory and disturbance pathways. In order to satisfy this -assumption, the model is generally run until the productivity and loss -terms find a stable long-term equilibrium; at this point the model is -considered 'spun up'. - -Because of the coupling between the slowest SOM pools and productivity -through N downregulation of photosynthesis, equilibration of the model -for initialization purposes will take an extremely long time in the -standard mode. This is particularly true for the CENTURY-based -decomposition cascade, which includes a passive pool. In order to -rapidly equilibrate the model, a modified version of the “accelerated -decomposition” :ref:`(Thornton and Rosenbloon, 2005) ` is used. The fundamental -idea of this approach is to allow fluxes between the various pools (both -turnover-defined and vertically-defined fluxes) adjust rapidly, while -keeping the pool sizes themselves small so that they can fill quickly. -To do this, the base decomposition rate :math:`{k}_{i}` for each -pool *i* is accelerated by a term :math:`{a}_{i}` such that the slow -pools are collapsed onto an approximately annual timescale :ref:`Koven et al. (2013) `. Accelerating the pools beyond this timescale distorts the -seasonal and/or diurnal cycles of decomposition and N mineralization, -thus leading to a substantially different ecosystem productivity than -the full model. For the vertical model, the vertical transport terms are -also accelerated by the same term :math:`{a}_{i}`, as is the -radioactive decay when :math:`{}^{14}`\ C is enabled, following the same -principle of keeping fluxes between pools (or fluxes lost to decay) -close to the full model while keeping the pools sizes small. When -leaving the accelerated decomposition mode, the concentration of C and N -in pools that had been accelerated are multiplied by the same term -:math:`{a}_{i}`, to bring the model into approximate equilibrium. -Note that in CLM, the model can also transition into accelerated -decomposition mode from the standard mode (by dividing the pools by -:math:`{a}_{i}`), and that the transitions into and out of -accelerated decomposition mode are handled automatically by CLM upon -loading from restart files (which preserve information about the mode of -the model when restart files were written). - -The base acceleration terms for the two decomposition cascades are shown in -Tables 15.1 and 15.3. In addition to the base terms, CLM5 also -includes a geographic term to the acceleration in order to apply -larger values to high-latitude systems, where decomposition rates are -particularly slow and thus equilibration can take significantly longer -than in temperate or tropical climates. This geographic term takes -the form of a logistic equation, where :math:`{a}_{i}` is equal to the -product of the base acceleration term and :math:`{a}_{l}` below: - -.. math:: - :label: 21.65) +For transient experiments, it is usually assumed that the carbon cycle is starting from a point of relatively close equilibrium, i.e. that productivity is balanced by ecosystem carbon losses through respiratory and disturbance pathways. In order to satisfy this assumption, the model is generally run until the productivity and loss terms find a stable long-term equilibrium; at this point the model is considered 'spun up'. - a_l = 1 + 50 / \left ( 1 + exp \left (-0.1 * (abs(latitude) - - 60 ) \right ) \right ) +Because of the coupling between the slowest SOM pools and productivity through N downregulation of photosynthesis, equilibration of the model for initialization purposes will take an extremely long time in the standard mode. This is particularly true for the CENTURY-based decomposition cascade, which includes a passive pool. In order to rapidly equilibrate the model, a modified version of the "accelerated decomposition" :ref:`(Thornton and Rosenbloon, 2005) ` is used. The fundamental idea of this approach is to allow fluxes between the various pools (both turnover-defined and vertically-defined fluxes) adjust rapidly, while keeping the pool sizes themselves small so that they can fill quickly To do this, the base decomposition rate :math:`{k}_{i}` for each pool *i* is accelerated by a term :math:`{a}_{i}` such that the slow pools are collapsed onto an approximately annual timescale :ref:`Koven et al. (2013) `. Accelerating the pools beyond this timescale distorts the seasonal and/or diurnal cycles of decomposition and N mineralization, thus leading to a substantially different ecosystem productivity than the full model. For the vertical model, the vertical transport terms are also accelerated by the same term :math:`{a}_{i}`, as is the radioactive decay when :math:`{}^{14}`\ C is enabled, following the same principle of keeping fluxes between pools (or fluxes lost to decay close to the full model while keeping the pools sizes small. When leaving the accelerated decomposition mode, the concentration of C and N in pools that had been accelerated are multiplied by the same term :math:`{a}_{i}`, to bring the model into approximate equilibrium Note that in CLM, the model can also transition into accelerated decomposition mode from the standard mode (by dividing the pools by :math:`{a}_{i}`), and that the transitions into and out of accelerated decomposition mode are handled automatically by CLM upon loading from restart files (which preserve information about the mode of the model when restart files were written). + +The base acceleration terms for the two decomposition cascades are shown in Tables 15.1 and 15.3. In addition to the base terms, CLM5 also includes a geographic term to the acceleration in order to apply larger values to high-latitude systems, where decomposition rates are particularly slow and thus equilibration can take significantly longer than in temperate or tropical climates. This geographic term takes the form of a logistic equation, where :math:`{a}_{i}` is equal to the product of the base acceleration term and :math:`{a}_{l}` below: - +.. math:: + :label: 21.65) + a_l = 1 + 50 / \left ( 1 + exp \left (-0.1 * (abs(latitude) - + 60 ) \right ) \right ) diff --git a/doc/source/tech_note/Dust/CLM50_Tech_Note_Dust.rst b/doc/source/tech_note/Dust/CLM50_Tech_Note_Dust.rst index 2b3064c921..ad593b6060 100644 --- a/doc/source/tech_note/Dust/CLM50_Tech_Note_Dust.rst +++ b/doc/source/tech_note/Dust/CLM50_Tech_Note_Dust.rst @@ -3,190 +3,121 @@ Dust Model ============== -Atmospheric dust is mobilized from the land by wind in the CLM. The most -important factors determining soil erodibility and dust emission include -the wind friction speed, the vegetation cover, and the soil moisture. -The CLM dust mobilization scheme (:ref:`Mahowald et al. 2006`) -accounts for these factors based on the DEAD (Dust Entrainment and Deposition) -model of :ref:`Zender et al. (2003)`. Please refer to the -:ref:`Zender et al. (2003)` article for additional -information regarding the equations presented in this section. - -The total vertical mass flux of dust, :math:`F_{j}` -(kg m\ :sup:`-2` s\ :sup:`-1`), from the ground into transport bin -:math:`j` is given by +Atmospheric dust is mobilized from the land by wind in the CLM. The most important factors determining soil erodibility and dust emission include the wind friction speed, the vegetation cover, and the soil moisture The CLM dust mobilization scheme (:ref:`Mahowald et al. 2006` accounts for these factors based on the DEAD (Dust Entrainment and Deposition model of :ref:`Zender et al. (2003)`. Please refer to the :ref:`Zender et al. (2003)` article for additional information regarding the equations presented in this section. + +The total vertical mass flux of dust, :math:`F_{j}` (kg m\ :sup:`-2` s\ :sup:`-1`), from the ground into transport bin :math:`j` is given by .. math:: :label: 29.1 F_{j} =TSf_{m} \alpha Q_{s} \sum _{i=1}^{I}M_{i,j} -where :math:`T` is a global factor that compensates for the DEAD model’s -sensitivity to horizontal and temporal resolution and equals 5 x -10\ :sup:`-4` in the CLM instead of 7 x 10\ :sup:`-4` in -:ref:`Zender et al. (2003)`. :math:`S` is the source -erodibility factor set to 1 in the CLM and serves as a place holder -at this time. +where :math:`T` is a global factor that compensates for the DEAD model's sensitivity to horizontal and temporal resolution and equals 5 x 10\ :sup:`-4` in the CLM instead of 7 x 10\ :sup:`-4` in :ref:`Zender et al. (2003)`. :math:`S` is the source erodibility factor set to 1 in the CLM and serves as a place holder at this time. -The grid cell fraction of exposed bare soil suitable for dust -mobilization :math:`f_{m}` is given by +The grid cell fraction of exposed bare soil suitable for dust mobilization :math:`f_{m}` is given by .. math:: - :label: 29.2 + :label: 29.2 f_{m} =\left(1-f_{lake} \right)\left(1-f_{sno} \right)\left(1-f_{v} \right)\frac{w_{liq,1} }{w_{liq,1} +w_{ice,1} } -where :math:`f_{lake}` and :math:`f_{sno}` -are the CLM grid cell fractions of lake (section -:numref:`Surface Data`) and snow cover (section -:numref:`Snow Covered Area Fraction`), all ranging from zero to one. Not mentioned -by :ref:`Zender et al. (2003)`, :math:`w_{liq,\, 1}` and -:math:`{}_{w_{ice,\, 1} }` are the CLM top soil layer liquid water and -ice contents (mm) entered as a ratio expressing the decreasing ability -of dust to mobilize from increasingly frozen soil. The grid cell -fraction of vegetation cover,\ :math:`{}_{f_{v} }`, is defined as +where :math:`f_{lake}` and :math:`f_{sno}` are the CLM grid cell fractions of lake (section :numref:`Surface Data`) and snow cover (section :numref:`Snow Covered Area Fraction`), all ranging from zero to one. Not mentioned by :ref:`Zender et al. (2003)`, :math:`w_{liq,\, 1}` and :math:`{}_{w_{ice,\, 1} }` are the CLM top soil layer liquid water and ice contents (mm) entered as a ratio expressing the decreasing ability of dust to mobilize from increasingly frozen soil. The grid cell fraction of vegetation cover,\ :math:`{}_{f_{v} }`, is defined as .. math:: :label: 29.3 0\le f_{v} =\frac{L+S}{\left(L+S\right)_{t} } \le 1{\rm \; \; \; \; where\; }\left(L+S\right)_{t} =0.3{\rm \; m}^{2} {\rm m}^{-2} -where equation applies only for dust mobilization and is not related to -the plant functional type fractions prescribed from the CLM input data -or simulated by the CLM dynamic vegetation model (Chapter 22). :math:`L` -and :math:`S` are the CLM leaf and stem area index values -(m :sup:`2` m\ :sup:`-2`) averaged at the land unit level so -as to include all the pfts and the bare ground present in a vegetated -land unit. :math:`L` and :math:`S` may be prescribed from the CLM -input data (section :numref:`Phenology and vegetation burial by snow`) -or simulated by the CLM biogeochemistry model (Chapter -:numref:`rst_Vegetation Phenology and Turnover`). +where equation :eq:`29.3` applies only for dust mobilization and is not related to the plant functional type fractions prescribed from the CLM input data or simulated by the CLM dynamic vegetation model (Chapter 22). :math:`L` and :math:`S` are the CLM leaf and stem area index values (m :sup:`2` m\ :sup:`-2`) averaged at the land unit level so as to include all the pfts and the bare ground present in a vegetated land unit. :math:`L` and :math:`S` may be prescribed from the CLM input data (section :numref:`Phenology and vegetation burial by snow`) or simulated by the CLM biogeochemistry model (Chapter :numref:`rst_Vegetation Phenology and Turnover`). -The sandblasting mass efficiency :math:`\alpha` (m :sup:`-1`) is -calculated as +The sandblasting mass efficiency :math:`\alpha` (m :sup:`-1`) is calculated as .. math:: - :label: 29.4 + :label: 29.4 \alpha =100e^{\left(13.4M_{clay} -6.0\right)\ln 10} {\rm \; \; }\left\{\begin{array}{l} {M_{clay} =\% clay\times 0.01{\rm \; \; \; 0}\le \% clay\le 20} \\ {M_{clay} =20\times 0.01{\rm \; \; \; \; \; \; \; \; 20<\% }clay\le 100} \end{array}\right. -where :math:`M_{clay}` is the mass fraction of clay -particles in the soil and %clay is determined from the surface dataset -(section :numref:`Surface Data`). :math:`M_{clay} =0` corresponds to sand and -:math:`M_{clay} =0.2` to sandy loam. +where :math:`M_{clay}` is the mass fraction of clay particles in the soil and %clay is determined from the surface dataset (section :numref:`Surface Data`). :math:`M_{clay} =0` corresponds to sand and :math:`M_{clay} =0.2` to sandy loam. -:math:`Q_{s}` is the total horizontally saltating mass flux (kg -m\ :sup:`-1` s\ :sup:`-1`) of “large” particles (:numref:`Table Dust Mass fraction`), -also referred to as the vertically integrated streamwise mass flux +:math:`Q_{s}` is the total horizontally saltating mass flux (kg m\ :sup:`-1` s\ :sup:`-1`) of "large" particles (:numref:`Table Dust Mass fraction`), also referred to as the vertically integrated streamwise mass flux .. math:: :label: 29.5 Q_{s} = \left\{ - \begin{array}{lr} - \frac{c_{s} \rho _{atm} u_{*s}^{3} }{g} \left(1-\frac{u_{*t} }{u_{*s} } \right)\left(1+\frac{u_{*t} }{u_{*s} } \right)^{2} {\rm \; } & \qquad {\rm for\; }u_{*t} w_{t} } \end{array}\right. where .. math:: - :label: 29.8 + :label: 29.8 w_{t} =a\left(0.17M_{clay} +0.14M_{clay}^{2} \right){\rm \; \; \; \; \; \; 0}\le M_{clay} =\% clay\times 0.01\le 1 and .. math:: - :label: 29.9 + :label: 29.9 w=\frac{\theta _{1} \rho _{liq} }{\rho _{d,1} } -where :math:`a=M_{clay}^{-1}` for tuning purposes, -:math:`\theta _{1}` is the volumetric soil moisture in the top soil -layer (m :math:`{}^{3 }`\ m\ :sup:`-3`) (section :numref:`Soil Water`), -:math:`\rho _{liq}` is the density of liquid water (kg -m\ :sup:`-3`) (:numref:`Table Physical constants`), and :math:`\rho _{d,\, 1}` -is the bulk density of soil in the top soil layer (kg m\ :sup:`-3`) -defined as in section :numref:`Soil and Snow Thermal Properties` -rather than as in :ref:`Zender et al. (2003)`. -:math:`Re_{*t}^{f}` from equation is the threshold friction Reynolds -factor +where :math:`a=M_{clay}^{-1}` for tuning purposes, :math:`\theta _{1}` is the volumetric soil moisture in the top soil layer (m :math:`{}^{3 }`\ m\ :sup:`-3`) (section :numref:`Soil Water`), :math:`\rho _{liq}` is the density of liquid water (kg m\ :sup:`-3`) (:numref:`Table Physical constants`), and :math:`\rho _{d,\, 1}` is the bulk density of soil in the top soil layer (kg m\ :sup:`-3`) defined as in section :numref:`Soil and Snow Thermal Properties` rather than as in :ref:`Zender et al. (2003)`. :math:`Re_{*t}^{f}` from equation :eq:`29.6` is the threshold friction Reynolds factor .. math:: - :label: 29.10 + :label: 29.10 Re_{*t}^{f} =\left\{\begin{array}{l} {\frac{0.1291^{2} }{-1+1.928Re_{*t} } {\rm \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; \; for\; 0.03}\le Re_{*t} \le 10} \\ {0.12^{2} \left(1-0.0858e^{-0.0617(Re_{*t} -10)} \right)^{2} {\rm \; for\; }Re_{*t} >10} \end{array}\right. -and :math:`Re_{*t}` is the threshold friction Reynolds number -approximation for optimally sized particles +and :math:`Re_{*t}` is the threshold friction Reynolds number approximation for optimally sized particles .. math:: - :label: 29.11 + :label: 29.11 Re_{*t} =0.38+1331\left(100D_{osp} \right)^{1.56} -In :eq:`29.5` , :math:`u_{*s}` is defined as the wind friction speed -(m s\ :sup:`-1`) accounting for the Owen effect (:ref:`Owen 1964`) +In :eq:`29.5`, :math:`u_{*s}` is defined as the wind friction speed (m s\ :sup:`-1`) accounting for the Owen effect (:ref:`Owen 1964`) .. math:: - :label: 29.12 + :label: 29.12 - u_{\*s} = \left\{ - \begin{array}{lr} - u_{\*} & \quad {\rm \; for \;} U_{10} ` but here for 10 m above the ground, and -:math:`U_{10,\, t}` is the threshold wind speed at 10 m (m -s\ :sup:`-1`) +where :math:`u_{*}` is the CLM wind friction speed (m s\ :sup:`-1`), also known as friction velocity (section :numref:`Monin-Obukhov Similarity Theory`), :math:`U_{10}` \ is the 10-m wind speed (m s\ :sup:`-1`) calculated as the wind speed at the top of the canopy in section 4.3 of :ref:`Bonan (1996)` but here for 10 m above the ground, and :math:`U_{10,\, t}` is the threshold wind speed at 10 m (m s\ :sup:`-1`) .. math:: :label: 29.13 U_{10,t} =u_{*t} \frac{U_{10} }{u_{*} } -In equation we sum :math:`M_{i,\, j}` over :math:`I=3` source modes -:math:`i` where :math:`M_{i,\, j}` is the mass fraction of each source -mode :math:`i` carried in each of *:math:`J=4`* transport bins :math:`j` +In equation :eq:`29.1` we sum :math:`M_{i,\, j}` over :math:`I=3` source modes :math:`i` where :math:`M_{i,\, j}` is the mass fraction of each source mode :math:`i` carried in each of *:math:`J=4`* transport bins :math:`j` .. math:: :label: 29.14 M_{i,j} =\frac{m_{i} }{2} \left[{\rm erf}\left(\frac{\ln {\textstyle\frac{D_{j,\max } }{\tilde{D}_{v,i} }} }{\sqrt{2} \ln \sigma _{g,i} } \right)-{\rm erf}\left(\frac{\ln {\textstyle\frac{D_{j,\min } }{\tilde{D}_{v,i} }} }{\sqrt{2} \ln \sigma _{g,i} } \right)\right] -where :math:`m_{i}` , :math:`\tilde{D}_{v,\, i}` , and -:math:`\sigma _{g,\, i}` are the mass fraction, mass median diameter, -and geometric standard deviation assigned to each particle source mode -:math:`i` (:numref:`Table Dust Mass fraction`), while :math:`D_{j,\, \min }` and -:math:`D_{j,\, \max }` are the minimum and maximum diameters (m) in -each transport bin :math:`j` (:numref:`Table Dust Minimum and maximum particle diameters`). +where :math:`m_{i}`, :math:`\tilde{D}_{v,\, i}`, and :math:`\sigma _{g,\, i}` are the mass fraction, mass median diameter, and geometric standard deviation assigned to each particle source mode :math:`i` (:numref:`Table Dust Mass fraction`), while :math:`D_{j,\, \min }` and :math:`D_{j,\, \max }` are the minimum and maximum diameters (m) in each transport bin :math:`j` (:numref:`Table Dust Minimum and maximum particle diameters`). .. _Table Dust Mass fraction: diff --git a/doc/source/tech_note/Ecosystem/CLM50_Tech_Note_Ecosystem.rst b/doc/source/tech_note/Ecosystem/CLM50_Tech_Note_Ecosystem.rst index 50b918e6a2..33c035f282 100644 --- a/doc/source/tech_note/Ecosystem/CLM50_Tech_Note_Ecosystem.rst +++ b/doc/source/tech_note/Ecosystem/CLM50_Tech_Note_Ecosystem.rst @@ -5,7 +5,7 @@ Surface Characterization, Vertical Discretization, and Model Input Requirements .. _Surface Characterization: -Surface Characterization +Surface Characterization ----------------------------- .. _Surface Heterogeneity and Data Structure: @@ -13,118 +13,35 @@ Surface Characterization Surface Heterogeneity and Data Structure ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Spatial land surface heterogeneity in CLM is represented as a nested -subgrid hierarchy in which grid cells are composed of multiple land -units, snow/soil columns, and PFTs (:numref:`Figure CLM subgrid hierarchy`). -Each grid cell can have -a different number of land units, each land unit can have a different -number of columns, and each column can have multiple PFTs. The first -subgrid level, the land unit, is intended to capture the broadest -spatial patterns of subgrid heterogeneity. The current land units are -glacier, lake, urban, vegetated, and crop (when the crop model option is -turned on). The land unit level can be used to further delineate these -patterns. For example, the urban land unit is divided into density -classes representing the tall building district, high density, and -medium density urban areas. - -The second subgrid level, the column, is intended to capture potential -variability in the soil and snow state variables within a single land -unit. For example, the vegetated land unit could contain several columns -with independently evolving vertical profiles of soil water and -temperature. Similarly, the managed vegetation land unit can be divided -into two columns, irrigated and non-irrigated. The default snow/soil -column is represented by 25 layers for ground (with up to 20 of these -layers classified as soil layers and the remaining layers classified as -bedrock layers) and up to 10 layers for snow, depending on snow -depth. The central characteristic of the column subgrid level is that -this is where the state variables for water and energy in the soil and -snow are defined, as well as the fluxes of these components within the -soil and snow. Regardless of the number and type of PFTs occupying space -on the column, the column physics operates with a single set of upper -boundary fluxes, as well as a single set of transpiration fluxes from -multiple soil levels. These boundary fluxes are weighted averages over -all PFTs. Currently, for lake and vegetated land units, a single column -is assigned to each land unit. The crop land unit is split into -irrigated and unirrigated columns with a single crop occupying each -column. The urban land units have five columns (roof, sunlit walls and -shaded walls, and pervious and impervious canyon floor) (Oleson et -al. 2010b). The glacier land unit is separated into up to 10 elevation -classes. +Spatial land surface heterogeneity in CLM is represented as a nested subgrid hierarchy in which grid cells are composed of multiple land units, snow/soil columns, and PFTs (:numref:`Figure CLM subgrid hierarchy`). Each grid cell can have a different number of land units, each land unit can have a different number of columns, and each column can have multiple PFTs. The first subgrid level, the land unit, is intended to capture the broadest spatial patterns of subgrid heterogeneity. The current land units are glacier, lake, urban, vegetated, and crop (when the crop model option is turned on). The land unit level can be used to further delineate these patterns. For example, the urban land unit is divided into density classes representing the tall building district, high density, and medium density urban areas. + +The second subgrid level, the column, is intended to capture potential variability in the soil and snow state variables within a single land unit. For example, the vegetated land unit could contain several columns with independently evolving vertical profiles of soil water and temperature. Similarly, the managed vegetation land unit can be divided into two columns, irrigated and non-irrigated. The default snow/soil column is represented by 25 layers for ground (with up to 20 of these layers classified as soil layers and the remaining layers classified as bedrock layers) and up to 10 layers for snow, depending on snow depth. The central characteristic of the column subgrid level is that this is where the state variables for water and energy in the soil and snow are defined, as well as the fluxes of these components within the soil and snow. Regardless of the number and type of PFTs occupying space on the column, the column physics operates with a single set of upper boundary fluxes, as well as a single set of transpiration fluxes from multiple soil levels. These boundary fluxes are weighted averages over all PFTs. Currently, for lake and vegetated land units, a single column is assigned to each land unit. The crop land unit is split into irrigated and unirrigated columns with a single crop occupying each column. The urban land units have five columns (roof, sunlit walls and shaded walls, and pervious and impervious canyon floor) (Oleson et al. 2010b). The glacier land unit is separated into up to 10 elevation classes. .. _Figure CLM subgrid hierarchy: .. Figure:: image1.png - Configuration of the CLM subgrid hierarchy. Box in upper right shows hypothetical subgrid distribution for a single grid cell. Note that the Crop land unit is only used when the model is run with the crop model active. Abbreviations: TBD – Tall Building District; HD – High Density; MD – Medium Density, G – Glacier, L – Lake, U – Urban, C – Crop, V – Vegetated, PFT – Plant Functional Type, Irr – Irrigated, UIrr – Unirrigated. Red arrows indicate allowed land unit transitions. Purple arrows indicate allowed patch-level transitions. - -The third subgrid level is referred to as the patch level. Patches can be PFTs or bare ground on the vegetated land unit -and crop functional types (CFTs) on the crop land unit. -The patch level is intended to capture the -biogeophysical and biogeochemical differences between broad categories -of plants in terms of their functional characteristics. On the vegetated -land unit, up to 16 possible PFTs that differ in physiology and -structure may coexist on a single column. All fluxes to and from the -surface are defined at the PFT level, as are the vegetation state -variables (e.g. vegetation temperature and canopy water storage). On the -crop land unit, typically, different crop types can be represented on each -crop land unit column (see Chapter :numref:`rst_Crops and Irrigation` for details). - -In addition to state and flux variable data structures for conserved -components at each subgrid level (e.g., energy, water, carbon), each -subgrid level also has a physical state data structure for handling -quantities that are not involved in conservation checks (diagnostic -variables). For example, the urban canopy air temperature and humidity -are defined through physical state variables at the land unit level, the -number of snow layers and the soil roughness lengths are defined as -physical state variables at the column level, and the leaf area index -and the fraction of canopy that is wet are defined as physical state -variables at the PFT level. - -The standard configuration of the model subgrid hierarchy is illustrated -in :numref:`Figure CLM subgrid hierarchy`. Here, only four PFTs are shown -associated with the single -column beneath the vegetated land unit but up to sixteen are possible. -The crop land unit is present only when the crop model is active. - -Note that the biogeophysical processes related to soil and snow require -PFT level properties to be aggregated to the column level. For example, -the net heat flux into the ground is required as a boundary condition -for the solution of snow/soil temperatures (Chapter :numref:`rst_Soil and Snow Temperatures`). This column -level property must be determined by aggregating the net heat flux from -all PFTs sharing the column. This is generally accomplished in the model -by computing a weighted sum of the desired quantity over all PFTs whose -weighting depends on the PFT area relative to all PFTs, unless otherwise -noted in the text. + Configuration of the CLM subgrid hierarchy. Box in upper right shows hypothetical subgrid distribution for a single grid cell. Note that the Crop land unit is only used when the model is run with the crop model active. Abbreviations: TBD – Tall Building District; HD – High Density; MD – Medium Density, G – Glacier, L – Lake, U – Urban, C – Crop, V – Vegetated, PFT – Plant Functional Type, Irr – Irrigated, UIrr – Unirrigated. Red arrows indicate allowed land unit transitions. Purple arrows indicate allowed patch-level transitions. + +The third subgrid level is referred to as the patch level. Patches can be PFTs or bare ground on the vegetated land unit and crop functional types (CFTs) on the crop land unit. The patch level is intended to capture the biogeophysical and biogeochemical differences between broad categories of plants in terms of their functional characteristics. On the vegetated land unit, up to 16 possible PFTs that differ in physiology and structure may coexist on a single column. All fluxes to and from the surface are defined at the PFT level, as are the vegetation state variables (e.g. vegetation temperature and canopy water storage). On the crop land unit, typically, different crop types can be represented on each crop land unit column (see Chapter :numref:`rst_Crops and Irrigation` for details). + +In addition to state and flux variable data structures for conserved components at each subgrid level (e.g., energy, water, carbon), each subgrid level also has a physical state data structure for handling quantities that are not involved in conservation checks (diagnostic variables). For example, the urban canopy air temperature and humidity are defined through physical state variables at the land unit level, the number of snow layers and the soil roughness lengths are defined as physical state variables at the column level, and the leaf area index and the fraction of canopy that is wet are defined as physical state variables at the PFT level. + +The standard configuration of the model subgrid hierarchy is illustrated in :numref:`Figure CLM subgrid hierarchy`. Here, only four PFTs are shown associated with the single column beneath the vegetated land unit but up to sixteen are possible. The crop land unit is present only when the crop model is active. + +Note that the biogeophysical processes related to soil and snow require PFT level properties to be aggregated to the column level. For example, the net heat flux into the ground is required as a boundary condition for the solution of snow/soil temperatures (Chapter :numref:`rst_Soil and Snow Temperatures`). This column level property must be determined by aggregating the net heat flux from all PFTs sharing the column. This is generally accomplished in the model by computing a weighted sum of the desired quantity over all PFTs whose weighting depends on the PFT area relative to all PFTs, unless otherwise noted in the text. .. _Vegetation Composition: Vegetation Composition ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Vegetated surfaces are comprised of up to 15 possible plant functional -types (PFTs) plus bare ground (:numref:`Table Plant functional types`). An -additional PFT is added if -the irrigation model is active and six additional PFTs are added if the -crop model is active (Chapter :numref:`rst_Crops and Irrigation`). -In :numref:`Table Plant functional types`, IVT = 0,14 refers to the index of PCT_NAT_VEG -on the surface dataset while IVT = 15,18 refers to the index of PCT_CFT on the surface dataset. These -plant types differ in leaf and stem optical properties that determine reflection, -transmittance, and absorption of solar radiation (:numref:`Table Plant functional type optical properties`), root -distribution parameters that control the uptake of water from the soil -(:numref:`Table Plant functional type root distribution parameters`), aerodynamic parameters that determine resistance to heat, -moisture, and momentum transfer (:numref:`Table Plant functional type aerodynamic parameters`), and photosynthetic -parameters that determine stomatal resistance, photosynthesis, and -transpiration (:numref:`Table Plant functional type (PFT) stomatal conductance parameters`, -:numref:`Table Temperature dependence parameters for C3 photosynthesis`). The composition and abundance of PFTs -within a grid cell can either be prescribed as time-invariant fields -(e.g., using the present day dataset described in section 21.3.3) or can -evolve with time if the model is run in transient landcover mode -(Chapter :numref:`rst_Transient Landcover Change`). +Vegetated surfaces are comprised of up to 15 possible plant functional types (PFTs) plus bare ground (:numref:`Table Plant functional types`). An additional PFT is added if the irrigation model is active and six additional PFTs are added if the crop model is active (Chapter :numref:`rst_Crops and Irrigation`). In :numref:`Table Plant functional types`, IVT = 0,14 refers to the index of PCT_NAT_VEG on the surface dataset while IVT = 15,18 refers to the index of PCT_CFT on the surface dataset. These plant types differ in leaf and stem optical properties that determine reflection, transmittance, and absorption of solar radiation (:numref:`Table Plant functional type optical properties`), root distribution parameters that control the uptake of water from the soil (:numref:`Table Plant functional type root distribution parameters`), aerodynamic parameters that determine resistance to heat, moisture, and momentum transfer (:numref:`Table Plant functional type aerodynamic parameters`), and photosynthetic parameters that determine stomatal resistance, photosynthesis, and transpiration (:numref:`Table Plant functional type (PFT) stomatal conductance parameters`, :numref:`Table Temperature dependence parameters for C3 photosynthesis`). The composition and abundance of PFTs within a grid cell can either be prescribed as time-invariant fields (e.g., using the present day dataset described in section 21.3.3) or can evolve with time if the model is run in transient landcover mode (Chapter :numref:`rst_Transient Landcover Change`). .. _Table Plant functional types: .. table:: Plant functional types - + +-----+--------------------------------------------------------------+-------------------+ | IVT | Plant functional type | Acronym | +=====+==============================================================+===================+ @@ -175,22 +92,12 @@ evolve with time if the model is run in transient landcover mode Vegetation Structure ^^^^^^^^^^^^^^^^^^^^^^^^^^ -Vegetation structure is defined by leaf and stem area indices -(:math:`L,\, S`) and canopy top and bottom heights (:math:`z_{top}`,\ :math:`z_{bot}` ). -Separate leaf and -stem area indices and canopy heights are prescribed or calculated for each PFT. Daily leaf -and stem area indices are obtained from griddeddatasets of monthly values (section -:numref:`Surface Data`). Canopy top and bottom heights for trees are from ICESat (:ref:`Simard et al. (2011) `). -Canopy top and bottom heights for short vegetation are obtained from gridded datasets but are invariant in space -and time and were obtained from PFT-specific values (:ref:`Bonan et al. (2002a) `) (:numref:`Table Plant functional type canopy top and bottom heights`). -When the biogeochemistry model is active, -vegetation state (LAI, SAI, canopy top and bottom heights) are calculated prognostically -(see Chapter :numref:`rst_Vegetation Phenology and Turnover`). +Vegetation structure is defined by leaf and stem area indices (:math:`L,\, S`) and canopy top and bottom heights (:math:`z_{top}`,\ :math:`z_{bot}` ). Separate leaf and stem area indices and canopy heights are prescribed or calculated for each PFT. Daily leaf and stem area indices are obtained from griddeddatasets of monthly values (section :numref:`Surface Data`). Canopy top and bottom heights for trees are from ICESat (:ref:`Simard et al. (2011) `). Canopy top and bottom heights for short vegetation are obtained from gridded datasets but are invariant in space and time and were obtained from PFT-specific values (:ref:`Bonan et al. (2002a) `) (:numref:`Table Plant functional type canopy top and bottom heights`). When the biogeochemistry model is active, vegetation state (LAI, SAI, canopy top and bottom heights) are calculated prognostically (see Chapter :numref:`rst_Vegetation Phenology and Turnover`). .. _Table Plant functional type canopy top and bottom heights: .. table:: Plant functional type canopy top and bottom heights - + +--------------------------------------------------------------+-------------------+-------------------+ | Plant functional type | :math:`z_{top}` | :math:`z_{bot}` | +==============================================================+===================+===================+ @@ -220,36 +127,22 @@ vegetation state (LAI, SAI, canopy top and bottom heights) are calculated progno Phenology and vegetation burial by snow ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When the biogeochemistry model is inactive, leaf and stem area indices -(m\ :sup:`2` leaf area m\ :sup:`-2` ground area) are updated -daily by linearly interpolating between monthly values. Monthly PFT leaf -area index values are developed from the 1-km MODIS-derived monthly grid -cell average leaf area index of :ref:`Myneni et al. (2002) `, -as described in :ref:`Lawrence and Chase (2007) `. Stem area -ndex is calculated from the monthly PFT leaf area index using the methods of -:ref:`Zeng et al. (2002) `. The leaf and stem area indices are -adjusted for vertical burying by snow (:ref:`Wang and Zeng 2009 `) -as +When the biogeochemistry model is inactive, leaf and stem area indices (m\ :sup:`2` leaf area m\ :sup:`-2` ground area) are updated daily by linearly interpolating between monthly values. Monthly PFT leaf area index values are developed from the 1-km MODIS-derived monthly grid cell average leaf area index of :ref:`Myneni et al. (2002) `, as described in :ref:`Lawrence and Chase (2007) `. Stem area ndex is calculated from the monthly PFT leaf area index using the methods of :ref:`Zeng et al. (2002) `. The leaf and stem area indices are adjusted for vertical burying by snow (:ref:`Wang and Zeng 2009 `) as .. math:: - :label: 2.1 + :label: 2.1 A=A^{*} ( 1-f_{veg}^{sno} ) -where :math:`A^{\*}` is the leaf or stem area before adjustment for -snow, :math:`A` is the remaining exposed leaf or stem area, -:math:`f_{veg}^{sno}` is the vertical fraction of vegetation covered by snow +where :math:`A^{*}` is the leaf or stem area before adjustment for snow, :math:`A` is the remaining exposed leaf or stem area, :math:`f_{veg}^{sno}` is the vertical fraction of vegetation covered by snow .. math:: :label: 2.2 - {f_{veg}^{sno} = \frac{z_{sno} -z_{bot} }{z_{top} -z_{bot} } \qquad {\rm for\; tree\; and\; shrub}} \\ - {f_{veg}^{sno} = \frac{\min \left(z_{sno} ,\, z_{c} \right)}{z_{c} } \qquad {\rm for\; grass\; and\; crop}} + {f_{veg}^{sno} = \frac{z_{sno} -z_{bot} }{z_{top} -z_{bot} } \qquad {\rm for\; tree\; and\; shrub}} \\ + {f_{veg}^{sno} = \frac{\min \left(z_{sno} ,\, z_{c} \right)}{z_{c} } \qquad {\rm for\; grass\; and\; crop}} -where :math:`z_{sno} -z_{bot} \ge 0,{\rm \; }0\le f_{veg}^{sno} \le 1`, :math:`z_{sno}` is the depth of snow (m) -(Chapter :numref:`rst_Snow Hydrology`), and :math:`z_{c} = 0.2` is the snow depth when short vegetation is assumed to -be completely buried by snow (m). For numerical reasons, exposed leaf and stem area are set to zero if less than -0.05. If the sum of exposed leaf and stem area is zero, then the surface is treated as snow-covered ground. +where :math:`z_{sno} -z_{bot} \ge 0,{\rm \; }0\le f_{veg}^{sno} \le 1`, :math:`z_{sno}` is the depth of snow (m) (Chapter :numref:`rst_Snow Hydrology`), and :math:`z_{c} = 0.2` is the snow depth when short vegetation is assumed to be completely buried by snow (m). For numerical reasons, exposed leaf and stem area are set to zero if less than 0.05. If the sum of exposed leaf and stem area is zero, then the surface is treated as snow-covered ground. .. _Vertical Discretization: @@ -257,27 +150,16 @@ Vertical Discretization ---------------------------- .. (this was taken from Initialization; is it still needed? - Vegetated and glacier land units have fifteen vertical layers, while - lakes have ten. For soil points, temperature calculations are done over - all layers, :math:`N_{levgrnd} =15`, while hydrology calculations are - done over the top ten layers, :math:`N_{levsoi} =10`, the bottom five - layers being specified as bedrock. + Vegetated and glacier land units have fifteen vertical layers, while lakes have ten. For soil points, temperature calculations are done over all layers, :math:`N_{levgrnd} =15`, while hydrology calculations are done over the top ten layers, :math:`N_{levsoi} =10`, the bottom five layers being specified as bedrock. .. _Soil Layers: Soil Layers ^^^^^^^^^^^^^^^^^^^^^^^^^^ -The soil column can be discretized into an arbitrary number of layers. The default -vertical discretization (:numref:`Table Soil layer structure`) uses -:math:`N_{levgrnd} = 25` layers, of which :math:`N_{levsoi} = 20` are hydrologically and -biogeochemically active. The deepest 5 layers are only included in the thermodynamical -calculations (:ref:`Lawrence et al. 2008 `) described in Chapter -:numref:`rst_Soil and Snow Temperatures`. +The soil column can be discretized into an arbitrary number of layers. The default vertical discretization (:numref:`Table Soil layer structure`) uses :math:`N_{levgrnd} = 25` layers, of which :math:`N_{levsoi} = 20` are hydrologically and biogeochemically active. The deepest 5 layers are only included in the thermodynamical calculations (:ref:`Lawrence et al. 2008 `) described in Chapter :numref:`rst_Soil and Snow Temperatures`. -The layer structure of the soil is described by the node depth, :math:`z_{i}` -(m), the thickness of each layer, :math:`\Delta z_{i}` (m), and the depths -at the layer interfaces :math:`z_{h,\, i}` (m). +The layer structure of the soil is described by the node depth, :math:`z_{i}` (m), the thickness of each layer, :math:`\Delta z_{i}` (m), and the depths at the layer interfaces :math:`z_{h,\, i}` (m). .. _Table Soil layer structure: @@ -337,21 +219,14 @@ at the layer interfaces :math:`z_{h,\, i}` (m). | 25 | 41.998 | 15.115 | 49.556 | +---------------+------------------+------------------------+------------------------+ -Layer node depth (:math:`z_{i}` ), thickness (:math:`\Delta z_{i}` ), and depth at -layer interface (:math:`z_{h,\, i}` ) for default soil column. All in meters. +Layer node depth (:math:`z_{i}` ), thickness (:math:`\Delta z_{i}` ), and depth at layer interface (:math:`z_{h,\, i}` ) for default soil column. All in meters. .. _Depth to Bedrock: Depth to Bedrock ^^^^^^^^^^^^^^^^^^^^^^^^^^ -The hydrologically and biogeochemically active portion of the soil column can be -restricted to a thickness less than that of the maximum soil depth. By providing -a depth-to-bedrock dataset, which may vary spatially, the number of layers used -in the hydrologic and biogeochemical calculations, :math:`N_{bedrock}`, may be -specified, subject to the constraint :math:`N_{bedrock} \le N_{levsoi}`. -The default depth-to-bedrock values are from -:ref:`Pelletier et al. [2016]`. +The hydrologically and biogeochemically active portion of the soil column can be restricted to a thickness less than that of the maximum soil depth. By providing a depth-to-bedrock dataset, which may vary spatially, the number of layers used in the hydrologic and biogeochemical calculations, :math:`N_{bedrock}`, may be specified, subject to the constraint :math:`N_{bedrock} \le N_{levsoi}`. The default depth-to-bedrock values are from :ref:`Pelletier et al. [2016]`. .. _Model Input Requirements: @@ -363,20 +238,7 @@ Model Input Requirements Atmospheric Coupling ^^^^^^^^^^^^^^^^^^^^^^^^^^ -The current state of the atmosphere (:numref:`Table Atmospheric input to land model`) -at a given time step is -used to force the land model. This atmospheric state is provided by an -atmospheric model in coupled mode or from an observed dataset in land-only -mode (Chapter :numref:`rst_Land-Only Mode`). The land model then initiates a full set of -calculations for surface energy, constituent, momentum, and radiative -fluxes. The land model calculations are implemented in two steps. The -land model proceeds with the calculation of surface energy, constituent, -momentum, and radiative fluxes using the snow and soil hydrologic states -from the previous time step. The land model then updates the soil and -snow hydrology calculations based on these fluxes. These fields are -passed to the atmosphere (:numref:`Table Land model output to atmospheric model`). The albedos sent to the atmosphere -are for the solar zenith angle at the next time step but with surface -conditions from the current time step. +The current state of the atmosphere (:numref:`Table Atmospheric input to land model`) at a given time step is used to force the land model. This atmospheric state is provided by an atmospheric model in coupled mode or from an observed dataset in land-only mode (Chapter :numref:`rst_Land-Only Mode`). The land model then initiates a full set of calculations for surface energy, constituent, momentum, and radiative fluxes. The land model calculations are implemented in two steps. The land model proceeds with the calculation of surface energy, constituent, momentum, and radiative fluxes using the snow and soil hydrologic states from the previous time step. The land model then updates the soil and snow hydrology calculations based on these fluxes. These fields are passed to the atmosphere (:numref:`Table Land model output to atmospheric model`). The albedos sent to the atmosphere are for the solar zenith angle at the next time step but with surface conditions from the current time step. .. _Table Atmospheric input to land model: @@ -424,85 +286,19 @@ conditions from the current time step. | :sup:`5`\ Lightning frequency | :math:`I_{l}` | flash km\ :sup:`-2` hr\ :sup:`-1` | +------------------------------------------------------+------------------------------------------------+-------------------------------------------------+ -:sup:`1`\ The atmospheric reference height received from the -atmospheric model :math:`z'_{atm}` is assumed to be the height above -the surface as defined by the roughness length :math:`z_{0}` plus -displacement height :math:`d`. Thus, the reference height used for flux -computations (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) -is :math:`z_{atm} =z'_{atm} +z_{0} +d`. The -reference heights for temperature, wind, and specific humidity -(:math:`z_{atm,\, h}` , :math:`z_{atm,\, {\it m}}` , -:math:`z_{atm,\, w}` ) are required. These are set equal -to\ :math:`z_{atm}` . - -:sup:`2`\ CAM provides convective and large-scale liquid -and solid precipitation, which are added to yield total liquid -precipitation :math:`q_{rain}` and solid precipitation -:math:`q_{sno}` . -However, in CLM5, the atmosphere's partitioning into liquid and solid -precipitation is ignored. Instead, CLM repartitions total precipitation -using a linear ramp. For most landunits, this ramp generates all snow -below :math:`0 ^{\circ} C`, all rain above :math:`2 ^{\circ} C`, -and a mix of rain and snow for intermediate temperatures. For glaciers, -the end points of the ramp are :math:`-2 ^{\circ} C` and :math:`0 -^{\circ} C`, respectively. Changes to the phase of precipitation are -accompanied by a sensible heat flux (positive or negative) to conserve -energy. - -:sup:`3`\ There are 14 aerosol deposition rates required depending -on species and affinity for bonding with water; 8 of these are dust -deposition rates (dry and wet rates for 4 dust size bins, -:math:`D_{dst,\, dry1} ,\, D_{dst,\, dry2} ,\, D_{dst,\, dry3} ,\, D_{dst,\, dry4}` , -:math:`D_{dst,\, \, wet1} ,D_{dst,\, wet2} ,\, D_{dst,wet3} ,\, D_{dst,\, wet4}` ), -3 are black carbon deposition rates (dry and wet hydrophilic and dry -hydrophobic rates, -:math:`D_{bc,\, dryhphil} ,\, D_{bc,\, wethphil} ,\, D_{bc,\, dryhphob}` ), -and 3 are organic carbon deposition rates (dry and wet hydrophilic and -dry hydrophobic rates, -:math:`D_{oc,\, dryhphil} ,\, D_{oc,\, wethphil} ,\, D_{oc,\, dryhphob}` ). -These fluxes are computed interactively by the atmospheric model (when -prognostic aerosol representation is active) or are prescribed from a -time-varying (annual cycle or transient), globally-gridded deposition -file defined in the namelist (see the CLM4.5 User’s Guide). Aerosol -deposition rates were calculated in a transient 1850-2009 CAM simulation -(at a resolution of 1.9x2.5x26L) with interactive chemistry (troposphere -and stratosphere) driven by CCSM3 20\ :sup:`th` century -sea-surface temperatures and emissions (:ref:`Lamarque et al. 2010`) for -short-lived gases and aerosols; observed concentrations were specified -for methane, N\ :sub:`2`\ O, the ozone-depleting substances (CFCs) -,and CO\ :sub:`2`. The fluxes are used by the snow-related -parameterizations (Chapters :numref:`rst_Surface Albedos` and :numref:`rst_Snow Hydrology`). - -:sup:`4`\ The nitrogen deposition rate is required by the -biogeochemistry model when active and represents the total deposition of -mineral nitrogen onto the land surface, combining deposition of -NO\ :sub:`y` and NH\ :sub:`x`. The rate is supplied either -as a time-invariant spatially-varying annual mean rate or time-varying -for a transient simulation. Nitrogen deposition rates were calculated -from the same CAM chemistry simulation that generated the aerosol -deposition rates. - -:sup:`5`\ Climatological 3-hourly lightning frequency at -:math:`\sim`\ 1.8\ :sup:`o` resolution is provided, which was -calculated via bilinear interpolation from 1995-2011 NASA LIS/OTD grid -product v2.2 (http://ghrc.msfc.nasa.gov) 2-hourly, 2.5\ :sup:`o` -lightning frequency data. In future versions of the model, lightning -data may be obtained directly from the atmosphere model. - -Density of air (:math:`\rho _{atm}` ) (kg m\ :sup:`-3`) is also -required but is calculated directly from -:math:`\rho _{atm} =\frac{P_{atm} -0.378e_{atm} }{R_{da} T_{atm} }` -where :math:`P_{atm}` is atmospheric pressure (Pa), :math:`e_{atm}` is -atmospheric vapor pressure (Pa), :math:`R_{da}` is the gas constant for -dry air (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`), and -:math:`T_{atm}` is the atmospheric temperature (K). The atmospheric -vapor pressure :math:`e_{atm}` is derived from atmospheric specific -humidity :math:`q_{atm}` (kg kg\ :sup:`-1`) as -:math:`e_{atm} =\frac{q_{atm} P_{atm} }{0.622+0.378q_{atm} }` . - -The O\ :sub:`2` partial pressure (Pa) is required but is -calculated from molar ratio and the atmospheric pressure -:math:`P_{atm}` as :math:`o_{i} =0.209P_{atm}` . +:sup:`1`\ The atmospheric reference height received from the atmospheric model :math:`z'_{atm}` is assumed to be the height above the surface as defined by the roughness length :math:`z_{0}` plus displacement height :math:`d`. Thus, the reference height used for flux computations (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) is :math:`z_{atm} =z'_{atm} +z_{0} +d`. The reference heights for temperature, wind, and specific humidity (:math:`z_{atm,\, h}`, :math:`z_{atm,\, {\it m}}`, :math:`z_{atm,\, w}` ) are required. These are set equal to\ :math:`z_{atm}`. + +:sup:`2`\ CAM provides convective and large-scale liquid and solid precipitation, which are added to yield total liquid precipitation :math:`q_{rain}` and solid precipitation :math:`q_{sno}`. However, in CLM5, the atmosphere's partitioning into liquid and solid precipitation is ignored. Instead, CLM repartitions total precipitation using a linear ramp. For most landunits, this ramp generates all snow below :math:`0 ^{\circ} C`, all rain above :math:`2 ^{\circ} C`, and a mix of rain and snow for intermediate temperatures. For glaciers, the end points of the ramp are :math:`-2 ^{\circ} C` and :math:`0 ^{\circ} C`, respectively. Changes to the phase of precipitation are accompanied by a sensible heat flux (positive or negative) to conserve energy. + +:sup:`3`\ There are 14 aerosol deposition rates required depending on species and affinity for bonding with water; 8 of these are dust deposition rates (dry and wet rates for 4 dust size bins, :math:`D_{dst,\, dry1},\, D_{dst,\, dry2},\, D_{dst,\, dry3},\, D_{dst,\, dry4}`, :math:`D_{dst,\, \, wet1},D_{dst,\, wet2},\, D_{dst,wet3},\, D_{dst,\, wet4}` ), 3 are black carbon deposition rates (dry and wet hydrophilic and dry hydrophobic rates, :math:`D_{bc,\, dryhphil},\, D_{bc,\, wethphil},\, D_{bc,\, dryhphob}` ), and 3 are organic carbon deposition rates (dry and wet hydrophilic and dry hydrophobic rates, :math:`D_{oc,\, dryhphil},\, D_{oc,\, wethphil},\, D_{oc,\, dryhphob}` ). These fluxes are computed interactively by the atmospheric model (when prognostic aerosol representation is active) or are prescribed from a time-varying (annual cycle or transient), globally-gridded deposition file defined in the namelist (see the CLM4.5 User's Guide). Aerosol deposition rates were calculated in a transient 1850-2009 CAM simulation (at a resolution of 1.9x2.5x26L) with interactive chemistry (troposphere and stratosphere) driven by CCSM3 20\ :sup:`th` century sea-surface temperatures and emissions (:ref:`Lamarque et al. 2010`) for short-lived gases and aerosols; observed concentrations were specified for methane, N\ :sub:`2`\ O, the ozone-depleting substances (CFCs),and CO\ :sub:`2`. The fluxes are used by the snow-related parameterizations (Chapters :numref:`rst_Surface Albedos` and :numref:`rst_Snow Hydrology`). + +:sup:`4`\ The nitrogen deposition rate is required by the biogeochemistry model when active and represents the total deposition of mineral nitrogen onto the land surface, combining deposition of NO\ :sub:`y` and NH\ :sub:`x`. The rate is supplied either as a time-invariant spatially-varying annual mean rate or time-varying for a transient simulation. Nitrogen deposition rates were calculated from the same CAM chemistry simulation that generated the aerosol deposition rates. + +:sup:`5`\ Climatological 3-hourly lightning frequency at :math:`\sim`\ 1.8° resolution is provided, which was calculated via bilinear interpolation from 1995-2011 NASA LIS/OTD grid product v2.2 (http://ghrc.msfc.nasa.gov) 2-hourly, 2.5° lightning frequency data. In future versions of the model, lightning data may be obtained directly from the atmosphere model. + +Density of air (:math:`\rho _{atm}` ) (kg m\ :sup:`-3`) is also required but is calculated directly from :math:`\rho _{atm} =\frac{P_{atm} -0.378e_{atm} }{R_{da} T_{atm} }` where :math:`P_{atm}` is atmospheric pressure (Pa), :math:`e_{atm}` is atmospheric vapor pressure (Pa), :math:`R_{da}` is the gas constant for dry air (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`), and :math:`T_{atm}` is the atmospheric temperature (K). The atmospheric vapor pressure :math:`e_{atm}` is derived from atmospheric specific humidity :math:`q_{atm}` (kg kg\ :sup:`-1`) as :math:`e_{atm} =\frac{q_{atm} P_{atm} }{0.622+0.378q_{atm} }`. + +The O\ :sub:`2` partial pressure (Pa) is required but is calculated from molar ratio and the atmospheric pressure :math:`P_{atm}` as :math:`o_{i} =0.209P_{atm}`. .. _Table Land model output to atmospheric model: @@ -552,91 +348,27 @@ calculated from molar ratio and the atmospheric pressure | Net ecosystem exchange | NEE | kgCO\ :sub:`2` m\ :sup:`-2` s\ :sup:`-1` | +---------------------------------------+------------------------------------------------+--------------------------------------------------------------+ -:sup:`1`\ :math:`\lambda _{vap}` is the latent heat of -vaporization (J kg\ :sup:`-1`) (:numref:`Table Physical constants`) and :math:`\lambda` is -either the latent heat of vaporization :math:`\lambda _{vap}` or latent -heat of sublimation :math:`\lambda _{sub}` (J kg\ :sup:`-1`) -(:numref:`Table Physical constants`) depending on the liquid water and ice content of the top -snow/soil layer (section 5.4). +:sup:`1`\ :math:`\lambda _{vap}` is the latent heat of vaporization (J kg\ :sup:`-1`) (:numref:`Table Physical constants`) and :math:`\lambda` is either the latent heat of vaporization :math:`\lambda _{vap}` or latent heat of sublimation :math:`\lambda _{sub}` (J kg\ :sup:`-1`) (:numref:`Table Physical constants`) depending on the liquid water and ice content of the top snow/soil layer (section 5.4). -:sup:`2`\ There are :math:`j=1,\ldots ,4` dust transport bins. +:sup:`2`\ There are :math:`j=1,\ldots,4` dust transport bins. .. _Initialization: Initialization ^^^^^^^^^^^^^^^^^^^^ -Initialization of the land model (i.e., providing the model with initial -temperature and moisture states) depends on the type of run (startup or -restart) (see the CLM4.5 User’s Guide). A startup run starts the model -from either initial conditions that are set internally in the Fortran -code (referred to as arbitrary initial conditions) or from an initial -conditions dataset that enables the model to start from a spun up state -(i.e., where the land is in equilibrium with the simulated climate). In -restart runs, the model is continued from a previous simulation and -initialized from a restart file that ensures that the output is -bit-for-bit the same as if the previous simulation had not stopped. The -fields that are required from the restart or initial conditions files -can be obtained by examining the code. Arbitrary initial conditions are -specified as follows. - -Soil points are initialized with -surface ground temperature :math:`T_{g}` and soil layer temperature -:math:`T_{i}` , for :math:`i=1,\ldots ,N_{levgrnd}` , of 274 K, -vegetation temperature :math:`T_{v}` of 283 K, no snow or canopy water -(:math:`W_{sno} =0`, :math:`W_{can} =0`), and volumetric soil water -content :math:`\theta _{i} =0.15` mm\ :sup:`3` mm\ :sup:`-3` -for layers :math:`i=1,\ldots ,N_{levsoi}` and :math:`\theta _{i} =0.0` -mm\ :sup:`3` mm\ :sup:`-3` for layers -:math:`i=N_{levsoi} +1,\ldots ,N_{levgrnd}` . placeLake temperatures -(:math:`T_{g}` and :math:`T_{i}` ) are initialized at 277 K and -:math:`W_{sno} =0`. - -Glacier temperatures (:math:`T_{g} =T_{snl+1}` and :math:`T_{i}` for -:math:`i=snl+1,\ldots ,N_{levgrnd}` where :math:`snl` is the negative -of the number of snow layers, i.e., :math:`snl` ranges from –5 to 0) are -initialized to 250 K with a snow water equivalent :math:`W_{sno} =1000` -mm, snow depth :math:`z_{sno} =\frac{W_{sno} }{\rho _{sno} }` (m) where -:math:`\rho _{sno} =250` kg m\ :sup:`-3` is an initial estimate -for the bulk density of snow, and :math:`\theta _{i}` \ =1.0 for -:math:`i=1,\ldots ,N_{levgrnd}` . The snow layer structure (e.g., number -of snow layers :math:`snl` and layer thickness) is initialized based on -the snow depth (section 6.1). The snow liquid water and ice contents (kg -m\ :sup:`-2`) are initialized as :math:`w_{liq,\, i} =0` and -:math:`w_{ice,\, i} =\Delta z_{i} \rho _{sno}` , respectively, where -:math:`i=snl+1,\ldots ,0` are the snow layers, and :math:`\Delta z_{i}` -is the thickness of snow layer :math:`i` (m). The soil liquid water and -ice contents are initialized as :math:`w_{liq,\, i} =0` and -:math:`w_{ice,\, i} =\Delta z_{i} \rho _{ice} \theta _{i}` for -:math:`T_{i} \le T_{f}` , and -:math:`w_{liq,\, i} =\Delta z_{i} \rho _{liq} \theta _{i}` and -:math:`w_{ice,\, i} =0` for :math:`T_{i} >T_{f}` , where -:math:`\rho _{ice}` and :math:`\rho _{liq}` are the densities of ice -and liquid water (kg m\ :sup:`-3`) (:numref:`Table Physical constants`), and :math:`T_{f}` -is the freezing temperature of water (K) (:numref:`Table Physical constants`). All vegetated and -glacier land units are initialized with water stored in the unconfined -aquifer and unsaturated soil :math:`W_{a} =4000` mm and water table -depth :math:`z_{\nabla }` at five meters below the soil column. +Initialization of the land model (i.e., providing the model with initial temperature and moisture states) depends on the type of run (startup or restart) (see the CLM4.5 User's Guide). A startup run starts the model from either initial conditions that are set internally in the Fortran code (referred to as arbitrary initial conditions) or from an initial conditions dataset that enables the model to start from a spun up state (i.e., where the land is in equilibrium with the simulated climate). In restart runs, the model is continued from a previous simulation and initialized from a restart file that ensures that the output is bit-for-bit the same as if the previous simulation had not stopped. The fields that are required from the restart or initial conditions files can be obtained by examining the code. Arbitrary initial conditions are specified as follows. + +Soil points are initialized with surface ground temperature :math:`T_{g}` and soil layer temperature :math:`T_{i}`, for :math:`i=1,\ldots,N_{levgrnd}`, of 274 K, vegetation temperature :math:`T_{v}` of 283 K, no snow or canopy water (:math:`W_{sno} =0`, :math:`W_{can} =0`), and volumetric soil water content :math:`\theta _{i} =0.15` mm\ :sup:`3` mm\ :sup:`-3` for layers :math:`i=1,\ldots,N_{levsoi}` and :math:`\theta _{i} =0.0` mm\ :sup:`3` mm\ :sup:`-3` for layers :math:`i=N_{levsoi} +1,\ldots,N_{levgrnd}`. placeLake temperatures (:math:`T_{g}` and :math:`T_{i}` ) are initialized at 277 K and :math:`W_{sno} =0`. + +Glacier temperatures (:math:`T_{g} =T_{snl+1}` and :math:`T_{i}` for :math:`i=snl+1,\ldots,N_{levgrnd}` where :math:`snl` is the negative of the number of snow layers, i.e., :math:`snl` ranges from –5 to 0) are initialized to 250 K with a snow water equivalent :math:`W_{sno} =1000` mm, snow depth :math:`z_{sno} =\frac{W_{sno} }{\rho _{sno} }` (m) where :math:`\rho _{sno} =250` kg m\ :sup:`-3` is an initial estimate for the bulk density of snow, and :math:`\theta _{i}` \ =1.0 for :math:`i=1,\ldots,N_{levgrnd}`. The snow layer structure (e.g., number of snow layers :math:`snl` and layer thickness) is initialized based on the snow depth (section 6.1). The snow liquid water and ice contents (kg m\ :sup:`-2`) are initialized as :math:`w_{liq,\, i} =0` and :math:`w_{ice,\, i} =\Delta z_{i} \rho _{sno}`, respectively, where :math:`i=snl+1,\ldots,0` are the snow layers, and :math:`\Delta z_{i}` is the thickness of snow layer :math:`i` (m). The soil liquid water and ice contents are initialized as :math:`w_{liq,\, i} =0` and :math:`w_{ice,\, i} =\Delta z_{i} \rho _{ice} \theta _{i}` for :math:`T_{i} \le T_{f}`, and :math:`w_{liq,\, i} =\Delta z_{i} \rho _{liq} \theta _{i}` and :math:`w_{ice,\, i} =0` for :math:`T_{i} >T_{f}`, where :math:`\rho _{ice}` and :math:`\rho _{liq}` are the densities of ice and liquid water (kg m\ :sup:`-3`) (:numref:`Table Physical constants`), and :math:`T_{f}` is the freezing temperature of water (K) (:numref:`Table Physical constants`). All vegetated and glacier land units are initialized with water stored in the unconfined aquifer and unsaturated soil :math:`W_{a} =4000` mm and water table depth :math:`z_{\nabla }` at five meters below the soil column. .. _Surface Data: Surface Data ^^^^^^^^^^^^^^^^^^ -Required surface data for each land grid cell are listed in -:numref:`Table Surface data required for CLM and their base spatial resolution` -and include the glacier, lake, and urban fractions of the grid cell -(vegetated and crop occupy the remainder), the fractional cover of each -plant functional type (PFT), monthly leaf and stem area index and canopy -top and bottom heights for each PFT, soil color, soil texture, soil -organic matter density, maximum fractional saturated area, slope, -elevation, biogenic volatile organic compounds (BVOCs) emissions -factors, population density, gross domestic production, peat area -fraction, and peak month of agricultural burning. Optional surface data -include crop irrigation and managed crops. All fields are aggregated to -the model’s grid from high-resolution input datasets ( -:numref:`Table Surface data required for CLM and their base spatial resolution`) that -are obtained from a variety of sources described below. +Required surface data for each land grid cell are listed in :numref:`Table Surface data required for CLM and their base spatial resolution` and include the glacier, lake, and urban fractions of the grid cell (vegetated and crop occupy the remainder), the fractional cover of each plant functional type (PFT), monthly leaf and stem area index and canopy top and bottom heights for each PFT, soil color, soil texture, soil organic matter density, maximum fractional saturated area, slope, elevation, biogenic volatile organic compounds (BVOCs) emissions factors, population density, gross domestic production, peat area fraction, and peak month of agricultural burning. Optional surface data include crop irrigation and managed crops. All fields are aggregated to the model's grid from high-resolution input datasets ( :numref:`Table Surface data required for CLM and their base spatial resolution`) that are obtained from a variety of sources described below. .. _Table Surface data required for CLM and their base spatial resolution: @@ -645,228 +377,77 @@ are obtained from a variety of sources described below. +--------------------------------------------+---------------------------+ | Surface Field | Resolution | +============================================+===========================+ - | Percent glacier | 0.05\ :sup:`o` | + | Percent glacier | 0.05° | +--------------------------------------------+---------------------------+ - | Percent lake and lake depth | 0.05\ :sup:`o` | + | Percent lake and lake depth | 0.05° | +--------------------------------------------+---------------------------+ - | Percent urban | 0.05\ :sup:`o` | + | Percent urban | 0.05° | +--------------------------------------------+---------------------------+ - | Percent plant functional types (PFTs) | 0.05\ :sup:`o` | + | Percent plant functional types (PFTs) | 0.05° | +--------------------------------------------+---------------------------+ - | Monthly leaf and stem area index | 0.5\ :sup:`o` | + | Monthly leaf and stem area index | 0.5° | +--------------------------------------------+---------------------------+ - | Canopy height (top, bottom) | 0.5\ :sup:`o` | + | Canopy height (top, bottom) | 0.5° | +--------------------------------------------+---------------------------+ - | Soil color | 0.5\ :sup:`o` | + | Soil color | 0.5° | +--------------------------------------------+---------------------------+ - | Percent sand, percent clay | 0.083\ :sup:`o` | + | Percent sand, percent clay | 0.083° | +--------------------------------------------+---------------------------+ - | Soil organic matter density | 0.083\ :sup:`o` | + | Soil organic matter density | 0.083° | +--------------------------------------------+---------------------------+ - | Maximum fractional saturated area | 0.125\ :sup:`o` | + | Maximum fractional saturated area | 0.125° | +--------------------------------------------+---------------------------+ | Elevation | 1km | +--------------------------------------------+---------------------------+ | Slope | 1km | +--------------------------------------------+---------------------------+ - | Biogenic Volatile Organic Compounds | 0.5\ :sup:`o` | + | Biogenic Volatile Organic Compounds | 0.5° | +--------------------------------------------+---------------------------+ - | Crop Irrigation | 0.083\ :sup:`o` | + | Crop Irrigation | 0.083° | +--------------------------------------------+---------------------------+ - | Managed crops | 0.5\ :sup:`o` | + | Managed crops | 0.5° | +--------------------------------------------+---------------------------+ - | Population density | 0.5\ :sup:`o` | + | Population density | 0.5° | +--------------------------------------------+---------------------------+ - | Gross domestic production | 0.5\ :sup:`o` | + | Gross domestic production | 0.5° | +--------------------------------------------+---------------------------+ - | Peat area fraction | 0.5\ :sup:`o` | + | Peat area fraction | 0.5° | +--------------------------------------------+---------------------------+ - | Peak month of agricultural waste burning | 0.5\ :sup:`o` | + | Peak month of agricultural waste burning | 0.5° | +--------------------------------------------+---------------------------+ -At the base spatial resolution of 0.05\ :sup:`o`, the percentage of -each PFT is defined with respect to the vegetated portion of the grid -cell and the sum of the PFTs is 100%. The percent lake, -glacier, and urban at their base resolution are specified with respect -to the entire grid cell. The surface dataset creation routines re-adjust -the PFT percentages to ensure that the sum of all land cover types in -the grid cell sum to 100%. A minimum threshold of 0.1% of the grid cell -by area is required for urban areas. - -The percentage glacier mask was derived from vector data of global -glacier and ice sheet spatial coverage. Vector data for glaciers (ice -caps, icefields and mountain glaciers) were taken from the first -globally complete glacier inventory, the Randolph Glacier Inventory -version 1.0 (RGIv1.0: :ref:`Arendt et al. 2012 `). -Vector data for the Greenland Ice Sheet were provided by Frank Paul and -Tobias Bolch (University of Zurich: :ref:`Rastner et al. 2012 -`). Antarctic Ice Sheet data were provided by Andrew -Bliss (University of Alaska) and were extracted from the Scientific -Committee on Antarctic Research (SCAR) Antarctic Digital Database -version 5.0. Floating ice is only provided for the Antarctic and does -not include the small area of Arctic ice shelves. High spatial -resolution vector data were then processed to determine the area of -glacier, ice sheet and floating ice within 30-second grid cells -globally. The 30-second glacier, ice sheet and Antarctic ice shelf masks -were subsequently draped over equivalent-resolution GLOBE topography -(Global Land One-km Base Elevation Project, Hastings et al. 1999) to -extract approximate ice-covered elevations of ice-covered regions. Grid -cells flagged as land-ice in the mask but ocean in GLOBE (typically, -around ice sheets at high latitudes) were designated land-ice with an -elevation of 0 meters. Finally, the high-resolution mask/topography -datasets were aggregated and processed into three 3-minute datasets: -3-minute fractional areal land ice coverage (including both glaciers and -ice sheets); 3-minute distributions of areal glacier fractional coverage -by elevation and areal ice sheet fractional coverage by elevation. Ice -fractions were binned at 100 meter intervals, with bin edges defined -from 0 to 6000 meters (plus one top bin encompassing all remaining -high-elevation ice, primarily in the Himalaya). These distributions by -elevation are used to divide each glacier land unit into columns based -on elevation class. - -When running with the CISM ice sheet model, CISM dictates glacier areas -and elevations in its domain, overriding the values specified by CLM's -datasets. In typical CLM5 configurations, this means that CISM dictates -glacier areas and elevations over Greenland. - -Percent lake and lake depth are area-averaged from the 90-second -resolution data of :ref:`Kourzeneva (2009, 2010) ` to the 0.05\ :sup:`o` -resolution using the MODIS land-mask. Percent urban is derived from -LandScan 2004, a population density dataset derived from census data, -nighttime lights satellite observations, road proximity and slope -(:ref:`Dobson et al. 2000 `) as described by -:ref:`Jackson et al. (2010) ` at 1km -resolution and aggregated to 0.05\ :sup:`o`. A number of urban -radiative, thermal, and morphological fields are also required and are -obtained from :ref:`Jackson et al. (2010) `. Their description can be found in -Table 3 of the Community Land Model Urban (CLMU) technical note (:ref:`Oleson -et al. 2010b `). - -Percent PFTs are derived from MODIS satellite data as described in -:ref:`Lawrence and Chase (2007) ` (section 21.3.3). -Prescribed PFT leaf area index is derived from the MODIS satellite data of -:ref:`Myneni et al. (2002) ` using the de-aggregation methods -described in :ref:`Lawrence and Chase (2007) ` -(section 2.2.3). Prescribed PFT stem area index is derived from PFT leaf -area index phenology combined with the methods of :ref:`Zeng et al. (2002) `. -Prescribed canopy top and bottom heights are from :ref:`Bonan (1996) ` as -described in :ref:`Bonan et al. (2002b) `. If the biogeochemistry model is -active, it supplies the leaf and stem area index and canopy top and -bottom heights dynamically, and the prescribed values are ignored. - -Soil color determines dry and saturated soil albedo (section :numref:`Ground Albedos`). -Soil colors are from :ref:`Lawrence and Chase (2007) `. - -The soil texture and organic matter content determine soil thermal and -hydrologic properties (sections 6.3 and 7.4.1). The International -Geosphere-Biosphere Programme (IGBP) soil dataset (Global Soil Data Task -2000) of 4931 soil mapping units and their sand and clay content for -each soil layer were used to create a mineral soil texture dataset -:ref:`(Bonan et al. 2002b) `. Soil organic matter data is merged from two -sources. The majority of the globe is from ISRIC-WISE (:ref:`Batjes, 2006 `). -The high latitudes come from the 0.25\ :sup:`o` version of the -Northern Circumpolar Soil Carbon Database (:ref:`Hugelius et al. 2012 `). Both -datasets report carbon down to 1m depth. Carbon is partitioned across -the top seven CLM4 layers (:math:`\sim`\ 1m depth) as in -:ref:`Lawrence and Slater (2008) `. - -The maximum fractional saturated area (:math:`f_{\max }` ) is used in -determining surface runoff and infiltration (section 7.3). Maximum -fractional saturated area at 0.125\ :sup:`o` resolution is -calculated from 1-km compound topographic indices (CTIs) based on the -USGS HYDRO1K dataset (:ref:`Verdin and Greenlee 1996 `) -following the algorithm in :ref:`Niu et al. (2005) `. -:math:`f_{\max }` is the ratio between the number -of 1-km pixels with CTIs equal to or larger than the mean CTI and the -total number of pixels in a 0.125\ :sup:`o` grid cell. See -section 7.3.1 and :ref:`Li et al. (2013b) ` for further details. Slope and -elevation are also obtained from the USGS HYDRO1K 1-km dataset -(:ref:`Verdin and Greenlee 1996 `). Slope is used in the -surface water parameterization (section :numref:`Surface Water Storage`), and -elevation is used to calculate the grid cell standard deviation of -topography for the snow cover fraction parameterization (section :numref:`Snow Covered Area Fraction`). - -Biogenic Volatile Organic Compounds emissions factors are from the Model -of Emissions of Gases and Aerosols from Nature version 2.1 (MEGAN2.1; -:ref:`Guenther et al. 2012 `). - -The default list of PFTs includes an unmanaged crop treated as a second -C3 grass (:numref:`Table Plant functional types`). The unmanaged crop has grid cell fractional cover -assigned from MODIS satellite data (:ref:`Lawrence and Chase (2007) `). A managed -crop option uses grid cell fractional cover from the present-day crop -dataset of :ref:`Ramankutty and Foley (1998) ` -(CLM4CNcrop). Managed crops are assigned in the proportions given by -:ref:`Ramankutty and Foley (1998) ` without -exceeding the area previously assigned to the unmanaged crop. The -unmanaged crop continues to occupy any of its original area that remains -and continues to be handled just by the CN part of CLM4CNcrop. The -managed crop types (corn, soybean, and temperate cereals) were chosen -based on the availability of corresponding algorithms in AgroIBIS -(:ref:`Kucharik et al. 2000 `; -:ref:`Kucharik and Brye 2003 `). Temperate cereals -include wheat, barley, and rye here. All temperate cereals are treated -as summer crops (like spring wheat, for example) at this time. Winter -cereals (such as winter wheat) may be introduced in a future version of -the model. - -To allow crops to coexist with natural vegetation in a grid cell and be -treated by separate models (i.e., CLM4.5BGCcrop versus the Dynamic -Vegetation version (CLM4.5BGCDV)), we separate the vegetated land unit -into a naturally vegetated land unit and a human managed land unit. PFTs -in the naturally vegetated land unit share one soil column and compete -for water (default CLM setting). PFTs in the human managed land unit do -not share soil columns and thus permit for differences in land -management between crops. - -CLM includes the option to irrigate cropland areas that are equipped for -irrigation. The application of irrigation responds dynamically to climate -(see Chapter :numref:`rst_Crops and Irrigation`). In CLM, irrigation is -implemented for the C3 generic crop only. When irrigation is enabled, the -cropland area of each grid cell is divided into an irrigated and unirrigated -fraction according to a dataset of areas equipped for irrigation -(:ref:`Siebert et al. (2005) `). The area of irrigated -cropland in each grid cell is given by the -smaller of the grid cell’s total cropland area, according to the default -CLM4 dataset, and the grid cell’s area equipped for irrigation. The -remainder of the grid cell’s cropland area (if any) is then assigned to -unirrigated cropland. Irrigated and unirrigated crops are placed on -separate soil columns, so that irrigation is only applied to the soil -beneath irrigated crops. - -Several input datasets are required for the fire model (:ref:`Li et al. 2013a `) -including population density, gross domestic production, peat area -fraction, and peak month of agricultural waste burning. Population -density at 0.5\ :sup:`o` resolution for 1850-2100 combines 5-min -resolution decadal population density data for 1850–1980 from the -Database of the Global Environment version 3.1 (HYDEv3.1) with -0.5\ :sup:`o` resolution population density data for 1990, 1995, -2000, and 2005 from the Gridded Population of the World version 3 -dataset (GPWv3) (CIESIN, 2005). Gross Domestic Production (GDP) per -capita in 2000 at 0.5\ :sup:`o` is from :ref:`Van Vuuren et al. (2006) `, -which is the base-year GDP data for IPCC-SRES and derived from -country-level World Bank’s World Development Indicators (WDI) measured -in constant 1995 US$ (:ref:`World Bank, 2004 `) and the UN Statistics Database -(:ref:`UNSTAT, 2005 `). The peatland area fraction at 0.5\ :sup:`o` -resolution is derived from three vector datasets: peatland data in -Indonesia and Malaysian Borneo (:ref:`Olson et al. 2001 `); peatland data in -Canada (:ref:`Tarnocai et al. 2011 `); and bog, fen and mire data in boreal -regions (north of 45\ :sup:`o`\ N) outside Canada provided by the -Global Lakes and Wetlands Database (GLWD) (:ref:`Lehner and Döll, 2004 `). The -climatological peak month for agricultural waste burning is from :ref:`van der -Werf et al. (2010) `. +At the base spatial resolution of 0.05°, the percentage of each PFT is defined with respect to the vegetated portion of the grid cell and the sum of the PFTs is 100%. The percent lake, glacier, and urban at their base resolution are specified with respect to the entire grid cell. The surface dataset creation routines re-adjust the PFT percentages to ensure that the sum of all land cover types in the grid cell sum to 100%. A minimum threshold of 0.1% of the grid cell by area is required for urban areas. + +The percentage glacier mask was derived from vector data of global glacier and ice sheet spatial coverage. Vector data for glaciers (ice caps, icefields and mountain glaciers) were taken from the first globally complete glacier inventory, the Randolph Glacier Inventory version 1.0 (RGIv1.0: :ref:`Arendt et al. 2012 `). Vector data for the Greenland Ice Sheet were provided by Frank Paul and Tobias Bolch (University of Zurich: :ref:`Rastner et al. 2012 `). Antarctic Ice Sheet data were provided by Andrew Bliss (University of Alaska) and were extracted from the Scientific Committee on Antarctic Research (SCAR) Antarctic Digital Database version 5.0. Floating ice is only provided for the Antarctic and does not include the small area of Arctic ice shelves. High spatial resolution vector data were then processed to determine the area of glacier, ice sheet and floating ice within 30-second grid cells globally. The 30-second glacier, ice sheet and Antarctic ice shelf masks were subsequently draped over equivalent-resolution GLOBE topography (Global Land One-km Base Elevation Project, Hastings et al. 1999) to extract approximate ice-covered elevations of ice-covered regions. Grid cells flagged as land-ice in the mask but ocean in GLOBE (typically, around ice sheets at high latitudes) were designated land-ice with an elevation of 0 meters. Finally, the high-resolution mask/topography datasets were aggregated and processed into three 3-minute datasets: 3-minute fractional areal land ice coverage (including both glaciers and ice sheets); 3-minute distributions of areal glacier fractional coverage by elevation and areal ice sheet fractional coverage by elevation. Ice fractions were binned at 100 meter intervals, with bin edges defined from 0 to 6000 meters (plus one top bin encompassing all remaining high-elevation ice, primarily in the Himalaya). These distributions by elevation are used to divide each glacier land unit into columns based on elevation class. + +When running with the CISM ice sheet model, CISM dictates glacier areas and elevations in its domain, overriding the values specified by CLM's datasets. In typical CLM5 configurations, this means that CISM dictates glacier areas and elevations over Greenland. + +Percent lake and lake depth are area-averaged from the 90-second resolution data of :ref:`Kourzeneva (2009, 2010) ` to the 0.05° resolution using the MODIS land-mask. Percent urban is derived from LandScan 2004, a population density dataset derived from census data, nighttime lights satellite observations, road proximity and slope (:ref:`Dobson et al. 2000 `) as described by :ref:`Jackson et al. (2010) ` at 1km resolution and aggregated to 0.05°. A number of urban radiative, thermal, and morphological fields are also required and are obtained from :ref:`Jackson et al. (2010) `. Their description can be found in Table 3 of the Community Land Model Urban (CLMU) technical note (:ref:`Oleson et al. 2010b `). + +Percent PFTs are derived from MODIS satellite data as described in :ref:`Lawrence and Chase (2007) ` (section 21.3.3). Prescribed PFT leaf area index is derived from the MODIS satellite data of :ref:`Myneni et al. (2002) ` using the de-aggregation methods described in :ref:`Lawrence and Chase (2007) ` (section 2.2.3). Prescribed PFT stem area index is derived from PFT leaf area index phenology combined with the methods of :ref:`Zeng et al. (2002) `. Prescribed canopy top and bottom heights are from :ref:`Bonan (1996) ` as described in :ref:`Bonan et al. (2002b) `. If the biogeochemistry model is active, it supplies the leaf and stem area index and canopy top and bottom heights dynamically, and the prescribed values are ignored. + +Soil color determines dry and saturated soil albedo (section :numref:`Ground Albedos`). Soil colors are from :ref:`Lawrence and Chase (2007) `. + +The soil texture and organic matter content determine soil thermal and hydrologic properties (sections 6.3 and 7.4.1). The International Geosphere-Biosphere Programme (IGBP) soil dataset (Global Soil Data Task 2000) of 4931 soil mapping units and their sand and clay content for each soil layer were used to create a mineral soil texture dataset :ref:`(Bonan et al. 2002b) `. Soil organic matter data is merged from two sources. The majority of the globe is from ISRIC-WISE (:ref:`Batjes, 2006 `). The high latitudes come from the 0.25° version of the Northern Circumpolar Soil Carbon Database (:ref:`Hugelius et al. 2012 `). Both datasets report carbon down to 1m depth. Carbon is partitioned across the top seven CLM4 layers (:math:`\sim`\ 1m depth) as in :ref:`Lawrence and Slater (2008) `. + +The maximum fractional saturated area (:math:`f_{\max }` ) is used in determining surface runoff and infiltration (section 7.3). Maximum fractional saturated area at 0.125° resolution is calculated from 1-km compound topographic indices (CTIs) based on the USGS HYDRO1K dataset (:ref:`Verdin and Greenlee 1996 `) following the algorithm in :ref:`Niu et al. (2005) `. :math:`f_{\max }` is the ratio between the number of 1-km pixels with CTIs equal to or larger than the mean CTI and the total number of pixels in a 0.125° grid cell. See section 7.3.1 and :ref:`Li et al. (2013b) ` for further details. Slope and elevation are also obtained from the USGS HYDRO1K 1-km dataset (:ref:`Verdin and Greenlee 1996 `). Slope is used in the surface water parameterization (section :numref:`Surface Water Storage`), and elevation is used to calculate the grid cell standard deviation of topography for the snow cover fraction parameterization (section :numref:`Snow Covered Area Fraction`). + +Biogenic Volatile Organic Compounds emissions factors are from the Model of Emissions of Gases and Aerosols from Nature version 2.1 (MEGAN2.1; :ref:`Guenther et al. 2012 `). + +The default list of PFTs includes an unmanaged crop treated as a second C3 grass (:numref:`Table Plant functional types`). The unmanaged crop has grid cell fractional cover assigned from MODIS satellite data (:ref:`Lawrence and Chase (2007) `). A managed crop option uses grid cell fractional cover from the present-day crop dataset of :ref:`Ramankutty and Foley (1998) ` (CLM4CNcrop). Managed crops are assigned in the proportions given by :ref:`Ramankutty and Foley (1998) ` without exceeding the area previously assigned to the unmanaged crop. The unmanaged crop continues to occupy any of its original area that remains and continues to be handled just by the CN part of CLM4CNcrop. The managed crop types (corn, soybean, and temperate cereals) were chosen based on the availability of corresponding algorithms in AgroIBIS (:ref:`Kucharik et al. 2000 `; :ref:`Kucharik and Brye 2003 `). Temperate cereals include wheat, barley, and rye here. All temperate cereals are treated as summer crops (like spring wheat, for example) at this time. Winter cereals (such as winter wheat) may be introduced in a future version of the model. + +To allow crops to coexist with natural vegetation in a grid cell and be treated by separate models (i.e., CLM4.5BGCcrop versus the Dynamic Vegetation version (CLM4.5BGCDV)), we separate the vegetated land unit into a naturally vegetated land unit and a human managed land unit. PFTs in the naturally vegetated land unit share one soil column and compete for water (default CLM setting). PFTs in the human managed land unit do not share soil columns and thus permit for differences in land management between crops. + +CLM includes the option to irrigate cropland areas that are equipped for irrigation. The application of irrigation responds dynamically to climate (see Chapter :numref:`rst_Crops and Irrigation`). In CLM, irrigation is implemented for the C3 generic crop only. When irrigation is enabled, the cropland area of each grid cell is divided into an irrigated and unirrigated fraction according to a dataset of areas equipped for irrigation (:ref:`Siebert et al. (2005) `). The area of irrigated cropland in each grid cell is given by the smaller of the grid cell's total cropland area, according to the default CLM4 dataset, and the grid cell's area equipped for irrigation. The remainder of the grid cell's cropland area (if any) is then assigned to unirrigated cropland. Irrigated and unirrigated crops are placed on separate soil columns, so that irrigation is only applied to the soil beneath irrigated crops. + +Several input datasets are required for the fire model (:ref:`Li et al. 2013a `) including population density, gross domestic production, peat area fraction, and peak month of agricultural waste burning. Population density at 0.5° resolution for 1850-2100 combines 5-min resolution decadal population density data for 1850–1980 from the Database of the Global Environment version 3.1 (HYDEv3.1) with 0.5° resolution population density data for 1990, 1995, 2000, and 2005 from the Gridded Population of the World version 3 dataset (GPWv3) (CIESIN, 2005). Gross Domestic Production (GDP) per capita in 2000 at 0.5° is from :ref:`Van Vuuren et al. (2006) `, which is the base-year GDP data for IPCC-SRES and derived from country-level World Bank's World Development Indicators (WDI) measured in constant 1995 US$ (:ref:`World Bank, 2004 `) and the UN Statistics Database (:ref:`UNSTAT, 2005 `). The peatland area fraction at 0.5° resolution is derived from three vector datasets: peatland data in Indonesia and Malaysian Borneo (:ref:`Olson et al. 2001 `); peatland data in Canada (:ref:`Tarnocai et al. 2011 `); and bog, fen and mire data in boreal regions (north of 45°N) outside Canada provided by the Global Lakes and Wetlands Database (GLWD) (:ref:`Lehner and Döll, 2004 `). The climatological peak month for agricultural waste burning is from :ref:`van der Werf et al. (2010) `. .. _Adjustable Parameters and Physical Constants: Adjustable Parameters and Physical Constants ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Values of certain adjustable parameters inherent in the biogeophysical -or biogeochemical parameterizations have either been obtained from the -literature or calibrated based on comparisons with observations. These -are described in the text. Physical constants, generally shared by all -of the components in the coupled modeling system, are presented in -:numref:`Table Physical constants`. - +Values of certain adjustable parameters inherent in the biogeophysical or biogeochemical parameterizations have either been obtained from the literature or calibrated based on comparisons with observations. These are described in the text. Physical constants, generally shared by all of the components in the coupled modeling system, are presented in :numref:`Table Physical constants`. .. _Table Physical constants: @@ -875,16 +456,16 @@ of the components in the coupled modeling system, are presented in :widths: 40, 20, 20, 20 "Pi", :math:`\pi`, 3.14159265358979323846, "\-" - "Acceleration of gravity", :math:`g`, 9.80616, m s\ :sup:`-2` + "Acceleration of gravity", :math:`g`, 9.80616, m s\ :sup:`-2` "Standard pressure", :math:`P_{std}`, 101325, "Pa" "Stefan-Boltzmann constant", :math:`\sigma`, 5.67 :math:`\times 10^{-8}`, W m :sup:`-2` K :math:`{}^{-4}` "Boltzmann constant", :math:`\kappa`, 1.38065 :math:`\times 10^{-23}`, J K :sup:`-1` molecule :sup:`-1` - "Avogadro’s number", :math:`N_{A}`, 6.02214 :math:`\times 10^{26}`, molecule kmol\ :sup:`-1` + "Avogadro's number", :math:`N_{A}`, 6.02214 :math:`\times 10^{26}`, molecule kmol\ :sup:`-1` "Universal gas constant", :math:`R_{gas}`, :math:`N_{A} \kappa`, J K :sup:`-1` kmol :sup:`-1` "Molecular weight of dry air", :math:`MW_{da}`, 28.966, kg kmol :sup:`-1` - "Dry air gas constant", :math:`R_{da}`, :math:`{R_{gas} \mathord{\left/ {\vphantom {R_{gas} MW_{da} }} \right. \kern-\nulldelimiterspace} MW_{da} }`, J K :sup:`-1` kg :sup:`-1` + "Dry air gas constant", :math:`R_{da}`, :math:`{R_{gas} \mathord{\left/ {\vphantom {R_{gas} MW_{da} }} \right.} MW_{da} }`, J K :sup:`-1` kg :sup:`-1` "Molecular weight of water vapor", :math:`MW_{wv}`, 18.016, kg kmol :sup:`-1` - "Water vapor gas constant", :math:`R_{wv}`, :math:`{R_{gas} \mathord{\left/ {\vphantom {R_{gas} MW_{wv} }} \right. \kern-\nulldelimiterspace} MW_{wv} }`, J K :sup:`-1` kg :sup:`-1` + "Water vapor gas constant", :math:`R_{wv}`, :math:`{R_{gas} \mathord{\left/ {\vphantom {R_{gas} MW_{wv} }} \right.} MW_{wv} }`, J K :sup:`-1` kg :sup:`-1` "Von Karman constant", :math:`k`, 0.4, "\-" "Freezing temperature of fresh water", :math:`T_{f}`, 273.15, K "Density of liquid water", :math:`\rho _{liq}`, 1000, kg m :sup:`-3` diff --git a/doc/source/tech_note/External_Nitrogen_Cycle/CLM50_Tech_Note_External_Nitrogen_Cycle.rst b/doc/source/tech_note/External_Nitrogen_Cycle/CLM50_Tech_Note_External_Nitrogen_Cycle.rst index ca7a4a9d2c..f594778562 100644 --- a/doc/source/tech_note/External_Nitrogen_Cycle/CLM50_Tech_Note_External_Nitrogen_Cycle.rst +++ b/doc/source/tech_note/External_Nitrogen_Cycle/CLM50_Tech_Note_External_Nitrogen_Cycle.rst @@ -21,279 +21,120 @@ CLM5.0 includes the following changes to terrestrial nitrogen inputs: Overview ----------------------------------------------------- -In addition to the relatively rapid cycling of nitrogen within the plant -– litter – soil organic matter system, CLM also represents several -processes which couple the internal nitrogen cycle to external sources -and sinks. Inputs of new mineral nitrogen are from atmospheric -deposition and biological nitrogen fixation. Losses of mineral nitrogen -are due to nitrification, denitrification, leaching, and losses in fire. -While the short-term dynamics of nitrogen limitation depend on the -behavior of the internal nitrogen cycle, establishment of total -ecosystem nitrogen stocks depends on the balance between sources and -sinks in the external nitrogen cycle (:ref:`Thomas et al. 2015 `). - -As with CLM4.5, CLM5.0 represents inorganic N transformations based on the Century N-gas model; this -includes separate NH\ :sub:`4`\ :sup:`+` and -NO\ :sub:`3`\ :sup:`-` pools, as well as -environmentally controlled nitrification and denitrification rates that is described below. +In addition to the relatively rapid cycling of nitrogen within the plant – litter – soil organic matter system, CLM also represents several processes which couple the internal nitrogen cycle to external sources and sinks. Inputs of new mineral nitrogen are from atmospheric deposition and biological nitrogen fixation. Losses of mineral nitrogen are due to nitrification, denitrification, leaching, and losses in fire. While the short-term dynamics of nitrogen limitation depend on the behavior of the internal nitrogen cycle, establishment of total ecosystem nitrogen stocks depends on the balance between sources and sinks in the external nitrogen cycle (:ref:`Thomas et al. 2015 `). + +As with CLM4.5, CLM5.0 represents inorganic N transformations based on the Century N-gas model; this includes separate NH\ :sub:`4`\ :sup:`+` and NO\ :sub:`3`\ :sup:`-` pools, as well as environmentally controlled nitrification and denitrification rates that is described below. Atmospheric Nitrogen Deposition ------------------------------------ -CLM uses a single variable to represent the total deposition of mineral -nitrogen onto the land surface, combining wet and dry deposition of -NO\ :sub:`y` and NH\ :sub:`x` as a single flux -(:math:`{NF}_{ndep\_sminn}`, gN m\ :sup:`-2` s\ :sup:`-1`). This flux -is intended to represent total reactive -nitrogen deposited to the land surface which originates from the -following natural and anthropogenic sources (Galloway et al. 2004): -formation of NO\ :sub:`x` during lightning, -NO\ :math:`{}_{x }`\ and NH\ :sub:`3` emission from wildfire, -NO\ :sub:`x` emission from natural soils, NH\ :sub:`3` -emission from natural soils, vegetation, and wild animals, -NO\ :sub:`x` and NH\ :sub:`3` emission during fossil fuel -combustion (both thermal and fuel NO\ :sub:`x` production), -NO\ :sub:`x` and NH\ :sub:`3` emission from other industrial -processes, NO\ :sub:`x` and NH\ :sub:`3` emission from fire -associated with deforestation, NO\ :sub:`x` and NH\ :sub:`3` -emission from agricultural burning, NO\ :sub:`x` emission from -agricultural soils, NH\ :sub:`3` emission from agricultural crops, -NH\ :sub:`3` emission from agricultural animal waste, and -NH\ :sub:`3` emission from human waste and waste water. The -deposition flux is provided as a spatially and (potentially) temporally -varying dataset (see section :numref:`Atmospheric Coupling` for a description of the default -input dataset). - -The nitrogen deposition flux is assumed to enter the NH\ :sub:`4`\ :sup:`+` pool, -and is vertically distributed throughout the soil profile. Although N deposition -inputs include both oxidized and reduced forms, CLM5 only reads in total -N deposition. This approach is held over from CLM4.0, which only represented a -single mineral nitrogen pool, however, real pathways for wet and dry -nitrogen deposition can be more complex than currently represented in -the CLM5.0, including release from melting snowpack and direct foliar -uptake of deposited NO\ :sub:`y` (:ref:`Tye et al. 2005 `; -:ref:`Vallano and Sparks, 2007 `). - -In offline (uncoupled) CLM5.0 simulations monthly -estimates of N deposition are provided, as opposed to decadal files -supplied with previous versions of the model. In coupled simulations, -N depositions fluxes are passed to the land model at the frequency of -the time step (every half hour) through the coupler. +CLM uses a single variable to represent the total deposition of mineral nitrogen onto the land surface, combining wet and dry deposition of NO\ :sub:`y` and NH\ :sub:`x` as a single flux (:math:`{NF}_{ndep\_sminn}`, gN m\ :sup:`-2` s\ :sup:`-1`). This flux is intended to represent total reactive nitrogen deposited to the land surface which originates from the following natural and anthropogenic sources (Galloway et al. 2004): formation of NO\ :sub:`x` during lightning, NO\ :math:`{}_{x }`\ and NH\ :sub:`3` emission from wildfire, NO\ :sub:`x` emission from natural soils, NH\ :sub:`3` emission from natural soils, vegetation, and wild animals, NO\ :sub:`x` and NH\ :sub:`3` emission during fossil fuel combustion (both thermal and fuel NO\ :sub:`x` production), NO\ :sub:`x` and NH\ :sub:`3` emission from other industrial processes, NO\ :sub:`x` and NH\ :sub:`3` emission from fire associated with deforestation, NO\ :sub:`x` and NH\ :sub:`3` emission from agricultural burning, NO\ :sub:`x` emission from agricultural soils, NH\ :sub:`3` emission from agricultural crops, NH\ :sub:`3` emission from agricultural animal waste, and NH\ :sub:`3` emission from human waste and waste water. The deposition flux is provided as a spatially and (potentially) temporally varying dataset (see section :numref:`Atmospheric Coupling` for a description of the default input dataset). + +The nitrogen deposition flux is assumed to enter the NH\ :sub:`4`\ :sup:`+` pool, and is vertically distributed throughout the soil profile. Although N deposition inputs include both oxidized and reduced forms, CLM5 only reads in total N deposition. This approach is held over from CLM4.0, which only represented a single mineral nitrogen pool, however, real pathways for wet and dry nitrogen deposition can be more complex than currently represented in the CLM5.0, including release from melting snowpack and direct foliar uptake of deposited NO\ :sub:`y` (:ref:`Tye et al. 2005 `; :ref:`Vallano and Sparks, 2007 `). +In offline (uncoupled) CLM5.0 simulations monthly estimates of N deposition are provided, as opposed to decadal files supplied with previous versions of the model. In coupled simulations, N depositions fluxes are passed to the land model at the frequency of the time step (every half hour) through the coupler. Biological Nitrogen Fixation --------------------------------- -The fixation of new reactive nitrogen from atmospheric N\ :sub:`2` -by soil microorganisms is an important component of both preindustrial -and modern-day nitrogen budgets, but a mechanistic understanding of -global-scale controls on biological nitrogen fixation (BNF) is still -only poorly developed (:ref:`Cleveland et al. 1999 `; -:ref:`Galloway et al. 2004 `). CLM5.0 uses the FUN -model (chapter :numref:`rst_FUN`) to -calculate the carbon cost and nitrogen acquired through symbotic -nitrogen fixation. This nitrogen is immediately available to plants. - -:ref:`Cleveland et al. (1999) ` suggested -an empirical relationships that predicts BNF as a function of -either evapotranspiration rate or net primary productivity for -natural vegetation. CLM5.0 adopts the evapotranspiration approach -to calculate asymbiotic, or free-living, N fixation. This function -has been modified from the :ref:`Cleveland et al. (1999) -` estimates to provide lower estimate of -free-living nitrogen fixation in CLM5.0 -(:math:`{CF}_{ann\_ET}`, mm yr\ :sup:`-1`). -This moves away from the NPP approach used in CLM4.0 and 4.5 and -avoids unrealistically increasing freeliving rates of N fixation -under global change scenarios (:ref:`Wieder et al. 2015 -` The expression used is: +The fixation of new reactive nitrogen from atmospheric N\ :sub:`2` by soil microorganisms is an important component of both preindustrial and modern-day nitrogen budgets, but a mechanistic understanding of global-scale controls on biological nitrogen fixation (BNF) is still only poorly developed (:ref:`Cleveland et al. 1999 `; :ref:`Galloway et al. 2004 `). CLM5.0 uses the FUN model (chapter :numref:`rst_FUN`) to calculate the carbon cost and nitrogen acquired through symbotic nitrogen fixation. This nitrogen is immediately available to plants. + +:ref:`Cleveland et al. (1999) ` suggested an empirical relationships that predicts BNF as a function of either evapotranspiration rate or net primary productivity for natural vegetation. CLM5.0 adopts the evapotranspiration approach to calculate asymbiotic, or free-living, N fixation. This function has been modified from the :ref:`Cleveland et al. (1999) ` estimates to provide lower estimate of free-living nitrogen fixation in CLM5.0 (:math:`{CF}_{ann\_ET}`, mm yr\ :sup:`-1`). This moves away from the NPP approach used in CLM4.0 and 4.5 and avoids unrealistically increasing freeliving rates of N fixation under global change scenarios (:ref:`Wieder et al. 2015 ` The expression used is: .. math:: - :label: 22.1) + :label: 22.1) - NF_{nfix,sminn} ={0.0006\left(0.0117+CF_{ann\_ ET}\right)\mathord{\left/ {\vphantom {0.0006\left(0.0117+ CF_{ann\_ ET}\right) \left(86400\cdot 365\right)}} \right. \kern-\nulldelimiterspace} \left(86400\cdot 365\right)} + NF_{nfix,sminn} ={0.0006\left(0.0117+CF_{ann\_ ET}\right)\mathord{\left/ {\vphantom {0.0006\left(0.0117+ CF_{ann\_ ET}\right) \left(86400\cdot 365\right)}} \right.} \left(86400\cdot 365\right)} Where :math:`{NF}_{nfix,sminn}` (gN m\ :sup:`-2` s\ :sup:`-1`) is the rate of free-living nitrogen fixation in :numref:`Figure Biological nitrogen fixation`. - .. _Figure Biological nitrogen fixation: .. figure:: image1.png Free-living nitrogen fixation as a function of annual evapotranspiration. Results here show annual N inputs from free-living N fixations, but the model actually calculates inputs on a per second basis. -As with Atmospheric N deposition, free-living N inputs are added directly to the -NH\ :sub:`4`\ :sup:`+` pool. +As with Atmospheric N deposition, free-living N inputs are added directly to the NH\ :sub:`4`\ :sup:`+` pool. Nitrification and Denitrification Losses of Nitrogen --------------------------------------------------------- -Nitrification is an autotrophic process that converts less mobile ammonium -ions into nitrate, that can more easily be lost from soil systems by leaching -or denitrification. The process catalyzed by ammonia oxidizing archaea and -bacteria that convert ammonium (NH\ :sub:`4`\ :sup:`+`) into nitrite, which -is subsequently oxidized into nitrate (NO\ :sub:`3`\ :sup:`-`). Conditions -favoring nitrification include high NH\ :sub:`4`\ :sup:`+` concentrations, -well aerated soils, a neutral pH and warmer temperatures. - -Under aerobic conditions in the soil oxygen is the preferred electron -acceptor supporting the metabolism of heterotrophs, but anaerobic -conditions favor the activity of soil heterotrophs which use nitrate as -an electron acceptor (e.g. *Pseudomonas* and *Clostridium*) supporting -respiration. This process, known as denitrification, results in the -transformation of nitrate to gaseous N\ :sub:`2`, with smaller -associated production of NO\ :sub:`x` and N\ :sub:`2`\ O. It -is typically assumed that nitrogen fixation and denitrification -were approximately balanced in the preindustrial biosphere ( -:ref:`Galloway et al. 2004 `). It is likely -that denitrification can occur within anaerobic -microsites within an otherwise aerobic soil environment, leading to -large global denitrification fluxes even when fluxes per unit area are -rather low (:ref:`Galloway et al. 2004 `). - -CLM includes a detailed representation of nitrification and -denitrification based on the Century N model (:ref:`Parton -et al. 1996 `, :ref:`2001 `; -:ref:`del Grosso et al. 2000 `). In this -approach, nitrification of NH\ :sub:`4`\ :sup:`+` to NO\ :sub:`3`\ :sup:`-` -is a function of temperature, moisture, and pH: +Nitrification is an autotrophic process that converts less mobile ammonium ions into nitrate, that can more easily be lost from soil systems by leaching or denitrification. The process catalyzed by ammonia oxidizing archaea and bacteria that convert ammonium (NH\ :sub:`4`\ :sup:`+`) into nitrite, which is subsequently oxidized into nitrate (NO\ :sub:`3`\ :sup:`-`). Conditions favoring nitrification include high NH\ :sub:`4`\ :sup:`+` concentrations, well aerated soils, a neutral pH and warmer temperatures. + +Under aerobic conditions in the soil oxygen is the preferred electron acceptor supporting the metabolism of heterotrophs, but anaerobic conditions favor the activity of soil heterotrophs which use nitrate as an electron acceptor (e.g. *Pseudomonas* and *Clostridium*) supporting respiration. This process, known as denitrification, results in the transformation of nitrate to gaseous N\ :sub:`2`, with smaller associated production of NO\ :sub:`x` and N\ :sub:`2`\ O. It is typically assumed that nitrogen fixation and denitrification were approximately balanced in the preindustrial biosphere ( :ref:`Galloway et al. 2004 `). It is likely that denitrification can occur within anaerobic microsites within an otherwise aerobic soil environment, leading to large global denitrification fluxes even when fluxes per unit area are rather low (:ref:`Galloway et al. 2004 `). + +CLM includes a detailed representation of nitrification and denitrification based on the Century N model (:ref:`Parton et al. 1996 `, :ref:`2001 `; :ref:`del Grosso et al. 2000 `). In this approach, nitrification of NH\ :sub:`4`\ :sup:`+` to NO\ :sub:`3`\ :sup:`-` is a function of temperature, moisture, and pH: .. math:: - :label: 22.2) + :label: 22.2) f_{nitr,p} =\left[NH_{4} \right]k_{nitr} f\left(T\right)f\left(H_{2} O\right)f\left(pH\right) -where :math:`{f}_{nitr,p}` is the potential nitrification rate -(prior to competition for NH\ :sub:`4`\ :sup:`+` by plant -uptake and N immobilization), :math:`{k}_{nitr}` is the maximum -nitrification rate (10 % day\ :math:`\mathrm{-}`\ 1, -(:ref:`Parton et al. 2001 `), and *f(T)* and -*f(H\)*\ :sub:`2`\ O) are rate modifiers for temperature and -moisture content. CLM uses the same rate modifiers as -are used in the decomposition routine. *f(pH)* is a rate -modifier for pH; however, because CLM does not calculate pH, -instead a fixed pH value of 6.5 is used in the pH function of -:ref:`Parton et al. (1996) `. - -The potential denitrification rate is co-limited by -NO\ :sup:`-3` concentration and C consumption rates, and occurs only in the anoxic fraction of soils: +where :math:`{f}_{nitr,p}` is the potential nitrification rate (prior to competition for NH\ :sub:`4`\ :sup:`+` by plant uptake and N immobilization), :math:`{k}_{nitr}` is the maximum nitrification rate (10 % day\ :math:`\mathrm{-}`\ 1, (:ref:`Parton et al. 2001 `), and *f(T)* and *f(H\)*\ :sub:`2`\ O) are rate modifiers for temperature and moisture content. CLM uses the same rate modifiers as are used in the decomposition routine. *f(pH)* is a rate modifier for pH; however, because CLM does not calculate pH, instead a fixed pH value of 6.5 is used in the pH function of :ref:`Parton et al. (1996) `. + +The potential denitrification rate is co-limited by NO\ :sup:`-3` concentration and C consumption rates, and occurs only in the anoxic fraction of soils: .. math:: - :label: 22.3) + :label: 22.3) f_{denitr,p} =\min \left(f(decomp),f\left(\left[NO_{3} ^{-} \right]\right)\right)frac_{anox} -where :math:`{f}_{denitr,p}` is the potential denitrification rate -and *f(decomp)* and *f([NO*\ :sub:`3`\ :sup:`-` *])* -are the carbon- and nitrate- limited denitrification rate functions, -respectively, (:ref:`del Grosso et al. 2000 `). -Because the modified CLM includes explicit treatment of soil -biogeochemical vertical profiles, including diffusion of the trace -gases O\ :sub:`2` and CH\ :sub:`4` (:ref:`Riley et al. 2011a -`), the calculation of anoxic fraction :math:`{frac}_{anox}` -uses this information following the anoxic microsite formulation -of :ref:`Arah and Vinten (1995) `. +where :math:`{f}_{denitr,p}` is the potential denitrification rate and *f(decomp)* and *f([NO*\ :sub:`3`\ :sup:`-` *])* are the carbon- and nitrate- limited denitrification rate functions, respectively, (:ref:`del Grosso et al. 2000 `). Because the modified CLM includes explicit treatment of soil biogeochemical vertical profiles, including diffusion of the trace gases O\ :sub:`2` and CH\ :sub:`4` (:ref:`Riley et al. 2011a `), the calculation of anoxic fraction :math:`{frac}_{anox}` uses this information following the anoxic microsite formulation of :ref:`Arah and Vinten (1995) `. .. math:: - :label: 22.4) + :label: 22.4) frac_{anox} =\exp \left(-aR_{\psi }^{-\alpha } V^{-\beta } C^{\gamma } \left[\theta +\chi \varepsilon \right]^{\delta } \right) -where *a*, :math:`\alpha`, :math:`\beta`, :math:`\gamma`, and :math:`\delta` are constants (equal to -1.5x10\ :sup:`-10`, 1.26, 0.6, 0.6, and 0.85, respectively), :math:`{R}_{\psi}` is the -radius of a typical pore space at moisture content :math:`\psi`, *V* -is the O\ :sub:`2` consumption rate, *C* is the O\ :sub:`2` -concentration, :math:`\theta` is the water-filled pore space, -:math:`\chi` is the ratio of diffusivity of oxygen in water to that in -air, and :math:`\epsilon` is the air-filled pore space (:ref:`Arah and -Vinten (1995) `). These parameters are all calculated -separately at each -layer to define a profile of anoxic porespace fraction in the soil. - -The nitrification/denitrification models used here also predict fluxes -of N\ :sub:`2`\ O via a “hole-in-the-pipe” approach (:ref:`Firestone and -Davidson, 1989 `). A constant fraction -(6 \* 10\ :math:`{}^{-4}`, :ref:`Li et al. 2000 `) of the -nitrification flux is assumed to be N\ :sub:`2`\ O, while the fraction -of denitrification going to N\ :sub:`2`\ O, \ :math:`{P}_{N2:N2O}`, is variable, following -the Century (:ref:`del Grosso et al. 2000 `) approach: +where *a*, :math:`\alpha`, :math:`\beta`, :math:`\gamma`, and :math:`\delta` are constants (equal to 1.5x10\ :sup:`-10`, 1.26, 0.6, 0.6, and 0.85, respectively), :math:`{R}_{\psi}` is the radius of a typical pore space at moisture content :math:`\psi`, *V* is the O\ :sub:`2` consumption rate, *C* is the O\ :sub:`2` concentration, :math:`\theta` is the water-filled pore space, :math:`\chi` is the ratio of diffusivity of oxygen in water to that in air, and :math:`\epsilon` is the air-filled pore space (:ref:`Arah and Vinten (1995) `). These parameters are all calculated separately at each layer to define a profile of anoxic porespace fraction in the soil. + +The nitrification/denitrification models used here also predict fluxes of N\ :sub:`2`\ O via a "hole-in-the-pipe" approach (:ref:`Firestone and Davidson, 1989 `). A constant fraction (6 * 10\ :math:`{}^{-4}`, :ref:`Li et al. 2000 `) of the nitrification flux is assumed to be N\ :sub:`2`\ O, while the fraction of denitrification going to N\ :sub:`2`\ O, \ :math:`{P}_{N2:N2O}`, is variable, following the Century (:ref:`del Grosso et al. 2000 `) approach: .. math:: - :label: 22.5) + :label: 22.5) P_{N_{2} :N_{2} O} =\max \left(0.16k_{1} ,k_{1} \exp \left(-0.8P_{NO_{3} :CO_{2} } \right)\right)f_{WFPS} -where :math:`{P}_{NO3:CO2}` is the ratio of CO\ :sub:`2` -production in a given soil layer to the -NO\ :sub:`3`\ :sup:`-`` concentration, :math:`{k}_{1}` is -a function of :math:`{d}_{g}`, the gas diffusivity through the soil -matrix: +where :math:`{P}_{NO3:CO2}` is the ratio of CO\ :sub:`2` production in a given soil layer to the NO\ :sub:`3`\ :sup:`-` concentration, :math:`{k}_{1}` is a function of :math:`{d}_{g}`, the gas diffusivity through the soil matrix: .. math:: - :label: 22.6) + :label: 22.6) k_{1} =\max \left(1.7,38.4-350*d_{g} \right) and :math:`{f}_{WFPS}` is a function of the water filled pore space *WFPS:* .. math:: - :label: 22.16) + :label: 22.16) f_{WFPS} =\max \left(0.1,0.015\times WFPS-0.32\right) Leaching Losses of Nitrogen -------------------------------- -Soil mineral nitrogen remaining after plant uptake, immobilization, and -denitrification is subject to loss as a dissolved component of -hydrologic outflow from the soil column (leaching). This leaching loss -(:math:`{NF}_{leached}`, gN m\ :sup:`-2` s\ :sup:`-1`) -depends on the concentration of dissolved mineral (inorganic) nitrogen -in soil water solution (*DIN*, gN kgH\ :sub:`2`\ O), and the rate -of hydrologic discharge from the soil column to streamflow -(:math:`{Q}_{dis}`, kgH\ :sub:`2`\ O m\ :sup:`-2` -s\ :sup:`-1`, section :numref:`Lateral Sub-surface Runoff`), as +Soil mineral nitrogen remaining after plant uptake, immobilization, and denitrification is subject to loss as a dissolved component of hydrologic outflow from the soil column (leaching). This leaching loss (:math:`{NF}_{leached}`, gN m\ :sup:`-2` s\ :sup:`-1`) depends on the concentration of dissolved mineral (inorganic) nitrogen in soil water solution (*DIN*, gN kgH\ :sub:`2`\ O), and the rate of hydrologic discharge from the soil column to streamflow (:math:`{Q}_{dis}`, kgH\ :sub:`2`\ O m\ :sup:`-2` s\ :sup:`-1`, section :numref:`Lateral Sub-surface Runoff`), as .. math:: - :label: 22.17) + :label: 22.17) NF_{leached} =DIN\cdot Q_{dis} . -*DIN* is calculated assuming that a constant fraction (*sf*, proportion) -of the remaining soil mineral N pool is in soluble form, and that this -entire fraction is dissolved in the total soil water. For the Century- -based formulation in CLM5.0, the leaching acts only on the -NO\ :sub:`3`\ :sup:`-`` pool (which is assumed to be 100% -soluble), while the NH\ :sub:`4`\ :sup:`+` pool is assumed -to be 100% adsorbed onto mineral surfaces and unaffected by leaching. -*DIN* is then given as +*DIN* is calculated assuming that a constant fraction (*sf*, proportion) of the remaining soil mineral N pool is in soluble form, and that this entire fraction is dissolved in the total soil water. For the Century- based formulation in CLM5.0, the leaching acts only on the NO\ :sub:`3`\ :sup:`-` pool (which is assumed to be 100% soluble), while the NH\ :sub:`4`\ :sup:`+` pool is assumed to be 100% adsorbed onto mineral surfaces and unaffected by leaching. *DIN* is then given as .. math:: - :label: 22.18) + :label: 22.18) DIN=\frac{NS_{sminn} sf}{WS_{tot\_ soil} } -where :math:`{WS}_{tot\_soil}` (kgH:sub:`2`\ O m\ :sup:`-2`) is the total mass of soil water content integrated -over the column. The total mineral nitrogen leaching flux is limited on -each time step to not exceed the soluble fraction of :math:`{NS}_{sminn}` +where :math:`{WS}_{tot\_soil}` (kgH\ :sub:`2`\ O m\ :sup:`-2`) is the total mass of soil water content integrated over the column. The total mineral nitrogen leaching flux is limited on each time step to not exceed the soluble fraction of :math:`{NS}_{sminn}` .. math:: - :label: 22.19) + :label: 22.19) NF_{leached} =\min \left(NF_{leached} ,\frac{NS_{sminn} sf}{\Delta t} \right). Losses of Nitrogen Due to Fire ----------------------------------- -The final pathway for nitrogen loss is through combustion, also known as -pyrodenitrification. Detailed equations are provided, together with the -effects of fire on the carbon budget, in Chapter :numref:`rst_Fire`). It is assumed in -CLM-CN that losses of N due to fire are restricted to vegetation and -litter pools (including coarse woody debris). Loss rates of N are -determined by the fraction of biomass lost to combustion, assuming that -most of the nitrogen in the burned biomass is lost to the atmosphere -(:ref:`Schlesinger, 1997 `; :ref:`Smith et al. 2005 -`). It is assumed that soil organic -matter pools of carbon and nitrogen are not directly affected by fire -(:ref:`Neff et al. 2005 `). +The final pathway for nitrogen loss is through combustion, also known as pyrodenitrification. Detailed equations are provided, together with the effects of fire on the carbon budget, in Chapter :numref:`rst_Fire`. It is assumed in CLM-CN that losses of N due to fire are restricted to vegetation and litter pools (including coarse woody debris). Loss rates of N are determined by the fraction of biomass lost to combustion, assuming that most of the nitrogen in the burned biomass is lost to the atmosphere (:ref:`Schlesinger, 1997 `; :ref:`Smith et al. 2005 `). It is assumed that soil organic matter pools of carbon and nitrogen are not directly affected by fire (:ref:`Neff et al. 2005 `). diff --git a/doc/source/tech_note/FUN/CLM50_Tech_Note_FUN.rst b/doc/source/tech_note/FUN/CLM50_Tech_Note_FUN.rst index 658653da48..428f114a5d 100644 --- a/doc/source/tech_note/FUN/CLM50_Tech_Note_FUN.rst +++ b/doc/source/tech_note/FUN/CLM50_Tech_Note_FUN.rst @@ -6,7 +6,6 @@ Fixation and Uptake of Nitrogen (FUN) Introduction ----------------- - The Fixation and Uptake of Nitrogen model is based on work by :ref:`Fisher et al. (2010)`, :ref:`Brzostek et al. (2014)`, and :ref:`Shi et al. (2016)`. The concept of FUN is that in most cases, Nitrogen uptake requires the expenditure of energy in the form of carbon, and further, that there are numerous potential sources of Nitrogen in the environment which a plant may exchange for carbon. The ratio of carbon expended to Nitrogen acquired is referred to here as the cost, or exchange rate, of N acquisition (:math:`E_{nacq}`, gC/gN)). There are eight pathways for N uptake: 1. Fixation by symbiotic bacteria in root nodules (for N fixing plants) (:math:`_{fix}`) @@ -18,48 +17,41 @@ The Fixation and Uptake of Nitrogen model is based on work by :ref:`Fisher et al 7. Nonmycorrhizal uptake of NH4 (:math:`_{nonmyc,no3}`) 8. Nonmycorrhizal uptake of NO3 (:math:`_{nonmyc,nh4}`) - -The notation suffix for each pathway is given in parentheses here. At each timestep, each of these pathways is associated with a cost term (:math:`N_{cost,x}`), a payment in carbon (:math:`C_{nuptake,x}`), and an influx of Nitrogen (:math:`N_{uptake,x}`) where :math:`x` is one of the eight uptake streams listed above. - +The notation suffix for each pathway is given in parentheses here. At each timestep, each of these pathways is associated with a cost term (:math:`N_{cost,x}`), a payment in carbon (:math:`C_{nuptake,x}`), and an influx of Nitrogen (:math:`N_{uptake,x}`) where :math:`x` is one of the eight uptake streams listed above. For each PFT, we define a fraction of the total C acquisition that can be used for N fixation (:math:`f_{fixers}`), which is broadly equivalent to the fraction of a given PFT that is capable of fixing Nitrogen, and thus represents an upper limit on the amount to which fixation can be increased in low n conditions. For each PFT, the cost calculation is conducted twice. Once where fixation is possible and once where it is not. (:math:`f_{fixers}`) +For all of the active uptake pathways, whose cost depends on varying concentrations of N through the soil profile, the costs and fluxes are also determined by soil layer :math:`j`. -For all of the active uptake pathways, whose cost depends on varying concentrations of N through the soil profile, the costs and fluxes are also determined by soil layer :math:`j`. - - - -Boundary conditions of FUN +Boundary conditions of FUN -------------------------------------------------------- Available Carbon ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The carbon available for FUN, :math:`C_{avail}` (gC m\ :sup:`-2`) is the total canopy photosynthetic uptake (GPP), minus the maintenance respiration fluxes (:math:`m_r`) and multiplied by the time step in seconds (:math:`\delta t`). Thus, the remainder of this chapter considers fluxes per timestep, and integrates these fluxes as they are calculated. +The carbon available for FUN, :math:`C_{avail}` (gC m\ :sup:`-2`) is the total canopy photosynthetic uptake (GPP), minus the maintenance respiration fluxes (:math:`m_r`) and multiplied by the time step in seconds (:math:`\delta t`). Thus, the remainder of this chapter considers fluxes per timestep, and integrates these fluxes as they are calculated. .. math:: C_{avail} = (GPP - m_r) \delta t -Growth respiration is thus only calculated on the part of the carbon uptake that remains after expenditure of C by the FUN module. +Growth respiration is thus only calculated on the part of the carbon uptake that remains after expenditure of C by the FUN module. Available Soil Nitrogen ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cost of Nitrogen Fixation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The cost of fixation is derived from :ref:`Houlton et al. (2008)`. +The cost of fixation is derived from :ref:`Houlton et al. (2008)`. .. math:: N_{cost,fix} = -s_{fix}/(1.25 e^{a_{fix} + b_{fix} . t_{soil} (1 - 0.5 t_{soil}/ c_{fix}) }) - -Herein, :math:`a_{fix}`, :math:`b_{fix}` and :math:`c_{fix}` are all parameters of the temperature response function of fixation reported by Houlton et al. (2008) (:math:`exp[a+bT_s(1-0.5T_s/c)`). t_{soil} is the soil temperature in C. The values of these parameters are fitted to empirical data as a=-3.62 :math:`\pm` 0.52, b=0.27:math:`\pm` 0.04 and c=25.15 :math:`\pm` 0.66. 1.25 converts from the temperature response function to a 0-1 limitation factor (as specifically employed by Houlton et al.). This function is a 'rate' of uptake for a given temperature. Here we assimilated the rate of fixation into the cost term by assuming that the rate is analagous to a conductance for N, and inverting the term to produce a cost/resistance analagoue. We then multiply this temperature term by the minimum cost at optimal temperature (:math:`s_{fix}`) to give a temperature limited cost in terms of C to N ratios. - +Herein, :math:`a_{fix}`, :math:`b_{fix}` and :math:`c_{fix}` are all parameters of the temperature response function of fixation reported by Houlton et al. (2008) (:math:`exp[a+bT_s(1-0.5T_s/c)`). t_{soil} is the soil temperature in C. The values of these parameters are fitted to empirical data as a=-3.62 :math:`\pm` 0.52, b=0.27:math:`\pm` 0.04 and c=25.15 :math:`\pm` 0.66. 1.25 converts from the temperature response function to a 0-1 limitation factor (as specifically employed by Houlton et al.). This function is a 'rate' of uptake for a given temperature. Here we assimilated the rate of fixation into the cost term by assuming that the rate is analagous to a conductance for N, and inverting the term to produce a cost/resistance analagoue. We then multiply this temperature term by the minimum cost at optimal temperature (:math:`s_{fix}`) to give a temperature limited cost in terms of C to N ratios. Cost of Active Uptake ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The cost of N uptake from soil, for each layer :math:`j`, is controlled by two uptake parameters that pertain respectively to the relationship between soil N content and N uptake, and root C density and N uptake. +The cost of N uptake from soil, for each layer :math:`j`, is controlled by two uptake parameters that pertain respectively to the relationship between soil N content and N uptake, and root C density and N uptake. For non-mycorrhizal uptake: @@ -75,12 +67,11 @@ and for active uptake: where :math:`k_{n,active}` varies according to whether we are considering ecto or arbuscular mycorrhizal uptake. - .. math:: :label: 18.2 - k_{n,active} = - \left\{\begin{array}{lr} + k_{n,active} = + \left\{\begin{array}{lr} k_{n,Eactive}& e = 1\\ k_{n,Aactive}& e = 0 \end{array}\right\} @@ -93,83 +84,79 @@ The total cost of N uptake is calculated based on the assumption that carbon is .. math:: - N_{conductance,f}= \sum{(1/N_{cost,x})} - + N_{conductance,f}= \sum{(1/N_{cost,x})} -From this, we then calculate the fraction of the carbon allocated to each pathway as +From this, we then calculate the fraction of the carbon allocated to each pathway as .. math:: C_{frac,x} = \frac{1/N_{cost,x}}{N_{conductance}} - -These fractions are used later, to calculate the carbon expended on different uptake pathways. Next, the N acquired from each uptake stream per unit C spent (:math:`N_{exch,x}`, gN/gC) is determined as +These fractions are used later, to calculate the carbon expended on different uptake pathways. Next, the N acquired from each uptake stream per unit C spent (:math:`N_{exch,x}`, gN/gC) is determined as .. math:: N_{exch,x} = \frac{C_{frac,x}}{N_{cost,x}} -We then determine the total amount of N uptake per unit C spent (:math:`N_{exch,tot}`, gN/gC) as the sum of all the uptake streams. +We then determine the total amount of N uptake per unit C spent (:math:`N_{exch,tot}`, gN/gC) as the sum of all the uptake streams. .. math:: N_{exch,tot} = \sum{N_{exch,x}} -and thus the subsequent overall N cost is +and thus the subsequent overall N cost is .. math:: N_{cost,tot} = 1/{N_{exch,tot}} - Retranslocation is determined via a different set of mechanisms, once the :math:`N_{cost,tot}` is known. + Retranslocation is determined via a different set of mechanisms, once the :math:`N_{cost,tot}` is known. Nitrogen Retranslocation -------------------------------------------------------- -The retranslocation uses an iterative algorithm to remove Nitrogen from each piece of falling litter. There are two pathways for this, 'free' uptake which removes the labile N pool, and 'paid-for' uptake which uses C to extract N from increasingly more recalcitrant pools. +The retranslocation uses an iterative algorithm to remove Nitrogen from each piece of falling litter. There are two pathways for this, 'free' uptake which removes the labile N pool, and 'paid-for' uptake which uses C to extract N from increasingly more recalcitrant pools. -At each timestep, the pool of carbon in falling leaves (:math:`C_{fallingleaf}`, g m\ :sup:`-2`) is generated from the quantity of litterfall on that day (see Phenology chapter for details). The amount of N in the litter pool (:math:`N_{fallingleaf}`, g m\ :sup:`-2`) is calculated as the total leaf N multiplied by the fraction of the leaf pool passed to litter that timestep. +At each timestep, the pool of carbon in falling leaves (:math:`C_{fallingleaf}`, g m\ :sup:`-2`) is generated from the quantity of litterfall on that day (see Phenology chapter for details). The amount of N in the litter pool (:math:`N_{fallingleaf}`, g m\ :sup:`-2`) is calculated as the total leaf N multiplied by the fraction of the leaf pool passed to litter that timestep. .. math:: N_{fallingleaf} = N_{leaf}.C_{fallingleaf}/C_{leaf} -The carbon available at the beginning of the iterative retranslocation calculation is equal to the :math:`C_{avail}` input into FUN. +The carbon available at the beginning of the iterative retranslocation calculation is equal to the :math:`C_{avail}` input into FUN. .. math:: C_{avail,retrans,0} = C_{avail} - Free Retranslocation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Some part of the leaf Nitrogen pool is removed without the need for an C expenditure. This 'free' N uptake amount, (:math:`N_{retrans,free}`, gN m\ :sup:`-2`) is calculated as +Some part of the leaf Nitrogen pool is removed without the need for an C expenditure. This 'free' N uptake amount, (:math:`N_{retrans,free}`, gN m\ :sup:`-2`) is calculated as .. math:: N_{retrans,free} = max(N_{fallingleaf} - (C_{fallingleaf}/CN_{litter,min} ),0.0) -where :math:`CN_{litter,min}` is the minimum C:N ratio of the falling litter (currently set to 1.5 x the target C:N ratio). +where :math:`CN_{litter,min}` is the minimum C:N ratio of the falling litter (currently set to 1.5 x the target C:N ratio). -The new :math:`N_{fallingleaf}` (gN m\ :sup:`-2`) is then determined as +The new :math:`N_{fallingleaf}` (gN m\ :sup:`-2`) is then determined as .. math:: N_{fallingleaf} = N_{fallingleaf} - N_{retrans,free} -and the new litter C:N ratio as +and the new litter C:N ratio as .. math:: CN_{fallingleaf}=C_{fallingleaf}/N_{fallingleaf} - Paid-for Retranslocation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The remaining calculations conduct an iterative calculation to determine the degree to which N retranslocation from leaves is paid for as C:N ratios and thus cost increase as N is extracted. The iteration continues until either +The remaining calculations conduct an iterative calculation to determine the degree to which N retranslocation from leaves is paid for as C:N ratios and thus cost increase as N is extracted. The iteration continues until either -1. The cost of retranslocation (:math:`cost_{retrans}` increases beyond the cost of acquiring N from alternative pathways (:math:`N_{cost,tot}`). -2. :math:`CN_{fallingleaf}` rises to a maximum level, after which no more extraction is possible (representing unavoidable N loss) or +1. The cost of retranslocation (:math:`cost_{retrans}` increases beyond the cost of acquiring N from alternative pathways (:math:`N_{cost,tot}`). +2. :math:`CN_{fallingleaf}` rises to a maximum level, after which no more extraction is possible (representing unavoidable N loss) or 3. There is no more carbon left to pay for extraction. -First we calculate the cost of extraction (:math:`cost_{retrans}`, gC/gN) for the current leaf C:N ratio as +First we calculate the cost of extraction (:math:`cost_{retrans}`, gC/gN) for the current leaf C:N ratio as .. math:: @@ -180,10 +167,10 @@ where :math:`k_{retrans}` is a parameter controlling the overall cost of resorp Next, we calculate the amount of C needed to be spent to increase the falling leaf C:N ratio by 1.0 in this iteration :math:`i` (:math:`C_{retrans_spent,i}`, gC m\ :sup:`-2`) as: .. math:: - C_{retrans,spent,i} = cost_{retrans}.(N_{fallingleaf} - C_{fallingleaf}/ + C_{retrans,spent,i} = cost_{retrans}.(N_{fallingleaf} - C_{fallingleaf}/ (CN_{fallingleaf} + 1.0)) -(wherein the retranslocation cost is assumed to not change over the increment of 1.0 in C:N ratio). Next, we calculate whether this is larger than the remaining C available to spend. +(wherein the retranslocation cost is assumed to not change over the increment of 1.0 in C:N ratio). Next, we calculate whether this is larger than the remaining C available to spend. .. math:: @@ -195,11 +182,11 @@ The amount of N retranslocated from the leaf in this iteration (:math:`N_{retran N_{retrans,paid,i} = min(N_{fallingleaf},C_{retrans,spent,i} / cost_{retrans}) -The next step calculates the growth C which is accounted for by this amount of N extraction in this iteration (:math:`C_{retrans,accounted,i}`). This is calculated using the current plant C:N ratio, and also for the additional C which will need to be spent on growth respiration to build this amount of new tissue. +The next step calculates the growth C which is accounted for by this amount of N extraction in this iteration (:math:`C_{retrans,accounted,i}`). This is calculated using the current plant C:N ratio, and also for the additional C which will need to be spent on growth respiration to build this amount of new tissue. .. math:: - C_{retrans,accounted,i} = N_{retrans,paid,i} . CN_{plant} . (1.0 + gr_{frac}) + C_{retrans,accounted,i} = N_{retrans,paid,i} . CN_{plant} . (1.0 + gr_{frac}) Then the falling leaf N is updated: @@ -213,10 +200,9 @@ and the :math:`CN_{fallingleaf}` and cost_{retrans} are updated. The amount of a C_{avail,retrans,i+1} = C_{avail,retrans,i} - C_{retrans,spent,i} - C_{retrans,accounted,i} - Outputs of Retranslocation algorithm. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The final output of the retranslocation calculation are the retranslocated N (:math:`N_{retrans}`, gN m\ :sup:`-2`), C spent on retranslocation (:math:`C_{retrans_paid}`, gC m\ :sup:`-2`), and C accounted for by retranslocation (:math:`C_{retrans_accounted}`, gC m\ :sup:`-2`). +The final output of the retranslocation calculation are the retranslocated N (:math:`N_{retrans}`, gN m\ :sup:`-2`), C spent on retranslocation (:math:`C_{retrans_paid}`, gC m\ :sup:`-2`), and C accounted for by retranslocation (:math:`C_{retrans_accounted}`, gC m\ :sup:`-2`). For paid-for uptake, we accumulate the total carbon spent on retranslocation (:math:`C_{spent_retrans}`), @@ -236,12 +222,11 @@ where N acquired by paid-for retranslocation is N_{retrans,paid} = \sum{N_{retrans,paid,i}} -The total carbon accounted for by retranslocation is the sum of the C accounted for by paid-for N uptake (:math:`N_{retrans_paid}`) and by free N uptake (:math:`N_{retrans_free}`). +The total carbon accounted for by retranslocation is the sum of the C accounted for by paid-for N uptake (:math:`N_{retrans_paid}`) and by free N uptake (:math:`N_{retrans_free}`). .. math:: C_{retrans,accounted} = \sum{C_{retrans,accounted,i}}+N_{retrans,free}.CN_{plant} . (1.0 + gr_{frac}) - The total available carbon in FUN to spend on fixation and active uptake (:math:`C_{tospend}`, gC m\ :sup:`-2`) is calculated as the carbon available minus that account for by retranslocation: @@ -249,103 +234,95 @@ The total available carbon in FUN to spend on fixation and active uptake (:math: C_{tospend} = C_{avail} - C_{retrans,accounted} - Carbon expenditure on fixation and active uptake. -------------------------------------------------------- -At each model timestep, the overall cost of N uptake is calculated (see below) in terms of C:N ratios. The available carbon (:math:`C_{avail}`, g m\ :sup:`-2` s\ :sup:`-1`) is then allocated to two alternative outcomes, payment for N uptake, or conservation for growth. For each carbon conserved for growth, a corresponding quantity of N must be made available. In the case where the plant target C:N ratio is fixed, the partitioning between carbon for growth (:math:`C_{growth}`) and carbon for N uptake (:math:`C_{nuptake}`) is calculated by solving a system of simultaneous equations. First, the carbon available must equal the carbon spent on N uptake plus that saved for growth. +At each model timestep, the overall cost of N uptake is calculated (see below) in terms of C:N ratios. The available carbon (:math:`C_{avail}`, g m\ :sup:`-2` s\ :sup:`-1`) is then allocated to two alternative outcomes, payment for N uptake, or conservation for growth. For each carbon conserved for growth, a corresponding quantity of N must be made available. In the case where the plant target C:N ratio is fixed, the partitioning between carbon for growth (:math:`C_{growth}`) and carbon for N uptake (:math:`C_{nuptake}`) is calculated by solving a system of simultaneous equations. First, the carbon available must equal the carbon spent on N uptake plus that saved for growth. .. math:: - C_{growth}+C_{nuptake}=C_{avail} - + C_{growth}+C_{nuptake}=C_{avail} + Second, the nitrogen acquired from expenditure of N (left hand side of term below) must equal the N that is required to match the growth carbon (right hand side of term below). .. math:: - + C_{nuptake}/N_{cost} =C_{growth}/CN_{target} The solution to these two equated terms can be used to estimate the ideal :math:`C_{nuptake}` as follows, - .. math:: + .. math:: C_{nuptake} =C_{tospend}/ ( (1.0+f_{gr}*(CN_{target} / N_{cost}) + 1) . -and the other C and N fluxes can be determined following the logic above. +and the other C and N fluxes can be determined following the logic above. Modifications to allow variation in C:N ratios -------------------------------------------------------- -The original FUN model as developed by :ref:`Fisher et al. (2010)` and :ref:`Brzostek et al. (2014)` assumes a fixed plant tissue C:N ratio. This means that in the case where N is especially limiting, all excess carbon will be utilized in an attempt to take up more Nitrogen. It has been repeatedly observed, however, that in these circumstances in real life, plants have some flexibility in the C:N stoichiometry of their tissues, and therefore, this assumption may not be realistic. However, the degree to which the C:N ratio varies with N availability is poorly documented, and existing global nitrogen models use a variety of heuristic methods by which to incorporate changing C:N ratios (Zaehle and Friend 2010; Ghimire et al. 2016). This algorithm exists as a placeholder to allow variable C:N ratios to occur, and to allow exploration of how much the parameters controlling their flexibility has on model outcomes. Incorporation of emerging understanding of the controls on tissue stoichiometry should ultimately replace this scheme. +The original FUN model as developed by :ref:`Fisher et al. (2010)` and :ref:`Brzostek et al. (2014)` assumes a fixed plant tissue C:N ratio. This means that in the case where N is especially limiting, all excess carbon will be utilized in an attempt to take up more Nitrogen. It has been repeatedly observed, however, that in these circumstances in real life, plants have some flexibility in the C:N stoichiometry of their tissues, and therefore, this assumption may not be realistic. However, the degree to which the C:N ratio varies with N availability is poorly documented, and existing global nitrogen models use a variety of heuristic methods by which to incorporate changing C:N ratios (Zaehle and Friend 2010; Ghimire et al. 2016). This algorithm exists as a placeholder to allow variable C:N ratios to occur, and to allow exploration of how much the parameters controlling their flexibility has on model outcomes. Incorporation of emerging understanding of the controls on tissue stoichiometry should ultimately replace this scheme. Thus, in CLM5, we introduce the capacity for tissue C:N ratios to be prognostic, rather than static. Overall N and C availability (:math:`N_{uptake}` and :math:`C_{growth}`) and hence tissue C:N ratios, are both determined by FUN. Allocation to individual tissues is discussed in the allocation chapter -Here we introduce an algorithm which adjusts the C expenditure on uptake to allow varying tissue C:N ratios. Increasing C spent on uptake will directly reduce the C:N ratio, and reducing C spent on uptake (retaining more for tissue growth) will increase it. C spent on uptake is impacted by both the N cost in the environment, and the existing tissue C:N ratio of the plant. The output of this algorithm is :math:`\gamma_{FUN}`, the fraction of the ideal :math:`C_{nuptake}` calculated from -the FUN equation above +Here we introduce an algorithm which adjusts the C expenditure on uptake to allow varying tissue C:N ratios. Increasing C spent on uptake will directly reduce the C:N ratio, and reducing C spent on uptake (retaining more for tissue growth) will increase it. C spent on uptake is impacted by both the N cost in the environment, and the existing tissue C:N ratio of the plant. The output of this algorithm is :math:`\gamma_{FUN}`, the fraction of the ideal :math:`C_{nuptake}` calculated from the FUN equation above - .. math:: + .. math:: C_{nuptake} = C_{nuptake}.\gamma_{FUN} - Response of C expenditure to Nitrogen uptake cost ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The environmental cost of Nitrogen (:math:`N_{cost,tot}`) is used to determine :math:`\gamma_{FUN}`. - - .. math:: - \gamma_{FUN} = max(0.0,1.0 - (N_{cost,tot}-a_{cnflex})/b_{cnflex}) +The environmental cost of Nitrogen (:math:`N_{cost,tot}`) is used to determine :math:`\gamma_{FUN}`. -where :math:`a_{cnflex}` and :math:`b_{cnflex}` are parameters fitted to give flexible C:N ranges over the operating range of N costs of the model. Calibration of these parameters should be subject to future testing in idealized experimental settings; they are here intended as a placeholder to allow some flexible stoichiometry, in the absence of adequate understanding of this process. Here :math:`a_{cnflex}` operates as the :math:`N_{cost,tot}` above which there is a modification in the C expenditure (to allow higher C:N ratios), and :math:`b_{cnflex}` is the scalar which determines how much the C expenditure is modified for a given discrepancy between :math:`a_{cnflex}` and the actual cost of uptake. + .. math:: + \gamma_{FUN} = max(0.0,1.0 - (N_{cost,tot}-a_{cnflex})/b_{cnflex}) +where :math:`a_{cnflex}` and :math:`b_{cnflex}` are parameters fitted to give flexible C:N ranges over the operating range of N costs of the model. Calibration of these parameters should be subject to future testing in idealized experimental settings; they are here intended as a placeholder to allow some flexible stoichiometry, in the absence of adequate understanding of this process. Here :math:`a_{cnflex}` operates as the :math:`N_{cost,tot}` above which there is a modification in the C expenditure (to allow higher C:N ratios), and :math:`b_{cnflex}` is the scalar which determines how much the C expenditure is modified for a given discrepancy between :math:`a_{cnflex}` and the actual cost of uptake. Response of C expenditure to plant C:N ratios ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We first calculate a :math:`\delta_{CN}`, which is the difference between the target C:N (:math:`target_{CN}`) a model parameter, and the existing C:N ratio (:math:`CN_{plant}`) - .. math:: - + .. math:: + CN_{plant} = \frac{C_{leaf} + C_{leaf,storage}}{N_{leaf} + N_{leaf,storage})} and - .. math:: + .. math:: \delta_{CN} = CN_{plant} - target_{CN} - We then increase :math:`\gamma_{FUN}` to account for situations where (even if N is expensive) plant C:N ratios have increased too far from the target. Where :math:`\delta_{CN}` is negative, we reduce C spent on N uptake and retain more C for growth - + .. math:: - \gamma_{FUN} = - \left\{\begin{array}{lr} + \gamma_{FUN} = + \left\{\begin{array}{lr} \gamma_{FUN}+ 0.5.(delta_{CN}/c_{flexcn})& delta_{CN} > 0\\ \gamma_{FUN}+(1-\gamma_{FUN}).min(1,\delta_{CN}/c_{flexcn}) & delta_{CN} < 0 \end{array}\right\} We then restrict the degree to which C expenditure can be reduced (to prevent unrealistically high C:N ratios) as - .. math:: - \gamma_{FUN} = max(min(1.0,\gamma_{FUN}),0.5) - - + .. math:: + \gamma_{FUN} = max(min(1.0,\gamma_{FUN}),0.5) + Calculation of N uptake streams from active uptake and fixation ---------------------------------------------------------------- - -Once the final :math:`C_{nuptake}` is known, the fluxes of C to the individual pools can be derived as + +Once the final :math:`C_{nuptake}` is known, the fluxes of C to the individual pools can be derived as .. math:: C_{nuptake,x} = C_{frac,x}.C_{nuptake} - .. math:: N_{uptake,x} = \frac{C_{nuptake}}{N_{cost}} - - + Following this, we determine whether the extraction estimates exceed the pool size for each source of N. Where :math:`N_{active,no3} + N_{nonmyc,no3} > N_{avail,no3}`, we calculate the unmet uptake, :math:`N_{unmet,no3}` .. math:: N_{unmet,no3} = N_{active,no3} + N_{nonmyc,no3} - N_{avail,no3} - -then modify both fluxes to account + +then modify both fluxes to account .. math:: @@ -354,14 +331,14 @@ then modify both fluxes to account .. math:: N_{nonmyc,no3} = N_{nonmyc,no3} + N_{unmet,no3}.\frac{N_{nonmyc,no3}}{N_{active,no3}+N_{nonmyc,no3}} - + and similarly, for NH4, where :math:`N_{active,nh4} + N_{nonmyc,nh4} > N_{avail,nh4}`, we calculate the unmet uptake, :math:`N_{unmet,no3}` .. math:: N_{unmet,nh4} = N_{active,nh4} + N_{nonmyc,nh4} - N_{avail,nh4} - -then modify both fluxes to account + +then modify both fluxes to account .. math:: @@ -371,8 +348,7 @@ then modify both fluxes to account N_{nonmyc,nh4} = N_{nonmyc,nh4} + N_{unmet,nh4}.\frac{N_{nonmyc,nh4}}{N_{active,nh4}+N_{nonmyc,nh4}} - -and then update the C spent to account for hte new lower N acquisition in that layer/pool. +and then update the C spent to account for hte new lower N acquisition in that layer/pool. .. math:: @@ -380,14 +356,10 @@ and then update the C spent to account for hte new lower N acquisition in that l C_{active,no3} = N_{active,no3}.N_{cost,active,no3}\\ C_{nonmyc,no3} = N_{nonmyc,no3}.N_{cost,nonmyc,no3}\\ C_{nonmyc,no3} = N_{nonmyc,no3}.N_{cost,nonmyc,no3}\\ - -Following this, we determine how much carbon is accounted for for each soil layer. +Following this, we determine how much carbon is accounted for for each soil layer. .. math:: C_{accounted,x,j} = C_{spent,j,x} - (N_{acquired,j,x}.CN_{plant}.(1.0+ gr_{frac})) - - - diff --git a/doc/source/tech_note/Fire/CLM50_Tech_Note_Fire.rst b/doc/source/tech_note/Fire/CLM50_Tech_Note_Fire.rst index 912bc7e378..84dd2bcaaf 100644 --- a/doc/source/tech_note/Fire/CLM50_Tech_Note_Fire.rst +++ b/doc/source/tech_note/Fire/CLM50_Tech_Note_Fire.rst @@ -3,34 +3,21 @@ Fire ======== -The fire parameterization in CLM contains four components: non-peat -fires outside cropland and tropical closed forests, agricultural fires -in cropland, deforestation fires in the tropical closed forests, and -peat fires (see :ref:`Li et al. 2012a `, -:ref:`Li et al. 2012b `, :ref:`Li et al. 2013 `, -:ref:`Li and Lawrence 2017 ` for details). -In this fire parameterization, burned area is affected by climate and -weather conditions, vegetation composition and structure, and human -activities. After burned area is calculated, we estimate the fire impact, -including biomass and peat burning, fire-induced vegetation mortality, -adjustment of the carbon and nitrogen (C/N) pools, and fire emissions. +The fire parameterization in CLM contains four components: non-peat fires outside cropland and tropical closed forests, agricultural fires in cropland, deforestation fires in the tropical closed forests, and peat fires (see :ref:`Li et al. 2012a `, :ref:`Li et al. 2012b `, :ref:`Li et al. 2013 `, :ref:`Li and Lawrence 2017 ` for details). In this fire parameterization, burned area is affected by climate and weather conditions, vegetation composition and structure, and human activities. After burned area is calculated, we estimate the fire impact, including biomass and peat burning, fire-induced vegetation mortality, adjustment of the carbon and nitrogen (C/N) pools, and fire emissions. .. _Non-peat fires outside cropland and tropical closed forest: Non-peat fires outside cropland and tropical closed forest --------------------------------------------------------------- -Burned area in a grid cell, \ :math:`A_{b}` (km\ :sup:`2` s :sup:`-1`), -is determined by +Burned area in a grid cell, \ :math:`A_{b}` (km\ :sup:`2` s :sup:`-1`), is determined by .. math:: :label: 23.1 A_{b} =N_{f} a -where :math:`N_{f}` (count s\ :sup:`-1`) is fire -counts in the grid cell; :math:`a` (km\ :sup:`2`) is average fire -spread area of a fire. +where :math:`N_{f}` (count s\ :sup:`-1`) is fire counts in the grid cell; :math:`a` (km\ :sup:`2`) is average fire spread area of a fire. .. _Fire counts: @@ -44,12 +31,7 @@ Fire counts :math:`N_{f}` is taken as N_{f} = N_{i} f_{b} f_{m} f_{se,o} -where :math:`N_{i}` ( count s\ :sup:`-1`) is the -number of ignition sources due to natural causes and human activities; -:math:`f_{b}` and :math:`f_{m}` (fractions) represent the availability -and combustibility of fuel, respectively; :math:`f_{se,o}` is the -fraction of anthropogenic and natural fires unsuppressed by humans and -related to the socioeconomic conditions. +where :math:`N_{i}` ( count s\ :sup:`-1`) is the number of ignition sources due to natural causes and human activities; :math:`f_{b}` and :math:`f_{m}` (fractions) represent the availability and combustibility of fuel, respectively; :math:`f_{se,o}` is the fraction of anthropogenic and natural fires unsuppressed by humans and related to the socioeconomic conditions. :math:`N_{i}` (count s\ :sup:`-1`) is given as @@ -58,128 +40,90 @@ related to the socioeconomic conditions. N_{i} = \left(I_{n} +I_{a} \right) A_{g} -where :math:`I_{n}` (count km\ :sup:`-2` s\ :sup:`-1`) and :math:`I_{a}` -(count km\ :sup:`-2` s\ :sup:`-1`) are the number of natural and anthropogenic -ignitions per km\ :sup:`2`, respectively; :math:`A_{g}` is the area of the -grid cell (km\ :sup:`2`). :math:`I_{n}` is estimated by +where :math:`I_{n}` (count km\ :sup:`-2` s\ :sup:`-1`) and :math:`I_{a}` (count km\ :sup:`-2` s\ :sup:`-1`) are the number of natural and anthropogenic ignitions per km\ :sup:`2`, respectively; :math:`A_{g}` is the area of the grid cell (km\ :sup:`2`). :math:`I_{n}` is estimated by .. math:: :label: 23.4 I_{n} = \gamma \psi I_{l} -where :math:`\gamma` \ =0.22 is ignition efficiency of cloud-to-ground -lightning; :math:`\psi =\frac{1}{5.16+2.16\cos [3min(60,\lambda )]}` is the -cloud-to-ground lightning fraction and depends on the latitude -:math:`\lambda` (degrees) ; :math:`I_{l}` (flash km\ :sup:`-2` s\ :sup:`-1`) is -the total lightning flashes. :math:`I_{a}` is modeled as a monotonic -increasing function of population density: +where :math:`\gamma` \ =0.22 is ignition efficiency of cloud-to-ground lightning; :math:`\psi =\frac{1}{5.16+2.16\cos [3min(60,\lambda )]}` is the cloud-to-ground lightning fraction and depends on the latitude :math:`\lambda` (degrees); :math:`I_{l}` (flash km\ :sup:`-2` s\ :sup:`-1`) is the total lightning flashes. :math:`I_{a}` is modeled as a monotonic increasing function of population density: .. math:: :label: 23.5 I_{a} =\frac{\alpha D_{P} k(D_{P} )}{n} -where :math:`\alpha =0.01` (count person\ :sup:`-1` mon\ :sup:`-1`) is the number of potential ignition sources by a -person per month; :math:`D_{P}` (person km\ :sup:`-2`) is the population density; :math:`k(D_{P} )=6.8D_{P} ^{-0.6}` represents anthropogenic ignition -potential as a function of human population density :math:`D_{P}` ; *n* -is the seconds in a month. +where :math:`\alpha =0.01` (count person\ :sup:`-1` mon\ :sup:`-1`) is the number of potential ignition sources by a person per month; :math:`D_{P}` (person km\ :sup:`-2`) is the population density; :math:`k(D_{P} )=6.8D_{P} ^{-0.6}` represents anthropogenic ignition potential as a function of human population density :math:`D_{P}`; *n* is the seconds in a month. Fuel availability :math:`f_{b}` is given as .. math:: :label: 23.6 - f_{b} =\left\{\begin{array}{c} + f_{b} =\left\{\begin{array}{c} {0} \\ {\frac{B_{ag} -B_{low} }{B_{up} -B_{low} } } \\ {1} \end{array} - \begin{array}{cc} {} & {} \end{array}\begin{array}{c} {B_{ag} B_{up} } + \begin{array}{cc} {} & {} \end{array}\begin{array}{c} {B_{ag} B_{up} } \end{array}\right\} \ , -where :math:`B_{ag}` (g C m\ :sup:`-2`) is the biomass of combined leaf, -stem, litter, and woody debris pools; :math:`B_{low}` = 105 g C m :sup:`-2` -is the lower fuel threshold below which fire does not occur; :math:`B_{up}` -= 1050 g C m\ :sup:`-2` is the upper fuel threshold above which fire -occurrence is not limited by fuel availability. +where :math:`B_{ag}` (g C m\ :sup:`-2`) is the biomass of combined leaf, stem, litter, and woody debris pools; :math:`B_{low}` = 105 g C m :sup:`-2` is the lower fuel threshold below which fire does not occur; :math:`B_{up}` = 1050 g C m\ :sup:`-2` is the upper fuel threshold above which fire occurrence is not limited by fuel availability. Fuel combustibility :math:`f_{m}` is estimated by .. math:: - :label: 23.7 + :label: 23.7 - f_{m} = {f_{RH} f_{\beta}}, &\qquad T_{17cm} > T_{f} - -where :math:`f_{RH}` and :math:`f_{\beta }` represent the dependence of -fuel combustibility on relative humidity :math:`RH` (%) and root-zone -soil moisture limitation :math:`\beta` (fraction); :math:`T_{17cm}` is -the temperature of the top 17 cm of soil (K) and :math:`T_{f}` is the -freezing temperature. :math:`f_{RH}` is a weighted average of real time -:math:`RH` (:math:`RH_{0}`) and 30-day running mean :math:`RH` -(:math:`RH_{30d}`): + f_{m} = {f_{RH} f_{\beta}}, \qquad T_{17cm} > T_{f} + +where :math:`f_{RH}` and :math:`f_{\beta }` represent the dependence of fuel combustibility on relative humidity :math:`RH` (%) and root-zone soil moisture limitation :math:`\beta` (fraction); :math:`T_{17cm}` is the temperature of the top 17 cm of soil (K) and :math:`T_{f}` is the freezing temperature. :math:`f_{RH}` is a weighted average of real time :math:`RH` (:math:`RH_{0}`) and 30-day running mean :math:`RH` (:math:`RH_{30d}`): .. math:: :label: 23.8 - + f_{RH} = (1-w) l_{RH_{0}} + wl_{RH_{30d}} -where weight :math:`w=\max [0,\min (1,\frac{B_{ag}-2500}{2500})]`, -:math:`l_{{RH}_{0}}=1-\max [0,\min (1,\frac{RH_{0}-30}{80-30})]`, and -:math:`l_{{RH}_{30d}}=1-\max [0.75,\min (1,\frac{RH_{30d}}{90})]`. -:math:`f_{\beta}` is given by +where weight :math:`w=\max [0,\min (1,\frac{B_{ag}-2500}{2500})]`, :math:`l_{{RH}_{0}}=1-\max [0,\min (1,\frac{RH_{0}-30}{80-30})]`, and :math:`l_{{RH}_{30d}}=1-\max [0.75,\min (1,\frac{RH_{30d}}{90})]`. :math:`f_{\beta}` is given by .. math:: :label: 23.9 - f_{\beta } =\left\{\begin{array}{cccc} - {1} & {} & {} & {\beta\le \beta_{low} } \\ {\frac{\beta_{up} -\beta}{\beta_{up} -\beta_{low} } } & {} & {} & {\beta_{low} <\beta<\beta_{up} } \\ - {0} & {} & {} & {\beta\ge \beta_{up} } + f_{\beta } =\left\{\begin{array}{cccc} + {1} & {} & {} & {\beta\le \beta_{low} } \\ {\frac{\beta_{up} -\beta}{\beta_{up} -\beta_{low} } } & {} & {} & {\beta_{low} <\beta<\beta_{up} } \\ + {0} & {} & {} & {\beta\ge \beta_{up} } \end{array}\right\} \ , -where :math:`\beta _{low}` \ =0.85 and :math:`\beta _{up}` \ =0.98 are the -lower and upper thresholds, respectively. +where :math:`\beta _{low}` \ =0.85 and :math:`\beta _{up}` \ =0.98 are the lower and upper thresholds, respectively. -For scarcely populated regions (:math:`D_{p} \le 0.1` person -km :sup:`-2`), we assume that anthropogenic suppression on fire -occurrence is negligible, i.e., :math:`f_{se,o} =1.0`. In regions of -:math:`D_{p} >0.1` person km\ :sup:`-2`, we parameterize the -fraction of anthropogenic and natural fires unsuppressed by human -activities as +For scarcely populated regions (:math:`D_{p} \le 0.1` person km :sup:`-2`), we assume that anthropogenic suppression on fire occurrence is negligible, i.e., :math:`f_{se,o} =1.0`. In regions of :math:`D_{p} >0.1` person km\ :sup:`-2`, we parameterize the fraction of anthropogenic and natural fires unsuppressed by human activities as .. math:: :label: 23.10 f_{se,o} =f_{d} f_{e} -where :math:`{f}_{d}` and :math:`{f}_{e}` are the effects of the -demographic and economic conditions on fire occurrence. The demographic -influence on fire occurrence is +where :math:`{f}_{d}` and :math:`{f}_{e}` are the effects of the demographic and economic conditions on fire occurrence. The demographic influence on fire occurrence is .. math:: :label: 23.11 f_{d} =0.01 + 0.98 \exp (-0.025D_{P} ). -For shrub and grass PFTs, the economic influence on fire occurrence is -parameterized as a function of Gross Domestic Product GDP (k 1995US$ -capita\ :sup:`-1`): +For shrub and grass PFTs, the economic influence on fire occurrence is parameterized as a function of Gross Domestic Product GDP (k 1995US$ capita\ :sup:`-1`): .. math:: :label: 23.12 f_{e} =0.1+0.9\times \exp [-\pi (\frac{GDP}{8} )^{0.5} ] -which captures 73% of the observed MODIS fire counts with variable GDP -in regions where shrub and grass PFTs are dominant (fractional coverage -of shrub and grass PFTs :math:`>` 50%). In regions outside tropical -closed forests and dominated by trees (fractional coverage of tree PFTs -:math:`>` 50%), we use +which captures 73% of the observed MODIS fire counts with variable GDP in regions where shrub and grass PFTs are dominant (fractional coverage of shrub and grass PFTs :math:`>` 50%). In regions outside tropical closed forests and dominated by trees (fractional coverage of tree PFTs :math:`>` 50%), we use .. math:: :label: 23.13 - f_{e} =\left\{\begin{array}{c} + f_{e} =\left\{\begin{array}{c} {0.39} \\ {0.79} \\ {1} \end{array} - \begin{array}{cc} {} & {} \end{array}\begin{array}{c} {GDP > 20 } \\ - { 8 < GDP \le 20 } \\ { GDP \le 8 } + \begin{array}{cc} {} & {} \end{array}\begin{array}{c} {GDP > 20 } \\ + { 8 < GDP \le 20 } \\ { GDP \le 8 } \end{array}\right\} \ , to reproduce the relationship between MODIS fire counts and GDP. @@ -189,35 +133,23 @@ to reproduce the relationship between MODIS fire counts and GDP. Average spread area of a fire ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Fire fighting capacity depends on socioeconomic conditions and affects -fire spread area. Due to a lack of observations, we consider the -socioeconomic impact on the average burned area rather than separately -on fire spread rate and fire duration: +Fire fighting capacity depends on socioeconomic conditions and affects fire spread area. Due to a lack of observations, we consider the socioeconomic impact on the average burned area rather than separately on fire spread rate and fire duration: .. math:: :label: 23.14 a=a^{*} F_{se} -where :math:`a^{*}` is the average burned area of a fire without -anthropogenic suppression and :math:`F_{se}` is the socioeconomic -effect on fire spread area. +where :math:`a^{*}` is the average burned area of a fire without anthropogenic suppression and :math:`F_{se}` is the socioeconomic effect on fire spread area. -Average burned area of a fire without anthropogenic suppression is -assumed elliptical in shape with the wind direction along the major axis -and the point of ignition at one of the foci. According to the area -formula for an ellipse, average burned area of a fire can be represented -as: +Average burned area of a fire without anthropogenic suppression is assumed elliptical in shape with the wind direction along the major axis and the point of ignition at one of the foci. According to the area formula for an ellipse, average burned area of a fire can be represented as: .. math:: :label: 23.15 a^{*} =\pi \frac{l}{2} \frac{w}{2} \times 10^{-6} =\frac{\pi u_{p}^{2} \tau ^{2} }{4L_{B} } (1+\frac{1}{H_{B} } )^{2} \times 10^{-6} -where :math:`u_{p}` (m s\ :sup:`-1`) is the fire spread rate in the -downwind direction; :math:`\tau` (s) is average fire duration; :math:`L_{B}` -and :math:`H_{B}` are length-to-breadth ratio and head-to-back ratio of -the ellipse; 10 :sup:`-6` converts m :sup:`2` to km :sup:`2`. +where :math:`u_{p}` (m s\ :sup:`-1`) is the fire spread rate in the downwind direction; :math:`\tau` (s) is average fire duration; :math:`L_{B}` and :math:`H_{B}` are length-to-breadth ratio and head-to-back ratio of the ellipse; 10 :sup:`-6` converts m :sup:`2` to km :sup:`2`. According to :ref:`Arora and Boer (2005)`, @@ -226,9 +158,7 @@ According to :ref:`Arora and Boer (2005)`, L_{B} =1.0+10.0[1-\exp (-0.06W)] -where :math:`W`\ (m s\ :sup:`-1`) is the wind speed. According to -the mathematical properties of the ellipse, the head-to-back ratio -:math:`H_{B}` is +where :math:`W`\ (m s\ :sup:`-1`) is the wind speed. According to the mathematical properties of the ellipse, the head-to-back ratio :math:`H_{B}` is .. math:: :label: 23.17 @@ -242,53 +172,30 @@ The fire spread rate in the downwind direction is represented as u_{p} =u_{\max } C_{m} g(W) -(:ref:`Arora and Boer, 2005`), where :math:`u_{\max }` -(m s\ :sup:`-1`) is the PFT-dependent average maximum fire spread -rate in natural vegetation regions; :math:`C_{m} =\sqrt{f_{m}}` and :math:`g(W)` -represent the dependence of :math:`u_{p}` on fuel wetness and wind -speed :math:`W`, respectively. :math:`u_{\max }` is set to 0.33 -m s :sup:`-1`\ for grass PFTs, 0.28 m s :sup:`-1` for shrub PFTs, 0.26 -m s\ :sup:`-1` for needleleaf tree PFTs, and 0.25 m s\ :sup:`-1` for -other tree PFTs. :math:`g(W)` is derived from the mathematical properties -of the ellipse and equation :eq:`23.16` and :eq:`23.17`. +(:ref:`Arora and Boer, 2005`), where :math:`u_{\max }` (m s\ :sup:`-1`) is the PFT-dependent average maximum fire spread rate in natural vegetation regions; :math:`C_{m} =\sqrt{f_{m}}` and :math:`g(W)` represent the dependence of :math:`u_{p}` on fuel wetness and wind speed :math:`W`, respectively. :math:`u_{\max }` is set to 0.33 m s :sup:`-1`\ for grass PFTs, 0.28 m s :sup:`-1` for shrub PFTs, 0.26 m s\ :sup:`-1` for needleleaf tree PFTs, and 0.25 m s\ :sup:`-1` for other tree PFTs. :math:`g(W)` is derived from the mathematical properties of the ellipse and equation :eq:`23.16` and :eq:`23.17`. .. math:: :label: 23.19 g(W)=\frac{2L_{B} }{1+\frac{1}{H_{B} } } g(0). -Since g(\ *W*)=1.0, and \ :math:`L_{B}` and :math:`H_{B}` are at their -maxima \ :math:`L_{B} ^{\max } =11.0` and \ :math:`H_{B} ^{\max } =482.0` -when :math:`W\to \infty` , g(0) can be derived as +Since g(\ *W*)=1.0, and \ :math:`L_{B}` and :math:`H_{B}` are at their maxima \ :math:`L_{B} ^{\max } =11.0` and \ :math:`H_{B} ^{\max } =482.0` when :math:`W\to \infty`, g(0) can be derived as .. math:: :label: 23.20 g(0)=\frac{1+\frac{1}{H_{B} ^{\max } } }{2L_{B} ^{\max } } =0.05. -In the absence of globally gridded data on barriers to fire (e.g. -rivers, lakes, roads, firebreaks) and human fire-fighting efforts, -average fire duration is simply assumed equal to 1 which is the observed -2001–2004 mean persistence of most fires in the world -(:ref:`Giglio et al. 2006 `). +In the absence of globally gridded data on barriers to fire (e.g. rivers, lakes, roads, firebreaks) and human fire-fighting efforts, average fire duration is simply assumed equal to 1 which is the observed 2001–2004 mean persistence of most fires in the world (:ref:`Giglio et al. 2006 `). -As with the socioeconomic influence on fire occurrence, we assume that -the socioeconomic influence on fire spreading is negligible in regions -of :math:`D_{p} \le 0.1` person km\ :sup:`-2`, i.e., -:math:`F_{se} = 1.0`. In regions of :math:`D_{p} >0.1` person -km\ :sup:`-2`, we parameterize such socioeconomic influence as: +As with the socioeconomic influence on fire occurrence, we assume that the socioeconomic influence on fire spreading is negligible in regions of :math:`D_{p} \le 0.1` person km\ :sup:`-2`, i.e., :math:`F_{se} = 1.0`. In regions of :math:`D_{p} >0.1` person km\ :sup:`-2`, we parameterize such socioeconomic influence as: .. math:: :label: 23.21 F_{se} =F_{d} F_{e} -where :math:`{F}_{d}` and :math:`{F}_{e}` are -effects of the demographic and economic conditions on the average spread -area of a fire, and are identified by maximizing the explained -variability of the GFED3 burned area fraction with both socioeconomic -indices in grid cells with various dominant vegetation types. For shrub -and grass PFTs, the demographic impact factor is +where :math:`{F}_{d}` and :math:`{F}_{e}` are effects of the demographic and economic conditions on the average spread area of a fire, and are identified by maximizing the explained variability of the GFED3 burned area fraction with both socioeconomic indices in grid cells with various dominant vegetation types. For shrub and grass PFTs, the demographic impact factor is .. math:: :label: 23.22 @@ -302,8 +209,7 @@ and the economic impact factor is F_{e} =0.2+0.8\times \exp (-\pi \frac{GDP}{7} ). -For tree PFTs outside tropical closed forests, the demographic and -economic impact factors are given as +For tree PFTs outside tropical closed forests, the demographic and economic impact factors are given as .. math:: :label: 23.24 @@ -315,79 +221,51 @@ and .. math:: :label: 23.25 - F_{e} =\left\{\begin{array}{cc} - {0.62,} & {GDP>20} \\ {0.83,} & {820} \\ {0.83,} & {8` that shows the -2001-2009 average contribution of cropland fires is 4.7% of the total -global burned area. +where :math:`a_{1}` (s\ :sup:`-1`) is a constant; :math:`f_{se}` represents the socioeconomic effect on fires; :math:`f_{t}` determines the seasonality of agricultural fires; :math:`f_{crop}` is the fractional coverage of cropland. :math:`a_{1}` \ = 1.6x10\ :sup:`-4` \hr\ :sup:`-1`\ is estimated using an inverse method, by matching 1997-2004 simulations to the analysis of :ref:`van der Werf et al. (2010) ` that shows the 2001-2009 average contribution of cropland fires is 4.7% of the total global burned area. The socioeconomic factor :math:`f_{se}` is given as follows: @@ -431,112 +302,47 @@ and f_{e} =0.01+0.99\times \exp (-\pi \frac{GDP}{10} ) -are the effects of population density and GDP on burned area, derived -in a similar way to equation :eq:`23.32` and :eq:`23.33`. :math:`f_{t}` -is set to 1 at the first time step during the climatological peak month -for agricultural fires (:ref:`van der Werf et al. 2010 `); -:math:`{f}_{t}` is set to 0 otherwise. Peak -month in this dataset correlates with the month after harvesting or the -month before planting. In CLM we use this dataset the same way whether -the CROP option is active or not, without regard to the CROP option’s -simulated planting and harvesting dates. - -In the post-fire region, fire impact is parameterized similar to section -:numref:`Fire impact` but with combustion completeness factors and tissue -mortality factors for crop PFTs -(:numref:`Table PFT-specific combustion completeness and fire mortality factors`). +are the effects of population density and GDP on burned area, derived in a similar way to equation :eq:`23.32` and :eq:`23.33`. :math:`f_{t}` is set to 1 at the first time step during the climatological peak month for agricultural fires (:ref:`van der Werf et al. 2010 `); :math:`{f}_{t}` is set to 0 otherwise. Peak month in this dataset correlates with the month after harvesting or the month before planting. In CLM we use this dataset the same way whether the CROP option is active or not, without regard to the CROP option's simulated planting and harvesting dates. + +In the post-fire region, fire impact is parameterized similar to section :numref:`Fire impact` but with combustion completeness factors and tissue mortality factors for crop PFTs (:numref:`Table PFT-specific combustion completeness and fire mortality factors`). .. _Deforestation fires: - + Deforestation fires ------------------------ -CLM focuses on deforestation fires in tropical closed forests. Tropical -closed forests are defined as grid cells with tropical tree (BET and BDT tropical) -coverage :math:`>` 60% according to the FAO classification. Deforestation fires -are defined as fires caused by deforestation, including escaped -deforestation fires, termed degradation fires. Deforestation and -degradation fires are assumed to occur outside of cropland areas in -these grid cells. Burned area is controlled by the deforestation rate -and climate: +CLM focuses on deforestation fires in tropical closed forests. Tropical closed forests are defined as grid cells with tropical tree (BET and BDT tropical) coverage :math:`>` 60% according to the FAO classification. Deforestation fires are defined as fires caused by deforestation, including escaped deforestation fires, termed degradation fires. Deforestation and degradation fires are assumed to occur outside of cropland areas in these grid cells. Burned area is controlled by the deforestation rate and climate: .. math:: :label: 23.34 A_{b} = b \ f_{lu} f_{cli,d} f_{b} A_{g} -where :math:`b` (s\ :sup:`-1`) is a global constant; -:math:`f_{lu}` (fraction) represents the effect of decreasing -fractional coverage of tree PFTs derived from land use data; -:math:`f_{cli,d}` (fraction) represents the effect of climate -conditions on the burned area. +where :math:`b` (s\ :sup:`-1`) is a global constant; :math:`f_{lu}` (fraction) represents the effect of decreasing fractional coverage of tree PFTs derived from land use data; :math:`f_{cli,d}` (fraction) represents the effect of climate conditions on the burned area. -Constants :math:`b` and :math:`{f}_{lu}` are calibrated -based on observations and reanalysis datasets in the Amazon rainforest -(tropical closed forests within 15.5 :sup:`o` S :math:`\text{-}` 10.5 -:sup:`o` N, 30.5 :sup:`o` W :math:`\text{-}` 91 :sup:`o` W). -:math:`b` = 0.033 d\ :sup:`-1` and :math:`f_{lu}` is defined as +Constants :math:`b` and :math:`{f}_{lu}` are calibrated based on observations and reanalysis datasets in the Amazon rainforest (tropical closed forests within 15.5 °S :math:`\text{-}` 10.5 °N, 30.5 ° W :math:`\text{-}` 91 ° W). :math:`b` = 0.033 d\ :sup:`-1` and :math:`f_{lu}` is defined as .. math:: :label: 23.35 f_{lu} = \max (0.0005,0.19D-0.001) -where :math:`D` (yr\ :sup:`-1`) is the annual loss of tree cover -based on CLM land use and land cover change data. +where :math:`D` (yr\ :sup:`-1`) is the annual loss of tree cover based on CLM land use and land cover change data. The effect of climate on deforestation fires is parameterized as: .. math:: :label: 23.36 - \begin{array}{ll} + \begin{array}{ll} f_{cli,d} \quad = & \quad \max \left[0,\min (1,\frac{b_{2} -P_{60d} }{b_{2} } )\right]^{0.5} \times \\ - & \quad \max \left[0,\min (1,\frac{b_{3} -P_{10d} }{b_{3} } )\right]^{0.5} \times \\ + & \quad \max \left[0,\min (1,\frac{b_{3} -P_{10d} }{b_{3} } )\right]^{0.5} \times \\ & \quad \max \left[0,\min (1,\frac{0.25-P}{0.25} )\right] \end{array} -where :math:`P` (mm d :sup:`-1`) is instantaneous precipitation, while -:math:`P_{60d}` (mm d\ :sup:`-1`) and :math:`P_{10d}` (mm d :sup:`-1`) -are 60-day and 10-day running means of precipitation, respectively; -:math:`b_{2}` (mm d :sup:`-1`) and :math:`b_{3}` (mm d :sup:`-1`) are -the grid-cell dependent thresholds of :math:`P_{60d}` and :math:`P_{10d}` ; -0.25 mm d :sup:`-1` is the maximum precipitation rate for drizzle. -:ref:`Le Page et al. (2010) ` analyzed the relationship -between large-scale deforestation fire counts and precipitation during 2003 -:math:`\text{-}`\ 2006 in southern Amazonia where tropical evergreen trees -(BET Tropical) are dominant. Figure 2 in -:ref:`Le Page et al. (2010) ` showed that fires generally -occurred if both :math:`P_{60d}` and :math:`P_{10d}` were less than about -4.0 mm d :sup:`-1`, and fires occurred more frequently in a drier environment. -Based on the 30-yr (1985 to 2004) precipitation data in -:ref:`Qian et al. (2006) `. The climatological precipitation -of dry months (P < 4.0 mm d :sup:`-1`) in a year over tropical deciduous -tree (BDT Tropical) dominated regions is 46% of that over BET Tropical -dominated regions, so we set the PFT-dependent thresholds of :math:`P_{60d}` -and :math:`P_{10d}` as 4.0 mm d :sup:`-1` for BET Tropical and 1.8 mm d -:sup:`-1` (= 4.0 mm d :sup:`-1` :math:`\times` 46%) for BDT Tropical, and -:math:`b`\ :sub:`2` and :math:`b`\ :sub:`3` are the average of thresholds -of BET Tropical and BDT Tropical weighted bytheir coverage. - -The post-fire area due to deforestation is not limited to land-type -conversion regions. In the tree-reduced region, the maximum fire carbon -emissions are assumed to be 80% of the total conversion flux. According -to the fraction of conversion flux for tropical trees in the -tree-reduced region (60%) assigned by CLM4-CN, to reach the maximum fire -carbon emissions in a conversion region requires burning this region -about twice when we set PFT-dependent combustion completeness factors to -about 0.3 for stem [the mean of 0.2\ :math:`{-}`\ 0.4 used in -:ref:`van der Werf et al. (2010) `. Therefore, when -the burned area calculated from equation :eq:`23.36` is -no more than twice the tree-reduced area, we assume no escaped fires -outside the land-type conversion region, and the fire-related fraction -of the total conversion flux is estimated as -:math:`\frac{A_{b} /A_{g} }{2D}` . Otherwise, 80% of the total -conversion flux is assumed to be fire carbon emissions, and the biomass -combustion and vegetation mortality outside the tree-reduced regions -with an area fraction of :math:`\frac{A_{b} }{A_{g} } -2D` are set as in -section :numref:`Fire impact`. +where :math:`P` (mm d :sup:`-1`) is instantaneous precipitation, while :math:`P_{60d}` (mm d\ :sup:`-1`) and :math:`P_{10d}` (mm d :sup:`-1`) are 60-day and 10-day running means of precipitation, respectively; :math:`b_{2}` (mm d :sup:`-1`) and :math:`b_{3}` (mm d :sup:`-1`) are the grid-cell dependent thresholds of :math:`P_{60d}` and :math:`P_{10d}`; 0.25 mm d :sup:`-1` is the maximum precipitation rate for drizzle. :ref:`Le Page et al. (2010) ` analyzed the relationship between large-scale deforestation fire counts and precipitation during 2003 :math:`\text{-}`\ 2006 in southern Amazonia where tropical evergreen trees (BET Tropical) are dominant. Figure 2 in :ref:`Le Page et al. (2010) ` showed that fires generally occurred if both :math:`P_{60d}` and :math:`P_{10d}` were less than about 4.0 mm d :sup:`-1`, and fires occurred more frequently in a drier environment. Based on the 30-yr (1985 to 2004) precipitation data in :ref:`Qian et al. (2006) `. The climatological precipitation of dry months (P < 4.0 mm d :sup:`-1`) in a year over tropical deciduous tree (BDT Tropical) dominated regions is 46% of that over BET Tropical dominated regions, so we set the PFT-dependent thresholds of :math:`P_{60d}` and :math:`P_{10d}` as 4.0 mm d :sup:`-1` for BET Tropical and 1.8 mm d :sup:`-1` (= 4.0 mm d :sup:`-1` :math:`\times` 46%) for BDT Tropical, and :math:`b`\ :sub:`2` and :math:`b`\ :sub:`3` are the average of thresholds of BET Tropical and BDT Tropical weighted bytheir coverage. + +The post-fire area due to deforestation is not limited to land-type conversion regions. In the tree-reduced region, the maximum fire carbon emissions are assumed to be 80% of the total conversion flux. According to the fraction of conversion flux for tropical trees in the tree-reduced region (60%) assigned by CLM4-CN, to reach the maximum fire carbon emissions in a conversion region requires burning this region about twice when we set PFT-dependent combustion completeness factors to about 0.3 for stem [the mean of 0.2\ :math:`{-}`\ 0.4 used in :ref:`van der Werf et al. (2010) `. Therefore, when the burned area calculated from equation :eq:`23.36` is no more than twice the tree-reduced area, we assume no escaped fires outside the land-type conversion region, and the fire-related fraction of the total conversion flux is estimated as :math:`\frac{A_{b} /A_{g} }{2D}`. Otherwise, 80% of the total conversion flux is assumed to be fire carbon emissions, and the biomass combustion and vegetation mortality outside the tree-reduced regions with an area fraction of :math:`\frac{A_{b} }{A_{g} } -2D` are set as in section :numref:`Fire impact`. .. _Peat fires: @@ -550,20 +356,9 @@ The burned area due to peat fires is given as :math:`{A}_{b}`: A_{b} = c \ f_{cli,p} f_{peat} (1 - f_{sat} ) A_{g} -where :math:`c` (s\ :sup:`-1`) is a constant; :math:`f_{cli,p}` represents -the effect of climate on the burned area; :math:`f_{peat}` is the fractional -coverage of peatland in the grid cell; and :math:`f_{sat}` is the fraction -of the grid cell with a water table at the surface or higher. :math:`c` = 0.17 -:math:`\times` 10 :sup:`-3` hr\ :sup:`-1` for tropical peat fires and -:math:`c` = 0.9 :math:`\times` 10 :sup:`-5` hr :sup:`-1` for boreal peat fires -are derived using an inverse method, by matching simulations to earlier -studies: about 2.4 Mha peatland was burned over Indonesia in 1997 -(:ref:`Page et al. 2002 `) and the average burned area of peat -fires in Western Canada was 0.2 Mha yr :sup:`-1` for 1980-1999 -(:ref:`Turetsky et al. 2004 `). +where :math:`c` (s\ :sup:`-1`) is a constant; :math:`f_{cli,p}` represents the effect of climate on the burned area; :math:`f_{peat}` is the fractional coverage of peatland in the grid cell; and :math:`f_{sat}` is the fraction of the grid cell with a water table at the surface or higher. :math:`c` = 0.17 :math:`\times` 10 :sup:`-3` hr\ :sup:`-1` for tropical peat fires and :math:`c` = 0.9 :math:`\times` 10 :sup:`-5` hr :sup:`-1` for boreal peat fires are derived using an inverse method, by matching simulations to earlier studies: about 2.4 Mha peatland was burned over Indonesia in 1997 (:ref:`Page et al. 2002 `) and the average burned area of peat fires in Western Canada was 0.2 Mha yr :sup:`-1` for 1980-1999 (:ref:`Turetsky et al. 2004 `). -For tropical peat fires, :math:`f_{cli,p}` is set as a function of -long-term precipitation :math:`P_{60d}` : +For tropical peat fires, :math:`f_{cli,p}` is set as a function of long-term precipitation :math:`P_{60d}` : .. math:: :label: 23.38 @@ -579,48 +374,26 @@ For boreal peat fires, :math:`f_{cli,p}` is set to where :math:`\theta _{17cm}` is the wetness of the top 17 cm of soil. -Peat fires lead to peat burning and the combustion and mortality of -vegetation over peatlands. For tropical peat fires, based on -:ref:`Page et al. (2002) `, about 6% of the peat carbon loss -from stored carbon is caused by 33.9% of the peatland burned. Carbon emissions -due to peat burning (g C m\ :sup:`-2` s\ :sup:`-1`) are therefore set as the -product of 6%/33.9%, burned area fraction of peat fire (s\ :sup:`-1`), and -soil organic carbon (g C m\ :sup:`-2`). For boreal peat fires, the carbon -emissions due to peat burning are set as 2.2 kg C m\ :sup:`-2` \ peat fire -area (:ref:`Turetsky et al. 2002 `). Biomass combustion -and vegetation mortality in post-fire peatlands are set the same as section -:numref:`Fire impact` for non-crop PFTs and as section -:numref:`Agricultural fires` for crops PFTs. +Peat fires lead to peat burning and the combustion and mortality of vegetation over peatlands. For tropical peat fires, based on :ref:`Page et al. (2002) `, about 6% of the peat carbon loss from stored carbon is caused by 33.9% of the peatland burned. Carbon emissions due to peat burning (g C m\ :sup:`-2` s\ :sup:`-1`) are therefore set as the product of 6%/33.9%, burned area fraction of peat fire (s\ :sup:`-1`), and soil organic carbon (g C m\ :sup:`-2`). For boreal peat fires, the carbon emissions due to peat burning are set as 2.2 kg C m\ :sup:`-2` \ peat fire area (:ref:`Turetsky et al. 2002 `). Biomass combustion and vegetation mortality in post-fire peatlands are set the same as section :numref:`Fire impact` for non-crop PFTs and as section :numref:`Agricultural fires` for crops PFTs. .. _Fire trace gas and aerosol emissions: Fire trace gas and aerosol emissions ------------------------------------- -CESM2 is the first Earth system model that can model the full coupling -among fire, fire emissions, land, and atmosphere. CLM5, as the land -component of CESM2, calculates the surface trace gas and aerosol emissions -due to fire and fire emission heights, as the inputs of atmospheric -chemistry model and aerosol model. +CESM2 is the first Earth system model that can model the full coupling among fire, fire emissions, land, and atmosphere. CLM5, as the land component of CESM2, calculates the surface trace gas and aerosol emissions due to fire and fire emission heights, as the inputs of atmospheric chemistry model and aerosol model. -Emissions for trace gas and aerosol species x and the j-th PFT, :math:`E_{x,j}` -(g species s\ :sup:`-1`), are given by +Emissions for trace gas and aerosol species x and the j-th PFT, :math:`E_{x,j}` (g species s\ :sup:`-1`), are given by .. math:: :label: 23.40 E_{x,j} = EF_{x,j}\frac{\phi _{j} }{[C]}. -Here, :math:`EF_{x,j}` (g species (g dm)\ :sup:`-1`) is PFT-dependent emission -factor scaled from biome-level values (Li et al., in prep, also used for FireMIP -fire emissions data) by Dr. Val Martin and Dr. Li. :math:`[C]` = 0.5 -(g C (g dm)\ :sup:`-1`) is a conversion factor from dry matter to carbon. +Here, :math:`EF_{x,j}` (g species (g dm)\ :sup:`-1`) is PFT-dependent emission factor scaled from biome-level values (Li et al., in prep, also used for FireMIP fire emissions data) by Dr. Val Martin and Dr. Li. :math:`[C]` = 0.5 (g C (g dm)\ :sup:`-1`) is a conversion factor from dry matter to carbon. -Emission height is PFT-dependent: 4.3 km for needleleaf tree PFTs, 3 km for other -boreal and temperate tree PFTs, 2.5 km for tropical tree PFTs, 2 km for shrub -PFTs, and 1 km for grass and crop PFTs. These values are compiled from earlier -studies by Dr. Val Martin. +Emission height is PFT-dependent: 4.3 km for needleleaf tree PFTs, 3 km for other boreal and temperate tree PFTs, 2.5 km for tropical tree PFTs, 2 km for shrub PFTs, and 1 km for grass and crop PFTs. These values are compiled from earlier studies by Dr. Val Martin. -.. _Table PFT-specific combustion completeness and fire mortality factors: +.. _Table PFT-specific combustion completeness and fire mortality factors: .. table:: PFT-specific combustion completeness and fire mortality factors. @@ -658,13 +431,4 @@ studies by Dr. Val Martin. | Crop | 0.80 | 0.80 | 0.00 | 0.80 | 0.80 | 0.20 | 0.20 | 0.20 | 0.80 | 0.60 | 0.20 | +----------------------------------+---------------------------+---------------------------+---------------------------+-------------------------+--------------------------+------------------------------+------------------------------+--------------------------+------------------------+------------------------------+---------------------------------+ -Leaves (:math:`CC_{leaf}` ), stems (:math:`CC_{stem}` ), -roots (:math:`CC_{root}` ) , and transfer and storage carbon -(:math:`CC_{ts}` ); mortality factors for leaves -(:math:`M_{leaf}` ), live stems (:math:`M_{livestem,1}` ), -dead stems (:math:`M_{deadstem}` ), roots -(:math:`M_{root}` ), and transfer and storage carbon -(:math:`M_{ts}` ) related to the carbon transfers from these pools -to litter pool; mortality factors for live stems -(:math:`M_{livestem,2}` ) related to the carbon transfer from live -stems to dead stems; whole-plant mortality factor (:math:`\xi _{j}` ). +Leaves (:math:`CC_{leaf}` ), stems (:math:`CC_{stem}` ), roots (:math:`CC_{root}` ), and transfer and storage carbon (:math:`CC_{ts}` ); mortality factors for leaves (:math:`M_{leaf}` ), live stems (:math:`M_{livestem,1}` ), dead stems (:math:`M_{deadstem}` ), roots (:math:`M_{root}` ), and transfer and storage carbon (:math:`M_{ts}` ) related to the carbon transfers from these pools to litter pool; mortality factors for live stems (:math:`M_{livestem,2}` ) related to the carbon transfer from live stems to dead stems; whole-plant mortality factor (:math:`\xi _{j}` ). diff --git a/doc/source/tech_note/Fluxes/CLM50_Tech_Note_Fluxes.rst b/doc/source/tech_note/Fluxes/CLM50_Tech_Note_Fluxes.rst index 047d4e6723..c759d11f92 100644 --- a/doc/source/tech_note/Fluxes/CLM50_Tech_Note_Fluxes.rst +++ b/doc/source/tech_note/Fluxes/CLM50_Tech_Note_Fluxes.rst @@ -3,17 +3,7 @@ Momentum, Sensible Heat, and Latent Heat Fluxes ================================================== -The zonal :math:`\tau _{x}` and meridional :math:`\tau _{y}` momentum -fluxes (kg m\ :sup:`-1` s\ :sup:`-2`), sensible heat flux -:math:`H` (W m\ :sup:`-2`), and water vapor flux :math:`E` (kg m\ :sup:`-2` s\ :sup:`-1`) between the atmosphere at -reference height :math:`z_{atm,\, x}` (m) [where :math:`x` is height -for wind (momentum) (:math:`m`), temperature (sensible heat) -(:math:`h`), and humidity (water vapor) (:math:`w`); with zonal and -meridional winds :math:`u_{atm}` and :math:`v_{atm}` (m -s\ :sup:`-1`), potential temperature :math:`\theta _{atm}` (K), -and specific humidity :math:`q_{atm}` (kg kg\ :sup:`-1`)] and the -surface [with :math:`u_{s}` , :math:`v_{s}` , :math:`\theta _{s}` , and -:math:`q_{s}` ] are +The zonal :math:`\tau _{x}` and meridional :math:`\tau _{y}` momentum fluxes (kg m\ :sup:`-1` s\ :sup:`-2`), sensible heat flux :math:`H` (W m\ :sup:`-2`), and water vapor flux :math:`E` (kg m\ :sup:`-2` s\ :sup:`-1`) between the atmosphere at reference height :math:`z_{atm,\, x}` (m) [where :math:`x` is height for wind (momentum) (:math:`m`), temperature (sensible heat) (:math:`h`), and humidity (water vapor) (:math:`w`); with zonal and meridional winds :math:`u_{atm}` and :math:`v_{atm}` (m s\ :sup:`-1`), potential temperature :math:`\theta _{atm}` (K), and specific humidity :math:`q_{atm}` (kg kg\ :sup:`-1`)] and the surface [with :math:`u_{s}`, :math:`v_{s}`, :math:`\theta _{s}`, and :math:`q_{s}` ] are .. math:: :label: 5.1 @@ -35,14 +25,7 @@ surface [with :math:`u_{s}` , :math:`v_{s}` , :math:`\theta _{s}` , and E=-\rho _{atm} \frac{\left(q_{atm} -q_{s} \right)}{r_{aw} } . -These fluxes are derived in the next section from Monin-Obukhov -similarity theory developed for the surface layer (i.e., the nearly -constant flux layer above the surface sublayer). In this derivation, -:math:`u_{s}` and :math:`v_{s}` are defined to equal zero at height -:math:`z_{0m} +d` (the apparent sink for momentum) so that -:math:`r_{am}` is the aerodynamic resistance (s m\ :sup:`-1`) for -momentum between the atmosphere at height :math:`z_{atm,\, m}` and the -surface at height :math:`z_{0m} +d`. Thus, the momentum fluxes become +These fluxes are derived in the next section from Monin-Obukhov similarity theory developed for the surface layer (i.e., the nearly constant flux layer above the surface sublayer). In this derivation, :math:`u_{s}` and :math:`v_{s}` are defined to equal zero at height :math:`z_{0m} +d` (the apparent sink for momentum) so that :math:`r_{am}` is the aerodynamic resistance (s m\ :sup:`-1`) for momentum between the atmosphere at height :math:`z_{atm,\, m}` and the surface at height :math:`z_{0m} +d`. Thus, the momentum fluxes become .. math:: :label: 5.5 @@ -54,40 +37,21 @@ surface at height :math:`z_{0m} +d`. Thus, the momentum fluxes become \tau _{y} =-\rho _{atm} \frac{v_{atm} }{r_{am} } . -Likewise, :math:`\theta _{s}` and :math:`q_{s}` are defined at -heights :math:`z_{0h} +d` and :math:`z_{0w} +d` (the apparent sinks for -heat and water vapor, respectively - -:math:`r_{aw}` are the aerodynamic resistances (s m\ :sup:`-1`) -to sensible heat and water vapor transfer between the atmosphere at -heights :math:`z_{atm,\, h}` and :math:`z_{atm,\, w}` and the surface -at heights :math:`z_{0h} +d` and :math:`z_{0w} +d`, respectively. The -specific heat capacity of air :math:`C_{p}` (J kg\ :sup:`-1` -K\ :sup:`-1`) is a constant (:numref:`Table Physical constants`). The atmospheric potential -temperature used here is +Likewise, :math:`\theta _{s}` and :math:`q_{s}` are defined at heights :math:`z_{0h} +d` and :math:`z_{0w} +d` (the apparent sinks for heat and water vapor, respectively :math:`r_{aw}` are the aerodynamic resistances (s m\ :sup:`-1`) to sensible heat and water vapor transfer between the atmosphere at heights :math:`z_{atm,\, h}` and :math:`z_{atm,\, w}` and the surface at heights :math:`z_{0h} +d` and :math:`z_{0w} +d`, respectively. The specific heat capacity of air :math:`C_{p}` (J kg\ :sup:`-1` K\ :sup:`-1`) is a constant (:numref:`Table Physical constants`). The atmospheric potential temperature used here is .. math:: :label: 5.7 \theta _{atm} =T_{atm} +\Gamma _{d} z_{atm,\, h} -where :math:`T_{atm}` is the air temperature (K) at height -:math:`z_{atm,\, h}` and :math:`\Gamma _{d} =0.0098` K -m\ :sup:`-1` is the negative of the dry adiabatic lapse rate [this -expression is first-order equivalent to -:math:`\theta _{atm} =T_{atm} \left({P_{srf} \mathord{\left/ {\vphantom {P_{srf} P_{atm} }} \right. \kern-\nulldelimiterspace} P_{atm} } \right)^{{R_{da} \mathord{\left/ {\vphantom {R_{da} C_{p} }} \right. \kern-\nulldelimiterspace} C_{p} } }` -(:ref:`Stull 1988 `), where :math:`P_{srf}` is the surface pressure (Pa), -:math:`P_{atm}` is the atmospheric pressure (Pa), and :math:`R_{da}` -is the gas constant for dry air (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`)]. By definition, -:math:`\theta _{s} =T_{s}` . The density of moist air (kg m\ :sup:`-3`) is +where :math:`T_{atm}` is the air temperature (K) at height :math:`z_{atm,\, h}` and :math:`\Gamma _{d} =0.0098` K m\ :sup:`-1` is the negative of the dry adiabatic lapse rate [this expression is first-order equivalent to :math:`\theta _{atm} =T_{atm} \left({P_{srf} \mathord{\left/ {\vphantom {P_{srf} P_{atm} }} \right.} P_{atm} } \right)^{{R_{da} \mathord{\left/ {\vphantom {R_{da} C_{p} }} \right.} C_{p} } }` (:ref:`Stull 1988 `), where :math:`P_{srf}` is the surface pressure (Pa), :math:`P_{atm}` is the atmospheric pressure (Pa), and :math:`R_{da}` is the gas constant for dry air (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`)]. By definition, :math:`\theta _{s} =T_{s}`. The density of moist air (kg m\ :sup:`-3`) is .. math:: :label: 5.8 \rho _{atm} =\frac{P_{atm} -0.378e_{atm} }{R_{da} T_{atm} } -where the atmospheric vapor pressure :math:`e_{atm}` (Pa) is derived -from the atmospheric specific humidity :math:`q_{atm}` +where the atmospheric vapor pressure :math:`e_{atm}` (Pa) is derived from the atmospheric specific humidity :math:`q_{atm}` .. math:: :label: 5.9 @@ -99,17 +63,7 @@ from the atmospheric specific humidity :math:`q_{atm}` Monin-Obukhov Similarity Theory ----------------------------------- -The surface vertical kinematic fluxes of momentum -:math:`\overline{u'w'}` and :math:`\overline{v'w'}` (m\ :sup:`2` s\ :sub:`-2`), sensible heat :math:`\overline{\theta 'w'}` -(K m s :sup:`-1`), and latent heat :math:`\overline{q'w'}` (kg kg\ :sup:`-1` m s\ :sup:`-1`), where :math:`u'`, :math:`v'`, -:math:`w'`, :math:`\theta '`, and :math:`q'` are zonal horizontal wind, -meridional horizontal wind, vertical velocity, potential temperature, -and specific humidity turbulent fluctuations about the mean, are defined -from Monin-Obukhov similarity applied to the surface layer. This theory -states that when scaled appropriately, the dimensionless mean horizontal -wind speed, mean potential temperature, and mean specific humidity -profile gradients depend on unique functions of -:math:`\zeta =\frac{z-d}{L}` (:ref:`Zeng et al. 1998`) as +The surface vertical kinematic fluxes of momentum :math:`\overline{u'w'}` and :math:`\overline{v'w'}` (m\ :sup:`2` s\ :sub:`-2`), sensible heat :math:`\overline{\theta 'w'}` (K m s :sup:`-1`), and latent heat :math:`\overline{q'w'}` (kg kg\ :sup:`-1` m s\ :sup:`-1`), where :math:`u'`, :math:`v'`, :math:`w'`, :math:`\theta '`, and :math:`q'` are zonal horizontal wind, meridional horizontal wind, vertical velocity, potential temperature, and specific humidity turbulent fluctuations about the mean, are defined from Monin-Obukhov similarity applied to the surface layer. This theory states that when scaled appropriately, the dimensionless mean horizontal wind speed, mean potential temperature, and mean specific humidity profile gradients depend on unique functions of :math:`\zeta =\frac{z-d}{L}` (:ref:`Zeng et al. 1998`) as .. math:: :label: 5.10 @@ -126,20 +80,7 @@ profile gradients depend on unique functions of \frac{k\left(z-d\right)}{q_{*} } \frac{\partial q}{\partial z} =\phi _{w} \left(\zeta \right) -where :math:`z` is height in the surface layer (m), :math:`d` is the -displacement height (m), :math:`L` is the Monin-Obukhov length scale (m) -that accounts for buoyancy effects resulting from vertical density -gradients (i.e., the atmospheric stability), k is the von Karman -constant (:numref:`Table Physical constants`), and :math:`\left|{\it u}\right|` is the -atmospheric wind speed (m s\ :sup:`-1`). :math:`\phi _{m}` , -:math:`\phi _{h}` , and :math:`\phi _{w}` are universal (over any -surface) similarity functions of :math:`\zeta` that relate the constant -fluxes of momentum, sensible heat, and latent heat to the mean profile -gradients of :math:`\left|{\it u}\right|`, :math:`\theta` , and -:math:`q` in the surface layer. In neutral conditions, -:math:`\phi _{m} =\phi _{h} =\phi _{w} =1`. The velocity (i.e., friction -velocity) :math:`u_{\*}` (m s\ :sup:`-1`), temperature -:math:`\theta _{\*}` (K), and moisture :math:`q_{\*}` (kg kg\ :sup:`-1`) scales are +where :math:`z` is height in the surface layer (m), :math:`d` is the displacement height (m), :math:`L` is the Monin-Obukhov length scale (m) that accounts for buoyancy effects resulting from vertical density gradients (i.e., the atmospheric stability), k is the von Karman constant (:numref:`Table Physical constants`), and :math:`\left|{\it u}\right|` is the atmospheric wind speed (m s\ :sup:`-1`). :math:`\phi _{m}`, :math:`\phi _{h}`, and :math:`\phi _{w}` are universal (over any surface) similarity functions of :math:`\zeta` that relate the constant fluxes of momentum, sensible heat, and latent heat to the mean profile gradients of :math:`\left|{\it u}\right|`, :math:`\theta`, and :math:`q` in the surface layer. In neutral conditions, :math:`\phi _{m} =\phi _{h} =\phi _{w} =1`. The velocity (i.e., friction velocity) :math:`u_{*}` (m s\ :sup:`-1`), temperature :math:`\theta _{*}` (K), and moisture :math:`q_{*}` (kg kg\ :sup:`-1`) scales are .. math:: :label: 5.13 @@ -156,11 +97,7 @@ velocity) :math:`u_{\*}` (m s\ :sup:`-1`), temperature q_{*} u_{*} =-\overline{q'w'}=-\frac{E}{\rho _{atm} } -where :math:`\left|{\it \tau }\right|` is the shearing stress (kg m\ :sup:`-1` s\ :sup:`-2`), with zonal and meridional -components :math:`\overline{u'w'}=-\frac{\tau _{x} }{\rho _{atm} }` and -:math:`\overline{v'w'}=-\frac{\tau _{y} }{\rho _{atm} }` , respectively, -:math:`H` is the sensible heat flux (W m\ :sup:`-2`) and :math:`E` -is the water vapor flux (kg m\ :sup:`-2` s\ :sup:`-1`). +where :math:`\left|{\it \tau }\right|` is the shearing stress (kg m\ :sup:`-1` s\ :sup:`-2`), with zonal and meridional components :math:`\overline{u'w'}=-\frac{\tau _{x} }{\rho _{atm} }` and :math:`\overline{v'w'}=-\frac{\tau _{y} }{\rho _{atm} }`, respectively, :math:`H` is the sensible heat flux (W m\ :sup:`-2`) and :math:`E` is the water vapor flux (kg m\ :sup:`-2` s\ :sup:`-1`). The length scale :math:`L` is the Monin-Obukhov length defined as @@ -169,32 +106,16 @@ The length scale :math:`L` is the Monin-Obukhov length defined as L=-\frac{u_{*}^{3} }{k\left(\frac{g}{\overline{\theta _{v,\, atm} }} \right)\theta '_{v} w'} =\frac{u_{*}^{2} \overline{\theta _{v,\, atm} }}{kg\theta _{v*} } -where :math:`g` is the acceleration of gravity (m s\ :sup:`-2`) -(:numref:`Table Physical constants`), and -:math:`\overline{\theta _{v,\, atm} }=\overline{\theta _{atm} }\left(1+0.61q_{atm} \right)` -is the reference virtual potential temperature. :math:`L>0` indicates -stable conditions. :math:`L<0` indicates unstable conditions. -:math:`L=\infty` for neutral conditions. The temperature scale -:math:`\theta _{v*}` is defined as +where :math:`g` is the acceleration of gravity (m s\ :sup:`-2`) (:numref:`Table Physical constants`), and :math:`\overline{\theta _{v,\, atm} }=\overline{\theta _{atm} }\left(1+0.61q_{atm} \right)` is the reference virtual potential temperature. :math:`L>0` indicates stable conditions. :math:`L<0` indicates unstable conditions. :math:`L=\infty` for neutral conditions. The temperature scale :math:`\theta _{v*}` is defined as .. math:: :label: 5.17 \theta _{v*} u_{*} =\left[\theta _{*} \left(1+0.61q_{atm} \right)+0.61\overline{\theta _{atm} }q_{*} \right]u_{*} -where :math:`\overline{\theta _{atm} }` is the atmospheric potential -temperature. +where :math:`\overline{\theta _{atm} }` is the atmospheric potential temperature. -Following :ref:`Panofsky and Dutton (1984)`, the differential equations for -:math:`\phi _{m} \left(\zeta \right)`, -:math:`\phi _{h} \left(\zeta \right)`, and -:math:`\phi _{w} \left(\zeta \right)` can be integrated formally without -commitment to their exact forms. Integration between two arbitrary -heights in the surface layer :math:`z_{2}` and :math:`z_{1}` -(:math:`z_{2} >z_{1}` ) with horizontal winds -:math:`\left|{\it u}\right|_{1}` and :math:`\left|{\it u}\right|_{2}` , -potential temperatures :math:`\theta _{1}` and :math:`\theta _{2}` , -and specific humidities :math:`q_{1}` and :math:`q_{2}` results in +Following :ref:`Panofsky and Dutton (1984)`, the differential equations for :math:`\phi _{m} \left(\zeta \right)`, :math:`\phi _{h} \left(\zeta \right)`, and :math:`\phi _{w} \left(\zeta \right)` can be integrated formally without commitment to their exact forms. Integration between two arbitrary heights in the surface layer :math:`z_{2}` and :math:`z_{1}` (:math:`z_{2} >z_{1}` ) with horizontal winds :math:`\left|{\it u}\right|_{1}` and :math:`\left|{\it u}\right|_{2}`, potential temperatures :math:`\theta _{1}` and :math:`\theta _{2}`, and specific humidities :math:`q_{1}` and :math:`q_{2}` results in .. math:: :label: 5.18 @@ -211,28 +132,24 @@ and specific humidities :math:`q_{1}` and :math:`q_{2}` results in q_{2} -q_{1} =\frac{q_{*} }{k} \left[\ln \left(\frac{z_{2} -d}{z_{1} -d} \right)-\psi _{w} \left(\frac{z_{2} -d}{L} \right)+\psi _{w} \left(\frac{z_{1} -d}{L} \right)\right]. -The functions :math:`\psi _{m} \left(\zeta \right)`, -:math:`\psi _{h} \left(\zeta \right)`, and -:math:`\psi _{w} \left(\zeta \right)` are defined as +The functions :math:`\psi _{m} \left(\zeta \right)`, :math:`\psi _{h} \left(\zeta \right)`, and :math:`\psi _{w} \left(\zeta \right)` are defined as .. math:: :label: 5.21 - \psi _{m} \left(\zeta \right)=\int _{{z_{0m} \mathord{\left/ {\vphantom {z_{0m} L}} \right. \kern-\nulldelimiterspace} L} }^{\zeta }\frac{\left[1-\phi _{m} \left(x\right)\right]}{x} \, dx + \psi _{m} \left(\zeta \right)=\int _{{z_{0m} \mathord{\left/ {\vphantom {z_{0m} L}} \right.} L} }^{\zeta }\frac{\left[1-\phi _{m} \left(x\right)\right]}{x} \, dx .. math:: :label: 5.22 - \psi _{h} \left(\zeta \right)=\int _{{z_{0h} \mathord{\left/ {\vphantom {z_{0h} L}} \right. \kern-\nulldelimiterspace} L} }^{\zeta }\frac{\left[1-\phi _{h} \left(x\right)\right]}{x} \, dx + \psi _{h} \left(\zeta \right)=\int _{{z_{0h} \mathord{\left/ {\vphantom {z_{0h} L}} \right.} L} }^{\zeta }\frac{\left[1-\phi _{h} \left(x\right)\right]}{x} \, dx .. math:: :label: 5.23 - \psi _{w} \left(\zeta \right)=\int _{{z_{0w} \mathord{\left/ {\vphantom {z_{0w} L}} \right. \kern-\nulldelimiterspace} L} }^{\zeta }\frac{\left[1-\phi _{w} \left(x\right)\right]}{x} \, dx + \psi _{w} \left(\zeta \right)=\int _{{z_{0w} \mathord{\left/ {\vphantom {z_{0w} L}} \right.} L} }^{\zeta }\frac{\left[1-\phi _{w} \left(x\right)\right]}{x} \, dx -where :math:`z_{0m}` , :math:`z_{0h}` , and :math:`z_{0w}` are the -roughness lengths (m) for momentum, sensible heat, and water vapor, -respectively. +where :math:`z_{0m}`, :math:`z_{0h}`, and :math:`z_{0w}` are the roughness lengths (m) for momentum, sensible heat, and water vapor, respectively. Defining the surface values @@ -242,7 +159,7 @@ Defining the surface values .. math:: q_{1} =q_{s} {\rm \; at\; }z_{1} =z_{0w} +d, -and the atmospheric values at :math:`z_{2} =z_{atm,\, x}` +and the atmospheric values at :math:`z_{2} =z_{atm,\, x}` .. math:: :label: 5.24 @@ -270,27 +187,23 @@ the integral forms of the flux-gradient relations are q_{atm} -q_{s} =\frac{q_{*} }{k} \left[\ln \left(\frac{z_{atm,\, w} -d}{z_{0w} } \right)-\psi _{w} \left(\frac{z_{atm,\, w} -d}{L} \right)+\psi _{w} \left(\frac{z_{0w} }{L} \right)\right]. -The constraint :math:`V_{a} \ge 1` is required simply for numerical -reasons to prevent :math:`H` and :math:`E` from becoming small with -small wind speeds. The convective velocity :math:`U_{c}` accounts for -the contribution of large eddies in the convective boundary layer to -surface fluxes as follows +The constraint :math:`V_{a} \ge 1` is required simply for numerical reasons to prevent :math:`H` and :math:`E` from becoming small with small wind speeds. The convective velocity :math:`U_{c}` accounts for the contribution of large eddies in the convective boundary layer to surface fluxes as follows .. math:: :label: 5.28 U_{c} = \left\{ - \begin{array}{ll} - 0 & \qquad \zeta \ge {\rm 0} \quad {\rm (stable)} \\ - \beta w_{*} & \qquad \zeta < 0 \quad {\rm (unstable)} + \begin{array}{ll} + 0 & \qquad \zeta \ge {\rm 0} \quad {\rm (stable)} \\ + \beta w_{*} & \qquad \zeta < 0 \quad {\rm (unstable)} \end{array} \right\} -where :math:`w_{*}` is the convective velocity scale +where :math:`w_{*}` is the convective velocity scale .. math:: :label: 5.29 - w_{*} =\left(\frac{-gu_{\*} \theta _{v*} z_{i} }{\overline{\theta _{v,\, atm} }} \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right. \kern-\nulldelimiterspace} 3} } , + w_{*} =\left(\frac{-gu_{*} \theta _{v*} z_{i} }{\overline{\theta _{v,\, atm} }} \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right.} 3} } , :math:`z_{i} =1000` is the convective boundary layer height (m), and :math:`\beta =1`. @@ -299,11 +212,11 @@ The momentum flux gradient relations are (:ref:`Zeng et al. 1998 ` .. math:: :label: 5.30 - \begin{array}{llr} - \phi _{m} \left(\zeta \right)=0.7k^{{2\mathord{\left/ {\vphantom {2 3}} \right. \kern-\nulldelimiterspace} 3} } \left(-\zeta \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right. \kern-\nulldelimiterspace} 3} } & \qquad {\rm for\; }\zeta <-1.574 & \ {\rm \; (very\; unstable)} \\ - \phi _{m} \left(\zeta \right)=\left(1-16\zeta \right)^{-{1\mathord{\left/ {\vphantom {1 4}} \right. \kern-\nulldelimiterspace} 4} } & \qquad {\rm for\; -1.574}\le \zeta <0 & \ {\rm \; (unstable)} \\ - \phi _{m} \left(\zeta \right)=1+5\zeta & \qquad {\rm for\; }0\le \zeta \le 1& \ {\rm \; (stable)} \\ - \phi _{m} \left(\zeta \right)=5+\zeta & \qquad {\rm for\; }\zeta >1 & \ {\rm\; (very\; stable).} + \begin{array}{llr} + \phi _{m} \left(\zeta \right)=0.7k^{{2\mathord{\left/ {\vphantom {2 3}} \right.} 3} } \left(-\zeta \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right.} 3} } & \qquad {\rm for\; }\zeta <-1.574 & \ {\rm \; (very\; unstable)} \\ + \phi _{m} \left(\zeta \right)=\left(1-16\zeta \right)^{-{1\mathord{\left/ {\vphantom {1 4}} \right.} 4} } & \qquad {\rm for\; -1.574}\le \zeta <0 & \ {\rm \; (unstable)} \\ + \phi _{m} \left(\zeta \right)=1+5\zeta & \qquad {\rm for\; }0\le \zeta \le 1& \ {\rm \; (stable)} \\ + \phi _{m} \left(\zeta \right)=5+\zeta & \qquad {\rm for\; }\zeta >1 & \ {\rm\; (very\; stable).} \end{array} The sensible and latent heat flux gradient relations are (:ref:`Zeng et al. 1998 `) @@ -311,31 +224,21 @@ The sensible and latent heat flux gradient relations are (:ref:`Zeng et al. 1998 .. math:: :label: 5.31 - \begin{array}{llr} - \phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)=0.9k^{{4\mathord{\left/ {\vphantom {4 3}} \right. \kern-\nulldelimiterspace} 3} } \left(-\zeta \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right. \kern-\nulldelimiterspace} 3} } & \qquad {\rm for\; }\zeta <-0.465 & \ {\rm \; (very\; unstable)} \\ - \phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)=\left(1-16\zeta \right)^{-{1\mathord{\left/ {\vphantom {1 2}} \right. \kern-\nulldelimiterspace} 2} } & \qquad {\rm for\; -0.465}\le \zeta <0 & \ {\rm \; (unstable)} \\ - \phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)=1+5\zeta & \qquad {\rm for\; }0\le \zeta \le 1 & \ {\rm \; (stable)} \\ - \phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)=5+\zeta & \qquad {\rm for\; }\zeta >1 & \ {\rm \; (very\; stable).} + \begin{array}{llr} + \phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)=0.9k^{{4\mathord{\left/ {\vphantom {4 3}} \right.} 3} } \left(-\zeta \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right.} 3} } & \qquad {\rm for\; }\zeta <-0.465 & \ {\rm \; (very\; unstable)} \\ + \phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)=\left(1-16\zeta \right)^{-{1\mathord{\left/ {\vphantom {1 2}} \right.} 2} } & \qquad {\rm for\; -0.465}\le \zeta <0 & \ {\rm \; (unstable)} \\ + \phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)=1+5\zeta & \qquad {\rm for\; }0\le \zeta \le 1 & \ {\rm \; (stable)} \\ + \phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)=5+\zeta & \qquad {\rm for\; }\zeta >1 & \ {\rm \; (very\; stable).} \end{array} -To ensure continuous functions of -:math:`\phi _{m} \left(\zeta \right)`, -:math:`\phi _{h} \left(\zeta \right)`, and -:math:`\phi _{w} \left(\zeta \right)`, the simplest approach (i.e., -without considering any transition regimes) is to match the relations -for very unstable and unstable conditions at :math:`\zeta _{m} =-1.574` -for :math:`\phi _{m} \left(\zeta \right)` and -:math:`\zeta _{h} =\zeta _{w} =-0.465` for -:math:`\phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)` -(:ref:`Zeng et al. 1998 `). The flux gradient relations can be integrated to -yield wind profiles for the following conditions: +To ensure continuous functions of :math:`\phi _{m} \left(\zeta \right)`, :math:`\phi _{h} \left(\zeta \right)`, and :math:`\phi _{w} \left(\zeta \right)`, the simplest approach (i.e., without considering any transition regimes) is to match the relations for very unstable and unstable conditions at :math:`\zeta _{m} =-1.574` for :math:`\phi _{m} \left(\zeta \right)` and :math:`\zeta _{h} =\zeta _{w} =-0.465` for :math:`\phi _{h} \left(\zeta \right)=\phi _{w} \left(\zeta \right)` (:ref:`Zeng et al. 1998 `). The flux gradient relations can be integrated to yield wind profiles for the following conditions: Very unstable :math:`\left(\zeta <-1.574\right)` .. math:: :label: 5.32 - V_{a} =\frac{u_{*} }{k} \left\{\left[\ln \frac{\zeta _{m} L}{z_{0m} } -\psi _{m} \left(\zeta _{m} \right)\right]+1.14\left[\left(-\zeta \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right. \kern-\nulldelimiterspace} 3} } -\left(-\zeta _{m} \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right. \kern-\nulldelimiterspace} 3} } \right]+\psi _{m} \left(\frac{z_{0m} }{L} \right)\right\} + V_{a} =\frac{u_{*} }{k} \left\{\left[\ln \frac{\zeta _{m} L}{z_{0m} } -\psi _{m} \left(\zeta _{m} \right)\right]+1.14\left[\left(-\zeta \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right.} 3} } -\left(-\zeta _{m} \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right.} 3} } \right]+\psi _{m} \left(\frac{z_{0m} }{L} \right)\right\} Unstable :math:`\left(-1.574\le \zeta <0\right)` @@ -355,7 +258,7 @@ Very stable :math:`\left(\zeta >1\right)` .. math:: :label: 5.35 - + V_{a} =\frac{u_{*} }{k} \left\{\left[\ln \frac{L}{z_{0m} } +5\right]+\left[5\ln \zeta +\zeta -1\right]-5\frac{z_{0m} }{L} \right\} where @@ -367,7 +270,7 @@ where and -:math:`x=\left(1-16\zeta \right)^{{1\mathord{\left/ {\vphantom {1 4}} \right. \kern-\nulldelimiterspace} 4} }` . +:math:`x=\left(1-16\zeta \right)^{{1\mathord{\left/ {\vphantom {1 4}} \right.} 4} }` . The potential temperature profiles are: @@ -376,7 +279,7 @@ Very unstable :math:`\left(\zeta <-0.465\right)` .. math:: :label: 5.37 - \theta _{atm} -\theta _{s} =\frac{\theta _{*} }{k} \left\{\left[\ln \frac{\zeta _{h} L}{z_{0h} } -\psi _{h} \left(\zeta _{h} \right)\right]+0.8\left[\left(-\zeta _{h} \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right. \kern-\nulldelimiterspace} 3} } -\left(-\zeta \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right. \kern-\nulldelimiterspace} 3} } \right]+\psi _{h} \left(\frac{z_{0h} }{L} \right)\right\} + \theta _{atm} -\theta _{s} =\frac{\theta _{*} }{k} \left\{\left[\ln \frac{\zeta _{h} L}{z_{0h} } -\psi _{h} \left(\zeta _{h} \right)\right]+0.8\left[\left(-\zeta _{h} \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right.} 3} } -\left(-\zeta \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right.} 3} } \right]+\psi _{h} \left(\frac{z_{0h} }{L} \right)\right\} Unstable :math:`\left(-0.465\le \zeta <0\right)` @@ -385,7 +288,6 @@ Unstable :math:`\left(-0.465\le \zeta <0\right)` \theta _{atm} -\theta _{s} =\frac{\theta _{*} }{k} \left\{\left[\ln \frac{z_{atm,\, h} -d}{z_{0h} } -\psi _{h} \left(\zeta \right)\right]+\psi _{h} \left(\frac{z_{0h} }{L} \right)\right\} - Stable :math:`\left(0\le \zeta \le 1\right)` .. math:: @@ -407,7 +309,7 @@ Very unstable :math:`\left(\zeta <-0.465\right)` .. math:: :label: 5.41 - q_{atm} -q_{s} =\frac{q_{*} }{k} \left\{\left[\ln \frac{\zeta _{w} L}{z_{0w} } -\psi _{w} \left(\zeta _{w} \right)\right]+0.8\left[\left(-\zeta _{w} \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right. \kern-\nulldelimiterspace} 3} } -\left(-\zeta \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right. \kern-\nulldelimiterspace} 3} } \right]+\psi _{w} \left(\frac{z_{0w} }{L} \right)\right\} + q_{atm} -q_{s} =\frac{q_{*} }{k} \left\{\left[\ln \frac{\zeta _{w} L}{z_{0w} } -\psi _{w} \left(\zeta _{w} \right)\right]+0.8\left[\left(-\zeta _{w} \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right.} 3} } -\left(-\zeta \right)^{{-1\mathord{\left/ {\vphantom {-1 3}} \right.} 3} } \right]+\psi _{w} \left(\frac{z_{0w} }{L} \right)\right\} Unstable :math:`\left(-0.465\le \zeta <0\right)` @@ -437,45 +339,31 @@ where \psi _{h} \left(\zeta \right)=\psi _{w} \left(\zeta \right)=2\ln \left(\frac{1+x^{2} }{2} \right). -Using the definitions of :math:`u_{*}` , :math:`\theta _{*}` , and -:math:`q_{*}` , an iterative solution of these equations can be used to -calculate the surface momentum, sensible heat, and water vapor flux -using atmospheric and surface values for :math:`\left|{\it u}\right|`, -:math:`\theta` , and :math:`q` except that :math:`L` depends on -:math:`u_{*}` , :math:`\theta _{*}` , and :math:`q_{*}` . However, the -bulk Richardson number +Using the definitions of :math:`u_{*}`, :math:`\theta _{*}`, and :math:`q_{*}`, an iterative solution of these equations can be used to calculate the surface momentum, sensible heat, and water vapor flux using atmospheric and surface values for :math:`\left|{\it u}\right|`, :math:`\theta`, and :math:`q` except that :math:`L` depends on :math:`u_{*}`, :math:`\theta _{*}`, and :math:`q_{*}`. However, the bulk Richardson number .. math:: :label: 5.46 R_{iB} =\frac{\theta _{v,\, atm} -\theta _{v,\, s} }{\overline{\theta _{v,\, atm} }} \frac{g\left(z_{atm,\, m} -d\right)}{V_{a}^{2} } - -is related to :math:`\zeta` (:ref:`Arya 2001 `) as +is related to :math:`\zeta` (:ref:`Arya 2001 `) as .. math:: :label: 5.47 R_{iB} =\zeta \left[\ln \left(\frac{z_{atm,\, h} -d}{z_{0h} } \right)-\psi _{h} \left(\zeta \right)\right]\left[\ln \left(\frac{z_{atm,\, m} -d}{z_{0m} } \right)-\psi _{m} \left(\zeta \right)\right]^{-2} . -Using -:math:`\phi _{h} =\phi _{m}^{2} =\left(1-16\zeta \right)^{-{1\mathord{\left/ {\vphantom {1 2}} \right. \kern-\nulldelimiterspace} 2} }` -for unstable conditions and :math:`\phi _{h} =\phi _{m} =1+5\zeta` for -stable conditions to determine :math:`\psi _{m} \left(\zeta \right)` and -:math:`\psi _{h} \left(\zeta \right)`, the inverse relationship -:math:`\zeta =f\left(R_{iB} \right)` can be solved to obtain a first -guess for :math:`\zeta` and thus :math:`L` from +Using :math:`\phi _{h} =\phi _{m}^{2} =\left(1-16\zeta \right)^{-{1\mathord{\left/ {\vphantom {1 2}} \right.} 2} }` for unstable conditions and :math:`\phi _{h} =\phi _{m} =1+5\zeta` for stable conditions to determine :math:`\psi _{m} \left(\zeta \right)` and :math:`\psi _{h} \left(\zeta \right)`, the inverse relationship :math:`\zeta =f\left(R_{iB} \right)` can be solved to obtain a first guess for :math:`\zeta` and thus :math:`L` from .. math:: :label: 5.48 \begin{array}{lcr} - \zeta =\frac{R_{iB} \ln \left(\frac{z_{atm,\, m} -d}{z_{0m} } \right)}{1-5\min \left(R_{iB} ,0.19\right)} & \qquad 0.01\le \zeta \le 2 & \qquad {\rm for\; }R_{iB} \ge 0 {\rm \; (neutral\; or\; stable)} \\ + \zeta =\frac{R_{iB} \ln \left(\frac{z_{atm,\, m} -d}{z_{0m} } \right)}{1-5\min \left(R_{iB} ,0.19\right)} & \qquad 0.01\le \zeta \le 2 & \qquad {\rm for\; }R_{iB} \ge 0 {\rm \; (neutral\; or\; stable)} \\ \zeta =R_{iB} \ln \left(\frac{z_{atm,\, m} -d}{z_{0m} } \right) & \qquad -100\le \zeta \le -0.01 & \qquad {\rm for\; }R_{iB} <0 \ {\rm \; (unstable)} \end{array}. -Upon iteration (section :numref:`Numerical Implementation`), the following is used to determine -:math:`\zeta` and thus :math:`L` +Upon iteration (section :numref:`Numerical Implementation`), the following is used to determine :math:`\zeta` and thus :math:`L` .. math:: :label: 5.49 @@ -484,23 +372,21 @@ Upon iteration (section :numref:`Numerical Implementation`), the following is us where -.. math:: +.. math:: - \begin{array}{cr} - 0.01\le \zeta \le 2 & \qquad {\rm for\; }\zeta \ge 0{\rm \; (neutral\; or\; stable)} \\ + \begin{array}{cr} + 0.01\le \zeta \le 2 & \qquad {\rm for\; }\zeta \ge 0{\rm \; (neutral\; or\; stable)} \\ {\rm -100}\le \zeta \le {\rm -0.01} & \qquad {\rm for\; }\zeta <0{\rm \; (unstable)} \end{array}. -The difference in virtual potential air temperature between the -reference height and the surface is +The difference in virtual potential air temperature between the reference height and the surface is .. math:: :label: 5.50 \theta _{v,\, atm} -\theta _{v,\, s} =\left(\theta _{atm} -\theta _{s} \right)\left(1+0.61q_{atm} \right)+0.61\overline{\theta _{atm} }\left(q_{atm} -q_{s} \right). -The momentum, sensible heat, and water vapor fluxes between the surface -and the atmosphere can also be written in the form +The momentum, sensible heat, and water vapor fluxes between the surface and the atmosphere can also be written in the form .. math:: :label: 5.51 @@ -539,17 +425,14 @@ where the aerodynamic resistances (s m\ :sup:`-1`) are \begin{array}{l} {r_{aw} =\frac{q_{atm} -q_{s} }{q_{*} u_{*} } =\frac{1}{k^{2} V_{a} } \left[\ln \left(\frac{z_{atm,\, m} -d}{z_{0m} } \right)-\psi _{m} \left(\frac{z_{atm,\, m} -d}{L} \right)+\psi _{m} \left(\frac{z_{0m} }{L} \right)\right]} \\ {\qquad \left[\ln \left(\frac{z_{atm,\, {\it w}} -d}{z_{0w} } \right)-\psi _{w} \left(\frac{z_{atm,\, w} -d}{L} \right)+\psi _{w} \left(\frac{z_{0w} }{L} \right)\right]} \end{array}. -A 2-m height “screen” temperature is useful for comparison with -observations +A 2-m height "screen" temperature is useful for comparison with observations .. math:: :label: 5.58 T_{2m} =\theta _{s} +\frac{\theta _{*} }{k} \left[\ln \left(\frac{2+z_{0h} }{z_{0h} } \right)-\psi _{h} \left(\frac{2+z_{0h} }{L} \right)+\psi _{h} \left(\frac{z_{0h} }{L} \right)\right] -where for convenience, “2-m” is defined as 2 m above the apparent sink -for sensible heat (:math:`z_{0h} +d`). Similarly, a 2-m height specific -humidity is defined as +where for convenience, "2-m" is defined as 2 m above the apparent sink for sensible heat (:math:`z_{0h} +d`). Similarly, a 2-m height specific humidity is defined as .. math:: :label: 5.59 @@ -563,15 +446,12 @@ Relative humidity is RH_{2m} =\min \left(100,\, \frac{q_{2m} }{q_{sat}^{T_{2m} } } \times 100\right) -where :math:`q_{sat}^{T_{2m} }` is the saturated specific humidity at -the 2-m temperature :math:`T_{2m}` (section :numref:`Saturation Vapor Pressure`). +where :math:`q_{sat}^{T_{2m} }` is the saturated specific humidity at the 2-m temperature :math:`T_{2m}` (section :numref:`Saturation Vapor Pressure`). -A 10-m wind speed is calculated as (note that this is not consistent -with the 10-m wind speed calculated for the dust model as described in -Chapter :numref:`rst_Dust Model`) +A 10-m wind speed is calculated as (note that this is not consistent with the 10-m wind speed calculated for the dust model as described in Chapter :numref:`rst_Dust Model`) .. math:: - :label: 5.61 + :label: 5.61 u_{10m} =\left\{\begin{array}{l} {V_{a} \qquad z_{atm,\, m} \le 10} \\ {V_{a} -\frac{u_{*} }{k} \left[\ln \left(\frac{z_{atm,\, m} -d}{10+z_{0m} } \right)-\psi _{m} \left(\frac{z_{atm,\, m} -d}{L} \right)+\psi _{m} \left(\frac{10+z_{0m} }{L} \right)\right]\qquad z_{atm,\, m} >10} \end{array}\right\} @@ -580,49 +460,33 @@ Chapter :numref:`rst_Dust Model`) Sensible and Latent Heat Fluxes for Non-Vegetated Surfaces -------------------------------------------------------------- -Surfaces are considered non-vegetated for the surface flux calculations -if leaf plus stem area index :math:`L+S<0.05` (section -:numref:`Phenology and vegetation burial by snow`). By -definition, this includes bare soil and glaciers. The -solution for lakes is described in Chapter :numref:`rst_Lake Model`. For these surfaces, the -surface may be exposed to the atmosphere, snow covered, and/or surface -water covered, so that the sensible heat flux :math:`H_{g}` (W -m\ :sup:`-2`) is, with reference to :numref:`Figure Schematic diagram of sensible heat fluxes`, +Surfaces are considered non-vegetated for the surface flux calculations if leaf plus stem area index :math:`L+S<0.05` (section :numref:`Phenology and vegetation burial by snow`). By definition, this includes bare soil and glaciers. The solution for lakes is described in Chapter :numref:`rst_Lake Model`. For these surfaces, the surface may be exposed to the atmosphere, snow covered, and/or surface water covered, so that the sensible heat flux :math:`H_{g}` (W m\ :sup:`-2`) is, with reference to :numref:`Figure Schematic diagram of sensible heat fluxes`, .. math:: :label: 5.62 H_{g} =\left(1-f_{sno} -f_{h2osfc} \right)H_{soil} +f_{sno} H_{snow} +f_{h2osfc} H_{h2osfc} -where :math:`\left(1-f_{sno} -f_{h2osfc} \right)`, :math:`f_{sno}` , and -:math:`f_{h2osfc}` are the exposed, snow covered, and surface water -covered fractions of the grid cell. The individual fluxes based on the -temperatures of the soil :math:`T_{1}` , snow :math:`T_{snl+1}` , and -surface water :math:`T_{h2osfc}` are +where :math:`\left(1-f_{sno} -f_{h2osfc} \right)`, :math:`f_{sno}`, and :math:`f_{h2osfc}` are the exposed, snow covered, and surface water covered fractions of the grid cell. The individual fluxes based on the temperatures of the soil :math:`T_{1}`, snow :math:`T_{snl+1}`, and surface water :math:`T_{h2osfc}` are .. math:: - :label: 5.63 + :label: 5.63 H_{soil} =-\rho _{atm} C_{p} \frac{\left(\theta _{atm} -T_{1} \right)}{r_{ah} } .. math:: - :label: 5.64 + :label: 5.64 H_{sno} =-\rho _{atm} C_{p} \frac{\left(\theta _{atm} -T_{snl+1} \right)}{r_{ah} } .. math:: - :label: 5.65 + :label: 5.65 H_{h2osfc} =-\rho _{atm} C_{p} \frac{\left(\theta _{atm} -T_{h2osfc} \right)}{r_{ah} } -where :math:`\rho _{atm}` is the density of atmospheric air (kg m\ :sup:`-3`), :math:`C_{p}` is the specific heat capacity of air -(J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`), -:math:`\theta _{atm}` is the atmospheric potential temperature (K), and -:math:`r_{ah}` is the aerodynamic resistance to sensible heat transfer -(s m\ :sup:`-1`). +where :math:`\rho _{atm}` is the density of atmospheric air (kg m\ :sup:`-3`), :math:`C_{p}` is the specific heat capacity of air (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`), :math:`\theta _{atm}` is the atmospheric potential temperature (K), and :math:`r_{ah}` is the aerodynamic resistance to sensible heat transfer (s m\ :sup:`-1`). -The water vapor flux :math:`E_{g}` (kg m\ :sup:`-2` s\ :sup:`-1`) is, with reference to -:numref:`Figure Schematic diagram of latent heat fluxes`, +The water vapor flux :math:`E_{g}` (kg m\ :sup:`-2` s\ :sup:`-1`) is, with reference to :numref:`Figure Schematic diagram of latent heat fluxes`, .. math:: :label: 5.66 @@ -630,133 +494,93 @@ The water vapor flux :math:`E_{g}` (kg m\ :sup:`-2` s\ :sup:`-1`) is, with refe E_{g} =\left(1-f_{sno} -f_{h2osfc} \right)E_{soil} +f_{sno} E_{snow} +f_{h2osfc} E_{h2osfc} .. math:: - :label: 5.67 + :label: 5.67 E_{soil} =-\frac{\rho _{atm} \left(q_{atm} -q_{soil} \right)}{r_{aw} + r_{soil}} .. math:: - :label: 5.68 + :label: 5.68 E_{sno} =-\frac{\rho _{atm} \left(q_{atm} -q_{sno} \right)}{r_{aw} } .. math:: - :label: 5.69 + :label: 5.69 E_{h2osfc} =-\frac{\rho _{atm} \left(q_{atm} -q_{h2osfc} \right)}{r_{aw} } -where :math:`q_{atm}` is the atmospheric specific humidity (kg kg\ :sup:`-1`), :math:`q_{soil}` , :math:`q_{sno}` , -and :math:`q_{h2osfc}` are the specific humidities (kg kg\ :sup:`-1`) of the soil, snow, and surface water, respectively, -:math:`r_{aw}` is the aerodynamic resistance to water vapor transfer (s m\ :sup:`-1`), and :math:`r _{soi}` is the soil -resistance to water vapor transfer (s m\ :sup:`-1`). The specific humidities of the snow :math:`q_{sno}` and surface water -:math:`q_{h2osfc}` are assumed to be at the saturation specific humidity of their respective temperatures +where :math:`q_{atm}` is the atmospheric specific humidity (kg kg\ :sup:`-1`), :math:`q_{soil}`, :math:`q_{sno}`, and :math:`q_{h2osfc}` are the specific humidities (kg kg\ :sup:`-1`) of the soil, snow, and surface water, respectively, :math:`r_{aw}` is the aerodynamic resistance to water vapor transfer (s m\ :sup:`-1`), and :math:`r _{soi}` is the soil resistance to water vapor transfer (s m\ :sup:`-1`). The specific humidities of the snow :math:`q_{sno}` and surface water :math:`q_{h2osfc}` are assumed to be at the saturation specific humidity of their respective temperatures .. math:: - :label: 5.70 + :label: 5.70 q_{sno} =q_{sat}^{T_{snl+1} } .. math:: - :label: 5.71 + :label: 5.71 q_{h2osfc} =q_{sat}^{T_{h2osfc} } -The specific humidity of the soil surface :math:`q_{soil}` is assumed -to be proportional to the saturation specific humidity +The specific humidity of the soil surface :math:`q_{soil}` is assumed to be proportional to the saturation specific humidity .. math:: - :label: 5.72 + :label: 5.72 q_{soil} =\alpha _{soil} q_{sat}^{T_{1} } -where :math:`q_{sat}^{T_{1} }` is the saturated specific humidity at -the soil surface temperature :math:`T_{1}` (section :numref:`Saturation Vapor Pressure`). The factor -:math:`\alpha _{soil}` is a function of the surface soil water matric -potential :math:`\psi` as in :ref:`Philip (1957)` +where :math:`q_{sat}^{T_{1} }` is the saturated specific humidity at the soil surface temperature :math:`T_{1}` (section :numref:`Saturation Vapor Pressure`). The factor :math:`\alpha _{soil}` is a function of the surface soil water matric potential :math:`\psi` as in :ref:`Philip (1957)` .. math:: - :label: 5.73 + :label: 5.73 \alpha _{soil} =\exp \left(\frac{\psi _{1} g}{1\times 10^{3} R_{wv} T_{1} } \right) -where :math:`R_{wv}` is the gas constant for water vapor (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`), :math:`g` is the -gravitational acceleration (m s\ :sup:`-2`) (:numref:`Table Physical constants`), and -:math:`\psi _{1}` is the soil water matric potential of the top soil -layer (mm). The soil water matric potential :math:`\psi _{1}` is +where :math:`R_{wv}` is the gas constant for water vapor (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`), :math:`g` is the gravitational acceleration (m s\ :sup:`-2`) (:numref:`Table Physical constants`), and :math:`\psi _{1}` is the soil water matric potential of the top soil layer (mm). The soil water matric potential :math:`\psi _{1}` is .. math:: - :label: 5.74 + :label: 5.74 \psi _{1} =\psi _{sat,\, 1} s_{1}^{-B_{1} } \ge -1\times 10^{8} -where :math:`\psi _{sat,\, 1}` is the saturated matric potential (mm) -(section :numref:`Hydraulic Properties`), -:math:`B_{1}` is the :ref:`Clapp and Hornberger (1978) ` -parameter (section :numref:`Hydraulic Properties`), -and :math:`s_{1}` is the wetness of the top soil layer with respect to saturation. -The surface wetness :math:`s_{1}` is a function of the liquid water and ice content +where :math:`\psi _{sat,\, 1}` is the saturated matric potential (mm) (section :numref:`Hydraulic Properties`), :math:`B_{1}` is the :ref:`Clapp and Hornberger (1978) ` parameter (section :numref:`Hydraulic Properties`), and :math:`s_{1}` is the wetness of the top soil layer with respect to saturation. The surface wetness :math:`s_{1}` is a function of the liquid water and ice content .. math:: - :label: 5.75 + :label: 5.75 s_{1} =\frac{1}{\Delta z_{1} \theta _{sat,\, 1} } \left[\frac{w_{liq,\, 1} }{\rho _{liq} } +\frac{w_{ice,\, 1} }{\rho _{ice} } \right]\qquad 0.01\le s_{1} \le 1.0 -where :math:`\Delta z_{1}` is the thickness of the top soil layer (m), -:math:`\rho _{liq}` and :math:`\rho _{ice}` are the density of liquid -water and ice (kg m\ :sup:`-3`) (:numref:`Table Physical constants`), :math:`w_{liq,\, 1}` -and :math:`w_{ice,\, 1}` are the mass of liquid water and ice of the -top soil layer (kg m\ :sup:`-2`) (Chapter :numref:`rst_Hydrology`), and -:math:`\theta _{sat,\, 1}` is the saturated volumetric water content -(i.e., porosity) of the top soil layer (mm\ :sup:`3` mm\ :sup:`-3`) (section :numref:`Hydraulic Properties`). If -:math:`q_{sat}^{T_{1} } >q_{atm}` and :math:`q_{atm} >q_{soil}` , then -:math:`q_{soil} =q_{atm}` and :math:`\frac{dq_{soil} }{dT} =0`. This -prevents large increases (decreases) in :math:`q_{soil}` for small -increases (decreases) in soil moisture in very dry soils. +where :math:`\Delta z_{1}` is the thickness of the top soil layer (m), :math:`\rho _{liq}` and :math:`\rho _{ice}` are the density of liquid water and ice (kg m\ :sup:`-3`) (:numref:`Table Physical constants`), :math:`w_{liq,\, 1}` and :math:`w_{ice,\, 1}` are the mass of liquid water and ice of the top soil layer (kg m\ :sup:`-2`) (Chapter :numref:`rst_Hydrology`), and :math:`\theta _{sat,\, 1}` is the saturated volumetric water content (i.e., porosity) of the top soil layer (mm\ :sup:`3` mm\ :sup:`-3`) (section :numref:`Hydraulic Properties`). If :math:`q_{sat}^{T_{1} } >q_{atm}` and :math:`q_{atm} >q_{soil}`, then :math:`q_{soil} =q_{atm}` and :math:`\frac{dq_{soil} }{dT} =0`. This prevents large increases (decreases) in :math:`q_{soil}` for small increases (decreases) in soil moisture in very dry soils. -The resistance to water vapor transfer occurring within the soil matrix -:math:`r_{soil}` (s m\ :sup:`-1`) is +The resistance to water vapor transfer occurring within the soil matrix :math:`r_{soil}` (s m\ :sup:`-1`) is .. math:: - :label: 5.76 + :label: 5.76 r_{soil} = \frac{DSL}{D_{v} \tau} -where :math:`DSL` is the thickness of the dry surface layer (m), :math:`D_{v}` -is the molecular diffusivity of water vapor in air (m\ :sup:`2` s\ :sup:`-2`) -and :math:`\tau` (*unitless*) describes the tortuosity of the vapor flow paths through -the soil matrix (:ref:`Swenson and Lawrence 2014 `). +where :math:`DSL` is the thickness of the dry surface layer (m), :math:`D_{v}` is the molecular diffusivity of water vapor in air (m\ :sup:`2` s\ :sup:`-2`) and :math:`\tau` (*unitless*) describes the tortuosity of the vapor flow paths through the soil matrix (:ref:`Swenson and Lawrence 2014 `). The thickness of the dry surface layer is given by .. math:: :label: 5.77 - DSL = + DSL = \begin{array}{lr} D_{max} \ \frac{\left( \theta_{init} - \theta_{1}\right)} {\left(\theta_{init} - \theta_{air}\right)} & \qquad \theta_{1} < \theta_{init} \\ 0 & \qquad \theta_{1} \ge \theta_{init} \end{array} -where :math:`D_{max}` is a parameter specifying the length scale -of the maximum DSL thickness (default value = 15 mm), -:math:`\theta_{init}` (mm\ :sup:`3` mm\ :sup:`-3`) is the moisture value -at which the DSL initiates, :math:`\theta_{1}` (mm\ :sup:`3` mm\ :sup:`-3`) -is the moisture value of the top model soil layer, and -:math:`\theta_{air}` (mm\ :sup:`3` mm\ :sup:`-3`) is the 'air dry' soil -moisture value (:ref:`Dingman 2002 `): +where :math:`D_{max}` is a parameter specifying the length scale of the maximum DSL thickness (default value = 15 mm), :math:`\theta_{init}` (mm\ :sup:`3` mm\ :sup:`-3`) is the moisture value at which the DSL initiates, :math:`\theta_{1}` (mm\ :sup:`3` mm\ :sup:`-3`) is the moisture value of the top model soil layer, and :math:`\theta_{air}` (mm\ :sup:`3` mm\ :sup:`-3`) is the 'air dry' soil moisture value (:ref:`Dingman 2002 `): .. math:: :label: 5.78 \theta_{air} = \Phi \left( \frac{\Psi_{sat}}{\Psi_{air}} \right)^{\frac{1}{B_{1}}} \ . -where :math:`\Phi` is the porosity (mm\ :sup:`3` mm\ :sup:`-3`), -:math:`\Psi_{sat}` is the saturated soil matric potential (mm), -:math:`\Psi_{air} = 10^{7}` mm is the air dry matric potential, and -:math:`B_{1}` is a function of soil texture (section -:numref:`Hydraulic Properties`). +where :math:`\Phi` is the porosity (mm\ :sup:`3` mm\ :sup:`-3`), :math:`\Psi_{sat}` is the saturated soil matric potential (mm), :math:`\Psi_{air} = 10^{7}` mm is the air dry matric potential, and :math:`B_{1}` is a function of soil texture (section :numref:`Hydraulic Properties`). -The soil tortuosity is +The soil tortuosity is .. math:: :label: 5.79 @@ -766,48 +590,54 @@ The soil tortuosity is where :math:`\Phi_{air}` (mm\ :sup:`3` mm\ :sup:`-3`) is the air filled pore space .. math:: - :label: 5.80 + :label: 5.80 \Phi_{air} = \Phi - \theta_{air} \ . :math:`D_{v}` depends on temperature .. math:: - :label: 5.81 + :label: 5.81 D_{v} = 2.12 \times 10^{-5} \left(\frac{T_{1}}{T_{f}}\right)^{1.75} \ . -where :math:`T_{1}` (K) is the temperature of the top soil layer and -:math:`T_{f}` (K) is the freezing temperature of water -(:numref:`Table Physical Constants`). - -The roughness lengths used to calculate :math:`r_{am}` , -:math:`r_{ah}` , and :math:`r_{aw}` are :math:`z_{0m} =z_{0m,\, g}` , -:math:`z_{0h} =z_{0h,\, g}` , and :math:`z_{0w} =z_{0w,\, g}` . The -displacement height :math:`d=0`. The momentum roughness length is -:math:`z_{0m,\, g} =0.01` for soil, glaciers, and -:math:`z_{0m,\, g} =0.0024` for snow-covered surfaces -(:math:`f_{sno} >0`). In general, :math:`z_{0m}` is different from -:math:`z_{0h}` because the transfer of momentum is affected by pressure -fluctuations in the turbulent waves behind the roughness elements, while -for heat and water vapor transfer no such dynamical mechanism exists. -Rather, heat and water vapor must be transferred by molecular diffusion -across the interfacial sublayer. The following relation from -:ref:`Zilitinkevich (1970) ` is adopted by -:ref:`Zeng and Dickinson 1998 ` +where :math:`T_{1}` (K) is the temperature of the top soil layer and :math:`T_{f}` (K) is the freezing temperature of water (:numref:`Table Physical Constants`). + +The roughness lengths used to calculate :math:`r_{am}`, :math:`r_{ah}`, and :math:`r_{aw}` are :math:`z_{0m} =z_{0m,\, g}`, :math:`z_{0h} =z_{0h,\, g}`, and :math:`z_{0w} =z_{0w,\, g}`. The displacement height :math:`d=0`. The momentum roughness length is :math:`z_{0m,\, g} =0.0023` for glaciers without snow (:math:`f_{sno} =0) {\rm }`, and :math:`z_{0m,\, g} =0.00085` for bare soil surfaces without snow (:math:`f_{sno} =0) {\rm }` (:ref:`Meier et al. (2022) `). + +For bare soil and glaciers with snow ( :math:`f_{sno} > 0` ), the momentum roughness length is evaluated based on accumulated snow melt :math:`M_{a} {\rm }` (:ref:`Meier et al. (2022) `). For :math:`M_{a} >=1\times 10^{-5}` + +.. math:: + :label: 5.81a + + z_{0m,\, g} =\exp (b_{1} \tan ^{-1} \left[\frac{log_{10} (M_{a}) + 0.23)} {0.08}\right] + b_{4})\times 10^{-3} + +where :math:`M_{a}` is accumulated snow melt (meters water equivalent), :math:`b_{1} =1.4` and :math:`b_{4} =-0.31`. For :math:`M_{a} <1\times 10^{-5}` + +.. math:: + :label: 5.81b + + z_{0m,\, g} =\exp (-b_{1} 0.5 \pi + b_{4})\times 10^{-3} + +Accumulated snow melt :math:`M_{a}` at the current time step :math:`t` is defined as + +.. math:: + :label: 5.81c + + M ^{t}_{a} = M ^{t-1}_{a} - (q ^{t}_{sno} \Delta t + q ^{t}_{snowmelt} \Delta t)\times 10^{-3} + +where :math:`M ^{t}_{a}` and :math:`M ^{t-1}_{a}` are the accumulated snowmelt at the current time step and previous time step, respectively (m), :math:`q ^{t}_{sno} \Delta t` is the freshly fallen snow (mm), and :math:`q ^{t}_{snowmelt} \Delta t` is the melted snow (mm). + +The scalar roughness lengths (:math:`z_{0q,\, g}` for latent heat and :math:`z_{0h,\ g}` for sensible heat) are calculated as (:ref:`Meier et al. (2022) `) .. math:: :label: 5.82 - z_{0h,\, g} =z_{0w,\, g} =z_{0m,\, g} e^{-a\left({u_{*} z_{0m,\, g} \mathord{\left/ {\vphantom {u_{*} z_{0m,\, g} \upsilon }} \right. \kern-\nulldelimiterspace} \upsilon } \right)^{0.45} } + z_{0h,\, g}=z_{0q,\, g}=\frac{70 \nu}{u_{*}} \exp (-\beta {u_{*}} ^{0.5} |{\theta_{*}}| ^{0.25} ) -where the quantity -:math:`{u_{\*} z_{0m,\, g} \mathord{\left/ {\vphantom {u_{*} z_{0m,\, g} \upsilon }} \right. \kern-\nulldelimiterspace} \upsilon }` -is the roughness Reynolds number (and may be interpreted as the Reynolds number of the smallest turbulent eddy in the flow) with the kinematic -viscosity of air :math:`\upsilon =1.5\times 10^{-5}` m\ :sup:`2` s\ :sup:`-1` and :math:`a=0.13`. +where :math:`\beta` = 7.2, and :math:`\theta_{*}` is the potential temperature scale. -The numerical solution for the fluxes of momentum, sensible heat, and -water vapor flux from non-vegetated surfaces proceeds as follows: +The numerical solution for the fluxes of momentum, sensible heat, and water vapor flux from non-vegetated surfaces proceeds as follows: #. An initial guess for the wind speed :math:`V_{a}` is obtained from :eq:`5.24` assuming an initial convective velocity :math:`U_{c} =0` m @@ -828,7 +658,7 @@ water vapor flux from non-vegetated surfaces proceeds as follows: #. Humidity scale :math:`q_{*}` (:eq:`5.41`, :eq:`5.42`, :eq:`5.43`, :eq:`5.44`) #. Roughness lengths for sensible :math:`z_{0h,\, g}` and latent heat - :math:`z_{0w,\, g}` (:eq:`5.82` ) + :math:`z_{0w,\, g}` (:eq:`5.81a` , :eq:`5.81b` , :eq:`5.82`) #. Virtual potential temperature scale :math:`\theta _{v*}` ( :eq:`5.17`) @@ -848,10 +678,7 @@ water vapor flux from non-vegetated surfaces proceeds as follows: #. 2-m height air temperature :math:`T_{2m}` and specific humidity :math:`q_{2m}` (:eq:`5.58` , :eq:`5.59`) -The partial derivatives of the soil surface fluxes with respect to -ground temperature, which are needed for the soil temperature calculations (section -:numref:`Numerical Solution Temperature`) and to update the soil surface fluxes -(section :numref:`Update of Ground Sensible and Latent Heat Fluxes`), are +The partial derivatives of the soil surface fluxes with respect to ground temperature, which are needed for the soil temperature calculations (section :numref:`Numerical Solution Temperature`) and to update the soil surface fluxes (section :numref:`Update of Ground Sensible and Latent Heat Fluxes`), are .. math:: :label: 5.83 @@ -866,44 +693,25 @@ ground temperature, which are needed for the soil temperature calculations (sect where .. math:: - :label: 5.85 + :label: 5.85 \frac{dq_{g} }{dT_{g} } =\left(1-f_{sno} -f_{h2osfc} \right)\alpha _{soil} \frac{dq_{sat}^{T_{soil} } }{dT_{soil} } +f_{sno} \frac{dq_{sat}^{T_{sno} } }{dT_{sno} } +f_{h2osfc} \frac{dq_{sat}^{T_{h2osfc} } }{dT_{h2osfc} } . -The partial derivatives -:math:`\frac{\partial r_{ah} }{\partial T_{g} }` and -:math:`\frac{\partial r_{aw} }{\partial T_{g} }` , which cannot be -determined analytically, are ignored for -:math:`\frac{\partial H_{g} }{\partial T_{g} }` and -:math:`\frac{\partial E_{g} }{\partial T_{g} }` . +The partial derivatives :math:`\frac{\partial r_{ah} }{\partial T_{g} }` and :math:`\frac{\partial r_{aw} }{\partial T_{g} }`, which cannot be determined analytically, are ignored for :math:`\frac{\partial H_{g} }{\partial T_{g} }` and :math:`\frac{\partial E_{g} }{\partial T_{g} }`. .. _Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces: Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces -------------------------------------------------------------------------- -In the case of a vegetated surface, the sensible heat :math:`H` and -water vapor flux :math:`E` are partitioned into vegetation and ground -fluxes that depend on vegetation :math:`T_{v}` and ground -:math:`T_{g}` temperatures in addition to surface temperature -:math:`T_{s}` and specific humidity :math:`q_{s}` . Because of the -coupling between vegetation temperature and fluxes, Newton-Raphson -iteration is used to solve for the vegetation temperature and the -sensible heat and water vapor fluxes from vegetation simultaneously -using the ground temperature from the previous time step. In section -:numref:`Theory`, the equations used in the iteration scheme are derived. Details -on the numerical scheme are provided in section :numref:`Numerical Implementation`. +In the case of a vegetated surface, the sensible heat :math:`H` and water vapor flux :math:`E` are partitioned into vegetation and ground fluxes that depend on vegetation :math:`T_{v}` and ground :math:`T_{g}` temperatures in addition to surface temperature :math:`T_{s}` and specific humidity :math:`q_{s}`. Because of the coupling between vegetation temperature and fluxes, Newton-Raphson iteration is used to solve for the vegetation temperature and the sensible heat and water vapor fluxes from vegetation simultaneously using the ground temperature from the previous time step. In section :numref:`Theory`, the equations used in the iteration scheme are derived. Details on the numerical scheme are provided in section :numref:`Numerical Implementation`. .. _Theory: Theory ^^^^^^^^^^^^ -The air within the canopy is assumed to have negligible capacity to -store heat so that the sensible heat flux :math:`H` between the surface -at height :math:`z_{0h} +d` and the atmosphere at height -:math:`z_{atm,\, h}` must be balanced by the sum of the sensible heat -from the vegetation :math:`H_{v}` and the ground :math:`H_{g}` +The air within the canopy is assumed to have negligible capacity to store heat so that the sensible heat flux :math:`H` between the surface at height :math:`z_{0h} +d` and the atmosphere at height :math:`z_{atm,\, h}` must be balanced by the sum of the sensible heat from the vegetation :math:`H_{v}` and the ground :math:`H_{g}` .. math:: :label: 5.86 @@ -913,7 +721,7 @@ from the vegetation :math:`H_{v}` and the ground :math:`H_{g}` where, with reference to :numref:`Figure Schematic diagram of sensible heat fluxes`, .. math:: - :label: 5.87 + :label: 5.87 H=-\rho _{atm} C_{p} \frac{\left(\theta _{atm} -T_{s} \right)}{r_{ah} } @@ -930,7 +738,7 @@ where, with reference to :numref:`Figure Schematic diagram of sensible heat flux where .. math:: - :label: 5.90 + :label: 5.90 H_{soil} =-\rho _{atm} C_{p} \frac{\left(T_{s} -T_{1} \right)}{r_{ah} ^{{'} } } @@ -940,24 +748,13 @@ where H_{sno} =-\rho _{atm} C_{p} \frac{\left(T_{s} -T_{snl+1} \right)}{r_{ah} ^{{'} } } .. math:: - :label: 5.92 + :label: 5.92 H_{h2osfc} =-\rho _{atm} C_{p} \frac{\left(T_{s} -T_{h2osfc} \right)}{r_{ah} ^{{'} } } -where :math:`\rho _{atm}` is the density of atmospheric air (kg m\ :sup:`-3`), :math:`C_{p}` is the specific heat capacity of air -(J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`), -:math:`\theta _{atm}` is the atmospheric potential temperature (K), and -:math:`r_{ah}` is the aerodynamic resistance to sensible heat transfer -(s m\ :sup:`-1`). - -Here, :math:`T_{s}` is the surface temperature at height -:math:`z_{0h} +d`, also referred to as the canopy air temperature. -:math:`L` and :math:`S` are the exposed leaf and stem area indices -(section :numref:`Phenology and vegetation burial by snow`), :math:`r_{b}` is the leaf boundary layer resistance (s -m\ :sup:`-1`), and :math:`r_{ah} ^{{'} }` is the aerodynamic -resistance (s m\ :sup:`-1`) to heat transfer between the ground at -height :math:`z_{0h} ^{{'} }` and the canopy air at height -:math:`z_{0h} +d`. +where :math:`\rho _{atm}` is the density of atmospheric air (kg m\ :sup:`-3`), :math:`C_{p}` is the specific heat capacity of air (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical constants`), :math:`\theta _{atm}` is the atmospheric potential temperature (K), and :math:`r_{ah}` is the aerodynamic resistance to sensible heat transfer (s m\ :sup:`-1`). + +Here, :math:`T_{s}` is the surface temperature at height :math:`z_{0h} +d`, also referred to as the canopy air temperature. :math:`L` and :math:`S` are the exposed leaf and stem area indices (section :numref:`Phenology and vegetation burial by snow`), :math:`r_{b}` is the leaf boundary layer resistance (s m\ :sup:`-1`), and :math:`r_{ah} ^{{'} }` is the aerodynamic resistance (s m\ :sup:`-1`) to heat transfer between the ground at height :math:`z_{0h} ^{{'} }` and the canopy air at height :math:`z_{0h} +d`. .. _Figure Schematic diagram of sensible heat fluxes: @@ -973,8 +770,7 @@ height :math:`z_{0h} ^{{'} }` and the canopy air at height Figure Schematic diagram of water vapor fluxes for (a) non-vegetated surfaces and (b) vegetated surfaces. -Equations :eq:`5.86` - :eq:`5.89` can be solved for the canopy air -temperature :math:`T_{s}` +Equations :eq:`5.86` - :eq:`5.89` can be solved for the canopy air temperature :math:`T_{s}` .. math:: :label: 5.93 @@ -998,32 +794,23 @@ where c_{v}^{h} =\frac{\left(L+S\right)}{r_{b} } -are the sensible heat conductances from the canopy air to the -atmosphere, the ground to canopy air, and leaf surface to canopy air, -respectively (m s\ :sup:`-1`). +are the sensible heat conductances from the canopy air to the atmosphere, the ground to canopy air, and leaf surface to canopy air, respectively (m s\ :sup:`-1`). -When the expression for :math:`T_{s}` is substituted into equation :eq:`5.88`, -the sensible heat flux from vegetation :math:`H_{v}` is a function of -:math:`\theta _{atm}` , :math:`T_{g}` , and :math:`T_{v}` +When the expression for :math:`T_{s}` is substituted into equation :eq:`5.88`, the sensible heat flux from vegetation :math:`H_{v}` is a function of :math:`\theta _{atm}`, :math:`T_{g}`, and :math:`T_{v}` .. math:: :label: 5.97 H_{v} = -\rho _{atm} C_{p} \left[c_{a}^{h} \theta _{atm} +c_{g}^{h} T_{g} -\left(c_{a}^{h} +c_{g}^{h} \right)T_{v} \right]\frac{c_{v}^{h} }{c_{a}^{h} +c_{v}^{h} +c_{g}^{h} } . -Similarly, the expression for :math:`T_{s}` can be substituted into -equation to obtain the sensible heat flux from ground :math:`H_{g}` +Similarly, the expression for :math:`T_{s}` can be substituted into equations :eq:`5.89`, :eq:`5.90`, :eq:`5.91`, and :eq:`5.92` to obtain the sensible heat flux from ground :math:`H_{g}` .. math:: :label: 5.98 H_{g} = -\rho _{atm} C_{p} \left[c_{a}^{h} \theta _{atm} +c_{v}^{h} T_{v} -\left(c_{a}^{h} +c_{v}^{h} \right)T_{g} \right]\frac{c_{g}^{h} }{c_{a}^{h} +c_{v}^{h} +c_{g}^{h} } . -The air within the canopy is assumed to have negligible capacity to -store water vapor so that the water vapor flux :math:`E` between the -surface at height :math:`z_{0w} +d` and the atmosphere at height -:math:`z_{atm,\, w}` must be balanced by the sum of the water vapor -flux from the vegetation :math:`E_{v}` and the ground :math:`E_{g}` +The air within the canopy is assumed to have negligible capacity to store water vapor so that the water vapor flux :math:`E` between the surface at height :math:`z_{0w} +d` and the atmosphere at height :math:`z_{atm,\, w}` must be balanced by the sum of the water vapor flux from the vegetation :math:`E_{v}` and the ground :math:`E_{g}` .. math:: :label: 5.99 @@ -1033,7 +820,7 @@ flux from the vegetation :math:`E_{v}` and the ground :math:`E_{g}` where, with reference to :numref:`Figure Schematic diagram of latent heat fluxes`, .. math:: - :label: 5.100 + :label: 5.100 E = -\rho _{atm} \frac{\left(q_{atm} -q_{s} \right)}{r_{aw} } @@ -1050,47 +837,28 @@ where, with reference to :numref:`Figure Schematic diagram of latent heat fluxes where .. math:: - :label: 5.103 + :label: 5.103 E_{soil} = -\rho _{atm} \frac{\left(q_{s} -q_{soil} \right)}{r_{aw} ^{{'} } +r_{soil} } .. math:: - :label: 5.104 + :label: 5.104 E_{sno} = -\rho _{atm} \frac{\left(q_{s} -q_{sno} \right)}{r_{aw} ^{{'} } +r_{soil} } .. math:: - :label: 5.105 + :label: 5.105 E_{h2osfc} = -\rho _{atm} \frac{\left(q_{s} -q_{h2osfc} \right)}{r_{aw} ^{{'} } +r_{soil} } -where :math:`q_{atm}` is the atmospheric specific humidity (kg kg\ :sup:`-1`), :math:`r_{aw}` is the aerodynamic resistance to -water vapor transfer (s m\ :sup:`-1`), :math:`q_{sat}^{T_{v} }` -(kg kg\ :sup:`-1`) is the saturation water vapor specific humidity -at the vegetation temperature (section :numref:`Saturation Vapor Pressure`), :math:`q_{g}` , -:math:`q_{sno}` , and :math:`q_{h2osfc}` are the specific humidities -of the soil, snow, and surface water (section :numref:`Sensible and Latent Heat Fluxes for Non-Vegetated Surfaces`), -:math:`r_{aw} ^{{'} }` is the aerodynamic resistance (s -m\ :sup:`-1`) to water vapor transfer between the ground at height -:math:`z_{0w} ^{{'} }` and the canopy air at height :math:`z_{0w} +d`, -and :math:`r_{soil}` (:eq:`5.76`) is a resistance to diffusion through the soil -(s m\ :sup:`-1`). :math:`r_{total}` is the total resistance to -water vapor transfer from the canopy to the canopy air and includes -contributions from leaf boundary layer and sunlit and shaded stomatal -resistances :math:`r_{b}` , :math:`r_{s}^{sun}` , and -:math:`r_{s}^{sha}` (:numref:`Figure Schematic diagram of latent heat fluxes`). -The water vapor flux from vegetation -is the sum of water vapor flux from wetted leaf and stem area -:math:`E_{v}^{w}` (evaporation of water intercepted by the canopy) and -transpiration from dry leaf surfaces :math:`E_{v}^{t}` - -.. math:: - :label: 5.106 +where :math:`q_{atm}` is the atmospheric specific humidity (kg kg\ :sup:`-1`), :math:`r_{aw}` is the aerodynamic resistance to water vapor transfer (s m\ :sup:`-1`), :math:`q_{sat}^{T_{v} }` (kg kg\ :sup:`-1`) is the saturation water vapor specific humidity at the vegetation temperature (section :numref:`Saturation Vapor Pressure`), :math:`q_{g}`, :math:`q_{sno}`, and :math:`q_{h2osfc}` are the specific humidities of the soil, snow, and surface water (section :numref:`Sensible and Latent Heat Fluxes for Non-Vegetated Surfaces`), :math:`r_{aw} ^{{'} }` is the aerodynamic resistance (s m\ :sup:`-1`) to water vapor transfer between the ground at height :math:`z_{0w} ^{{'} }` and the canopy air at height :math:`z_{0w} +d`, and :math:`r_{soil}` (:eq:`5.76`) is a resistance to diffusion through the soil (s m\ :sup:`-1`). :math:`r_{total}` is the total resistance to water vapor transfer from the canopy to the canopy air and includes contributions from leaf boundary layer and sunlit and shaded stomatal resistances :math:`r_{b}`, :math:`r_{s}^{sun}`, and :math:`r_{s}^{sha}` (:numref:`Figure Schematic diagram of latent heat fluxes`). The water vapor flux from vegetation is the sum of water vapor flux from wetted leaf and stem area :math:`E_{v}^{w}` (evaporation of water intercepted by the canopy) and transpiration from dry leaf surfaces :math:`E_{v}^{t}` + +.. math:: + :label: 5.106 E_{v} =E_{v}^{w} +E_{v}^{t} . -Equations :eq:`5.99` - :eq:`5.102` can be solved for the canopy specific humidity -:math:`q_{s}` +Equations :eq:`5.99` - :eq:`5.102` can be solved for the canopy specific humidity :math:`q_{s}` .. math:: :label: 5.107 @@ -1114,28 +882,19 @@ where c_{g}^{w} =\frac{1}{r_{aw} ^{{'} } +r_{soil} } -are the water vapor conductances from the canopy air to the atmosphere, -the leaf to canopy air, and ground to canopy air, respectively. The term -:math:`r''` is determined from contributions by wet leaves and -transpiration and limited by available water and potential evaporation -as +are the water vapor conductances from the canopy air to the atmosphere, the leaf to canopy air, and ground to canopy air, respectively. The term :math:`r''` is determined from contributions by wet leaves and transpiration and limited by available water and potential evaporation as .. math:: :label: 5.111 r'' = \left\{ - \begin{array}{lr} - \min \left(f_{wet} +r_{dry} ^{{'} {'} } ,\, \frac{E_{v}^{w,\, pot} r_{dry} ^{{'} {'} } +\frac{W_{can} }{\Delta t} }{E_{v}^{w,\, pot} } \right) & \qquad E_{v}^{w,\, pot} >0,\, \beta _{t} >0 \\ - \min \left(f_{wet} ,\, \frac{E_{v}^{w,\, pot} r_{dry} ^{{'} {'} } +\frac{W_{can} }{\Delta t} }{E_{v}^{w,\, pot} } \right) & \qquad E_{v}^{w,\, pot} >0,\, \beta _{t} \le 0 \\ - 1 & \qquad E_{v}^{w,\, pot} \le 0 + \begin{array}{lr} + \min \left(f_{wet} +r_{dry} ^{{'} {'} } ,\, \frac{E_{v}^{w,\, pot} r_{dry} ^{{'} {'} } +\frac{W_{can} }{\Delta t} }{E_{v}^{w,\, pot} } \right) & \qquad E_{v}^{w,\, pot} >0,\, \beta _{t} >0 \\ + \min \left(f_{wet} ,\, \frac{E_{v}^{w,\, pot} r_{dry} ^{{'} {'} } +\frac{W_{can} }{\Delta t} }{E_{v}^{w,\, pot} } \right) & \qquad E_{v}^{w,\, pot} >0,\, \beta _{t} \le 0 \\ + 1 & \qquad E_{v}^{w,\, pot} \le 0 \end{array}\right\} -where :math:`f_{wet}` is the fraction of leaves and stems that are wet -(section :numref:`Canopy Water`), :math:`W_{can}` is canopy water (kg m\ :sup:`-2`) -(section :numref:`Canopy Water`), :math:`\Delta t` is the time step (s), and -:math:`\beta _{t}` is a soil moisture function limiting transpiration -(Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`). The potential -evaporation from wet foliage per unit wetted area is +where :math:`f_{wet}` is the fraction of leaves and stems that are wet (section :numref:`Canopy Water`), :math:`W_{can}` is canopy water (kg m\ :sup:`-2`) (section :numref:`Canopy Water`), :math:`\Delta t` is the time step (s), and :math:`\beta _{t}` is a soil moisture function limiting transpiration (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`). The potential evaporation from wet foliage per unit wetted area is .. math:: :label: 5.112 @@ -1145,37 +904,27 @@ evaporation from wet foliage per unit wetted area is The term :math:`r_{dry} ^{{'} {'} }` is .. math:: - :label: 5.113 + :label: 5.113 r_{dry} ^{{'} {'} } =\frac{f_{dry} r_{b} }{L} \left(\frac{L^{sun} }{r_{b} +r_{s}^{sun} } +\frac{L^{sha} }{r_{b} +r_{s}^{sha} } \right) -where :math:`f_{dry}` is the fraction of leaves that are dry (section -:numref:`Canopy Water`), :math:`L^{sun}` and :math:`L^{sha}` are the sunlit and shaded -leaf area indices (section :numref:`Solar Fluxes`), and :math:`r_{s}^{sun}` and -:math:`r_{s}^{sha}` are the sunlit and shaded stomatal resistances (s -m\ :sup:`-1`) (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`). +where :math:`f_{dry}` is the fraction of leaves that are dry (section :numref:`Canopy Water`), :math:`L^{sun}` and :math:`L^{sha}` are the sunlit and shaded leaf area indices (section :numref:`Solar Fluxes`), and :math:`r_{s}^{sun}` and :math:`r_{s}^{sha}` are the sunlit and shaded stomatal resistances (s m\ :sup:`-1`) (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`). -When the expression for :math:`q_{s}` is substituted into equation :eq:`5.101`, -the water vapor flux from vegetation :math:`E_{v}` is a function of -:math:`q_{atm}` , :math:`q_{g}` , and :math:`q_{sat}^{T_{v} }` +When the expression for :math:`q_{s}` is substituted into equation :eq:`5.101`, the water vapor flux from vegetation :math:`E_{v}` is a function of :math:`q_{atm}`, :math:`q_{g}`, and :math:`q_{sat}^{T_{v} }` .. math:: :label: 5.114 E_{v} =-\rho _{atm} \left[c_{a}^{w} q_{atm} +c_{g}^{w} q_{g} -\left(c_{a}^{w} +c_{g}^{w} \right)q_{sat}^{T_{v} } \right]\frac{c_{v}^{w} }{c_{a}^{w} +c_{v}^{w} +c_{g}^{w} } . -Similarly, the expression for :math:`q_{s}` can be substituted into -:eq:`5.84` to obtain the water vapor flux from the ground beneath the -canopy :math:`E_{g}` +Similarly, the expression for :math:`q_{s}` can be substituted into :eq:`5.84` to obtain the water vapor flux from the ground beneath the canopy :math:`E_{g}` .. math:: :label: 5.115 E_{g} =-\rho _{atm} \left[c_{a}^{w} q_{atm} +c_{v}^{w} q_{sat}^{T_{v} } -\left(c_{a}^{w} +c_{v}^{w} \right)q_{g} \right]\frac{c_{g}^{w} }{c_{a}^{w} +c_{v}^{w} +c_{g}^{w} } . -The aerodynamic resistances to heat (moisture) transfer between the -ground at height :math:`z_{0h} ^{{'} }` (:math:`z_{0w} ^{{'} }` ) and -the canopy air at height :math:`z_{0h} +d` (:math:`z_{0w} +d`) are +The aerodynamic resistances to heat (moisture) transfer between the ground at height :math:`z_{0h} ^{{'} }` (:math:`z_{0w} ^{{'} }` ) and the canopy air at height :math:`z_{0h} +d` (:math:`z_{0w} +d`) are .. math:: :label: 5.116 @@ -1189,12 +938,7 @@ where U_{av} =V_{a} \sqrt{\frac{1}{r_{am} V_{a} } } =u_{*} -is the magnitude of the wind velocity incident on the leaves -(equivalent here to friction velocity) (m s\ :sup:`-1`) and -:math:`C_{s}` is the turbulent transfer coefficient between the -underlying soil and the canopy air. :math:`C_{s}` is obtained by -interpolation between values for dense canopy and bare soil -(:ref:`Zeng et al. 2005 `) +is the magnitude of the wind velocity incident on the leaves (equivalent here to friction velocity) (m s\ :sup:`-1`) and :math:`C_{s}` is the turbulent transfer coefficient between the underlying soil and the canopy air. :math:`C_{s}` is obtained by interpolation between values for dense canopy and bare soil (:ref:`Zeng et al. 2005 `) .. math:: :label: 5.118 @@ -1204,45 +948,36 @@ interpolation between values for dense canopy and bare soil where the weight :math:`W` is .. math:: - :label: 5.119 + :label: 5.119 W=e^{-\left(L+S\right)} . -The dense canopy turbulent transfer coefficient -(:ref:`Dickinson et al. 1993 `) is +The dense canopy turbulent transfer coefficient (:ref:`Dickinson et al. 1993 `) is .. math:: - :label: 5.120) + :label: 5.120) C_{s,\, dense} =0.004 \ . The bare soil turbulent transfer coefficient is .. math:: - :label: 5.121 + :label: 5.121 C_{s,\, bare} =\frac{k}{a} \left(\frac{z_{0m,\, g} U_{av} }{\upsilon } \right)^{-0.45} -where the kinematic viscosity of air -:math:`\upsilon =1.5\times 10^{-5}` m\ :sup:`2` s\ :sup:`-1` and :math:`a=0.13`. +where the kinematic viscosity of air :math:`\upsilon =1.5\times 10^{-5}` m\ :sup:`2` s\ :sup:`-1` and :math:`a=0.13`. The leaf boundary layer resistance :math:`r_{b}` is .. math:: :label: 5.122 - r_{b} =\frac{1}{C_{v} } \left({U_{av} \mathord{\left/ {\vphantom {U_{av} d_{leaf} }} \right. \kern-\nulldelimiterspace} d_{leaf} } \right)^{{-1\mathord{\left/ {\vphantom {-1 2}} \right. \kern-\nulldelimiterspace} 2} } + r_{b} =\frac{1}{C_{v} } \left({U_{av} \mathord{\left/ {\vphantom {U_{av} d_{leaf} }} \right.} d_{leaf} } \right)^{{-1\mathord{\left/ {\vphantom {-1 2}} \right.} 2} } -where :math:`C_{v} =0.01` m\ s\ :sup:`-1/2` is the turbulent -transfer coefficient between the canopy surface and canopy air, and -:math:`d_{leaf}` is the characteristic dimension of the leaves in the -direction of wind flow (:numref:`Table Plant functional type aerodynamic parameters`). +where :math:`C_{v} =0.01` m\ s\ :sup:`-1/2` is the turbulent transfer coefficient between the canopy surface and canopy air, and :math:`d_{leaf}` is the characteristic dimension of the leaves in the direction of wind flow (:numref:`Table Plant functional type aerodynamic parameters`). -The partial derivatives of the fluxes from the soil beneath the canopy -with respect to ground temperature, which are needed for the soil -temperature calculations (section :numref:`Numerical Solution Temperature`) -and to update the soil surface fluxes (section -:numref:`Update of Ground Sensible and Latent Heat Fluxes`), are +The partial derivatives of the fluxes from the soil beneath the canopy with respect to ground temperature, which are needed for the soil temperature calculations (section :numref:`Numerical Solution Temperature`) and to update the soil surface fluxes (section :numref:`Update of Ground Sensible and Latent Heat Fluxes`), are .. math:: :label: 5.123 @@ -1254,111 +989,142 @@ and to update the soil surface fluxes (section \frac{\partial E_{g} }{\partial T_{g} } = \frac{\rho _{atm} }{r'_{aw} +r_{soil} } \frac{c_{a}^{w} +c_{v}^{w} }{c_{a}^{w} +c_{v}^{w} +c_{g}^{w} } \frac{dq_{g} }{dT_{g} } . -The partial derivatives -:math:`\frac{\partial r'_{ah} }{\partial T_{g} }` and -:math:`\frac{\partial r'_{aw} }{\partial T_{g} }` , which cannot be -determined analytically, are ignored for -:math:`\frac{\partial H_{g} }{\partial T_{g} }` and -:math:`\frac{\partial E_{g} }{\partial T_{g} }` . +The partial derivatives :math:`\frac{\partial r'_{ah} }{\partial T_{g} }` and :math:`\frac{\partial r'_{aw} }{\partial T_{g} }`, which cannot be determined analytically, are ignored for :math:`\frac{\partial H_{g} }{\partial T_{g} }` and :math:`\frac{\partial E_{g} }{\partial T_{g} }`. + +The roughness lengths used to calculate :math:`r_{am}`, :math:`r_{ah}`, and :math:`r_{aw}` from :eq:`5.55`, :eq:`5.56`, and :eq:`5.57` are :math:`z_{0m} =z_{0m,\, v}`, :math:`z_{0h} =z_{0h,\, v}`, and :math:`z_{0w} =z_{0w,\, v}`. -The roughness lengths used to calculate :math:`r_{am}` , -:math:`r_{ah}` , and :math:`r_{aw}` from :eq:`5.55`, :eq:`5.56`, and :eq:`5.57` are -:math:`z_{0m} =z_{0m,\, v}` , :math:`z_{0h} =z_{0h,\, v}` , and -:math:`z_{0w} =z_{0w,\, v}` . The vegetation displacement height -:math:`d` and the roughness lengths are a function of plant height and -adjusted for canopy density following :ref:`Zeng and Wang (2007) ` +The vegetation roughness lengths and displacement height :math:`d` are from :ref:`Meier et al. (2022) ` .. math:: :label: 5.125 - z_{0m,\, v} = z_{0h,\, v} =z_{0w,\, v} =\exp \left[V\ln \left(z_{top} R_{z0m} \right)+\left(1-V\right)\ln \left(z_{0m,\, g} \right)\right] + z_{0m,\, v} = z_{0h,\, v} =z_{0w,\, v} = z_{top} (1 - \frac{d} {z_{top} } ) \exp (\psi_{h} - \frac{k U_{h}} {u_{*} } ) + +where :math:`z_{top}` is canopy top height (m) (:numref:`Table Plant functional type canopy top and bottom heights`), :math:`k` is the von Karman constant (:numref:`Table Physical constants`), and :math:`\psi_{h}` is the roughness sublayer influence function + +.. math:: + :label: 5.125a + + \psi_{h} = \ln(c_{w}) - 1 + c_{w}^{-1} + +where :math:`c_{w}` is a pft-dependent constant (:numref:`Table Plant functional type aerodynamic parameters`). + +The ratio of wind speed at canopy height to friction velocity, :math:`\frac{U_{h}} {u_{*}}` is derived from an implicit function of the roughness density :math:`\lambda` + +.. math:: + :label: 5.125b + + \frac{U_{h}} {u_{*} } =(C_{S} + \lambda C_{R})^{0.5} \exp(\frac{\min \left(\lambda, \lambda_{\max}\right) c U_{h}} {2 u_{*}}) + +where :math:`C_{S}` represents the drag coefficient of the ground in the absence of vegetation, :math:`C_{R}` is the drag coefficient of an isolated roughness element (plant), and :math:`c` is an empirical constant. These three are pft-dependent parameters (:numref:`Table Plant functional type aerodynamic parameters`). :math:`\lambda_{max}` is the maximum :math:`\lambda` above which :math:`\frac{U_{h}} {u_{*}}` becomes constant. :math:`\lambda_{max}` is set to the value of :math:`\lambda` for which :eq:`5.125b`, in the absence of :math:`\lambda_{max}`, would have its minimum. :math:`\lambda_{max}` is also a pft-dependent parameter (:numref:`Table Plant functional type aerodynamic parameters`). :eq:`5.125b` can be written as .. math:: - :label: 5.126 + :label: 5.125c - d = z_{top} R_{d} V + X \exp(-X) =(C_{S} + \lambda C_{R})^{0.5} c \frac{\lambda} {2 } -where :math:`z_{top}` is canopy top height (m) -(:numref:`Table Plant functional type canopy top and bottom heights`), -:math:`R_{z0m}` and :math:`R_{d}` are the ratio of momentum roughness -length and displacement height to canopy top height, respectively -(:numref:`Table Plant functional type aerodynamic parameters`), and :math:`z_{0m,\, g}` -is the ground momentum roughness length (m) (section -:numref:`Sensible and Latent Heat Fluxes for Non-Vegetated Surfaces`). The -fractional weight :math:`V` is determined from +where .. math:: - :label: 5.127 + :label: 5.125d + + X =\frac{c \lambda U_{h}} {2 u_{*} }. - V = \frac{1-\exp \left\{-\beta \min \left[L+S,\, \left(L+S\right)_{cr} \right]\right\}}{1-\exp \left[-\beta \left(L+S\right)_{cr} \right]} +:math:`X` and therefore :math:`\frac{U_{h}} {u_{*}}` can be solved for iteratively where the initial value of :math:`X` is + +.. math:: + :label: 5.125e + + X_{i=0} =(C_{S} + \lambda C_{R})^{0.5} c \frac{\lambda} {2 } + +and the next value of :math:`X` at :math:`i+1` is + +.. math:: + :label: 5.125f + + X_{i+1} =(C_{S} + \lambda C_{R})^{0.5} c \frac{\lambda} {2 } \exp(X_{i}). + +:math:`X` is updated until :math:`\frac{U_{h}} {u_{*}}` converges to within :math:`1 \times 10^{-4}` between iterations. + +:math:`\lambda` is set to half the total single-sided area of all canopy elements, here defined as the vegetation area index (VAI) defined as the sum of leaf (:math:`L`) and stem area index (:math:`S`), subject to a maximum of :math:`\lambda_{max}` and a minimum limit applied for numerical stability + +.. math:: + :label: 5.126 + + \lambda = \frac{\min(\max(1 \times 10^{-5}, VAI), \lambda_{max})} {2 } + +The displacement height :math:`d` is + +.. math:: + :label: 5.127 -where :math:`\beta =1` and :math:`\left(L+S\right)_{cr} = 2` -(m\ :sup:`2` m\ :sup:`-2`) is a critical value of exposed leaf -plus stem area for which :math:`z_{0m}` reaches its maximum. + d = z_{top}\left[1- \frac{1-\exp(-(c_{d1} 2 \lambda)^{0.5}} {(c_{d1} 2 \lambda)^{0.5} }\right] + +where :math:`c_{d1} =7.5`. .. _Table Plant functional type aerodynamic parameters: .. table:: Plant functional type aerodynamic parameters - +----------------------------------+--------------------+------------------+-------------------------+ - | Plant functional type | :math:`R_{z0m}` | :math:`R_{d}` | :math:`d_{leaf}` (m) | - +==================================+====================+==================+=========================+ - | NET Temperate | 0.055 | 0.67 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | NET Boreal | 0.055 | 0.67 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | NDT Boreal | 0.055 | 0.67 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | BET Tropical | 0.075 | 0.67 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | BET temperate | 0.075 | 0.67 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | BDT tropical | 0.055 | 0.67 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | BDT temperate | 0.055 | 0.67 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | BDT boreal | 0.055 | 0.67 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | BES temperate | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | BDS temperate | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | BDS boreal | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | C\ :sub:`3` arctic grass | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | C\ :sub:`3` grass | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | C\ :sub:`4` grass | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Crop R | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Crop I | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Corn R | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Corn I | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Temp Cereal R | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Temp Cereal I | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Winter Cereal R | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Winter Cereal I | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Soybean R | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Soybean I | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Miscanthus R | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Miscanthus I | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Switchgrass R | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ - | Switchgrass I | 0.120 | 0.68 | 0.04 | - +----------------------------------+--------------------+------------------+-------------------------+ + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Plant functional type | :math:`d_{leaf}` (m) | :math:`c_{w}` | :math:`C_{S}` | :math:`C_{R}` | :math:`c` | :math:`\lambda_{max}` | + +==================================+=======================+=========================+=========================+=========================+=========================+=========================+ + | NET Temperate | 0.04 | 9 | 0.003 | 0.05 | 0.09 | 4.55 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | NET Boreal | 0.04 | 9 | 0.003 | 0.05 | 0.09 | 4.55 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | NDT Boreal | 0.04 | 9 | 0.003 | 0.05 | 0.09 | 4.55 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | BET Tropical | 0.04 | 3 | 0.01 | 0.14 | 0.01 | 7.87 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | BET temperate | 0.04 | 3 | 0.01 | 0.14 | 0.01 | 7.87 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | BDT tropical | 0.04 | 1 | 0.013 | 0.13 | 0.06 | 8.88 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | BDT temperate | 0.04 | 1 | 0.013 | 0.13 | 0.06 | 8.88 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | BDT boreal | 0.04 | 1 | 0.013 | 0.13 | 0.06 | 8.88 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | BES temperate | 0.04 | 20 | 0.001 | 0.05 | 0.12 | 3.07 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | BDS temperate | 0.04 | 20 | 0.001 | 0.05 | 0.12 | 3.07 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | BDS boreal | 0.04 | 20 | 0.001 | 0.05 | 0.12 | 3.07 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | C\ :sub:`3` arctic grass | 0.04 | 19 | 0.001 | 0.05 | 0.08 | 4.61 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | C\ :sub:`3` grass | 0.04 | 19 | 0.001 | 0.05 | 0.08 | 4.61 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | C\ :sub:`4` grass | 0.04 | 19 | 0.001 | 0.05 | 0.08 | 4.61 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Crop R | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Crop I | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Corn R | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Corn I | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Temp Cereal R | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Temp Cereal I | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Winter Cereal R | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Winter Cereal I | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Soybean R | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Soybean I | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Miscanthus R | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Miscanthus I | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Switchgrass R | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ + | Switchgrass I | 0.04 | 3.5 | 0.001 | 0.05 | 0.04 | 5.3 | + +----------------------------------+-----------------------+-------------------------+-------------------------+-------------------------+-------------------------+-------------------------+ .. _Numerical Implementation: @@ -1372,56 +1138,35 @@ Canopy energy conservation gives -\overrightarrow{S}_{v} +\overrightarrow{L}_{v} \left(T_{v} \right)+H_{v} \left(T_{v} \right)+\lambda E_{v} \left(T_{v} \right)=0 -where :math:`\overrightarrow{S}_{v}` is the solar radiation absorbed by -the vegetation (section :numref:`Solar Fluxes`), :math:`\overrightarrow{L}_{v}` is the net -longwave radiation absorbed by vegetation (section :numref:`Longwave Fluxes`), and -:math:`H_{v}` and :math:`\lambda E_{v}` are the sensible and latent -heat fluxes from vegetation, respectively. The term :math:`\lambda` is -taken to be the latent heat of vaporization :math:`\lambda _{vap}` -(:numref:`Table Physical constants`). +where :math:`\overrightarrow{S}_{v}` is the solar radiation absorbed by the vegetation (section :numref:`Solar Fluxes`), :math:`\overrightarrow{L}_{v}` is the net longwave radiation absorbed by vegetation (section :numref:`Longwave Fluxes`), and :math:`H_{v}` and :math:`\lambda E_{v}` are the sensible and latent heat fluxes from vegetation, respectively. The term :math:`\lambda` is taken to be the latent heat of vaporization :math:`\lambda _{vap}` (:numref:`Table Physical constants`). -:math:`\overrightarrow{L}_{v}` , :math:`H_{v}` , and -:math:`\lambda E_{v}` depend on the vegetation temperature -:math:`T_{v}` . The Newton-Raphson method for finding roots of -non-linear systems of equations can be applied to iteratively solve for -:math:`T_{v}` as +:math:`\overrightarrow{L}_{v}`, :math:`H_{v}`, and :math:`\lambda E_{v}` depend on the vegetation temperature :math:`T_{v}`. The Newton-Raphson method for finding roots of non-linear systems of equations can be applied to iteratively solve for :math:`T_{v}` as .. math:: :label: 5.129 \Delta T_{v} =\frac{\overrightarrow{S}_{v} -\overrightarrow{L}_{v} -H_{v} -\lambda E_{v} }{\frac{\partial \overrightarrow{L}_{v} }{\partial T_{v} } +\frac{\partial H_{v} }{\partial T_{v} } +\frac{\partial \lambda E_{v} }{\partial T_{v} } } -where :math:`\Delta T_{v} =T_{v}^{n+1} -T_{v}^{n}` and the subscript -“n” indicates the iteration. +where :math:`\Delta T_{v} =T_{v}^{n+1} -T_{v}^{n}` and the subscript "n" indicates the iteration. The partial derivatives are .. math:: - :label: 5.130 + :label: 5.130 \frac{\partial \overrightarrow{L}_{v} }{\partial T_{v} } =4\varepsilon _{v} \sigma \left[2-\varepsilon _{v} \left(1-\varepsilon _{g} \right)\right]T_{v}^{3} .. math:: - :label: 5.131 + :label: 5.131 \frac{\partial H_{v} }{\partial T_{v} } =\rho _{atm} C_{p} \left(c_{a}^{h} +c_{g}^{h} \right)\frac{c_{v}^{h} }{c_{a}^{h} +c_{v}^{h} +c_{g}^{h} } .. math:: - :label: 5.132 + :label: 5.132 \frac{\partial \lambda E_{v} }{\partial T_{v} } =\lambda \rho _{atm} \left(c_{a}^{w} +c_{g}^{w} \right)\frac{c_{v}^{w} }{c_{a}^{w} +c_{v}^{w} +c_{g}^{w} } \frac{dq_{sat}^{T_{v} } }{dT_{v} } . -The partial derivatives -:math:`\frac{\partial r_{ah} }{\partial T_{v} }` and -:math:`\frac{\partial r_{aw} }{\partial T_{v} }` , which cannot be -determined analytically, are ignored for -:math:`\frac{\partial H_{v} }{\partial T_{v} }` and -:math:`\frac{\partial \lambda E_{v} }{\partial T_{v} }` . However, if -:math:`\zeta` changes sign more than four times during the temperature -iteration, :math:`\zeta =-0.01`. This helps prevent “flip-flopping” -between stable and unstable conditions. The total water vapor flux -:math:`E_{v}` , transpiration flux :math:`E_{v}^{t}` , and sensible heat -flux :math:`H_{v}` are updated for changes in leaf temperature as +The partial derivatives :math:`\frac{\partial r_{ah} }{\partial T_{v} }` and :math:`\frac{\partial r_{aw} }{\partial T_{v} }`, which cannot be determined analytically, are ignored for :math:`\frac{\partial H_{v} }{\partial T_{v} }` and :math:`\frac{\partial \lambda E_{v} }{\partial T_{v} }`. However, if :math:`\zeta` changes sign more than four times during the temperature iteration, :math:`\zeta =-0.01`. This helps prevent "flip-flopping" between stable and unstable conditions. The total water vapor flux :math:`E_{v}`, transpiration flux :math:`E_{v}^{t}`, and sensible heat flux :math:`H_{v}` are updated for changes in leaf temperature as .. math:: :label: 5.133 @@ -1438,299 +1183,200 @@ flux :math:`H_{v}` are updated for changes in leaf temperature as H_{v} =-\rho _{atm} C_{p} \left[c_{a}^{h} \theta _{atm} +c_{g}^{h} T_{g} -\left(c_{a}^{h} +c_{g}^{h} \right)\left(T_{v} +\Delta T_{v} \right)\right]\frac{c_{v}^{h} }{c_{a}^{h} +c_{v}^{h} +c_{g}^{h} } . -The numerical solution for vegetation temperature and the fluxes of -momentum, sensible heat, and water vapor flux from vegetated surfaces -proceeds as follows: +The numerical solution for vegetation temperature and the fluxes of momentum, sensible heat, and water vapor flux from vegetated surfaces proceeds as follows: -#. Initial values for canopy air temperature and specific humidity are - obtained from +#. Initial values for canopy air temperature and specific humidity are obtained from .. math:: - :label: 5.136 + :label: 5.136 T_{s} =\frac{T_{g} +\theta _{atm} }{2} .. math:: - :label: 5.137 + :label: 5.137 q_{s} =\frac{q_{g} +q_{atm} }{2} . -#. An initial guess for the wind speed :math:`V_{a}` is obtained from - :eq:`5.24` assuming an initial convective velocity :math:`U_{c} =0` m - s\ :sup:`-1` for stable conditions - (:math:`\theta _{v,\, atm} -\theta _{v,\, s} \ge 0` as evaluated from - :eq:`5.50` ) and :math:`U_{c} =0.5` for unstable conditions - (:math:`\theta _{v,\, atm} -\theta _{v,\, s} <0`). +#. An initial guess for the wind speed :math:`V_{a}` is obtained from :eq:`5.24` assuming an initial convective velocity :math:`U_{c} =0` m s\ :sup:`-1` for stable conditions (:math:`\theta _{v,\, atm} -\theta _{v,\, s} \ge 0` as evaluated from :eq:`5.50` ) and :math:`U_{c} =0.5` for unstable conditions (:math:`\theta _{v,\, atm} -\theta _{v,\, s} <0`). -#. An initial guess for the Monin-Obukhov length :math:`L` is obtained - from the bulk Richardson number using equation and :eq:`5.46` and :eq:`5.48`. +#. An initial guess for the Monin-Obukhov length :math:`L` is obtained from the bulk Richardson number using equations :eq:`5.46` and :eq:`5.48`. #. Iteration proceeds on the following system of equations: -#. Friction velocity :math:`u_{*}` (:eq:`5.32`, :eq:`5.33`, :eq:`5.34`, :eq:`5.35`) +#. Friction velocity :math:`u_{*}` (:eq:`5.32`, :eq:`5.33`, :eq:`5.34`, :eq:`5.35`) -#. Ratio :math:`\frac{\theta _{*} }{\theta _{atm} -\theta _{s} }` - (:eq:`5.37` , :eq:`5.38`, :eq:`5.39`, :eq:`5.40`) +#. Ratio :math:`\frac{\theta _{*} }{\theta _{atm} -\theta _{s} }` (:eq:`5.37`, :eq:`5.38`, :eq:`5.39`, :eq:`5.40`) -#. Ratio :math:`\frac{q_{*} }{q_{atm} -q_{s} }` (:eq:`5.41`, :eq:`5.42`, :eq:`5.43`, :eq:`5.44`) +#. Ratio :math:`\frac{q_{*} }{q_{atm} -q_{s} }` (:eq:`5.41`, :eq:`5.42`, :eq:`5.43`, :eq:`5.44`) -#. Aerodynamic resistances :math:`r_{am}` , :math:`r_{ah}` , and - :math:`r_{aw}` (:eq:`5.55`, :eq:`5.56`, :eq:`5.57`) +#. Aerodynamic resistances :math:`r_{am}`, :math:`r_{ah}`, and :math:`r_{aw}` (:eq:`5.55`, :eq:`5.56`, :eq:`5.57`) -#. Magnitude of the wind velocity incident on the leaves :math:`U_{av}` - (:eq:`5.117` ) +#. Magnitude of the wind velocity incident on the leaves :math:`U_{av}` (:eq:`5.117` ) -#. Leaf boundary layer resistance :math:`r_{b}` (:eq:`5.136` ) +#. Leaf boundary layer resistance :math:`r_{b}` (:eq:`5.136` ) -#. Aerodynamic resistances :math:`r_{ah} ^{{'} }` and - :math:`r_{aw} ^{{'} }` (:eq:`5.116` ) +#. Aerodynamic resistances :math:`r_{ah} ^{{'} }` and :math:`r_{aw} ^{{'} }`(:eq:`5.116` ) -#. Sunlit and shaded stomatal resistances :math:`r_{s}^{sun}` and - :math:`r_{s}^{sha}` (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`) +#. Sunlit and shaded stomatal resistances :math:`r_{s}^{sun}` and :math:`r_{s}^{sha}` (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`) -#. Sensible heat conductances :math:`c_{a}^{h}` , :math:`c_{g}^{h}` , - and :math:`c_{v}^{h}` (:eq:`5.94`, :eq:`5.95`, :eq:`5.96`) +#. Sensible heat conductances :math:`c_{a}^{h}`, :math:`c_{g}^{h}`, and :math:`c_{v}^{h}` (:eq:`5.94`, :eq:`5.95`, :eq:`5.96`) -#. Latent heat conductances :math:`c_{a}^{w}` , :math:`c_{v}^{w}` , and - :math:`c_{g}^{w}` (:eq:`5.108`, :eq:`5.109`, :eq:`5.110`) +#. Latent heat conductances :math:`c_{a}^{w}`, :math:`c_{v}^{w}`, and :math:`c_{g}^{w}` (:eq:`5.108`, :eq:`5.109`, :eq:`5.110`) -#. Sensible heat flux from vegetation :math:`H_{v}` (:eq:`5.97` ) +#. Sensible heat flux from vegetation :math:`H_{v}` (:eq:`5.97` ) -#. Latent heat flux from vegetation :math:`\lambda E_{v}` (:eq:`5.101` ) +#. Latent heat flux from vegetation :math:`\lambda E_{v}` (:eq:`5.101` ) -#. If the latent heat flux has changed sign from the latent heat flux - computed at the previous iteration - (:math:`\lambda E_{v} ^{n+1} \times \lambda E_{v} ^{n} <0`), the - latent heat flux is constrained to be 10% of the computed value. The - difference between the constrained and computed value - (:math:`\Delta _{1} =0.1\lambda E_{v} ^{n+1} -\lambda E_{v} ^{n+1}` ) - is added to the sensible heat flux later. +#. If the latent heat flux has changed sign from the latent heat flux computed at the previous iteration (:math:`\lambda E_{v} ^{n+1} \times \lambda E_{v} ^{n} <0`), the latent heat flux is constrained to be 10% of the computed value. The difference between the constrained and computed value (:math:`\Delta _{1} =0.1\lambda E_{v} ^{n+1} -\lambda E_{v} ^{n+1}` ) is added to the sensible heat flux later. -#. Change in vegetation temperature :math:`\Delta T_{v}` (:eq:`5.129` ) and - update the vegetation temperature as - :math:`T_{v}^{n+1} =T_{v}^{n} +\Delta T_{v}` . :math:`T_{v}` is - constrained to change by no more than 1ºK in one iteration. If this - limit is exceeded, the energy error is +#. Change in vegetation temperature :math:`\Delta T_{v}` (:eq:`5.129` ) and update the vegetation temperature as :math:`T_{v}^{n+1} =T_{v}^{n} +\Delta T_{v}`. :math:`T_{v}` is constrained to change by no more than 1°K in one iteration. If this limit is exceeded, the energy error is .. math:: - :label: 5.138 + :label: 5.138 \Delta _{2} =\overrightarrow{S}_{v} -\overrightarrow{L}_{v} -\frac{\partial \overrightarrow{L}_{v} }{\partial T_{v} } \Delta T_{v} -H_{v} -\frac{\partial H_{v} }{\partial T_{v} } \Delta T_{v} -\lambda E_{v} -\frac{\partial \lambda E_{v} }{\partial T_{v} } \Delta T_{v} -where :math:`\Delta T_{v} =1{\rm \; or\; }-1`. The error -:math:`\Delta _{2}` is added to the sensible heat flux later. +where :math:`\Delta T_{v} =1{\rm \; or\; }-1`. The error :math:`\Delta _{2}` is added to the sensible heat flux later. -#. Water vapor flux :math:`E_{v}` (:eq:`5.133` ) +#. Water vapor flux :math:`E_{v}` (:eq:`5.133` ) -#. Transpiration :math:`E_{v}^{t}` (:eq:`5.134` if :math:`\beta_{t} >0`, - otherwise :math:`E_{v}^{t} =0`) +#. Transpiration :math:`E_{v}^{t}` (:eq:`5.134` if :math:`\beta_{t} >0`, otherwise :math:`E_{v}^{t} =0`) -#. The water vapor flux :math:`E_{v}` is constrained to be less than or - equal to the sum of transpiration :math:`E_{v}^{t}` and the water - available from wetted leaves and stems - :math:`{W_{can} \mathord{\left/ {\vphantom {W_{can} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t}` . - The energy error due to this constraint is +#. The water vapor flux :math:`E_{v}` is constrained to be less than or equal to the sum of transpiration :math:`E_{v}^{t}` and the water available from wetted leaves and stems :math:`{W_{can} \mathord{\left/ {\vphantom {W_{can} \Delta t}} \right.} \Delta t}`. The energy error due to this constraint is .. math:: :label: 5.139 \Delta _{3} =\max \left(0,\, E_{v} -E_{v}^{t} -\frac{W_{can} }{\Delta t} \right). -The error :math:`\lambda \Delta _{3}` is added to the sensible heat -flux later. - -#. Sensible heat flux :math:`H_{v}` (:eq:`5.135` ). The three energy error - terms, :math:`\Delta _{1}` , :math:`\Delta _{2}` , and - :math:`\lambda \Delta _{3}` are also added to the sensible heat - flux. +The error :math:`\lambda \Delta _{3}` is added to the sensible heat flux later. -#. The saturated vapor pressure :math:`e_{i}` (Chapter - :numref:`rst_Stomatal Resistance and Photosynthesis`), saturated - specific humidity :math:`q_{sat}^{T_{v} }` and its derivative - :math:`\frac{dq_{sat}^{T_{v} } }{dT_{v} }` at the leaf surface - (section :numref:`Saturation Vapor Pressure`), are re-evaluated based on - the new :math:`T_{v}` . +#. Sensible heat flux :math:`H_{v}` (:eq:`5.135` ). The three energy error terms, :math:`\Delta _{1}`, :math:`\Delta _{2}`, and :math:`\lambda \Delta _{3}` are also added to the sensible heat flux. -#. Canopy air temperature :math:`T_{s}` (:eq:`5.93` ) +#. The saturated vapor pressure :math:`e_{i}` (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`), saturated specific humidity :math:`q_{sat}^{T_{v} }` and its derivative :math:`\frac{dq_{sat}^{T_{v} } }{dT_{v} }` at the leaf surface (section :numref:`Saturation Vapor Pressure`), are re-evaluated based on the new :math:`T_{v}`. -#. Canopy air specific humidity :math:`q_{s}` (:eq:`5.107` ) +#. Canopy air temperature :math:`T_{s}` (:eq:`5.93` ) -#. Temperature difference :math:`\theta _{atm} -\theta _{s}` +#. Canopy air specific humidity :math:`q_{s}` (:eq:`5.107` ) -#. Specific humidity difference :math:`q_{atm} -q_{s}` +#. Temperature difference :math:`\theta _{atm} -\theta _{s}` -#. Potential temperature scale - :math:`\theta _{*} =\frac{\theta _{*} }{\theta _{atm} -\theta _{s} } \left(\theta _{atm} -\theta _{s} \right)` - where :math:`\frac{\theta _{*} }{\theta _{atm} -\theta _{s} }` was - calculated earlier in the iteration +#. Specific humidity difference :math:`q_{atm} -q_{s}` -#. Humidity scale - :math:`q_{*} =\frac{q_{*} }{q_{atm} -q_{s} } \left(q_{atm} -q_{s} \right)` - where :math:`\frac{q_{*} }{q_{atm} -q_{s} }` was calculated earlier - in the iteration +#. Potential temperature scale :math:`\theta _{*} =\frac{\theta _{*} }{\theta _{atm} -\theta _{s} } \left(\theta _{atm} -\theta _{s} \right)` where :math:`\frac{\theta _{*} }{\theta _{atm} -\theta _{s} }` was calculated earlier in the iteration #. Humidity scale :math:`q_{*} =\frac{q_{*} }{q_{atm} -q_{s} } \left(q_{atm} -q_{s} \right)` where :math:`\frac{q_{*} }{q_{atm} -q_{s} }` was calculated earlier in the iteration #. Virtual potential temperature scale :math:`\theta _{v*}` (:eq:`5.17` ) -#. Virtual potential temperature scale :math:`\theta _{v*}` (:eq:`5.17` ) - -#. Wind speed including the convective velocity, :math:`V_{a}` (:eq:`5.24` ) +#. Wind speed including the convective velocity, :math:`V_{a}` (:eq:`5.24` ) #. Monin-Obukhov length :math:`L` (:eq:`5.49` ) -#. The iteration is stopped after two or more steps if - :math:`\tilde{\Delta }T_{v} <0.01` and - :math:`\left|\lambda E_{v}^{n+1} -\lambda E_{v}^{n} \right|<0.1` - where - :math:`\tilde{\Delta }T_{v} =\max \left(\left|T_{v}^{n+1} -T_{v}^{n} \right|,\, \left|T_{v}^{n} -T_{v}^{n-1} \right|\right)`, - or after forty iterations have been carried out. +#. The iteration is stopped after two or more steps if :math:`\tilde{\Delta }T_{v} <0.01` and :math:`\left|\lambda E_{v}^{n+1} -\lambda E_{v}^{n} \right|<0.1` where :math:`\tilde{\Delta }T_{v} =\max \left(\left|T_{v}^{n+1} -T_{v}^{n} \right|,\, \left|T_{v}^{n} -T_{v}^{n-1} \right|\right)`, or after forty iterations have been carried out. -#. Momentum fluxes :math:`\tau _{x}` , :math:`\tau _{y}` (:eq:`5.5`, :eq:`5.6`) +#. Momentum fluxes :math:`\tau _{x}`, :math:`\tau _{y}` (:eq:`5.5`, :eq:`5.6`) -#. Sensible heat flux from ground :math:`H_{g}` (:eq:`5.89` ) +#. Sensible heat flux from ground :math:`H_{g}` (:eq:`5.89` ) -#. Water vapor flux from ground :math:`E_{g}` (:eq:`5.102` ) +#. Water vapor flux from ground :math:`E_{g}` (:eq:`5.102` ) -#. 2-m height air temperature :math:`T_{2m}` , specific humidity - :math:`q_{2m}` , relative humidity :math:`RH_{2m}` \ (:eq:`5.58` , :eq:`5.59`, :eq:`5.60`) +#. 2-m height air temperature :math:`T_{2m}`, specific humidity :math:`q_{2m}`, relative humidity :math:`RH_{2m}` \ (:eq:`5.58`, :eq:`5.59`, :eq:`5.60`) .. _Update of Ground Sensible and Latent Heat Fluxes: Update of Ground Sensible and Latent Heat Fluxes ---------------------------------------------------- -The sensible and water vapor heat fluxes derived above for bare soil and -soil beneath canopy are based on the ground surface temperature from the -previous time step :math:`T_{g}^{n}` and are used as the surface -forcing for the solution of the soil temperature equations (section -:numref:`Numerical Solution Temperature`). This solution yields a new ground -surface temperature :math:`T_{g}^{n+1}` . The ground sensible and water -vapor fluxes are then updated for :math:`T_{g}^{n+1}` as +The sensible and water vapor heat fluxes derived above for bare soil and soil beneath canopy are based on the ground surface temperature from the previous time step :math:`T_{g}^{n}` and are used as the surface forcing for the solution of the soil temperature equations (section :numref:`Numerical Solution Temperature`). This solution yields a new ground surface temperature :math:`T_{g}^{n+1}`. The ground sensible and water vapor fluxes are then updated for :math:`T_{g}^{n+1}` as .. math:: - :label: 5.140 + :label: 5.140 H'_{g} =H_{g} +\left(T_{g}^{n+1} -T_{g}^{n} \right)\frac{\partial H_{g} }{\partial T_{g} } .. math:: - :label: 5.141 + :label: 5.141 E'_{g} =E_{g} +\left(T_{g}^{n+1} -T_{g}^{n} \right)\frac{\partial E_{g} }{\partial T_{g} } -where :math:`H_{g}` and :math:`E_{g}` are the sensible heat and water -vapor fluxes derived from equations and for non-vegetated surfaces and -equations and for vegetated surfaces using :math:`T_{g}^{n}` . One -further adjustment is made to :math:`H'_{g}` and :math:`E'_{g}` . If -the soil moisture in the top snow/soil layer is not sufficient to -support the updated ground evaporation, i.e., if :math:`E'_{g} > 0` and -:math:`f_{evap} < 1` where +where :math:`H_{g}`, :math:`E_{g}`, :math:`\frac{\partial H_{g} }{\partial T_{g} }`, and :math:`\frac{\partial E_{g} }{\partial T_{g} }` are the sensible heat and water vapor fluxes and their partial derivatives derived from equations :eq:`5.62`, :eq:`5.66`, :eq:`5.83`, and :eq:`5.84` for non-vegetated surfaces and equations :eq:`5.89`, :eq:`5.102`, :eq:`5.123`, and :eq:`5.124` for vegetated surfaces using :math:`T_{g}^{n}`. One further adjustment is made to :math:`H'_{g}` and :math:`E'_{g}`. If the soil moisture in the top snow/soil layer is not sufficient to support the updated ground evaporation, i.e., if :math:`E'_{g} > 0` and :math:`f_{evap} < 1` where .. math:: - :label: 5.142 + :label: 5.142 - f_{evap} =\frac{{\left(w_{ice,\; snl+1} +w_{liq,\, snl+1} \right)\mathord{\left/ {\vphantom {\left(w_{ice,\; snl+1} +w_{liq,\, snl+1} \right) \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} }{\sum _{j=1}^{npft}\left(E'_{g} \right)_{j} \left(wt\right)_{j} } \le 1, + f_{evap} =\frac{{\left(w_{ice,\; snl+1} +w_{liq,\, snl+1} \right)\mathord{\left/ {\vphantom {\left(w_{ice,\; snl+1} +w_{liq,\, snl+1} \right) \Delta t}} \right.} \Delta t} }{\sum _{j=1}^{npft}\left(E'_{g} \right)_{j} \left(wt\right)_{j} } \le 1, an adjustment is made to reduce the ground evaporation accordingly as .. math:: - :label: 5.143 + :label: 5.143 E''_{g} =f_{evap} E'_{g} . -The term -:math:`\sum _{j=1}^{npft}\left(E'_{g} \right)_{j} \left(wt\right)_{j}` -is the sum of :math:`E'_{g}` over all evaporating PFTs where -:math:`\left(E'_{g} \right)_{j}` is the ground evaporation from the -:math:`j^{th}` PFT on the column, :math:`\left(wt\right)_{j}` is the -relative area of the :math:`j^{th}` PFT with respect to the column, and -:math:`npft` is the number of PFTs on the column. -:math:`w_{ice,\, snl+1}` and :math:`w_{liq,\, snl+1}` are the ice and -liquid water contents (kg m\ :sup:`-2`) of the top snow/soil layer -(Chapter :numref:`rst_Hydrology`). Any resulting energy deficit is assigned -to sensible heat -as +The term :math:`\sum _{j=1}^{npft}\left(E'_{g} \right)_{j} \left(wt\right)_{j}` is the sum of :math:`E'_{g}` over all evaporating PFTs where :math:`\left(E'_{g} \right)_{j}` is the ground evaporation from the :math:`j^{th}` PFT on the column, :math:`\left(wt\right)_{j}` is the relative area of the :math:`j^{th}` PFT with respect to the column, and :math:`npft` is the number of PFTs on the column. :math:`w_{ice,\, snl+1}` and :math:`w_{liq,\, snl+1}` are the ice and liquid water contents (kg m\ :sup:`-2`) of the top snow/soil layer (Chapter :numref:`rst_Hydrology`). Any resulting energy deficit is assigned to sensible heat as .. math:: - :label: 5.144 + :label: 5.144 H''_{g} =H_{g} +\lambda \left(E'_{g} -E''_{g} \right). -The ground water vapor flux :math:`E''_{g}` is partitioned into evaporation -of liquid water from snow/soil :math:`q_{seva}` (kg\ m\ :sup:`-2` s\ :sup:`-1`), -sublimation from snow/soil ice :math:`q_{subl}` (kg m\ :sup:`-2` s\ :sup:`-1`), -liquid dew on snow/soil :math:`q_{sdew}` (kg m\ :sup:`-2` s\ :sup:`-1`), or -frost on snow/soil :math:`q_{frost}` (kg m\ :sup:`-2` s\ :sup:`-1`) as +The ground water vapor flux :math:`E''_{g}` is partitioned into evaporation of liquid water from snow/soil :math:`q_{seva}` (kg\ m\ :sup:`-2` s\ :sup:`-1`), sublimation from snow/soil ice :math:`q_{subl}` (kg m\ :sup:`-2` s\ :sup:`-1`), liquid dew on snow/soil :math:`q_{sdew}` (kg m\ :sup:`-2` s\ :sup:`-1`), or frost on snow/soil :math:`q_{frost}` (kg m\ :sup:`-2` s\ :sup:`-1`) as .. math:: - :label: 5.145 + :label: 5.145 q_{seva} =\max \left(E''_{sno} \frac{w_{liq,\, snl+1} }{w_{ice,\; snl+1} +w_{liq,\, snl+1} } ,0\right)\qquad E''_{sno} \ge 0,\, w_{ice,\; snl+1} +w_{liq,\, snl+1} >0 .. math:: - :label: 5.146 + :label: 5.146 q_{subl} =E''_{sno} -q_{seva} \qquad E''_{sno} \ge 0 .. math:: - :label: 5.147 + :label: 5.147 q_{sdew} =\left|E''_{sno} \right|\qquad E''_{sno} <0{\rm \; and\; }T_{g} \ge T_{f} .. math:: - :label: 5.148 + :label: 5.148 q_{frost} =\left|E''_{sno} \right|\qquad E''_{sno} <0{\rm \; and\; }T_{g} 0} \\ {\lambda _{vap} \qquad {\rm otherwise}} \end{array}\right\} -where :math:`\lambda _{sub}` and :math:`\lambda _{vap}` are the latent -heat of sublimation and vaporization, respectively (J -(kg\ :sup:`-1`) (:numref:`Table Physical constants`). When converting vegetation water vapor -flux to an energy flux, :math:`\lambda _{vap}` is used. +where :math:`\lambda _{sub}` and :math:`\lambda _{vap}` are the latent heat of sublimation and vaporization, respectively (J (kg\ :sup:`-1`) (:numref:`Table Physical constants`). When converting vegetation water vapor flux to an energy flux, :math:`\lambda _{vap}` is used. The system balances energy as .. math:: - :label: 5.153 + :label: 5.153 \overrightarrow{S}_{g} +\overrightarrow{S}_{v} +L_{atm} \, \downarrow -L\, \uparrow -H_{v} -H_{g} -\lambda _{vap} E_{v} -\lambda E_{g} -G=0. @@ -1739,43 +1385,33 @@ The system balances energy as Saturation Vapor Pressure ----------------------------- -Saturation vapor pressure :math:`e_{sat}^{T}` (Pa) and its derivative -:math:`\frac{de_{sat}^{T} }{dT}` , as a function of temperature -:math:`T` (ºC), are calculated from the eighth-order polynomial fits of -:ref:`Flatau et al. (1992) ` +Saturation vapor pressure :math:`e_{sat}^{T}` (Pa) and its derivative :math:`\frac{de_{sat}^{T} }{dT}`, as a function of temperature :math:`T` (°C), are calculated from the eighth-order polynomial fits of :ref:`Flatau et al. (1992) ` .. math:: - :label: 5.154 + :label: 5.154 e_{sat}^{T} =100\left[a_{0} +a_{1} T+\cdots +a_{n} T^{n} \right] .. math:: - :label: 5.155 + :label: 5.155 \frac{de_{sat}^{T} }{dT} =100\left[b_{0} +b_{1} T+\cdots +b_{n} T^{n} \right] -where the coefficients for ice are valid for -:math:`-75\, ^{\circ } {\rm C}\le T<0\, ^{\circ } {\rm C}` and the -coefficients for water are valid for -:math:`0\, ^{\circ } {\rm C}\le T\le 100\, ^{\circ } {\rm C}` -(:numref:`Table Coefficients for saturation vapor pressure` and -:numref:`Table Coefficients for derivative of esat`). -The saturated water vapor specific humidity :math:`q_{sat}^{T}` and its derivative -:math:`\frac{dq_{sat}^{T} }{dT}` are +where the coefficients for ice are valid for :math:`-75\, ^{\circ } {\rm C}\le T<0\, ^{\circ } {\rm C}` and the coefficients for water are valid for :math:`0\, ^{\circ } {\rm C}\le T\le 100\, ^{\circ } {\rm C}` (:numref:`Table Coefficients for saturation vapor pressure` and :numref:`Table Coefficients for derivative of esat`). The saturated water vapor specific humidity :math:`q_{sat}^{T}` and its derivative :math:`\frac{dq_{sat}^{T} }{dT}` are .. math:: - :label: 5.156 + :label: 5.156 q_{sat}^{T} =\frac{0.622e_{sat}^{T} }{P_{atm} -0.378e_{sat}^{T} } .. math:: - :label: 5.157 + :label: 5.157 \frac{dq_{sat}^{T} }{dT} =\frac{0.622P_{atm} }{\left(P_{atm} -0.378e_{sat}^{T} \right)^{2} } \frac{de_{sat}^{T} }{dT} . .. _Table Coefficients for saturation vapor pressure: -.. table:: Coefficients for :math:`e_{sat}^{T}` +.. table:: Coefficients for :math:`e_{sat}^{T}` +------------------+------------------------------------------+----------------------------------------+ | | water | ice | @@ -1798,10 +1434,10 @@ The saturated water vapor specific humidity :math:`q_{sat}^{T}` and its derivati +------------------+-------------------------------------------+---------------------------------------+ | :math:`a_{8}` | 2.09339997 :math:`\times 10^{-16}` | 2.62655803\ :math:`\times 10^{-15}` | +------------------+------------------------------------------+----------------------------------------+ - + .. _Table Coefficients for derivative of esat: -.. table:: Coefficients for :math:`\frac{de_{sat}^{T} }{dT}` +.. table:: Coefficients for :math:`\frac{de_{sat}^{T} }{dT}` +------------------+----------------------------------------+----------------------------------------+ | | water | ice | diff --git a/doc/source/tech_note/Glacier/CLM50_Tech_Note_Glacier.rst b/doc/source/tech_note/Glacier/CLM50_Tech_Note_Glacier.rst index 52313b9a5c..3e510561bb 100644 --- a/doc/source/tech_note/Glacier/CLM50_Tech_Note_Glacier.rst +++ b/doc/source/tech_note/Glacier/CLM50_Tech_Note_Glacier.rst @@ -3,199 +3,84 @@ Glaciers ======== -This chapter describes features of CLM that are specific to coupling to -an ice sheet model (in the CESM context, this is the CISM model; -:ref:`Lipscomb and Sacks (2012)` provide -documentation and user’s guide for CISM). General information -about glacier land units can be found elsewhere in this document (see -Chapter :numref:`rst_Surface Characterization, Vertical Discretization, -and Model Input Requirements` for an overview). +This chapter describes features of CLM that are specific to coupling to an ice sheet model (in the CESM context, this is the CISM model; :ref:`Lipscomb and Sacks (2012)` provide documentation and user's guide for CISM). General information about glacier land units can be found elsewhere in this document (see Chapter :numref:`rst_Surface Characterization, Vertical Discretization, and Model Input Requirements` for an overview). .. _Glaciers summary of CLM5.0 updates relative to CLM4.5: Summary of CLM5.0 updates relative to CLM4.5 -------------------------------------------- -Compared with CLM4.5 (:ref:`Oleson et al. 2013 `), -CLM5.0 contains substantial improvements in its capabilities for -land-ice science. This section summarizes these improvements, and the -following sections provide more details. +Compared with CLM4.5 (:ref:`Oleson et al. 2013 `), CLM5.0 contains substantial improvements in its capabilities for land-ice science. This section summarizes these improvements, and the following sections provide more details. -- All runs include multiple glacier elevation classes over Greenland and - Antarctica and compute ice sheet surface mass balance in those - regions. +- All runs include multiple glacier elevation classes over Greenland and Antarctica and compute ice sheet surface mass balance in those regions. -- A number of namelist parameters offer fine-grained control over - glacier behavior in different regions of the world (section - :numref:`Glacier regions`). (The options used outside of Greenland and - Antarctica reproduce the standard CLM4.5 glacier behavior.) +- A number of namelist parameters offer fine-grained control over glacier behavior in different regions of the world (section :numref:`Glacier regions`). (The options used outside of Greenland and Antarctica reproduce the standard CLM4.5 glacier behavior.) -- CLM can now keep its glacier areas and elevations in sync with CISM - when running with an evolving ice sheet. (However, in typical - configurations, the ice sheet geometry still remains fixed throughout - the run.) +- CLM can now keep its glacier areas and elevations in sync with CISM when running with an evolving ice sheet. (However, in typical configurations, the ice sheet geometry still remains fixed throughout the run.) -- The downscaling to elevation classes now includes downwelling longwave - radiation and partitioning of precipitation into rain vs. snow - (section :numref:`Multiple elevation class scheme`). +- The downscaling to elevation classes now includes downwelling longwave radiation and partitioning of precipitation into rain vs. snow (section :numref:`Multiple elevation class scheme`). -- Other land units within the CISM domain undergo the same downscaling - as the glacier land unit, and surface mass balance is computed for the - natural vegetated land unit. This allows CLM to produce glacial - inception when running with an evolving ice sheet model. +- Other land units within the CISM domain undergo the same downscaling as the glacier land unit, and surface mass balance is computed for the natural vegetated land unit. This allows CLM to produce glacial inception when running with an evolving ice sheet model. -- There have also been substantial improvements to CLM's snow physics, - as described in other chapters of this document. +- There have also been substantial improvements to CLM's snow physics, as described in other chapters of this document. .. _Overview Glaciers: Overview -------- -CLM is responsible for computing two quantities that are passed to the -ice sheet model: - -#. Surface mass balance (SMB) - the net annual accumulation/ablation of - mass at the upper surface (section - :numref:`Computation of the surface mass balance`) - -#. Ground surface temperature, which serves as an upper boundary - condition for CISM's temperature calculation - -The ice sheet model is typically run at much higher resolution than CLM -(e.g., :math:`\sim`\ 5 km rather than :math:`\sim`\ 100 km). To improve -the downscaling from CLM’s grid to the ice sheet grid, the glaciated -portion of each grid cell is divided into multiple elevation classes -(section :numref:`Multiple elevation class scheme`). The above -quantities are computed separately in each elevation class. The CESM -coupler then computes high-resolution quantities via horizontal and -vertical interpolation, and passes these high-resolution quantities to -CISM. - -There are several reasons for computing the SMB in CLM rather than in -CISM: - -#. It is much cheaper to compute the SMB in CLM for :math:`\sim`\ 10 - elevation classes than in CISM. For example, suppose we are - running CLM at a resolution of :math:`\sim`\ 50 km and CISM at - :math:`\sim`\ 5 km. Greenland has dimensions of about 1000 x 2000 km. - For CLM we would have 20 x 40 x 10 = 8,000 columns, whereas for - CISM we would have 200 x 400 = 80,000 columns. - -#. We can use the sophisticated snow physics parameterization already in - CLM instead of implementing a separate scheme for CISM. Any - improvements to CLM are applied to ice sheets automatically. - -#. The atmosphere model can respond during runtime to ice-sheet surface - changes (even in the absence of two-way feedbacks with CISM). As - shown by :ref:`Pritchard et al. (2008)`, runtime - albedo feedback from the ice sheet is critical for simulating - ice-sheet retreat on paleoclimate time scales. Without this feedback - the atmosphere warms much less, and the retreat is delayed. - -#. The improved SMB is potentially available in CLM for all glaciated - grid cells (e.g., in the Alps, Rockies, Andes, and Himalayas), not - just those which are part of ice sheets. - -In typical runs, CISM is not evolving; CLM computes the SMB and sends it -to CISM, but CISM's ice sheet geometry remains fixed over the course of -the run. In these runs, CISM serves two roles in the system: - -#. Over the CISM domain (typically Greenland in CESM2), CISM dictates - glacier areas and topographic elevations, overriding the values on - CLM's surface dataset. CISM also dictates the elevation of - non-glacier land units in its domain, and only in this domain are - atmospheric fields downscaled to non-glacier land units. (So if you - run with a stub glacier model - SGLC - then glacier areas and - elevations will be taken entirely from CLM's surface dataset, and no - downscaling will be done over non-glacier land units.) - -#. CISM provides the grid onto which SMB is downscaled. (If you run with - SGLC then SMB will still be computed in CLM, but it won't be - downscaled to a high-resolution ice sheet grid.) - -It is also possible to run CESM with an evolving ice sheet. In this -case, CLM responds to CISM's evolution by adjusting the areas of the -glacier land unit and each elevation class within this land unit, as well -as the mean topographic heights of each elevation class. Thus, CLM's -glacier areas and elevations remain in sync with CISM's. Conservation of -mass and energy is done as for other landcover change (see Chapter -:numref:`rst_Transient Landcover Change`). +CLM is responsible for computing two quantities that are passed to the ice sheet model: + +#. Surface mass balance (SMB) - the net annual accumulation/ablation of mass at the upper surface (section :numref:`Computation of the surface mass balance`) + +#. Ground surface temperature, which serves as an upper boundary condition for CISM's temperature calculation The ice sheet model is typically run at much higher resolution than CLM (e.g., :math:`\sim`\ 5 km rather than :math:`\sim`\ 100 km). To improve the downscaling from CLM's grid to the ice sheet grid, the glaciated portion of each grid cell is divided into multiple elevation classes (section :numref:`Multiple elevation class scheme`). The above quantities are computed separately in each elevation class. The CESM coupler then computes high-resolution quantities via horizontal and vertical interpolation, and passes these high-resolution quantities to CISM. + +There are several reasons for computing the SMB in CLM rather than in CISM: + +#. It is much cheaper to compute the SMB in CLM for :math:`\sim`\ 10 elevation classes than in CISM. For example, suppose we are running CLM at a resolution of :math:`\sim`\ 50 km and CISM at :math:`\sim`\ 5 km. Greenland has dimensions of about 1000 x 2000 km. For CLM we would have 20 x 40 x 10 = 8,000 columns, whereas for CISM we would have 200 x 400 = 80,000 columns. + +#. We can use the sophisticated snow physics parameterization already in CLM instead of implementing a separate scheme for CISM. Any improvements to CLM are applied to ice sheets automatically. + +#. The atmosphere model can respond during runtime to ice-sheet surface changes (even in the absence of two-way feedbacks with CISM). As shown by :ref:`Pritchard et al. (2008)`, runtime albedo feedback from the ice sheet is critical for simulating ice-sheet retreat on paleoclimate time scales. Without this feedback the atmosphere warms much less, and the retreat is delayed. + +#. The improved SMB is potentially available in CLM for all glaciated grid cells (e.g., in the Alps, Rockies, Andes, and Himalayas), not just those which are part of ice sheets. + +In typical runs, CISM is not evolving; CLM computes the SMB and sends it to CISM, but CISM's ice sheet geometry remains fixed over the course of the run. In these runs, CISM serves two roles in the system: + +#. Over the CISM domain (typically Greenland in CESM2), CISM dictates glacier areas and topographic elevations, overriding the values on CLM's surface dataset. CISM also dictates the elevation of non-glacier land units in its domain, and only in this domain are atmospheric fields downscaled to non-glacier land units. (So if you run with a stub glacier model - SGLC - then glacier areas and elevations will be taken entirely from CLM's surface dataset, and no downscaling will be done over non-glacier land units.) + +#. CISM provides the grid onto which SMB is downscaled. (If you run with SGLC then SMB will still be computed in CLM, but it won't be downscaled to a high-resolution ice sheet grid.) + +It is also possible to run CESM with an evolving ice sheet. In this case, CLM responds to CISM's evolution by adjusting the areas of the glacier land unit and each elevation class within this land unit, as well as the mean topographic heights of each elevation class. Thus, CLM's glacier areas and elevations remain in sync with CISM's. Conservation of mass and energy is done as for other landcover change (see Chapter :numref:`rst_Transient Landcover Change`). .. _Glacier regions: Glacier regions and their behaviors ----------------------------------- -The world's glaciers and ice sheets are broken down into a number of -different regions (four by default) that differ in three respects: +The world's glaciers and ice sheets are broken down into a number of different regions (four by default) that differ in three respects: #. Whether the gridcell's glacier land unit contains: - a. Multiple elevation classes (section :numref:`Multiple elevation - class scheme`) + a. Multiple elevation classes (section :numref:`Multiple elevation class scheme`) - b. Multiple elevation classes plus virtual elevation classes + b. Multiple elevation classes plus virtual elevation classes - c. Just a single elevation class whose elevation matches the - atmosphere's topographic height (so there is no adjustment in - atmospheric forcings due to downscaling). + c. Just a single elevation class whose elevation matches the atmosphere's topographic height (so there is no adjustment in atmospheric forcings due to downscaling). #. Treatment of glacial melt water: - a. Glacial melt water runs off and is replaced by ice, thus keeping - the column always frozen. In the absence of a dynamic ice sheet - model, this behavior implicitly assumes an infinite store of - glacial ice that can be melted (with appropriate adjustments made - to ensure mass and energy conservation). This behavior is - discussed in more detail in section :numref:`Computation of the - surface mass balance`. - - b. Glacial melt water remains in place until it refreezes - possibly - remaining in place indefinitely if the glacier column is in a warm - climate. With this behavior, ice melt does not result in any - runoff. Regions with this behavior cannot compute SMB, because - negative SMB would be meaningless (due to the liquid water on top - of the ice column). This behavior produces less realistic glacier - physics. However, it avoids the negative ice runoff that is needed - for the "replaced by ice" behavior to conserve mass and energy (as - described in section :numref:`Computation of the surface mass - balance`). Thus, in regions where CLM has glaciers but the - atmospheric forcings are too warm to sustain those glaciers, this - behavior avoids persistent negative ice runoff. This situation can - often occur for mountain glaciers, where topographic smoothing in - the atmosphere results in a too-warm climate. There, avoiding - persistent negative ice runoff can be more important than getting - the right glacier ice physics. - -#. Treatment of ice runoff from snow capping (as described in section - :numref:`Runoff from glaciers and snow-capped surfaces`). Note that this - is irrelevant in regions with an evolving, two-way-coupled ice sheet - (where the snow capping term is sent to CISM rather than running off): - - a. Ice runoff from snow capping remains ice. This is a crude - parameterization of iceberg calving, and so is appropriate in - regions where there is substantial iceberg calving in reality. - - b. Ice runoff from snow capping is melted (generating a negative - sensible heat flux) and runs off as liquid. This matches the - behavior for non-glacier columns. This is appropriate in regions - that have little iceberg calving in reality. This can be important - to avoid unrealistic cooling of the ocean and consequent runaway - sea ice growth. - -The default behaviors for the world's glacier and ice sheet regions are -described in :numref:`Table Glacier region behaviors`. Note that the -standard CISM grid covers Greenland plus enough surrounding area to -allow for ice sheet growth and to have a regular rectangular grid. We -need to have the "replaced by ice" melt behavior within the CISM domain -in order to compute SMB there, and we need virtual elevation classes in -that domain in order to compute SMB for all elevation classes and to -facilitate glacial advance and retreat in the two-way-coupled -case. However, this domain is split into Greenland itself and areas -outside Greenland so that ice runoff in the Canadian archipelago (which -is inside the CISM domain) is melted before reaching the ocean, to avoid -runaway sea ice growth in that region. + a. Glacial melt water runs off and is replaced by ice, thus keeping the column always frozen. In the absence of a dynamic ice sheet model, this behavior implicitly assumes an infinite store of glacial ice that can be melted (with appropriate adjustments made to ensure mass and energy conservation). This behavior is discussed in more detail in section :numref:`Computation of the surface mass balance`. + + b. Glacial melt water remains in place until it refreezes - possibly remaining in place indefinitely if the glacier column is in a warm climate. With this behavior, ice melt does not result in any runoff. Regions with this behavior cannot compute SMB, because negative SMB would be meaningless (due to the liquid water on top of the ice column). This behavior produces less realistic glacier physics. However, it avoids the negative ice runoff that is needed for the "replaced by ice" behavior to conserve mass and energy (as described in section :numref:`Computation of the surface mass balance`). Thus, in regions where CLM has glaciers but the atmospheric forcings are too warm to sustain those glaciers, this behavior avoids persistent negative ice runoff. This situation can often occur for mountain glaciers, where topographic smoothing in the atmosphere results in a too-warm climate. There, avoiding persistent negative ice runoff can be more important than getting the right glacier ice physics. + +#. Treatment of ice runoff from snow capping (as described in section :numref:`Runoff from glaciers and snow-capped surfaces`). Note that this is irrelevant in regions with an evolving, two-way-coupled ice sheet (where the snow capping term is sent to CISM rather than running off): + + a. Ice runoff from snow capping remains ice. This is a crude parameterization of iceberg calving, and so is appropriate in regions where there is substantial iceberg calving in reality. + + b. Ice runoff from snow capping is melted (generating a negative sensible heat flux) and runs off as liquid. This matches the behavior for non-glacier columns. This is appropriate in regions that have little iceberg calving in reality. This can be important to avoid unrealistic cooling of the ocean and consequent runaway sea ice growth. + +The default behaviors for the world's glacier and ice sheet regions are described in :numref:`Table Glacier region behaviors`. Note that the standard CISM grid covers Greenland plus enough surrounding area to allow for ice sheet growth and to have a regular rectangular grid. We need to have the "replaced by ice" melt behavior within the CISM domain in order to compute SMB there, and we need virtual elevation classes in that domain in order to compute SMB for all elevation classes and to facilitate glacial advance and retreat in the two-way-coupled case. However, this domain is split into Greenland itself and areas outside Greenland so that ice runoff in the Canadian archipelago (which is inside the CISM domain) is melted before reaching the ocean, to avoid runaway sea ice growth in that region. .. _Table Glacier region behaviors: @@ -224,172 +109,52 @@ runaway sea ice growth in that region. .. note:: - In regions that have both the ``Glacial melt = Replaced by ice`` and the ``Ice runoff = - Melted`` behaviors (by default, this is just the region inside the standard CISM grid - but outside Greenland itself): During periods of glacial melt, a negative ice runoff is - generated (due to the ``Glacial melt = Replaced by ice`` behavior); this negative ice - runoff is converted to a negative liquid runoff plus a positive sensible heat flux (due - to the ``Ice runoff = Melted`` behavior). We recommend that you limit the portion of - the globe with both of these behaviors combined, in order to avoid having too large of - an impact of this non-physical behavior. + In regions that have both the ``Glacial melt = Replaced by ice`` and the ``Ice runoff = Melted`` behaviors (by default, this is just the region inside the standard CISM grid but outside Greenland itself): During periods of glacial melt, a negative ice runoff is generated (due to the ``Glacial melt = Replaced by ice`` behavior); this negative ice runoff is converted to a negative liquid runoff plus a positive sensible heat flux (due to the ``Ice runoff = Melted`` behavior). We recommend that you limit the portion of the globe with both of these behaviors combined, in order to avoid having too large of an impact of this non-physical behavior. .. _Multiple elevation class scheme: Multiple elevation class scheme ------------------------------- -The glacier land unit contains multiple columns based on surface -elevation. These are known as elevation classes, and the land unit is -referred to as *glacier\_mec*. (As described in section :numref:`Glacier -regions`, some regions have only a single elevation class, but they are -still referred to as *glacier\_mec* land units.) The default is to have 10 -elevation classes whose lower limits are 0, 200, 400, 700, 1000, 1300, -1600, 2000, 2500, and 3000 m. Each column is characterized by a -fractional area and surface elevation that are read in during model -initialization, and then possibly overridden by CISM as the run -progresses. Each *glacier\_mec* column within a grid cell has distinct ice -and snow temperatures, snow water content, surface fluxes, and SMB. - -The atmospheric surface temperature, potential temperature, specific -humidity, density, and pressure are downscaled from the atmosphere's -mean grid cell elevation to the *glacier\_mec* column elevation using a -specified lapse rate (typically 6.0 deg/km) and an assumption of uniform -relative humidity. Longwave radiation is downscaled by assuming a linear -decrease in downwelling longwave radiation with increasing elevation -(0.032 W m\ :sup:`-2` m\ :sup:`-1`, limited to 0.5 - 1.5 times the -gridcell mean value, then normalized to conserve gridcell total energy) -:ref:`(Van Tricht et al., 2016)`. Total precipitation -is partitioned into rain vs. snow as described in Chapter -:numref:`rst_Surface Characterization, Vertical Discretization, and -Model Input Requirements`. The partitioning of precipitation is based on -the downscaled temperature, allowing rain to fall at lower elevations -while snow falls at higher elevations. - -This downscaling allows lower-elevation columns to undergo surface -melting while columns at higher elevations remain frozen. This gives a -more accurate simulation of summer melting, which is a highly nonlinear -function of air temperature. - -Within the CISM domain, this same downscaling procedure is also applied -to all non-urban land units. The elevation of non-glacier land units is -taken from the mean elevation of ice-free grid cells in CISM. This is -done in order to keep the glaciated and non-glaciated portions of the -CISM domain as consistent as possible. - -In contrast to most CLM subgrid units, glacier\_mec columns can be -active (i.e., have model calculations run there) even if their area is -zero. These are known as "virtual" columns. This is done because the ice -sheet model may require a SMB for some grid cells where CLM has zero -glacier area in that elevation range. Virtual columns also facilitate -glacial advance and retreat in the two-way coupled case. Virtual columns -do not affect energy exchange between the land and the atmosphere. +The glacier land unit contains multiple columns based on surface elevation. These are known as elevation classes, and the land unit is referred to as *glacier\_mec*. (As described in section :numref:`Glacier regions`, some regions have only a single elevation class, but they are still referred to as *glacier\_mec* land units.) The default is to have 10 elevation classes whose lower limits are 0, 200, 400, 700, 1000, 1300, 1600, 2000, 2500, and 3000 m. Each column is characterized by a fractional area and surface elevation that are read in during model initialization, and then possibly overridden by CISM as the run progresses. Each *glacier\_mec* column within a grid cell has distinct ice and snow temperatures, snow water content, surface fluxes, and SMB. + +The atmospheric surface temperature, potential temperature, specific humidity, density, and pressure are downscaled from the atmosphere's mean grid cell elevation to the *glacier\_mec* column elevation using a specified lapse rate (typically 6.0 deg/km) and an assumption of uniform relative humidity. Longwave radiation is downscaled by assuming a linear decrease in downwelling longwave radiation with increasing elevation (0.032 W m\ :sup:`-2` m\ :sup:`-1`, limited to 0.5 - 1.5 times the gridcell mean value, then normalized to conserve gridcell total energy) :ref:`(Van Tricht et al., 2016)`. Total precipitation is partitioned into rain vs. snow as described in Chapter :numref:`rst_Surface Characterization, Vertical Discretization, and Model Input Requirements`. The partitioning of precipitation is based on the downscaled temperature, allowing rain to fall at lower elevations while snow falls at higher elevations. + +This downscaling allows lower-elevation columns to undergo surface melting while columns at higher elevations remain frozen. This gives a more accurate simulation of summer melting, which is a highly nonlinear function of air temperature. + +Within the CISM domain, this same downscaling procedure is also applied to all non-urban land units. The elevation of non-glacier land units is taken from the mean elevation of ice-free grid cells in CISM. This is done in order to keep the glaciated and non-glaciated portions of the CISM domain as consistent as possible. + +In contrast to most CLM subgrid units, glacier\_mec columns can be active (i.e., have model calculations run there) even if their area is zero. These are known as "virtual" columns. This is done because the ice sheet model may require a SMB for some grid cells where CLM has zero glacier area in that elevation range. Virtual columns also facilitate glacial advance and retreat in the two-way coupled case. Virtual columns do not affect energy exchange between the land and the atmosphere. .. _Computation of the surface mass balance: Computation of the surface mass balance --------------------------------------- -This section describes the computation of surface mass balance and -associated runoff terms. The description here only applies to regions -where glacial melt runs off and is replaced by ice, not to regions where -glacial melt remains in place. Thus, by default, this only applies to -Greenland and Antarctica, not to mountain glaciers elsewhere in the -world. (See also section :numref:`Glacier regions`.) - -The SMB of a glacier or ice sheet is the net annual -accumulation/ablation of mass at the upper surface. Ablation is defined -as the mass of water that runs off to the ocean. Not all the surface -meltwater runs off; some of the melt percolates into the snow and -refreezes. Accumulation is primarily by snowfall and deposition, and -ablation is primarily by melting and evaporation/sublimation. CLM uses a -surface-energy-balance (SEB) scheme to compute the SMB. In this scheme, -the melting depends on the sum of the radiative, turbulent, and -conductive fluxes reaching the surface, as described elsewhere in this -document. - -Note that the SMB typically is defined as the total accumulation of ice -and snow, minus the total ablation. The SMB flux passed to CISM is the -mass balance for ice alone, not snow. We can think of CLM as owning the -snow, whereas CISM owns the underlying ice. Fluctuations in snow depth -between 0 and 10 m water equivalent are not reflected in the SMB passed -to CISM. In transient runs, this can lead to delays of a few decades in -the onset of accumulation or ablation in a given glacier column. - -SMB is computed and sent to the CESM coupler regardless of whether and -where CISM is operating. However, the effect of SMB terms on runoff -fluxes differs depending on whether and where CISM is evolving in -two-way-coupled mode. This is described by the variable -*glc\_dyn\_runoff\_routing*. (This is real-valued in the code to handle -the edge case where a CLM grid cell partially overlaps with the CISM -grid, but we describe it as a logical variable here for simplicity.) In -typical cases where CISM is not evolving, *glc\_dyn\_runoff\_routing* -will be false everywhere; in these cases, CISM's mass is not considered -to be part of the coupled system. In cases where CISM is evolving and -sending its own calving flux to the coupler, *glc\_dyn\_runoff\_routing* -will be true over the CISM domain and false elsewhere. - -Any snow capping (section :numref:`Runoff from glaciers and snow-capped -surfaces`) is added to :math:`q_{ice,frz}`. Any liquid water (i.e., -melted ice) below the snow pack in the glacier column is added to -:math:`q_{ice,melt}`, then is converted back to ice to maintain a -pure-ice column. Then the total SMB is given by :math:`q_{ice,tot}`: +This section describes the computation of surface mass balance and associated runoff terms. The description here only applies to regions where glacial melt runs off and is replaced by ice, not to regions where glacial melt remains in place. Thus, by default, this only applies to Greenland and Antarctica, not to mountain glaciers elsewhere in the world. (See also section :numref:`Glacier regions`.) + +The SMB of a glacier or ice sheet is the net annual accumulation/ablation of mass at the upper surface. Ablation is defined as the mass of water that runs off to the ocean. Not all the surface meltwater runs off; some of the melt percolates into the snow and refreezes. Accumulation is primarily by snowfall and deposition, and ablation is primarily by melting and evaporation/sublimation. CLM uses a surface-energy-balance (SEB) scheme to compute the SMB. In this scheme, the melting depends on the sum of the radiative, turbulent, and conductive fluxes reaching the surface, as described elsewhere in this document. + +Note that the SMB typically is defined as the total accumulation of ice and snow, minus the total ablation. The SMB flux passed to CISM is the mass balance for ice alone, not snow. We can think of CLM as owning the snow, whereas CISM owns the underlying ice. Fluctuations in snow depth between 0 and 10 m water equivalent are not reflected in the SMB passed to CISM. In transient runs, this can lead to delays of a few decades in the onset of accumulation or ablation in a given glacier column. + +SMB is computed and sent to the CESM coupler regardless of whether and where CISM is operating. However, the effect of SMB terms on runoff fluxes differs depending on whether and where CISM is evolving in two-way-coupled mode. This is described by the variable *glc\_dyn\_runoff\_routing*. (This is real-valued in the code to handle the edge case where a CLM grid cell partially overlaps with the CISM grid, but we describe it as a logical variable here for simplicity.) In typical cases where CISM is not evolving, *glc\_dyn\_runoff\_routing* will be false everywhere; in these cases, CISM's mass is not considered to be part of the coupled system. In cases where CISM is evolving and sending its own calving flux to the coupler, *glc\_dyn\_runoff\_routing* will be true over the CISM domain and false elsewhere. + +Any snow capping (section :numref:`Runoff from glaciers and snow-capped surfaces`) is added to :math:`q_{ice,frz}`. Any liquid water (i.e., melted ice) below the snow pack in the glacier column is added to :math:`q_{ice,melt}`, then is converted back to ice to maintain a pure-ice column. Then the total SMB is given by :math:`q_{ice,tot}`: .. math:: :label: 13.1 q_{ice,tot} = q_{ice,frz} - q_{ice,melt} -CLM is responsible for generating glacial surface melt, even when -running with an evolving ice sheet. Thus, :math:`q_{ice,melt}` is always -added to liquid runoff (:math:`q_{rgwl}`), regardless of -*glc\_dyn\_runoff\_routing*. However, the ice runoff flux depends on -*glc\_dyn\_runoff\_routing*. If *glc\_dyn\_runoff\_routing* is true, -then CISM controls the fate of the snow capping mass in -:math:`q_{ice,frz}` (e.g., eventually transporting it to lower -elevations where it can be melted or calved). Since CISM will now own -this mass, the snow capping flux does *not* contribute to any runoff -fluxes generated by CLM in this case. - -If *glc\_dyn\_runoff\_routing* is false, then CLM sends the snow capping -flux as runoff, as a crude representation of ice calving (see also -sections :numref:`Runoff from glaciers and snow-capped surfaces` and -:numref:`Glacier regions`). However, this ice runoff flux is reduced by -:math:`q_{ice,melt}`. This reduction is needed for conservation; its -need is subtle, but can be understood with either of these explanations: - -- When ice melts, we let the liquid run off and replace it with new - ice. That new ice needs to come from somewhere to keep the coupled - system in water balance. We "request" the new ice from the ocean by - generating a negative ice runoff equivalent to the amount we have - melted. - -- Ice melt removes mass from the system, as it should. But the snow - capping flux also removes mass from the system. The latter is a crude - parameterization of calving, assuming steady state - i.e., all ice - gain is balanced by ice loss. This removal of mass due to both - accumulation and melt represents a double-counting. Each unit of melt - indicates that one unit of accumulation should not have made it to the - ocean as ice, but instead melted before it got there. So we need to - correct for this double-counting by removing one unit of ice runoff - for each unit of melt. - -For a given point in space or time, this reduction can result in -negative ice runoff. However, when integrated over space and time, for -an ice sheet that is near equilibrium, this just serves to decrease the -too-high positive ice runoff from snow capping. (The treatment of snow -capping with *glc\_dyn\_runoff\_routing* false is based on this -near-equilibrium assumption - i.e., that ice accumulation is roughly -balanced by :math:`calving + melt`, integrated across space and time. -For glaciers and ice sheets that violate this assumption, either because -they are far out of equilibrium with the climate or because the model is -being run for hundreds of years, there are two ways to avoid the -unrealistic ice runoff from snow capping: by running with an evolving, -two-way-coupled ice sheet or by changing a glacier region's ice runoff -behavior as described in section :numref:`Glacier regions`.) - -In regions where SMB is computed for glaciers, SMB is also computed for -the natural vegetated land unit. Because there is no ice to melt in this -land unit, it can only generate a zero or positive SMB. A positive SMB -is generated once the snow pack reaches its maximum depth. When running -with an evolving ice sheet, this condition triggers glacial inception. +CLM is responsible for generating glacial surface melt, even when running with an evolving ice sheet. Thus, :math:`q_{ice,melt}` is always added to liquid runoff (:math:`q_{rgwl}`), regardless of *glc\_dyn\_runoff\_routing*. However, the ice runoff flux depends on *glc\_dyn\_runoff\_routing*. If *glc\_dyn\_runoff\_routing* is true, then CISM controls the fate of the snow capping mass in :math:`q_{ice,frz}` (e.g., eventually transporting it to lower elevations where it can be melted or calved). Since CISM will now own this mass, the snow capping flux does *not* contribute to any runoff fluxes generated by CLM in this case. + +If *glc\_dyn\_runoff\_routing* is false, then CLM sends the snow capping flux as runoff, as a crude representation of ice calving (see also sections :numref:`Runoff from glaciers and snow-capped surfaces` and :numref:`Glacier regions`). However, this ice runoff flux is reduced by :math:`q_{ice,melt}`. This reduction is needed for conservation; its need is subtle, but can be understood with either of these explanations: + +- When ice melts, we let the liquid run off and replace it with new ice. That new ice needs to come from somewhere to keep the coupled system in water balance. We "request" the new ice from the ocean by generating a negative ice runoff equivalent to the amount we have melted. + +- Ice melt removes mass from the system, as it should. But the snow capping flux also removes mass from the system. The latter is a crude parameterization of calving, assuming steady state - i.e., all ice gain is balanced by ice loss. This removal of mass due to both accumulation and melt represents a double-counting. Each unit of melt indicates that one unit of accumulation should not have made it to the ocean as ice, but instead melted before it got there. So we need to correct for this double-counting by removing one unit of ice runoff for each unit of melt. + +For a given point in space or time, this reduction can result in negative ice runoff. However, when integrated over space and time, for an ice sheet that is near equilibrium, this just serves to decrease the too-high positive ice runoff from snow capping. (The treatment of snow capping with *glc\_dyn\_runoff\_routing* false is based on this near-equilibrium assumption - i.e., that ice accumulation is roughly balanced by :math:`calving + melt`, integrated across space and time. For glaciers and ice sheets that violate this assumption, either because they are far out of equilibrium with the climate or because the model is being run for hundreds of years, there are two ways to avoid the unrealistic ice runoff from snow capping: by running with an evolving, two-way-coupled ice sheet or by changing a glacier region's ice runoff behavior as described in section :numref:`Glacier regions`.) + +In regions where SMB is computed for glaciers, SMB is also computed for the natural vegetated land unit. Because there is no ice to melt in this land unit, it can only generate a zero or positive SMB. A positive SMB is generated once the snow pack reaches its maximum depth. When running with an evolving ice sheet, this condition triggers glacial inception. diff --git a/doc/source/tech_note/Hydrology/CLM50_Tech_Note_Hydrology.rst b/doc/source/tech_note/Hydrology/CLM50_Tech_Note_Hydrology.rst index 323c8ea24b..130629f0eb 100644 --- a/doc/source/tech_note/Hydrology/CLM50_Tech_Note_Hydrology.rst +++ b/doc/source/tech_note/Hydrology/CLM50_Tech_Note_Hydrology.rst @@ -3,16 +3,7 @@ Hydrology ============ -The model parameterizes interception, throughfall, canopy drip, snow -accumulation and melt, water transfer between snow layers, infiltration, -evaporation, surface runoff, sub-surface drainage, redistribution within -the soil column, and groundwater discharge and recharge to simulate -changes in canopy water :math:`\Delta W_{can,\,liq}` , canopy snow water -:math:`\Delta W_{can,\,sno}` surface water :math:`\Delta W_{sfc}` , -snow water :math:`\Delta W_{sno}` , soil water -:math:`\Delta w_{liq,\, i}` , and soil ice :math:`\Delta w_{ice,\, i}` , -and water in the unconfined aquifer :math:`\Delta W_{a}` (all in kg -m\ :sup:`-2` or mm of H\ :sub:`2`\ O) (:numref:`Figure Hydrologic processes`). +The model parameterizes interception, throughfall, canopy drip, snow accumulation and melt, water transfer between snow layers, infiltration, evaporation, surface runoff, sub-surface drainage, redistribution within the soil column, and groundwater discharge and recharge to simulate changes in canopy water :math:`\Delta W_{can,\,liq}`, canopy snow water :math:`\Delta W_{can,\,sno}` surface water :math:`\Delta W_{sfc}`, snow water :math:`\Delta W_{sno}`, soil water :math:`\Delta w_{liq,\, i}`, and soil ice :math:`\Delta w_{ice,\, i}`, and water in the unconfined aquifer :math:`\Delta W_{a}` (all in kg m\ :sup:`-2` or mm of H\ :sub:`2`\ O) (:numref:`Figure Hydrologic processes`). The total water balance of the system is @@ -21,20 +12,7 @@ The total water balance of the system is \begin{array}{l} {\Delta W_{can,\,liq} +\Delta W_{can,\,sno} +\Delta W_{sfc} +\Delta W_{sno} +} \\ {\sum _{i=1}^{N_{levsoi} }\left(\Delta w_{liq,\, i} +\Delta w_{ice,\, i} \right)+\Delta W_{a} =\left(\begin{array}{l} {q_{rain} +q_{sno} -E_{v} -E_{g} -q_{over} } \\ {-q_{h2osfc} -q_{drai} -q_{rgwl} -q_{snwcp,\, ice} } \end{array}\right) \Delta t} \end{array} -where :math:`q_{rain}` is the liquid part of precipitation, -:math:`q_{sno}` is the solid part of precipitation, :math:`E_{v}` is -ET from vegetation (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), :math:`E_{g}` is ground evaporation -(Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), :math:`q_{over}` is surface runoff (section :numref:`Surface Runoff`), -:math:`q_{h2osfc}` is runoff from surface water storage (section :numref:`Surface Runoff`), -:math:`q_{drai}` is sub-surface drainage (section :numref:`Lateral Sub-surface Runoff`), -:math:`q_{rgwl}` and :math:`q_{snwcp,ice}` are liquid and solid runoff -from glaciers and lakes, and runoff from other surface types -due to snow capping (section :numref:`Runoff from glaciers and snow-capped surfaces`) (all in kg m\ :sup:`-2` -s\ :sup:`-1`), :math:`N_{levsoi}` is the number of soil layers -(note that hydrology calculations are only done over soil layers 1 to -:math:`N_{levsoi}` ; ground levels :math:`N_{levsoi} +1` \ to -:math:`N_{levgrnd}` are currently hydrologically inactive; :ref:`(Lawrence et -al. 2008) ` and :math:`\Delta t` is the time step (s). +where :math:`q_{rain}` is the liquid part of precipitation, :math:`q_{sno}` is the solid part of precipitation, :math:`E_{v}` is ET from vegetation (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), :math:`E_{g}` is ground evaporation (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), :math:`q_{over}` is surface runoff (section :numref:`Surface Runoff`), :math:`q_{h2osfc}` is runoff from surface water storage (section :numref:`Surface Runoff`), :math:`q_{drai}` is sub-surface drainage (section :numref:`Lateral Sub-surface Runoff`), :math:`q_{rgwl}` and :math:`q_{snwcp,ice}` are liquid and solid runoff from glaciers and lakes, and runoff from other surface types due to snow capping (section :numref:`Runoff from glaciers and snow-capped surfaces`) (all in kg m\ :sup:`-2` s\ :sup:`-1`), :math:`N_{levsoi}` is the number of soil layers (note that hydrology calculations are only done over soil layers 1 to :math:`N_{levsoi}`; ground levels :math:`N_{levsoi} +1` \ to :math:`N_{levgrnd}` are currently hydrologically inactive; :ref:`(Lawrence et al. 2008) ` and :math:`\Delta t` is the time step (s). .. _Figure Hydrologic processes: @@ -47,27 +25,19 @@ al. 2008) ` and :math:`\Delta t` is the time step (s). Canopy Water ---------------- -Liquid precipitation is either intercepted by the canopy, falls -directly to the snow/soil surface (throughfall), or drips off the -vegetation (canopy drip). Solid precipitation is treated similarly, -with the addition of unloading of previously intercepted snow. -Interception by vegetation is divided between liquid and solid phases -:math:`q_{intr,\,liq}` and :math:`q_{intr,\,ice}` -(kg m\ :sup:`-2` s\ :sup:`-1`) +Liquid precipitation is either intercepted by the canopy, falls directly to the snow/soil surface (throughfall), or drips off the vegetation (canopy drip). Solid precipitation is treated similarly, with the addition of unloading of previously intercepted snow. Interception by vegetation is divided between liquid and solid phases :math:`q_{intr,\,liq}` and :math:`q_{intr,\,ice}` (kg m\ :sup:`-2` s\ :sup:`-1`) .. math:: :label: 7.2 - q_{intr,\,liq} = f_{pi,\,liq} \ q_{rain} + q_{intr,\,liq} = f_{pi,\,liq} \ q_{rain} .. math:: :label: 7.3 q_{intr,\,ice} = f_{pi,\,ice} \ q_{sno} -where :math:`f_{pi,\,liq}` and :math:`f_{pi,\,ice}` are the -fractions of intercepted precipitation of rain and snow, -respectively +where :math:`f_{pi,\,liq}` and :math:`f_{pi,\,ice}` are the fractions of intercepted precipitation of rain and snow, respectively .. math:: :label: 7.2b @@ -79,13 +49,7 @@ respectively f_{pi,\,ice} =\alpha_{sno} \ \left\{1-\exp \left[-0.5\left(L+S\right)\right]\right\} \ , -and :math:`L` and :math:`S` are the exposed leaf and stem area index, -respectively (section :numref:`Phenology and vegetation burial by snow`), and -the :math:`\alpha`\'s scale the fractional area of a leaf that collects water -(:ref:`Lawrence et al. 2007 `). Default values of -:math:`\alpha_{liq}` and :math:`\alpha_{sno}` are set to 1. -Throughfall (kg m\ :sup:`-2` s\ :sup:`-1`) is also divided into -liquid and solid phases, reaching the ground (soil or snow surface) as +and :math:`L` and :math:`S` are the exposed leaf and stem area index, respectively (section :numref:`Phenology and vegetation burial by snow`), and the :math:`\alpha`\'s scale the fractional area of a leaf that collects water (:ref:`Lawrence et al. 2007 `). Default values of :math:`\alpha_{liq}` and :math:`\alpha_{sno}` are set to 1. Throughfall (kg m\ :sup:`-2` s\ :sup:`-1`) is also divided into liquid and solid phases, reaching the ground (soil or snow surface) as .. math:: :label: 7.4 @@ -123,12 +87,7 @@ and W_{can,sno}^{intr} =W_{can,sno}^{n} +q_{intr,\, ice} \Delta t\ge 0 - -are the the canopy liquid water and snow water equivalent after accounting for interception, -:math:`W_{can,\,liq}^{n}` and :math:`W_{can,\,sno}^{n}` are the canopy liquid and snow water -from the previous time step, and :math:`W_{can,\,liq}^{max }` and :math:`W_{can,\,snow}^{max }` -(kg m\ :sup:`-2` or mm of H\ :sub:`2`\ O) are the maximum amounts of liquid water and snow the canopy can hold. -They are defined by +are the the canopy liquid water and snow water equivalent after accounting for interception, :math:`W_{can,\,liq}^{n}` and :math:`W_{can,\,sno}^{n}` are the canopy liquid and snow water from the previous time step, and :math:`W_{can,\,liq}^{max }` and :math:`W_{can,\,snow}^{max }` (kg m\ :sup:`-2` or mm of H\ :sub:`2`\ O) are the maximum amounts of liquid water and snow the canopy can hold. They are defined by .. math:: :label: 7.10 @@ -140,17 +99,13 @@ They are defined by W_{can,\,sno}^{max } =p_{sno}\left(L+S\right). -The maximum storage of liquid water is :math:`p_{liq}=0.1` kg m\ :sup:`-2` -(:ref:`Dickinson et al. 1993 `), and that of snow -is :math:`p_{sno}=6` kg m\ :sup:`-2`, consistent with reported -field measurements (:ref:`Pomeroy et al. 1998 `). +The maximum storage of liquid water is :math:`p_{liq}=0.1` kg m\ :sup:`-2` (:ref:`Dickinson et al. 1993 `), and that of snow is :math:`p_{sno}=6` kg m\ :sup:`-2`, consistent with reported field measurements (:ref:`Pomeroy et al. 1998 `). -Canopy snow unloading from wind speed :math:`u` and above-freezing temperatures are modeled from linear -fluxes and e-folding times similar to :ref:`Roesch et al. (2001) ` +Canopy snow unloading from wind speed :math:`u` and above-freezing temperatures are modeled from linear fluxes and e-folding times similar to :ref:`Roesch et al. (2001) ` .. math:: :label: 7.12 - + q_{unl,\, wind} =\frac{u W_{can,sno}}{1.56\times 10^5 \text{ m}} .. math:: @@ -163,46 +118,44 @@ fluxes and e-folding times similar to :ref:`Roesch et al. (2001) T_{f} \\ 0 & T_v \le T_f - \end{array}\right\} + \end{array}\right\} .. math:: - :label: 7.18 + :label: 7.18 E_{v}^{ice} = - \left\{\begin{array}{lr} + \left\{\begin{array}{lr} 0 & T_v > T_f \\ E_{v}^{w} & T_v \le T_f \end{array}\right\}. -.. \begin{array}{lr} +.. \begin{array}{lr} .. E_{v}^{liq} = E_{v}^{w} \qquad T > 273 \text{K} \\ .. E_{v}^{ice} = E_{v}^{w} \qquad T \le 273 \text{K} .. \end{array} @@ -219,24 +172,16 @@ The total rate of liquid and solid precipitation reaching the ground is then q_{grnd,ice} =q_{thru,\, ice} +q_{drip,\, ice} +q_{unl,\, tot} . -Solid precipitation reaching the soil or snow surface, -:math:`q_{grnd,\, ice} \Delta t`, is added immediately to the snow pack -(Chapter :numref:`rst_Snow Hydrology`). The liquid part, -:math:`q_{grnd,\, liq} \Delta t` is added after surface fluxes -(Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) -and snow/soil temperatures (Chapter :numref:`rst_Soil and Snow Temperatures`) -have been determined. +Solid precipitation reaching the soil or snow surface, :math:`q_{grnd,\, ice} \Delta t`, is added immediately to the snow pack (Chapter :numref:`rst_Snow Hydrology`). The liquid part, :math:`q_{grnd,\, liq} \Delta t` is added after surface fluxes (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) and snow/soil temperatures (Chapter :numref:`rst_Soil and Snow Temperatures`) have been determined. -The wetted fraction of the canopy (stems plus leaves), which is required -for surface flux (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) -calculations, is (:ref:`Dickinson et al.1993 `) +The wetted fraction of the canopy (stems plus leaves), which is required for surface flux (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) calculations, is (:ref:`Dickinson et al.1993 `) .. math:: :label: 7.21 f_{wet} = - \left\{\begin{array}{lr} - \left[\frac{W_{can} }{p_{liq}\left(L+S\right)} \right]^{{2\mathord{\left/ {\vphantom {2 3}} \right. \kern-\nulldelimiterspace} 3} } \le 1 & \qquad L+S > 0 \\ + \left\{\begin{array}{lr} + \left[\frac{W_{can} }{p_{liq}\left(L+S\right)} \right]^{{2\mathord{\left/ {\vphantom {2 3}} \right.} 3} } \le 1 & \qquad L+S > 0 \\ 0 &\qquad L+S = 0 \end{array}\right\} @@ -246,48 +191,35 @@ while the fraction of the canopy that is dry and transpiring is :label: 7.22 f_{dry} = - \left\{\begin{array}{lr} - \frac{\left(1-f_{wet} \right)L}{L+S} & \qquad L+S > 0 \\ - 0 &\qquad L+S = 0 + \left\{\begin{array}{lr} + \frac{\left(1-f_{wet} \right)L}{L+S} & \qquad L+S > 0 \\ + 0 &\qquad L+S = 0 \end{array}\right\}. Similarly, the snow-covered fraction of the canopy is used for surface alebdo when intercepted snow is present (Chapter :numref:`rst_Surface Albedos`) - .. math:: :label: 7.23 - f_{can,\, sno} = - \left\{\begin{array}{lr} - \left[\frac{W_{can,\, sno} }{p_{sno}\left(L+S\right)} \right]^{{3\mathord{\left/ {\vphantom {3 20}} \right. \kern-\nulldelimiterspace} 20} } \le 1 & \qquad L+S > 0 \\ + f_{can,\, sno} = + \left\{\begin{array}{lr} + \left[\frac{W_{can,\, sno} }{p_{sno}\left(L+S\right)} \right]^{{3\mathord{\left/ {\vphantom {3 20}} \right.} 20} } \le 1 & \qquad L+S > 0 \\ 0 &\qquad L+S = 0 \end{array}\right\}. - .. _Surface Runoff, Surface Water Storage, and Infiltration: Surface Runoff, Surface Water Storage, and Infiltration ----------------------------------------------------------- -The moisture input at the grid cell surface ,\ :math:`q_{liq,\, 0}` , is -the sum of liquid precipitation reaching the ground and melt water from -snow (kg m\ :sup:`-2` s\ :sup:`-1`). The moisture flux is -then partitioned between surface runoff, surface water storage, and -infiltration into the soil. +The moisture input at the grid cell surface,\ :math:`q_{liq,\, 0}`, is the sum of liquid precipitation reaching the ground and melt water from snow (kg m\ :sup:`-2` s\ :sup:`-1`). The moisture flux is then partitioned between surface runoff, surface water storage, and infiltration into the soil. .. _Surface Runoff: Surface Runoff ^^^^^^^^^^^^^^^^^^^^ -The simple TOPMODEL-based (:ref:`Beven and Kirkby 1979 `) -runoff model (SIMTOP) described by :ref:`Niu et al. (2005) ` -is implemented to parameterize runoff. A -key concept underlying this approach is that of fractional saturated -area :math:`f_{sat}` , which is determined by the topographic -characteristics and soil moisture state of a grid cell. The saturated -portion of a grid cell contributes to surface runoff, :math:`q_{over}` , -by the saturation excess mechanism (Dunne runoff) +The simple TOPMODEL-based (:ref:`Beven and Kirkby 1979 `) runoff model (SIMTOP) described by :ref:`Niu et al. (2005) ` is implemented to parameterize runoff. A key concept underlying this approach is that of fractional saturated area :math:`f_{sat}`, which is determined by the topographic characteristics and soil moisture state of a grid cell. The saturated portion of a grid cell contributes to surface runoff, :math:`q_{over}`, by the saturation excess mechanism (Dunne runoff) .. math:: :label: 7.64 @@ -301,145 +233,79 @@ The fractional saturated area is a function of soil moisture f_{sat} =f_{\max } \ \exp \left(-0.5f_{over} z_{\nabla } \right) -where :math:`f_{\max }` is the potential or maximum value of -:math:`f_{sat}` , :math:`f_{over}` is a decay factor (m\ :sup:`-1`), and -:math:`z_{\nabla}` is the water table depth (m) (section -:numref:`Lateral Sub-surface Runoff`). The maximum saturated fraction, -:math:`f_{\max }`, is defined as the value of the discrete cumulative -distribution function (CDF) of the topographic index when the grid cell -mean water table depth is zero. Thus, :math:`f_{\max }` is the percent of -pixels in a grid cell whose topographic index is larger than or equal to -the grid cell mean topographic index. It should be calculated explicitly -from the CDF at each grid cell at the resolution that the model is run. -However, because this is a computationally intensive task for global -applications, :math:`f_{\max }` is calculated once at 0.125\ :sup:`o` -resolution using the 1-km compound topographic indices (CTIs) based on -the HYDRO1K dataset (:ref:`Verdin and Greenlee 1996 `) -from USGS following the algorithm in :ref:`Niu et al. (2005) ` -and then area-averaged to the desired model resolution (section -:numref:`Surface Data`). Pixels -with CTIs exceeding the 95 percentile threshold in each -0.125\ :sup:`o` grid cell are excluded from the calculation to -eliminate biased estimation of statistics due to large CTI values at -pixels on stream networks. For grid cells over regions without CTIs such -as Australia, the global mean :math:`f_{\max }` is used to fill the -gaps. See :ref:`Li et al. (2013b) ` for additional details. The decay factor -:math:`f_{over}` for global simulations was determined through -sensitivity analysis and comparison with observed runoff to be 0.5 -m\ :sup:`-1`. +where :math:`f_{\max }` is the potential or maximum value of :math:`f_{sat}`, :math:`f_{over}` is a decay factor (m\ :sup:`-1`), and :math:`z_{\nabla}` is the water table depth (m) (section :numref:`Lateral Sub-surface Runoff`). The maximum saturated fraction, :math:`f_{\max }`, is defined as the value of the discrete cumulative distribution function (CDF) of the topographic index when the grid cell mean water table depth is zero. Thus, :math:`f_{\max }` is the percent of pixels in a grid cell whose topographic index is larger than or equal to the grid cell mean topographic index. It should be calculated explicitly from the CDF at each grid cell at the resolution that the model is run. However, because this is a computationally intensive task for global applications, :math:`f_{\max }` is calculated once at 0.125° resolution using the 1-km compound topographic indices (CTIs) based on the HYDRO1K dataset (:ref:`Verdin and Greenlee 1996 `) from USGS following the algorithm in :ref:`Niu et al. (2005) ` and then area-averaged to the desired model resolution (section :numref:`Surface Data`). Pixels with CTIs exceeding the 95 percentile threshold in each 0.125° grid cell are excluded from the calculation to eliminate biased estimation of statistics due to large CTI values at pixels on stream networks. For grid cells over regions without CTIs such as Australia, the global mean :math:`f_{\max }` is used to fill the gaps. See :ref:`Li et al. (2013b) ` for additional details. The decay factor :math:`f_{over}` for global simulations was determined through sensitivity analysis and comparison with observed runoff to be 0.5 m\ :sup:`-1`. .. _Surface Water Storage: Surface Water Storage ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A surface water store has been added to the model to represent wetlands -and small, sub-grid scale water bodies. As a result, the wetland land -unit has been removed as of CLM4.5. The state variables for surface water are the -mass of water :math:`W_{sfc}` (kg m\ :sup:`-2`) and temperature -:math:`T_{h2osfc}` (Chapter :numref:`rst_Soil and Snow Temperatures`). -Surface water storage and outflow are -functions of fine spatial scale elevation variations called -microtopography. The microtopography is assumed to be distributed -normally around the grid cell mean elevation. Given the standard -deviation of the microtopographic distribution, :math:`\sigma _{micro}` -(m), the fractional area of the grid cell that is inundated can be -calculated. Surface water storage, :math:`Wsfc`, is related to the -height (relative to the grid cell mean elevation) of the surface water, -:math:`d`, by +A surface water store has been added to the model to represent wetlands and small, sub-grid scale water bodies. As a result, the wetland land unit has been removed as of CLM4.5. The state variables for surface water are the mass of water :math:`W_{sfc}` (kg m\ :sup:`-2`) and temperature :math:`T_{h2osfc}` (Chapter :numref:`rst_Soil and Snow Temperatures`). Surface water storage and outflow are functions of fine spatial scale elevation variations called microtopography. The microtopography is assumed to be distributed normally around the grid cell mean elevation. Given the standard deviation of the microtopographic distribution, :math:`\sigma _{micro}` (m), the fractional area of the grid cell that is inundated can be calculated. Surface water storage, :math:`Wsfc`, is related to the height (relative to the grid cell mean elevation) of the surface water, :math:`d`, by .. math:: :label: 7.66 W_{sfc} =\frac{d}{2} \left(1+erf\left(\frac{d}{\sigma _{micro} \sqrt{2} } \right)\right)+\frac{\sigma _{micro} }{\sqrt{2\pi } } e^{\frac{-d^{2} }{2\sigma _{micro} ^{2} } } -where :math:`erf` is the error function. For a given value of -:math:`W_{sfc}` , :eq:`7.66` can be solved for :math:`d` using the -Newton-Raphson method. Once :math:`d` is known, one can determine the -fraction of the area that is inundated as +where :math:`erf` is the error function. For a given value of :math:`W_{sfc}`, :eq:`7.66` can be solved for :math:`d` using the Newton-Raphson method. Once :math:`d` is known, one can determine the fraction of the area that is inundated as .. math:: :label: 7.67 f_{h2osfc} =\frac{1}{2} \left(1+erf\left(\frac{d}{\sigma _{micro} \sqrt{2} } \right)\right) -No global datasets exist for microtopography, so the default -parameterization is a simple function of slope +No global datasets exist for microtopography, so the default parameterization is a simple function of slope .. math:: :label: 7.68 \sigma _{micro} =\left(\beta +\beta _{0} \right)^{\eta } -where :math:`\beta` is the topographic slope, -:math:`\beta_{0} =\left(\sigma_{\max } \right)^{\frac{1}{\eta } }` \ determines -the maximum value of :math:`\sigma_{micro}` , and :math:`\eta` is an -adjustable parameter. Default values in the model are -:math:`\sigma_{\max } =0.4` and :math:`\eta =-3`. +where :math:`\beta` is the topographic slope, :math:`\beta_{0} =\left(\sigma_{\max } \right)^{\frac{1}{\eta } }` \ determines the maximum value of :math:`\sigma_{micro}`, and :math:`\eta` is an adjustable parameter. Default values in the model are :math:`\sigma_{\max } =0.4` and :math:`\eta =-3`. -If the spatial scale of the microtopography is small relative to that of -the grid cell, one can assume that the inundated areas are distributed -randomly within the grid cell. With this assumption, a result from -percolation theory can be used to quantify the fraction of the inundated -portion of the grid cell that is interconnected +If the spatial scale of the microtopography is small relative to that of the grid cell, one can assume that the inundated areas are distributed randomly within the grid cell. With this assumption, a result from percolation theory can be used to quantify the fraction of the inundated portion of the grid cell that is interconnected .. math:: :label: 7.69 \begin{array}{lr} f_{connected} =\left(f_{h2osfc} -f_{c} \right)^{\mu } & \qquad f_{h2osfc} >f_{c} \\ f_{connected} =0 &\qquad f_{h2osfc} \le f_{c} \end{array} -where :math:`f_{c}` is a threshold below which no single connected -inundated area spans the grid cell and :math:`\mu` is a scaling -exponent. Default values of :math:`f_{c}` and :math:`\mu` \ are 0.4 and -0.14, respectively. When the inundated fraction of the grid cell -surpasses :math:`f_{c}` , the surface water store acts as a linear -reservoir +where :math:`f_{c}` is a threshold below which no single connected inundated area spans the grid cell and :math:`\mu` is a scaling exponent. Default values of :math:`f_{c}` and :math:`\mu` \ are 0.4 and 0.14, respectively. When the inundated fraction of the grid cell surpasses :math:`f_{c}`, the surface water store acts as a linear reservoir .. math:: :label: 7.70 q_{out,h2osfc}=k_{h2osfc} \ f_{connected} \ (Wsfc-Wc)\frac{1}{\Delta t} -where :math:`q_{out,h2osfc}` is the surface water runoff, :math:`k_{h2osfc}` -is a constant, :math:`Wc` is the amount of surface water present when -:math:`f_{h2osfc} =f_{c}` , and :math:`\Delta t` is the model time step. -The linear storage coefficent :math:`k_{h2osfc} = \sin \left(\beta \right)` -is a function of grid cell mean topographic slope where :math:`\beta` -is the slope in radians. +where :math:`q_{out,h2osfc}` is the surface water runoff, :math:`k_{h2osfc}` is a constant, :math:`Wc` is the amount of surface water present when :math:`f_{h2osfc} =f_{c}`, and :math:`\Delta t` is the model time step. The linear storage coefficent :math:`k_{h2osfc} = \sin \left(\beta \right)` is a function of grid cell mean topographic slope where :math:`\beta` is the slope in radians. .. _Infiltration: Infiltration ^^^^^^^^^^^^^^^^^^ -The surface moisture flux remaining after surface runoff has been -removed, +The surface moisture flux remaining after surface runoff has been removed, .. math:: :label: 7.71 q_{in,surface} = (1-f_{sat}) \ q_{liq,\, 0} -is divided into inputs to surface water (:math:`q_{in,\, h2osfc}` ) and -the soil :math:`q_{in,soil}` . If :math:`q_{in,soil}` exceeds the -maximum soil infiltration capacity (kg m\ :sup:`-2` -s\ :sup:`-1`), +is divided into inputs to surface water (:math:`q_{in,\, h2osfc}` ) and the soil :math:`q_{in,soil}`. If :math:`q_{in,soil}` exceeds the maximum soil infiltration capacity (kg m\ :sup:`-2` s\ :sup:`-1`), .. math:: :label: 7.72 q_{infl,\, \max } =(1-f_{sat}) \ \Theta_{ice} k_{sat} -where :math:`\Theta_{ice}` is an ice impedance factor (section -:numref:`Hydraulic Properties`), infiltration excess (Hortonian) runoff is generated +where :math:`\Theta_{ice}` is an ice impedance factor (section :numref:`Hydraulic Properties`), infiltration excess (Hortonian) runoff is generated .. math:: :label: 7.73 q_{infl,\, excess} =\max \left(q_{in,soil} -\left(1-f_{h2osfc} \right)q_{\inf l,\max } ,0\right) -and transferred from :math:`q_{in,soil}` to :math:`q_{in,h2osfc}` . -After evaporative losses have been removed, these moisture fluxes are +and transferred from :math:`q_{in,soil}` to :math:`q_{in,h2osfc}`. After evaporative losses have been removed, these moisture fluxes are .. math:: :label: 7.74 @@ -460,7 +326,6 @@ The balance of surface water is then calculated as \Delta W_{sfc} =\left(q_{in,h2osfc} - q_{out,h2osfc} - q_{drain,h2osfc} \right) \ \Delta t. - Bottom drainage from the surface water store .. math:: @@ -468,73 +333,46 @@ Bottom drainage from the surface water store q_{drain,h2osfc} = \min \left(f_{h2osfc} q_{\inf l,\max } ,\frac{W_{sfc} }{\Delta t} \right) -is then added to :math:`q_{in,soil}` giving the total infiltration -into the surface soil layer +is then added to :math:`q_{in,soil}` giving the total infiltration into the surface soil layer .. math:: :label: 7.78 q_{infl} = q_{in,soil} + q_{drain,h2osfc} -Infiltration :math:`q_{infl}` and explicit surface runoff -:math:`q_{over}` are not allowed for glaciers. +Infiltration :math:`q_{infl}` and explicit surface runoff :math:`q_{over}` are not allowed for glaciers. .. _Soil Water: Soil Water -------------- -Soil water is predicted from a multi-layer model, in which the vertical -soil moisture transport is governed by infiltration, surface and -sub-surface runoff, gradient diffusion, gravity, and canopy transpiration -through root extraction (:numref:`Figure Hydrologic processes`). +Soil water is predicted from a multi-layer model, in which the vertical soil moisture transport is governed by infiltration, surface and sub-surface runoff, gradient diffusion, gravity, and canopy transpiration through root extraction (:numref:`Figure Hydrologic processes`). -For one-dimensional vertical water flow in soils, the conservation of -mass is stated as +For one-dimensional vertical water flow in soils, the conservation of mass is stated as .. math:: :label: 7.79 \frac{\partial \theta }{\partial t} =-\frac{\partial q}{\partial z} - e -where :math:`\theta` is the volumetric soil water content -(mm\ :sup:`3` of water / mm\ :sup:`-3` of soil), :math:`t` is -time (s), :math:`z` is height above some datum in the soil column (mm) -(positive upwards), :math:`q` is soil water flux (kg m\ :sup:`-2` -s\ :sup:`-1` or mm s\ :sup:`-1`) (positive upwards), and -:math:`e` is a soil moisture sink term (mm of water mm\ :sup:`-1` -of soil s\ :sup:`-1`) (ET loss). This equation is solved -numerically by dividing the soil column into multiple layers in the -vertical and integrating downward over each layer with an upper boundary -condition of the infiltration flux into the top soil layer -:math:`q_{infl}` and a zero-flux lower boundary condition at the -bottom of the soil column (sub-surface runoff is removed later in the -timestep, section :numref:`Lateral Sub-surface Runoff`). +where :math:`\theta` is the volumetric soil water content (mm\ :sup:`3` of water / mm\ :sup:`-3` of soil), :math:`t` is time (s), :math:`z` is height above some datum in the soil column (mm) (positive upwards), :math:`q` is soil water flux (kg m\ :sup:`-2` s\ :sup:`-1` or mm s\ :sup:`-1`) (positive upwards), and :math:`e` is a soil moisture sink term (mm of water mm\ :sup:`-1` of soil s\ :sup:`-1`) (ET loss). This equation is solved numerically by dividing the soil column into multiple layers in the vertical and integrating downward over each layer with an upper boundary condition of the infiltration flux into the top soil layer :math:`q_{infl}` and a zero-flux lower boundary condition at the bottom of the soil column (sub-surface runoff is removed later in the timestep, section :numref:`Lateral Sub-surface Runoff`). -The soil water flux :math:`q` in equation can be described by Darcy’s -law :ref:`(Dingman 2002) ` +The soil water flux :math:`q` in equation :eq:`7.79` can be described by Darcy's law :ref:`(Dingman 2002) ` .. math:: :label: 7.80 q = -k \frac{\partial \psi _{h} }{\partial z} -where :math:`k` is the hydraulic conductivity (mm s\ :sup:`-1`), -and :math:`\psi _{h}` is the hydraulic potential (mm). The hydraulic -potential is +where :math:`k` is the hydraulic conductivity (mm s\ :sup:`-1`), and :math:`\psi _{h}` is the hydraulic potential (mm). The hydraulic potential is .. math:: :label: 7.81 \psi _{h} =\psi _{m} +\psi _{z} -where :math:`\psi _{m}` is the soil matric potential (mm) (which is -related to the adsorptive and capillary forces within the soil matrix), -and :math:`\psi _{z}` is the gravitational potential (mm) (the vertical -distance from an arbitrary reference elevation to a point in the soil). -If the reference elevation is the soil surface, then -:math:`\psi _{z} =z`. Letting :math:`\psi =\psi _{m}` , Darcy’s law -becomes +where :math:`\psi _{m}` is the soil matric potential (mm) (which is related to the adsorptive and capillary forces within the soil matrix), and :math:`\psi _{z}` is the gravitational potential (mm) (the vertical distance from an arbitrary reference elevation to a point in the soil). If the reference elevation is the soil surface, then :math:`\psi _{z} =z`. Letting :math:`\psi =\psi _{m}`, Darcy's law becomes .. math:: :label: 7.82 @@ -549,80 +387,52 @@ Equation :eq:`7.82` can be further manipulated to yield q = -k \left[\frac{\partial \left(\psi +z\right)}{\partial z} \right] = -k \left(\frac{\partial \psi }{\partial z} + 1 \right) \ . -Substitution of this equation into equation :eq:`7.79`, with :math:`e = 0`, yields -the Richards equation :ref:`(Dingman 2002) ` +Substitution of this equation into equation :eq:`7.79`, with :math:`e = 0`, yields the Richards equation :ref:`(Dingman 2002) ` .. math:: :label: 7.84 - \frac{\partial \theta }{\partial t} = - \frac{\partial }{\partial z} \left[k\left(\frac{\partial \psi }{\partial z} + 1 + \frac{\partial \theta }{\partial t} = + \frac{\partial }{\partial z} \left[k\left(\frac{\partial \psi }{\partial z} + 1 \right)\right]. -In practice (Section :numref:`Numerical Solution Hydrology`), changes in soil -water content are predicted from :eq:`7.79` using finite-difference approximations -for :eq:`7.84`. +In practice (Section :numref:`Numerical Solution Hydrology`), changes in soil water content are predicted from :eq:`7.79` using finite-difference approximations for :eq:`7.84`. .. _Hydraulic Properties: Hydraulic Properties ^^^^^^^^^^^^^^^^^^^^^^^^^^ -The hydraulic conductivity :math:`k_{i}` (mm s\ :sup:`-1`) and -the soil matric potential :math:`\psi _{i}` (mm) for layer :math:`i` -vary with volumetric soil water :math:`\theta _{i}` and soil texture. -As with the soil thermal properties (section -:numref:`Soil And Snow Thermal Properties`) the hydraulic -properties of the soil are assumed to be a weighted combination of the -mineral properties, which are determined according to sand and clay -contents based on work by :ref:`Clapp and Hornberger (1978) -` and :ref:`Cosby et al. (1984) `, -and organic properties of the soil -(:ref:`Lawrence and Slater 2008 `). - -The hydraulic conductivity is defined at the depth of the interface of -two adjacent layers :math:`z_{h,\, i}` (:numref:`Figure Water flux schematic`) -and is a function of the saturated hydraulic conductivity -:math:`k_{sat} \left[z_{h,\, i} \right]`, the liquid volumetric soil -moisture of the two layers :math:`\theta _{i}` and :math:`\theta_{i+1}` -and an ice impedance factor :math:`\Theta_{ice}` +The hydraulic conductivity :math:`k_{i}` (mm s\ :sup:`-1`) and the soil matric potential :math:`\psi _{i}` (mm) for layer :math:`i` vary with volumetric soil water :math:`\theta _{i}` and soil texture. As with the soil thermal properties (section :numref:`Soil And Snow Thermal Properties`) the hydraulic properties of the soil are assumed to be a weighted combination of the mineral properties, which are determined according to sand and clay contents based on work by :ref:`Clapp and Hornberger (1978) ` and :ref:`Cosby et al. (1984) `, and organic properties of the soil (:ref:`Lawrence and Slater 2008 `). + +The hydraulic conductivity is defined at the depth of the interface of two adjacent layers :math:`z_{h,\, i}` (:numref:`Figure Water flux schematic`) and is a function of the saturated hydraulic conductivity :math:`k_{sat} \left[z_{h,\, i} \right]`, the liquid volumetric soil moisture of the two layers :math:`\theta _{i}` and :math:`\theta_{i+1}` and an ice impedance factor :math:`\Theta_{ice}` .. math:: :label: 7.85 k\left[z_{h,\, i} \right] = - \left\{\begin{array}{lr} - \Theta_{ice} k_{sat} \left[z_{h,\, i} \right]\left[\frac{0.5\left(\theta_{\, i} +\theta_{\, i+1} \right)}{0.5\left(\theta_{sat,\, i} +\theta_{sat,\, i+1} \right)} \right]^{2B_{i} +3} & \qquad 1 \le i \le N_{levsoi} - 1 \\ - \Theta_{ice} k_{sat} \left[z_{h,\, i} \right]\left(\frac{\theta_{\, i} }{\theta_{sat,\, i} } \right)^{2B_{i} +3} & \qquad i = N_{levsoi} + \left\{\begin{array}{lr} + \Theta_{ice} k_{sat} \left[z_{h,\, i} \right]\left[\frac{0.5\left(\theta_{\, i} +\theta_{\, i+1} \right)}{0.5\left(\theta_{sat,\, i} +\theta_{sat,\, i+1} \right)} \right]^{2B_{i} +3} & \qquad 1 \le i \le N_{levsoi} - 1 \\ + \Theta_{ice} k_{sat} \left[z_{h,\, i} \right]\left(\frac{\theta_{\, i} }{\theta_{sat,\, i} } \right)^{2B_{i} +3} & \qquad i = N_{levsoi} \end{array}\right\}. -The ice impedance factor is a function of ice content, and is meant to -quantify the increased tortuosity of the water flow when part of the -pore space is filled with ice. :ref:`Swenson et al. (2012) ` -used a power law form +The ice impedance factor is a function of ice content, and is meant to quantify the increased tortuosity of the water flow when part of the pore space is filled with ice. :ref:`Swenson et al. (2012) ` used a power law form .. math:: :label: 7.86 \Theta_{ice} = 10^{-\Omega F_{ice} } -where :math:`\Omega = 6`\ and :math:`F_{ice} = \frac{\theta_{ice} }{\theta_{sat} }` -is the ice-filled fraction of the pore space. +where :math:`\Omega = 6`\ and :math:`F_{ice} = \frac{\theta_{ice} }{\theta_{sat} }` is the ice-filled fraction of the pore space. -Because the hydraulic properties of mineral and organic soil may differ -significantly, the bulk hydraulic properties of each soil layer are -computed as weighted averages of the properties of the mineral and -organic components. The water content at saturation (i.e. porosity) is +Because the hydraulic properties of mineral and organic soil may differ significantly, the bulk hydraulic properties of each soil layer are computed as weighted averages of the properties of the mineral and organic components. The water content at saturation (i.e. porosity) is .. math:: :label: 7.90 \theta_{sat,i} =(1-f_{om,i} )\theta_{sat,\min ,i} +f_{om,i} \theta_{sat,om} -where :math:`f_{om,i}` is the soil organic matter fraction, -:math:`\theta_{sat,om}` is the -porosity of organic matter, and the porosity of the mineral soil -:math:`\theta_{sat,\min ,i}` is +where :math:`f_{om,i}` is the soil organic matter fraction, :math:`\theta_{sat,om}` is the porosity of organic matter, and the porosity of the mineral soil :math:`\theta_{sat,\min,i}` is .. math:: :label: 7.91 @@ -643,8 +453,7 @@ where :math:`B_{om}` is for organic matter and B_{\min ,i} =2.91+0.159(\% clay)_{i} . -The soil matric potential (mm) is defined at the node depth -:math:`z_{i}` of each layer :math:`i` (:numref:`Figure Water flux schematic`) +The soil matric potential (mm) is defined at the node depth :math:`z_{i}` of each layer :math:`i` (:numref:`Figure Water flux schematic`) .. math:: :label: 7.94 @@ -658,72 +467,45 @@ where the saturated soil matric potential (mm) is \psi _{sat,i} =(1-f_{om,i} )\psi _{sat,\min ,i} +f_{om,i} \psi _{sat,om} -where :math:`\psi _{sat,om}` \ is the -saturated organic matter matric potential and the saturated mineral soil -matric potential :math:`\psi _{sat,\min ,i}` \ is +where :math:`\psi _{sat,om}` \ is the saturated organic matter matric potential and the saturated mineral soil matric potential :math:`\psi _{sat,\min,i}` \ is .. math:: :label: 7.96 \psi _{sat,\, \min ,\, i} =-10.0\times 10^{1.88-0.0131(\% sand)_{i} } . -The saturated hydraulic conductivity, -:math:`k_{sat} \left[z_{h,\, i} \right]` (mm s\ :sup:`-1`), for -organic soils (:math:`k_{sat,\, om}` ) may be two to three orders of -magnitude larger than that of mineral soils (:math:`k_{sat,\, \min }` ). -Bulk soil layer values of :math:`k_{sat}` \ calculated as weighted -averages based on :math:`f_{om}` may therefore be determined primarily -by the organic soil properties even for values of :math:`f_{om}` as low -as 1 %. To better represent the influence of organic soil material on -the grid cell average saturated hydraulic conductivity, the soil organic -matter fraction is further subdivided into “connected” and “unconnected” -fractions using a result from percolation theory (:ref:`Stauffer and Aharony -1994 `, :ref:`Berkowitz and Balberg 1992 `). -Assuming that the organic and mineral fractions are randomly distributed throughout -a soil layer, percolation theory predicts that above a threshold value -:math:`f_{om} = f_{threshold}`, connected flow pathways consisting of -organic material only exist and span the soil space. Flow through these -pathways interacts only with organic material, and thus can be described -by :math:`k_{sat,\, om}`. This fraction of the grid cell is given by +The saturated hydraulic conductivity, :math:`k_{sat} \left[z_{h,\, i} \right]` (mm s\ :sup:`-1`), for organic soils (:math:`k_{sat,\, om}` ) may be two to three orders of magnitude larger than that of mineral soils (:math:`k_{sat,\, \min }` ). Bulk soil layer values of :math:`k_{sat}` \ calculated as weighted averages based on :math:`f_{om}` may therefore be determined primarily by the organic soil properties even for values of :math:`f_{om}` as low as 1 %. To better represent the influence of organic soil material on the grid cell average saturated hydraulic conductivity, the soil organic matter fraction is further subdivided into "connected" and "unconnected" fractions using a result from percolation theory (:ref:`Stauffer and Aharony 1994 `, :ref:`Berkowitz and Balberg 1992 `). Assuming that the organic and mineral fractions are randomly distributed throughout a soil layer, percolation theory predicts that above a threshold value :math:`f_{om} = f_{threshold}`, connected flow pathways consisting of organic material only exist and span the soil space. Flow through these pathways interacts only with organic material, and thus can be described by :math:`k_{sat,\, om}`. This fraction of the grid cell is given by .. math:: :label: 7.97 - \begin{array}{lr} - f_{perc} =\; N_{perc} \left(f_{om} {\rm \; }-f_{threshold} \right)^{\beta_{perc} } f_{om} {\rm \; } & \qquad f_{om} \ge f_{threshold} \\ - f_{perc} = 0 & \qquad f_{om} `) as +where saturated hydraulic conductivity for mineral soil depends on soil texture (:ref:`Cosby et al. 1984 `) as .. math:: :label: 7.99 k_{sat,\, \min } \left[z_{h,\, i} \right]=0.0070556\times 10^{-0.884+0.0153\left(\% sand\right)_{i} } . -The bulk soil layer saturated hydraulic conductivity is then computed -as +The bulk soil layer saturated hydraulic conductivity is then computed as .. math:: :label: 7.100 k_{sat} \left[z_{h,\, i} \right]=f_{uncon,\, i} k_{sat,\, uncon} \left[z_{h,\, i} \right]+(1-f_{uncon,\, i} )k_{sat,\, om} \left[z_{h,\, i} \right]. -The soil organic matter properties implicitly account for the standard observed profile of organic matter -properties as +The soil organic matter properties implicitly account for the standard observed profile of organic matter properties as .. math:: :label: 1.101 @@ -752,44 +534,30 @@ where :math:`zsapric =0.5` \m is the depth that organic matter takes on the char Numerical Solution ^^^^^^^^^^^^^^^^^^^^^^^^ -With reference to :numref:`Figure Water flux schematic`, the equation for -conservation of mass (equation :eq:`7.79`) can be integrated over each layer as +With reference to :numref:`Figure Water flux schematic`, the equation for conservation of mass (equation :eq:`7.79`) can be integrated over each layer as .. math:: :label: 7.101 \int _{-z_{h,\, i} }^{-z_{h,\, i-1} }\frac{\partial \theta }{\partial t} \, dz=-\int _{-z_{h,\, i} }^{-z_{h,\, i-1} }\frac{\partial q}{\partial z} \, dz-\int _{-z_{h,\, i} }^{-z_{h,\, i-1} } e\, dz . -Note that the integration limits are negative since :math:`z` is defined -as positive upward from the soil surface. This equation can be written -as +Note that the integration limits are negative since :math:`z` is defined as positive upward from the soil surface. This equation can be written as .. math:: :label: 7.102 \Delta z_{i} \frac{\partial \theta_{liq,\, i} }{\partial t} =-q_{i-1} +q_{i} -e_{i} -where :math:`q_{i}` is the flux of water across interface -:math:`z_{h,\, i}` , :math:`q_{i-1}` is the flux of water across -interface :math:`z_{h,\, i-1}` , and :math:`e_{i}` is a layer-averaged -soil moisture sink term (ET loss) defined as positive for flow out of -the layer (mm s\ :sup:`-1`). Taking the finite difference with -time and evaluating the fluxes implicitly at time :math:`n+1` yields +where :math:`q_{i}` is the flux of water across interface :math:`z_{h,\, i}`, :math:`q_{i-1}` is the flux of water across interface :math:`z_{h,\, i-1}`, and :math:`e_{i}` is a layer-averaged soil moisture sink term (ET loss) defined as positive for flow out of the layer (mm s\ :sup:`-1`). Taking the finite difference with time and evaluating the fluxes implicitly at time :math:`n+1` yields .. math:: :label: 7.103 \frac{\Delta z_{i} \Delta \theta_{liq,\, i} }{\Delta t} =-q_{i-1}^{n+1} +q_{i}^{n+1} -e_{i} -where -:math:`\Delta \theta_{liq,\, i} =\theta_{liq,\, i}^{n+1} -\theta_{liq,\, i}^{n}` -is the change in volumetric soil liquid water of layer :math:`i` in time -:math:`\Delta t`\ and :math:`\Delta z_{i}` is the thickness of layer -:math:`i` (mm). +where :math:`\Delta \theta_{liq,\, i} =\theta_{liq,\, i}^{n+1} -\theta_{liq,\, i}^{n}` is the change in volumetric soil liquid water of layer :math:`i` in time :math:`\Delta t`\ and :math:`\Delta z_{i}` is the thickness of layer :math:`i` (mm). -The water removed by transpiration in each layer :math:`e_{i}` is a -function of the total transpiration :math:`E_{v}^{t}` (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) and -the effective root fraction :math:`r_{e,\, i}` +The water removed by transpiration in each layer :math:`e_{i}` is a function of the total transpiration :math:`E_{v}^{t}` (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) and the effective root fraction :math:`r_{e,\, i}` .. math:: :label: 7.104 @@ -802,61 +570,35 @@ the effective root fraction :math:`r_{e,\, i}` Schematic diagram of numerical scheme used to solve for soil water fluxes. -Shown are three soil layers, :math:`i-1`, :math:`i`, and :math:`i+1`. -The soil matric potential :math:`\psi` and volumetric soil water -:math:`\theta_{liq}` are defined at the layer node depth :math:`z`. -The hydraulic conductivity :math:`k\left[z_{h} \right]` is defined at -the interface of two layers :math:`z_{h}` . The layer thickness is -:math:`\Delta z`. The soil water fluxes :math:`q_{i-1}` and -:math:`q_{i}` are defined as positive upwards. The soil moisture sink -term :math:`e` (ET loss) is defined as positive for flow out of the -layer. - +Shown are three soil layers, :math:`i-1`, :math:`i`, and :math:`i+1`. The soil matric potential :math:`\psi` and volumetric soil water :math:`\theta_{liq}` are defined at the layer node depth :math:`z`. The hydraulic conductivity :math:`k\left[z_{h} \right]` is defined at the interface of two layers :math:`z_{h}`. The layer thickness is :math:`\Delta z`. The soil water fluxes :math:`q_{i-1}` and :math:`q_{i}` are defined as positive upwards. The soil moisture sink term :math:`e` (ET loss) is defined as positive for flow out of the layer. -Note that because more than one plant functional type (PFT) may share a -soil column, the transpiration :math:`E_{v}^{t}` is a weighted sum of -transpiration from all PFTs whose weighting depends on PFT area as +Note that because more than one plant functional type (PFT) may share a soil column, the transpiration :math:`E_{v}^{t}` is a weighted sum of transpiration from all PFTs whose weighting depends on PFT area as .. math:: :label: 7.105 E_{v}^{t} =\sum _{j=1}^{npft}\left(E_{v}^{t} \right)_{j} \left(wt\right)_{j} -where :math:`npft` is the number of PFTs sharing a soil column, -:math:`\left(E_{v}^{t} \right)_{j}` is the transpiration from the -:math:`j^{th}` PFT on the column, and :math:`\left(wt\right)_{j}` is -the relative area of the :math:`j^{th}` PFT with respect to the column. -The effective root fraction :math:`r_{e,\, i}` is also a column-level -quantity that is a weighted sum over all PFTs. The weighting depends on -the per unit area transpiration of each PFT and its relative area as +where :math:`npft` is the number of PFTs sharing a soil column, :math:`\left(E_{v}^{t} \right)_{j}` is the transpiration from the :math:`j^{th}` PFT on the column, and :math:`\left(wt\right)_{j}` is the relative area of the :math:`j^{th}` PFT with respect to the column. The effective root fraction :math:`r_{e,\, i}` is also a column-level quantity that is a weighted sum over all PFTs. The weighting depends on the per unit area transpiration of each PFT and its relative area as .. math:: :label: 7.106 r_{e,\, i} =\frac{\sum _{j=1}^{npft}\left(r_{e,\, i} \right)_{j} \left(E_{v}^{t} \right)_{j} \left(wt\right)_{j} }{\sum _{j=1}^{npft}\left(E_{v}^{t} \right)_{j} \left(wt\right)_{j} } -where :math:`\left(r_{e,\, i} \right)_{j}` is the effective root -fraction for the :math:`j^{th}` PFT +where :math:`\left(r_{e,\, i} \right)_{j}` is the effective root fraction for the :math:`j^{th}` PFT .. math:: :label: 7.107 - \begin{array}{lr} - \left(r_{e,\, i} \right)_{j} =\frac{\left(r_{i} \right)_{j} \left(w_{i} \right)_{j} }{\left(\beta _{t} \right)_{j} } & \qquad \left(\beta _{t} \right)_{j} >0 \\ + \begin{array}{lr} + \left(r_{e,\, i} \right)_{j} =\frac{\left(r_{i} \right)_{j} \left(w_{i} \right)_{j} }{\left(\beta _{t} \right)_{j} } & \qquad \left(\beta _{t} \right)_{j} >0 \\ \left(r_{e,\, i} \right)_{j} =0 & \qquad \left(\beta _{t} \right)_{j} =0 \end{array} - -and :math:`\left(r_{i} \right)_{j}` is the fraction of roots in layer -:math:`i` (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`), -:math:`\left(w_{i} \right)_{j}` is a soil dryness or plant wilting factor -for layer :math:`i` (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`), and :math:`\left(\beta_{t} \right)_{j}` is a wetness factor for the total -soil column for the :math:`j^{th}` PFT (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`). -The soil water fluxes in :eq:`7.103`,, which are a function of -:math:`\theta_{liq,\, i}` and :math:`\theta_{liq,\, i+1}` because of -their dependence on hydraulic conductivity and soil matric potential, -can be linearized about :math:`\theta` using a Taylor series expansion -as +and :math:`\left(r_{i} \right)_{j}` is the fraction of roots in layer :math:`i` (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`), :math:`\left(w_{i} \right)_{j}` is a soil dryness or plant wilting factor for layer :math:`i` (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`), and :math:`\left(\beta_{t} \right)_{j}` is a wetness factor for the total soil column for the :math:`j^{th}` PFT (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`). + +The soil water fluxes in :eq:`7.103`,, which are a function of :math:`\theta_{liq,\, i}` and :math:`\theta_{liq,\, i+1}` because of their dependence on hydraulic conductivity and soil matric potential, can be linearized about :math:`\theta` using a Taylor series expansion as .. math:: :label: 7.108 @@ -868,9 +610,7 @@ as q_{i-1}^{n+1} =q_{i-1}^{n} +\frac{\partial q_{i-1} }{\partial \theta_{liq,\, i-1} } \Delta \theta_{liq,\, i-1} +\frac{\partial q_{i-1} }{\partial \theta_{liq,\, i} } \Delta \theta_{liq,\, i} . -Substitution of these expressions for :math:`q_{i}^{n+1}` and -:math:`q_{i-1}^{n+1}` into :eq:`7.103` results in a general tridiagonal -equation set of the form +Substitution of these expressions for :math:`q_{i}^{n+1}` and :math:`q_{i-1}^{n+1}` into :eq:`7.103` results in a general tridiagonal equation set of the form .. math:: :label: 7.110 @@ -890,7 +630,7 @@ where b_{i} =\frac{\partial q_{i} }{\partial \theta_{liq,\, i} } -\frac{\partial q_{i-1} }{\partial \theta_{liq,\, i} } -\frac{\Delta z_{i} }{\Delta t} .. math:: - :label: 7.113 + :label: 7.113 c_{i} =\frac{\partial q_{i} }{\partial \theta_{liq,\, i+1} } @@ -899,11 +639,9 @@ where r_{i} =q_{i-1}^{n} -q_{i}^{n} +e_{i} . -The tridiagonal equation set is solved over -:math:`i=1,\ldots ,N_{levsoi}`. +The tridiagonal equation set is solved over :math:`i=1,\ldots,N_{levsoi}`. -The finite-difference forms of the fluxes and partial derivatives in -equations :eq:`7.111` - :eq:`7.114` can be obtained from equation as +The finite-difference forms of the fluxes and partial derivatives in equations :eq:`7.111` - :eq:`7.114` can be obtained from equation :eq:`7.82` as .. math:: :label: 7.115 @@ -925,7 +663,6 @@ equations :eq:`7.111` - :eq:`7.114` can be obtained from equation as \frac{\partial q_{i-1} }{\partial \theta _{liq,\, i} } =\left[\frac{k\left[z_{h,\, i-1} \right]}{z_{i} -z_{i-1} } \frac{\partial \psi _{i} }{\partial \theta _{liq,\, i} } \right]-\frac{\partial k\left[z_{h,\, i-1} \right]}{\partial \theta _{liq,\, i} } \left[\frac{\left(\psi _{i-1} -\psi _{i} \right)+\left(z_{i} - z_{i-1} \right)}{z_{i} - z_{i-1} } \right] - .. math:: :label: 7.119 @@ -936,8 +673,7 @@ equations :eq:`7.111` - :eq:`7.114` can be obtained from equation as \frac{\partial q_{i} }{\partial \theta _{liq,\, i+1} } =\left[\frac{k\left[z_{h,\, i} \right]}{z_{i+1} -z_{i} } \frac{\partial \psi _{i+1} }{\partial \theta _{liq,\, i+1} } \right]-\frac{\partial k\left[z_{h,\, i} \right]}{\partial \theta _{liq,\, i+1} } \left[\frac{\left(\psi _{i} -\psi _{i+1} \right)+\left(z_{i+1} - z_{i} \right)}{z_{i+1} - z_{i} } \right]. -The derivatives of the soil matric potential at the node depth are -derived from :eq:`7.94` +The derivatives of the soil matric potential at the node depth are derived from :eq:`7.94` .. math:: :label: 7.121 @@ -954,24 +690,22 @@ derived from :eq:`7.94` \frac{\partial \psi _{i+1} }{\partial \theta_{liq,\, i+1} } =-B_{i+1} \frac{\psi _{i+1} }{\theta_{\, i+1} } -with the constraint -:math:`0.01\, \theta_{sat,\, i} \le \theta_{\, i} \le \theta_{sat,\, i}` . +with the constraint :math:`0.01\, \theta_{sat,\, i} \le \theta_{\, i} \le \theta_{sat,\, i}`. -The derivatives of the hydraulic conductivity at the layer interface are -derived from :eq:`7.85` +The derivatives of the hydraulic conductivity at the layer interface are derived from :eq:`7.85` .. math:: :label: 7.124 - \begin{array}{l} - {\frac{\partial k\left[z_{h,\, i-1} \right]}{\partial \theta _{liq,\, i-1} } - = \frac{\partial k\left[z_{h,\, i-1} \right]}{\partial \theta _{liq,\, i} } + \begin{array}{l} + {\frac{\partial k\left[z_{h,\, i-1} \right]}{\partial \theta _{liq,\, i-1} } + = \frac{\partial k\left[z_{h,\, i-1} \right]}{\partial \theta _{liq,\, i} } = \left(2B_{i-1} +3\right) \ \overline{\Theta}_{ice} \ k_{sat} \left[z_{h,\, i-1} \right] \ \left[\frac{\overline{\theta}_{liq}}{\overline{\theta}_{sat}} \right]^{2B_{i-1} +2} \left(\frac{0.5}{\overline{\theta}_{sat}} \right)} \end{array} -where :math:`\overline{\Theta}_{ice} = \Theta(\overline{\theta}_{ice})` :eq:`7.86`, -:math:`\overline{\theta}_{ice} = 0.5\left(\theta_{ice\, i-1} +\theta_{ice\, i} \right)`, -:math:`\overline{\theta}_{liq} = 0.5\left(\theta_{liq\, i-1} +\theta_{liq\, i} \right)`, -and +where :math:`\overline{\Theta}_{ice} = \Theta(\overline{\theta}_{ice})` :eq:`7.86`, +:math:`\overline{\theta}_{ice} = 0.5\left(\theta_{ice\, i-1} +\theta_{ice\, i} \right)`, +:math:`\overline{\theta}_{liq} = 0.5\left(\theta_{liq\, i-1} +\theta_{liq\, i} \right)`, +and :math:`\overline{\theta}_{sat} = 0.5\left(\theta_{sat,\, i-1} +\theta_{sat,\, i} \right)` and @@ -979,29 +713,25 @@ and .. math:: :label: 7.125 - \begin{array}{l} - {\frac{\partial k\left[z_{h,\, i} \right]}{\partial \theta _{liq,\, i} } - = \frac{\partial k\left[z_{h,\, i} \right]}{\partial \theta _{liq,\, i+1} } + \begin{array}{l} + {\frac{\partial k\left[z_{h,\, i} \right]}{\partial \theta _{liq,\, i} } + = \frac{\partial k\left[z_{h,\, i} \right]}{\partial \theta _{liq,\, i+1} } = \left(2B_{i} +3\right) \ \overline{\Theta}_{ice} \ k_{sat} \left[z_{h,\, i} \right] \ \left[\frac{\overline{\theta}_{liq}}{\overline{\theta}_{sat}} \right]^{2B_{i} +2} \left(\frac{0.5}{\overline{\theta}_{sat}} \right)} \end{array}. -where :math:`\overline{\theta}_{liq} = 0.5\left(\theta_{\, i} +\theta_{\, i+1} \right)`, +where :math:`\overline{\theta}_{liq} = 0.5\left(\theta_{\, i} +\theta_{\, i+1} \right)`, :math:`\overline{\theta}_{sat} = 0.5\left(\theta_{sat,\, i} +\theta_{sat,\, i+1} \right)`. Equation set for layer :math:`i=1` '''''''''''''''''''''''''''''''''''''''''' -For the top soil layer (:math:`i=1`), the boundary condition is the -infiltration rate (section :numref:`Surface Runoff`), -:math:`q_{i-1}^{n+1} =-q_{infl}^{n+1}` , and the water balance equation -is +For the top soil layer (:math:`i=1`), the boundary condition is the infiltration rate (section :numref:`Surface Runoff`), :math:`q_{i-1}^{n+1} =-q_{infl}^{n+1}`, and the water balance equation is .. math:: :label: 7.135 \frac{\Delta z_{i} \Delta \theta_{liq,\, i} }{\Delta t} =q_{infl}^{n+1} +q_{i}^{n+1} -e_{i} . -After grouping like terms, the coefficients of the tridiagonal set of -equations for :math:`i=1` are +After grouping like terms, the coefficients of the tridiagonal set of equations for :math:`i=1` are .. math:: :label: 7.136 @@ -1026,8 +756,7 @@ equations for :math:`i=1` are Equation set for layers :math:`i=2,\ldots ,N_{levsoi} -1` ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -The coefficients of the tridiagonal set of equations for -:math:`i=2,\ldots ,N_{levsoi} -1` are +The coefficients of the tridiagonal set of equations for :math:`i=2,\ldots,N_{levsoi} -1` are .. math:: :label: 7.140 @@ -1052,10 +781,7 @@ The coefficients of the tridiagonal set of equations for Equation set for layer :math:`i=N_{levsoi}` '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -For the lowest soil layer (:math:`i=N_{levsoi}` ), a zero-flux bottom boundary -condition is applied (:math:`q_{i}^{n} =0`) -and the coefficients of the tridiagonal set of equations for -:math:`i=N_{levsoi}` are +For the lowest soil layer (:math:`i=N_{levsoi}` ), a zero-flux bottom boundary condition is applied (:math:`q_{i}^{n} =0`) and the coefficients of the tridiagonal set of equations for :math:`i=N_{levsoi}` are .. math:: :label: 7.148 @@ -1080,23 +806,12 @@ and the coefficients of the tridiagonal set of equations for Adaptive Time Stepping ''''''''''''''''''''''''''''' -The length of the time step is adjusted in order to improve the accuracy -and stability of the numerical solutions. The difference between two numerical -approximations is used to estimate the temporal truncation error, and then -the step size :math:`\Delta t_{sub}` is adjusted to meet a user-prescribed error tolerance -:ref:`[Kavetski et al., 2002]`. The temporal truncation -error is estimated by comparing the flux obtained from the first-order -Taylor series expansion (:math:`q_{i-1}^{n+1}` and :math:`q_{i}^{n+1}`, -equations :eq:`7.108` and :eq:`7.109`) against the flux at the start of the -time step (:math:`q_{i-1}^{n}` and :math:`q_{i}^{n}`). Since the tridiagonal -solution already provides an estimate of :math:`\Delta \theta_{liq,i}`, it is -convenient to compute the error for each of the :math:`i` layers from equation -:eq:`7.103` as +The length of the time step is adjusted in order to improve the accuracy and stability of the numerical solutions. The difference between two numerical approximations is used to estimate the temporal truncation error, and then the step size :math:`\Delta t_{sub}` is adjusted to meet a user-prescribed error tolerance :ref:`[Kavetski et al., 2002]`. The temporal truncation error is estimated by comparing the flux obtained from the first-order Taylor series expansion (:math:`q_{i-1}^{n+1}` and :math:`q_{i}^{n+1}`, equations :eq:`7.108` and :eq:`7.109`) against the flux at the start of the time step (:math:`q_{i-1}^{n}` and :math:`q_{i}^{n}`). Since the tridiagonal solution already provides an estimate of :math:`\Delta \theta_{liq,i}`, it is convenient to compute the error for each of the :math:`i` layers from equation :eq:`7.103` as .. math:: :label: 7.152 - \epsilon_{i} = \left[ \frac{\Delta \theta_{liq,\, i} \Delta z_{i}}{\Delta t_{sub}} - + \epsilon_{i} = \left[ \frac{\Delta \theta_{liq,\, i} \Delta z_{i}}{\Delta t_{sub}} - \left( q_{i-1}^{n} - q_{i}^{n} + e_{i}\right) \right] \ \frac{\Delta t_{sub}}{2} and the maximum absolute error across all layers as @@ -1108,18 +823,9 @@ and the maximum absolute error across all layers as \epsilon_{crit} = {\rm max} \left( \left| \epsilon_{i} \right| \right) & \qquad 1 \le i \le nlevsoi \end{array} \ . -The adaptive step size selection is based on specified upper and lower error -tolerances, :math:`\tau_{U}` and :math:`\tau_{L}`. The solution is accepted if -:math:`\epsilon_{crit} \le \tau_{U}` and the procedure repeats until the adaptive -sub-stepping spans the full model time step (the sub-steps are doubled if -:math:`\epsilon_{crit} \le \tau_{L}`, i.e., if the solution is very accurate). -Conversely, the solution is rejected if :math:`\epsilon_{crit} > \tau_{U}`. In -this case the length of the sub-steps is halved and a new solution is obtained. -The halving of substeps continues until either :math:`\epsilon_{crit} \le \tau_{U}` -or the specified minimum time step length is reached. +The adaptive step size selection is based on specified upper and lower error tolerances, :math:`\tau_{U}` and :math:`\tau_{L}`. The solution is accepted if :math:`\epsilon_{crit} \le \tau_{U}` and the procedure repeats until the adaptive sub-stepping spans the full model time step (the sub-steps are doubled if :math:`\epsilon_{crit} \le \tau_{L}`, i.e., if the solution is very accurate). Conversely, the solution is rejected if :math:`\epsilon_{crit} > \tau_{U}`. In this case the length of the sub-steps is halved and a new solution is obtained. The halving of substeps continues until either :math:`\epsilon_{crit} \le \tau_{U}` or the specified minimum time step length is reached. -Upon solution of the tridiagonal equation set, the liquid water contents are updated -as follows +Upon solution of the tridiagonal equation set, the liquid water contents are updated as follows .. math:: :label: 7.164 @@ -1129,7 +835,7 @@ as follows The volumetric water content is .. math:: - :label: 7.165 + :label: 7.165 \theta_{i} =\frac{w_{liq,\, i} }{\Delta z_{i} \rho _{liq} } +\frac{w_{ice,\, i} }{\Delta z_{i} \rho _{ice} } . @@ -1138,109 +844,60 @@ The volumetric water content is Frozen Soils and Perched Water Table ---------------------------------------- -When soils freeze, the power-law form of the ice impedance factor -(section :numref:`Hydraulic Properties`) can greatly decrease the hydraulic -conductivity of the soil, leading to nearly impermeable soil layers. When unfrozen -soil layers are present above relatively ice-rich frozen layers, the -possibility exists for perched saturated zones. Lateral drainage from -perched saturated regions is parameterized as a function of the -thickness of the saturated zone +When soils freeze, the power-law form of the ice impedance factor (section :numref:`Hydraulic Properties`) can greatly decrease the hydraulic conductivity of the soil, leading to nearly impermeable soil layers. When unfrozen soil layers are present above relatively ice-rich frozen layers, the possibility exists for perched saturated zones. Lateral drainage from perched saturated regions is parameterized as a function of the thickness of the saturated zone .. math:: :label: 7.166 q_{drai,perch} =k_{drai,\, perch} \left(z_{frost} -z_{\nabla ,perch} \right) -where :math:`k_{drai,\, perch}` depends on topographic slope and soil -hydraulic conductivity, +where :math:`k_{drai,\, perch}` depends on topographic slope and soil hydraulic conductivity, .. math:: :label: 7.167 k_{drai,\, perch} =10^{-5} \sin (\beta )\left(\frac{\sum _{i=N_{perch} }^{i=N_{frost} }\Theta_{ice,i} k_{sat} \left[z_{i} \right]\Delta z_{i} }{\sum _{i=N_{perch} }^{i=N_{frost} }\Delta z_{i} } \right) -where :math:`\Theta_{ice}` is an ice impedance factor, :math:`\beta` -is the mean grid cell topographic slope in -radians, :math:`z_{frost}` \ is the depth to the frost table, and -:math:`z_{\nabla ,perch}` is the depth to the perched saturated zone. -The frost table :math:`z_{frost}` is defined as the shallowest frozen -layer having an unfrozen layer above it, while the perched water table -:math:`z_{\nabla ,perch}` is defined as the depth at which the -volumetric water content drops below a specified threshold. The default -threshold is set to 0.9. Drainage from the perched saturated zone -:math:`q_{drai,perch}` is removed from layers :math:`N_{perch}` -through :math:`N_{frost}` , which are the layers containing -:math:`z_{\nabla ,perch}` and, :math:`z_{frost}` \ respectively. +where :math:`\Theta_{ice}` is an ice impedance factor, +:math:`\beta` is the mean grid cell topographic slope in radians, +:math:`z_{frost}` \ is the depth to the frost table, and +:math:`z_{\nabla,perch}` is the depth to the perched saturated zone. The frost table :math:`z_{frost}` is defined as the shallowest frozen layer having an unfrozen layer above it, while the perched water table :math:`z_{\nabla,perch}` is defined as the depth at which the volumetric water content drops below a specified threshold. The default threshold is set to 0.9. Drainage from the perched saturated zone :math:`q_{drai,perch}` is removed from layers :math:`N_{perch}` through :math:`N_{frost}`, which are the layers containing :math:`z_{\nabla,perch}` and, :math:`z_{frost}` \ respectively. .. _Lateral Sub-surface Runoff: Lateral Sub-surface Runoff --------------------------------------- -Lateral sub-surface runoff occurs when saturated soil moisture conditions -exist within the soil column. Sub-surface runoff is +Lateral sub-surface runoff occurs when saturated soil moisture conditions exist within the soil column. Sub-surface runoff is .. math:: :label: 7.168 - q_{drai} = \Theta_{ice} K_{baseflow} tan \left( \beta \right) + q_{drai} = \Theta_{ice} K_{baseflow} tan \left( \beta \right) \Delta z_{sat}^{N_{baseflow}} \ , -where :math:`K_{baseflow}` is a calibration parameter, :math:`\beta` is the -topographic slope, the exponent :math:`N_{baseflow}` = 1, and :math:`\Delta z_{sat}` -is the thickness of the saturated portion of the soil column. +where :math:`K_{baseflow}` is a calibration parameter, :math:`\beta` is the topographic slope, the exponent :math:`N_{baseflow}` = 1, and :math:`\Delta z_{sat}` is the thickness of the saturated portion of the soil column. -The saturated thickness is +The saturated thickness is .. math:: :label: 7.1681 - \Delta z_{sat} = z_{bedrock} - z_{\nabla}, + \Delta z_{sat} = z_{bedrock} - z_{\nabla}, -where the water table :math:`z_{\nabla}` is determined by finding the -first soil layer above the bedrock depth (section :numref:`Depth to Bedrock`) -in which the volumetric water content drops below a specified threshold. -The default threshold is set to 0.9. +where the water table :math:`z_{\nabla}` is determined by finding the first soil layer above the bedrock depth (section :numref:`Depth to Bedrock`) in which the volumetric water content drops below a specified threshold. The default threshold is set to 0.9. -The specific yield, :math:`S_{y}` , which depends on the soil -properties and the water table location, is derived by taking the -difference between two equilibrium soil moisture profiles whose water -tables differ by an infinitesimal amount +The specific yield, :math:`S_{y}`, which depends on the soil properties and the water table location, is derived by taking the difference between two equilibrium soil moisture profiles whose water tables differ by an infinitesimal amount .. math:: :label: 7.174 S_{y} =\theta_{sat} \left(1-\left(1+\frac{z_{\nabla } }{\Psi _{sat} } \right)^{\frac{-1}{B} } \right) -where B is the Clapp-Hornberger exponent. Because :math:`S_{y}` is a -function of the soil properties, it results in water table dynamics that -are consistent with the soil water fluxes described in section :numref:`Soil Water`. - -After the above calculations, two numerical adjustments are implemented -to keep the liquid water content of each soil layer -(:math:`w_{liq,\, i}` ) within physical constraints of -:math:`w_{liq}^{\min } \le w_{liq,\, i} \le \left(\theta_{sat,\, i} -\theta_{ice,\, i} \right)\Delta z_{i}` -where :math:`w_{liq}^{\min } =0.01` (mm). First, beginning with the -bottom soil layer :math:`i=N_{levsoi}` , any excess liquid water in each -soil layer -(:math:`w_{liq,\, i}^{excess} =w_{liq,\, i} -\left(\theta_{sat,\, i} -\theta_{ice,\, i} \right)\Delta z_{i} \ge 0`) -is successively added to the layer above. Any excess liquid water that -remains after saturating the entire soil column (plus a maximum surface -ponding depth :math:`w_{liq}^{pond} =10` kg m\ :sup:`-2`), is -added to drainage :math:`q_{drai}` . Second, to prevent negative -:math:`w_{liq,\, i}` , each layer is successively brought up to -:math:`w_{liq,\, i} =w_{liq}^{\min }` by taking the required amount of -water from the layer below. If this results in -:math:`w_{liq,\, N_{levsoi} } ` for CLM5.0). @@ -67,7 +59,7 @@ P. O. Box 3000, Boulder, Colorado 80307-300 - :numref:`Figure Biological nitrogen fixation` Biological nitrogen fixation as a function of annual net primary production. -- :numref:`Figure Methane Schematic` Schematic representation of biological and physical processes integrated in CLM that affect the net CH4 surface flux. +- :numref:`Figure Methane Schematic` Schematic representation of biological and physical processes integrated in CLM that affect the net CH4 surface flux. - :numref:`Figure Schematic of land cover change` Schematic of land cover change impacts on CLM carbon pools and fluxes. @@ -141,9 +133,9 @@ P. O. Box 3000, Boulder, Colorado 80307-300 - :numref:`Table Crop plant functional types` Crop plant functional types (PFTs). -- :numref:`Table Crop phenology parameters` Crop phenology and morphology parameters. +- :numref:`Table Crop phenology parameters` Crop phenology and morphology parameters. -- :numref:`Table Crop allocation parameters` Crop allocation parameters. +- :numref:`Table Crop allocation parameters` Crop allocation parameters. - :numref:`Table Dust Mass fraction` Mass fraction m\ :sub:`i` , mass median diameter :sub:`v, i` , and geometric standard deviation :sub:`g, i` , per dust source mode i @@ -151,33 +143,14 @@ P. O. Box 3000, Boulder, Colorado 80307-300 **ACKNOWLEDGEMENTS** -The authors would like to acknowledge the substantial contributions of -the following members of the Land Model and Biogeochemistry Working -Groups to the development of the Community Land Model since its -inception in 1996: Benjamin Andre, Ian Baker, Michael Barlage, Mike -Bosilovich, Marcia Branstetter, Tony Craig, Aiguo Dai, Yongjiu Dai, Mark -Decker, Scott Denning, Robert Dickinson, Paul Dirmeyer, Jared Entin, Jay -Famiglietti, Johannes Feddema, Mark Flanner, Jon Foley, Andrew Fox, Inez -Fung, David Gochis, Alex Guenther, Tim Hoar, Forrest Hoffman, Paul -Houser, Trish Jackson, Brian Kauffman, Silvia Kloster, Natalie Mahowald, -Jiafu Mao, Lei Meng, Sheri Michelson, Guo-Yue Niu, Adam Phillips, Taotao -Qian, Jon Radakovich, James Randerson, Nan Rosenbloom, Steve Running, -Koichi Sakaguchi, Adam Schlosser, Andrew Slater, Reto Stöckli, Ying Sun, Quinn -Thomas, Peter Thornton, Mariana Vertenstein, Nicholas Viovy, Aihui Wang, Guiling Wang, -Zong-Liang Yang, Charlie Zender, Xiaodong Zeng, and Xubin Zeng. +The authors would like to acknowledge the substantial contributions of the following members of the Land Model and Biogeochemistry Working Groups to the development of the Community Land Model since its inception in 1996: Benjamin Andre, Ian Baker, Michael Barlage, Mike Bosilovich, Marcia Branstetter, Tony Craig, Aiguo Dai, Yongjiu Dai, Mark Decker, Scott Denning, Robert Dickinson, Paul Dirmeyer, Jared Entin, Jay Famiglietti, Johannes Feddema, Mark Flanner, Jon Foley, Andrew Fox, Inez Fung, David Gochis, Alex Guenther, Tim Hoar, Forrest Hoffman, Paul Houser, Trish Jackson, Brian Kauffman, Silvia Kloster, Natalie Mahowald, Jiafu Mao, Lei Meng, Sheri Michelson, Guo-Yue Niu, Adam Phillips, Taotao Qian, Jon Radakovich, James Randerson, Nan Rosenbloom, Steve Running, Koichi Sakaguchi, Adam Schlosser, Andrew Slater, Reto Stöckli, Ying Sun, Quinn Thomas, Peter Thornton, Mariana Vertenstein, Nicholas Viovy, Aihui Wang, Guiling Wang, Zong-Liang Yang, Charlie Zender, Xiaodong Zeng, and Xubin Zeng. .. _rst_Introduction: Introduction ================= -The purpose of this document is to fully describe the biogeophysical and -biogeochemical parameterizations and numerical implementation of version -5.0 of the Community Land Model (CLM5.0). Scientific justification and -evaluation of these parameterizations can be found in the referenced -scientific papers (:ref:`rst_References`). This document and the CLM5.0 -User’s Guide together provide the user with the scientific description -and operating instructions for CLM. +The purpose of this document is to fully describe the biogeophysical and biogeochemical parameterizations and numerical implementation of version 5.0 of the Community Land Model (CLM5.0). Scientific justification and evaluation of these parameterizations can be found in the referenced scientific papers (:ref:`rst_References`). This document and the CLM5.0 User's Guide together provide the user with the scientific description and operating instructions for CLM. Model History --------------- @@ -185,411 +158,104 @@ Model History Inception of CLM ^^^^^^^^^^^^^^^^^^^^^^ -The early development of the Community Land Model can be described as -the merging of a community-developed land model focusing on -biogeophysics and a concurrent effort at NCAR to expand the NCAR Land -Surface Model (NCAR LSM, :ref:`Bonan 1996`) to include the carbon cycle, -vegetation dynamics, and river routing. The concept of a -community-developed land component of the Community Climate System Model -(CCSM) was initially proposed at the CCSM Land Model Working Group -(LMWG) meeting in February 1996. Initial software specifications and -development focused on evaluating the best features of three existing -land models: the NCAR LSM (:ref:`Bonan 1996, 1998`) used in the Community -Climate Model (CCM3) and the initial version of CCSM; the Institute of -Atmospheric Physics, Chinese Academy of Sciences land model (IAP94) (:ref:`Dai -and Zeng 1997`); and the Biosphere-Atmosphere Transfer Scheme (BATS) -(:ref:`Dickinson et al. 1993`) used with CCM2. A scientific steering committee -was formed to review the initial specifications of the design provided -by Robert Dickinson, Gordon Bonan, Xubin Zeng, and Yongjiu Dai and to -facilitate further development. Steering committee members were selected -so as to provide guidance and expertise in disciplines not generally -well-represented in land surface models (e.g., carbon cycling, -ecological modeling, hydrology, and river routing) and included -scientists from NCAR, the university community, and government -laboratories (R. Dickinson, G. Bonan, X. Zeng, Paul Dirmeyer, Jay -Famiglietti, Jon Foley, and Paul Houser). - -The specifications for the new model, designated the Common Land Model, -were discussed and agreed upon at the June 1998 CCSM Workshop LMWG -meeting. An initial code was developed by Y. Dai and was examined in -March 1999 by Mike Bosilovich, P. Dirmeyer, and P. Houser. At this point -an extensive period of code testing was initiated. Keith Oleson, Y. Dai, -Adam Schlosser, and P. Houser presented preliminary results of offline -1-dimensional testing at the June 1999 CCSM Workshop LMWG meeting. -Results from more extensive offline testing at plot, catchment, and -large scale (up to global) were presented by Y. Dai, A. Schlosser, K. -Oleson, M. Bosilovich, Zong-Liang Yang, Ian Baker, P. Houser, and P. -Dirmeyer at the LMWG meeting hosted by COLA (Center for -Ocean-Land-Atmosphere Studies) in November 1999. Field data used for -validation included sites adopted by the Project for Intercomparison of -Land-surface Parameterization Schemes (:ref:`Henderson-Sellers et al. 1993`) -(Cabauw, Valdai, Red-Arkansas river basin) and others [FIFE (:ref:`Sellers et -al. 1988`), BOREAS :ref:`(Sellers et al. 1995`), HAPEX-MOBILHY (:ref:`André et al. -1986`), ABRACOS (:ref:`Gash et al. 1996`), Sonoran Desert (:ref:`Unland et al. 1996`), -GSWP (:ref:`Dirmeyer et al. 1999`)]. Y. Dai also presented results from a -preliminary coupling of the Common Land Model to CCM3, indicating that -the land model could be successfully coupled to a climate model. - -Results of coupled simulations using CCM3 and the Common Land Model were -presented by X. Zeng at the June 2000 CCSM Workshop LMWG meeting. -Comparisons with the NCAR LSM and observations indicated major -improvements to the seasonality of runoff, substantial reduction of a -summer cold bias, and snow depth. Some deficiencies related to runoff -and albedo were noted, however, that were subsequently addressed. Z.-L. -Yang and I. Baker demonstrated improvements in the simulation of snow -and soil temperatures. Sam Levis reported on efforts to incorporate a -river routing model to deliver runoff to the ocean model in CCSM. Soon -after the workshop, the code was delivered to NCAR for implementation -into the CCSM framework. Documentation for the Common Land Model is -provided by :ref:`Dai et al. (2001)` while the coupling with CCM3 is described -in :ref:`Zeng et al. (2002)`. The model was introduced to the modeling -community in :ref:`Dai et al. (2003)`. +The early development of the Community Land Model can be described as the merging of a community-developed land model focusing on biogeophysics and a concurrent effort at NCAR to expand the NCAR Land Surface Model (NCAR LSM, :ref:`Bonan 1996`) to include the carbon cycle, vegetation dynamics, and river routing. The concept of a community-developed land component of the Community Climate System Model (CCSM) was initially proposed at the CCSM Land Model Working Group (LMWG) meeting in February 1996. Initial software specifications and development focused on evaluating the best features of three existing land models: the NCAR LSM (:ref:`Bonan 1996, 1998`) used in the Community Climate Model (CCM3) and the initial version of CCSM; the Institute of Atmospheric Physics, Chinese Academy of Sciences land model (IAP94) (:ref:`Dai and Zeng 1997`); and the Biosphere-Atmosphere Transfer Scheme (BATS) (:ref:`Dickinson et al. 1993`) used with CCM2. A scientific steering committee was formed to review the initial specifications of the design provided by Robert Dickinson, Gordon Bonan, Xubin Zeng, and Yongjiu Dai and to facilitate further development. Steering committee members were selected so as to provide guidance and expertise in disciplines not generally well-represented in land surface models (e.g., carbon cycling, ecological modeling, hydrology, and river routing) and included scientists from NCAR, the university community, and government laboratories (R. Dickinson, G. Bonan, X. Zeng, Paul Dirmeyer, Jay Famiglietti, Jon Foley, and Paul Houser). + +The specifications for the new model, designated the Common Land Model, were discussed and agreed upon at the June 1998 CCSM Workshop LMWG meeting. An initial code was developed by Y. Dai and was examined in March 1999 by Mike Bosilovich, P. Dirmeyer, and P. Houser. At this point an extensive period of code testing was initiated. Keith Oleson, Y. Dai, Adam Schlosser, and P. Houser presented preliminary results of offline 1-dimensional testing at the June 1999 CCSM Workshop LMWG meeting. Results from more extensive offline testing at plot, catchment, and large scale (up to global) were presented by Y. Dai, A. Schlosser, K. Oleson, M. Bosilovich, Zong-Liang Yang, Ian Baker, P. Houser, and P. Dirmeyer at the LMWG meeting hosted by COLA (Center for Ocean-Land-Atmosphere Studies) in November 1999. Field data used for validation included sites adopted by the Project for Intercomparison of Land-surface Parameterization Schemes (:ref:`Henderson-Sellers et al. 1993`) (Cabauw, Valdai, Red-Arkansas river basin) and others [FIFE (:ref:`Sellers et al. 1988`), BOREAS :ref:`(Sellers et al. 1995`), HAPEX-MOBILHY (:ref:`André et al. 1986`), ABRACOS (:ref:`Gash et al. 1996`), Sonoran Desert (:ref:`Unland et al. 1996`), GSWP (:ref:`Dirmeyer et al. 1999`)]. Y. Dai also presented results from a preliminary coupling of the Common Land Model to CCM3, indicating that the land model could be successfully coupled to a climate model. + +Results of coupled simulations using CCM3 and the Common Land Model were presented by X. Zeng at the June 2000 CCSM Workshop LMWG meeting. Comparisons with the NCAR LSM and observations indicated major improvements to the seasonality of runoff, substantial reduction of a summer cold bias, and snow depth. Some deficiencies related to runoff and albedo were noted, however, that were subsequently addressed. Z.-L. Yang and I. Baker demonstrated improvements in the simulation of snow and soil temperatures. Sam Levis reported on efforts to incorporate a river routing model to deliver runoff to the ocean model in CCSM. Soon after the workshop, the code was delivered to NCAR for implementation into the CCSM framework. Documentation for the Common Land Model is provided by :ref:`Dai et al. (2001)` while the coupling with CCM3 is described in :ref:`Zeng et al. (2002)`. The model was introduced to the modeling community in :ref:`Dai et al. (2003)`. CLM2 ^^^^^^^^^^ -Concurrent with the development of the Common Land Model, the NCAR LSM -was undergoing further development at NCAR in the areas of carbon -cycling, vegetation dynamics, and river routing. The preservation of -these advancements necessitated several modifications to the Common Land -Model. The biome-type land cover classification scheme was replaced with -a plant functional type (PFT) representation with the specification of -PFTs and leaf area index from satellite data (:ref:`Oleson and Bonan 2000`; -:ref:`Bonan et al. 2002a, b`). This also required modifications to -parameterizations for vegetation albedo and vertical burying of -vegetation by snow. Changes were made to canopy scaling, leaf -physiology, and soil water limitations on photosynthesis to resolve -deficiencies indicated by the coupling to a dynamic vegetation model. -Vertical heterogeneity in soil texture was implemented to improve -coupling with a dust emission model. A river routing model was -incorporated to improve the fresh water balance over oceans. Numerous -modest changes were made to the parameterizations to conform to the -strict energy and water balance requirements of CCSM. Further -substantial software development was also required to meet coding -standards. The resulting model was adopted in May 2002 as the Community -Land Model (CLM2) for use with the Community Atmosphere Model (CAM2, the -successor to CCM3) and version 2 of the Community Climate System Model -(CCSM2). - -K. Oleson reported on initial results from a coupling of CCM3 with CLM2 -at the June 2001 CCSM Workshop LMWG meeting. Generally, the CLM2 -preserved most of the improvements seen in the Common Land Model, -particularly with respect to surface air temperature, runoff, and snow. -These simulations are documented in :ref:`Bonan et al. (2002a)`. Further small -improvements to the biogeophysical parameterizations, ongoing software -development, and extensive analysis and validation within CAM2 and CCSM2 -culminated in the release of CLM2 to the community in May 2002. - -Following this release, Peter Thornton implemented changes to the model -structure required to represent carbon and nitrogen cycling in the -model. This involved changing data structures from a single vector of -spatially independent sub-grid patches to one that recognizes three -hierarchical scales within a model grid cell: land unit, snow/soil -column, and PFT. Furthermore, as an option, the model can be configured -so that PFTs can share a single soil column and thus “compete” for -water. This version of the model (CLM2.1) was released to the community -in February 2003. CLM2.1, without the compete option turned on, produced -only round off level changes when compared to CLM2. +Concurrent with the development of the Common Land Model, the NCAR LSM was undergoing further development at NCAR in the areas of carbon cycling, vegetation dynamics, and river routing. The preservation of these advancements necessitated several modifications to the Common Land Model. The biome-type land cover classification scheme was replaced with a plant functional type (PFT) representation with the specification of PFTs and leaf area index from satellite data (:ref:`Oleson and Bonan 2000`; :ref:`Bonan et al. 2002a, b`). This also required modifications to parameterizations for vegetation albedo and vertical burying of vegetation by snow. Changes were made to canopy scaling, leaf physiology, and soil water limitations on photosynthesis to resolve deficiencies indicated by the coupling to a dynamic vegetation model. Vertical heterogeneity in soil texture was implemented to improve coupling with a dust emission model. A river routing model was incorporated to improve the fresh water balance over oceans. Numerous modest changes were made to the parameterizations to conform to the strict energy and water balance requirements of CCSM. Further substantial software development was also required to meet coding standards. The resulting model was adopted in May 2002 as the Community Land Model (CLM2) for use with the Community Atmosphere Model (CAM2, the successor to CCM3) and version 2 of the Community Climate System Model (CCSM2). + +K. Oleson reported on initial results from a coupling of CCM3 with CLM2 at the June 2001 CCSM Workshop LMWG meeting. Generally, the CLM2 preserved most of the improvements seen in the Common Land Model, particularly with respect to surface air temperature, runoff, and snow. These simulations are documented in :ref:`Bonan et al. (2002a)`. Further small improvements to the biogeophysical parameterizations, ongoing software development, and extensive analysis and validation within CAM2 and CCSM2 culminated in the release of CLM2 to the community in May 2002. + +Following this release, Peter Thornton implemented changes to the model structure required to represent carbon and nitrogen cycling in the model. This involved changing data structures from a single vector of spatially independent sub-grid patches to one that recognizes three hierarchical scales within a model grid cell: land unit, snow/soil column, and PFT. Furthermore, as an option, the model can be configured so that PFTs can share a single soil column and thus "compete" for water. This version of the model (CLM2.1) was released to the community in February 2003. CLM2.1, without the compete option turned on, produced only round off level changes when compared to CLM2. CLM3 ^^^^^^^^^^ -CLM3 implemented further software improvements related to performance -and model output, a re-writing of the code to support vector-based -computational platforms, and improvements in biogeophysical -parameterizations to correct deficiencies in the coupled model climate. -Of these parameterization improvements, two were shown to have a -noticeable impact on simulated climate. A variable aerodynamic -resistance for heat/moisture transfer from ground to canopy air that -depends on canopy density was implemented. This reduced unrealistically -high surface temperatures in semi-arid regions. The second improvement -added stability corrections to the diagnostic 2-m air temperature -calculation which reduced biases in this temperature. Competition -between PFTs for water, in which PFTs share a single soil column, is the -default mode of operation in this model version. CLM3 was released to -the community in June 2004. :ref:`Dickinson et al. (2006)` -describe the climate statistics of CLM3 when coupled to CCSM3.0. -:ref:`Hack et al. (2006)` provide an analysis of selected -features of the land hydrological cycle. -:ref:`Lawrence et al. (2007)` examine the impact of -changes in CLM3 -hydrological parameterizations on partitioning of evapotranspiration -(ET) and its effect on the timescales of ET response to precipitation -events, interseasonal soil moisture storage, soil moisture memory, and -land-atmosphere coupling. :ref:`Qian et al. (2006)` evaluate CLM3’s performance -in simulating soil moisture content, runoff, and river discharge when -forced by observed precipitation, temperature and other atmospheric -data. +CLM3 implemented further software improvements related to performance and model output, a re-writing of the code to support vector-based computational platforms, and improvements in biogeophysical parameterizations to correct deficiencies in the coupled model climate. Of these parameterization improvements, two were shown to have a noticeable impact on simulated climate. A variable aerodynamic resistance for heat/moisture transfer from ground to canopy air that depends on canopy density was implemented. This reduced unrealistically high surface temperatures in semi-arid regions. The second improvement added stability corrections to the diagnostic 2-m air temperature calculation which reduced biases in this temperature. Competition between PFTs for water, in which PFTs share a single soil column, is the default mode of operation in this model version. CLM3 was released to the community in June 2004. :ref:`Dickinson et al. (2006)` describe the climate statistics of CLM3 when coupled to CCSM3.0. :ref:`Hack et al. (2006)` provide an analysis of selected features of the land hydrological cycle. :ref:`Lawrence et al. (2007)` examine the impact of changes in CLM3 hydrological parameterizations on partitioning of evapotranspiration (ET) and its effect on the timescales of ET response to precipitation events, interseasonal soil moisture storage, soil moisture memory, and land-atmosphere coupling. :ref:`Qian et al. (2006)` evaluate CLM3's performance in simulating soil moisture content, runoff, and river discharge when forced by observed precipitation, temperature and other atmospheric data. CLM3.5 ^^^^^^^^^^^^ -Although the simulation of land surface climate by CLM3 was in many ways -adequate, most of the unsatisfactory aspects of the simulated climate -noted by the above studies could be traced directly to deficiencies in -simulation of the hydrological cycle. In 2004, a project was initiated -to improve the hydrology in CLM3 as part of the development of CLM -version 3.5. A selected set of promising approaches to alleviating the -hydrologic biases in CLM3 were tested and implemented. These included -new surface datasets based on Moderate Resolution Imaging -Spectroradiometer (MODIS) products, new parameterizations for canopy -integration, canopy interception, frozen soil, soil water availability, -and soil evaporation, a TOPMODEL-based model for surface and subsurface -runoff, a groundwater model for determining water table depth, and the -introduction of a factor to simulate nitrogen limitation on plant -productivity. :ref:`Oleson et al. (2008a)` show that CLM3.5 exhibits -significant improvements over CLM3 in its partitioning of global ET -which result in wetter soils, less plant water stress, increased -transpiration and photosynthesis, and an improved annual cycle of total -water storage. Phase and amplitude of the runoff annual cycle is -generally improved. Dramatic improvements in vegetation biogeography -result when CLM3.5 is coupled to a dynamic global vegetation model. -:ref:`Stöckli et al. (2008)` examine the performance of CLM3.5 at local scales -by making use of a network of long-term ground-based ecosystem -observations [FLUXNET (:ref:`Baldocchi et al. 2001`)]. Data from 15 FLUXNET -sites were used to demonstrate significantly improved soil hydrology and -energy partitioning in CLM3.5. CLM3.5 was released to the community in -May, 2007. +Although the simulation of land surface climate by CLM3 was in many ways adequate, most of the unsatisfactory aspects of the simulated climate noted by the above studies could be traced directly to deficiencies in simulation of the hydrological cycle. In 2004, a project was initiated to improve the hydrology in CLM3 as part of the development of CLM version 3.5. A selected set of promising approaches to alleviating the hydrologic biases in CLM3 were tested and implemented. These included new surface datasets based on Moderate Resolution Imaging Spectroradiometer (MODIS) products, new parameterizations for canopy integration, canopy interception, frozen soil, soil water availability, and soil evaporation, a TOPMODEL-based model for surface and subsurface runoff, a groundwater model for determining water table depth, and the introduction of a factor to simulate nitrogen limitation on plant productivity. :ref:`Oleson et al. (2008a)` show that CLM3.5 exhibits significant improvements over CLM3 in its partitioning of global ET which result in wetter soils, less plant water stress, increased transpiration and photosynthesis, and an improved annual cycle of total water storage. Phase and amplitude of the runoff annual cycle is generally improved. Dramatic improvements in vegetation biogeography result when CLM3.5 is coupled to a dynamic global vegetation model. :ref:`Stöckli et al. (2008)` examine the performance of CLM3.5 at local scales by making use of a network of long-term ground-based ecosystem observations [FLUXNET (:ref:`Baldocchi et al. 2001`)]. Data from 15 FLUXNET sites were used to demonstrate significantly improved soil hydrology and energy partitioning in CLM3.5. CLM3.5 was released to the community in May, 2007. CLM4 ^^^^^^^^^^ -The motivation for the next version of the model, CLM4, was to -incorporate several recent scientific advances in the understanding and -representation of land surface processes, expand model capabilities, and -improve surface and atmospheric forcing datasets (:ref:`Lawrence et al. 2011`). -Included in the first category are more sophisticated representations of -soil hydrology and snow processes. In particular, new treatments of soil -column-groundwater interactions, soil evaporation, aerodynamic -parameters for sparse/dense canopies, vertical burial of vegetation by -snow, snow cover fraction and aging, black carbon and dust deposition, -and vertical distribution of solar energy for snow were implemented. -Major new capabilities in the model include a representation of the -carbon-nitrogen cycle (CLM4CN, see next paragraph for additional -information), the ability to model land cover change in a transient -mode, inclusion of organic soil and deep soil into the existing mineral -soil treatment to enable more realistic modeling of permafrost, an urban -canyon model to contrast rural and urban energy balance and climate -(CLMU), and an updated biogenic volatile organic compounds (BVOC) model. -Other modifications of note include refinement of the global PFT, -wetland, and lake distributions, more realistic optical properties for -grasslands and croplands, and an improved diurnal cycle and spectral -distribution of incoming solar radiation to force the model in land-only -mode. - -Many of the ideas incorporated into the carbon and nitrogen cycle -component of CLM4 derive from the earlier development of the land-only -ecosystem process model Biome-BGC (Biome BioGeochemical Cycles), -originating at the Numerical Terradynamic Simulation Group (NTSG) at the -University of Montana, under the guidance of Prof. Steven Running. -Biome-BGC itself is an extension of an earlier model, Forest-BGC -(:ref:`Running and Coughlan, 1988`; :ref:`Running and Gower, 1991`), which -simulates water, carbon, and, to a limited extent, nitrogen fluxes for -forest ecosystems. Forest-BGC was designed to be driven by remote -sensing inputs of vegetation structure, and so used a diagnostic -(prescribed) leaf area index, or, in the case of the dynamic allocation -version of the model (:ref:`Running and Gower, 1991`), prescribed maximum -leaf area index. - -Biome-BGC expanded on the Forest-BGC logic by introducing a more -mechanistic calculation of leaf and canopy scale photosynthesis (:ref:`Hunt -and Running, 1992`), and extending the physiological parameterizations -to include multiple woody and non-woody vegetation types (:ref:`Hunt et al. -1996`; :ref:`Running and Hunt, 1993`). Later versions of Biome-BGC introduced -more mechanistic descriptions of belowground carbon and nitrogen cycles, -nitrogen controls on photosynthesis and decomposition, sunlit and shaded -canopies, vertical gradient in leaf morphology, and explicit treatment -of fire and harvest disturbance and regrowth dynamics (:ref:`Kimball et al. -1997`; :ref:`Thornton, 1998`; :ref:`Thornton et al. 2002`; :ref:`White et al. 2000`). -Biome-BGC version 4.1.2 (:ref:`Thornton et al. 2002`) provided a point of -departure for integrating new biogeochemistry components into CLM4. - -CLM4 was released to the community in June, 2010 along with the -Community Climate System Model version 4 (CCSM4). CLM4 is used in CCSM4, -CESM1, CESM1.1, and remains available as the default land component -model option for coupled simulations in CESM1.2. +The motivation for the next version of the model, CLM4, was to incorporate several recent scientific advances in the understanding and representation of land surface processes, expand model capabilities, and improve surface and atmospheric forcing datasets (:ref:`Lawrence et al. 2011`). Included in the first category are more sophisticated representations of soil hydrology and snow processes. In particular, new treatments of soil column-groundwater interactions, soil evaporation, aerodynamic parameters for sparse/dense canopies, vertical burial of vegetation by snow, snow cover fraction and aging, black carbon and dust deposition, and vertical distribution of solar energy for snow were implemented. Major new capabilities in the model include a representation of the carbon-nitrogen cycle (CLM4CN, see next paragraph for additional information), the ability to model land cover change in a transient mode, inclusion of organic soil and deep soil into the existing mineral soil treatment to enable more realistic modeling of permafrost, an urban canyon model to contrast rural and urban energy balance and climate (CLMU), and an updated biogenic volatile organic compounds (BVOC) model. Other modifications of note include refinement of the global PFT, wetland, and lake distributions, more realistic optical properties for grasslands and croplands, and an improved diurnal cycle and spectral distribution of incoming solar radiation to force the model in land-only mode. + +Many of the ideas incorporated into the carbon and nitrogen cycle component of CLM4 derive from the earlier development of the land-only ecosystem process model Biome-BGC (Biome BioGeochemical Cycles), originating at the Numerical Terradynamic Simulation Group (NTSG) at the University of Montana, under the guidance of Prof. Steven Running. Biome-BGC itself is an extension of an earlier model, Forest-BGC (:ref:`Running and Coughlan, 1988`; :ref:`Running and Gower, 1991`), which simulates water, carbon, and, to a limited extent, nitrogen fluxes for forest ecosystems. Forest-BGC was designed to be driven by remote sensing inputs of vegetation structure, and so used a diagnostic (prescribed) leaf area index, or, in the case of the dynamic allocation version of the model (:ref:`Running and Gower, 1991`), prescribed maximum leaf area index. + +Biome-BGC expanded on the Forest-BGC logic by introducing a more mechanistic calculation of leaf and canopy scale photosynthesis (:ref:`Hunt and Running, 1992`), and extending the physiological parameterizations to include multiple woody and non-woody vegetation types (:ref:`Hunt et al. 1996`; :ref:`Running and Hunt, 1993`). Later versions of Biome-BGC introduced more mechanistic descriptions of belowground carbon and nitrogen cycles, nitrogen controls on photosynthesis and decomposition, sunlit and shaded canopies, vertical gradient in leaf morphology, and explicit treatment of fire and harvest disturbance and regrowth dynamics (:ref:`Kimball et al. 1997`; :ref:`Thornton, 1998`; :ref:`Thornton et al. 2002`; :ref:`White et al. 2000`). Biome-BGC version 4.1.2 (:ref:`Thornton et al. 2002`) provided a point of departure for integrating new biogeochemistry components into CLM4. + +CLM4 was released to the community in June, 2010 along with the Community Climate System Model version 4 (CCSM4). CLM4 is used in CCSM4, CESM1, CESM1.1, and remains available as the default land component model option for coupled simulations in CESM1.2. CLM4.5 ^^^^^^^^^^^^ -The motivations for the development of CLM4.5 were similar to those for CLM4: -incorporate several recent scientific advances in the understanding and -representation of land surface processes, expand model capabilities, and -improve surface and atmospheric forcing datasets. - -Specifically, several parameterizations were revised to reflect new -scientific understanding and in an attempt to reduce biases identified -in CLM4 simulations including low soil carbon stocks especially in the -Arctic, excessive tropical GPP and unrealistically low Arctic GPP, a dry -soil bias in Arctic soils, unrealistically high LAI in the tropics, a -transient 20\ :math:`{}^{th}` century carbon response that was -inconsistent with observational estimates, and several other more minor -problems or biases. - -The main modifications include updates to canopy processes including a -revised canopy radiation scheme and canopy scaling of leaf processes, -co-limitations on photosynthesis, revisions to photosynthetic parameters -(:ref:`Bonan et al. 2011`), temperature acclimation of photosynthesis, and -improved stability of the iterative solution in the photosynthesis and -stomatal conductance model (:ref:`Sun et al. 2012`). Hydrology updates included -modifications such that hydraulic properties of frozen soils are -determined by liquid water content only rather than total water content -and the introduction of an ice impedance function, and other corrections -that increase the consistency between soil water state and water table -position and allow for a perched water table above icy permafrost ground -(:ref:`Swenson et al. 2012`). A new snow cover fraction parameterization is -incorporated that reflects the hysteresis in fractional snow cover for a -given snow depth between accumulation and melt phases (:ref:`Swenson and -Lawrence, 2012`). The lake model in CLM4 was replaced with a completely -revised and more realistic lake model (:ref:`Subin et al. 2012a`). A surface -water store was introduced, replacing the wetland land unit and -permitting prognostic wetland distribution modeling. The surface -energy fluxes are calculated separately (:ref:`Swenson and Lawrence, 2012`) for -snow-covered, water-covered, and snow/water-free portions of vegetated -and crop land units, and snow-covered and snow-free portions of glacier -land units. Globally constant river flow velocity is replaced with -variable flow velocity based on mean grid cell slope. A vertically -resolved soil biogeochemistry scheme is introduced with base -decomposition rates modified by soil temperature, water, and oxygen -limitations and also including vertical mixing of soil carbon and -nitrogen due to bioturbation, cryoturbation, and diffusion (:ref:`Koven et al. -2013`). The litter and soil carbon and nitrogen pool structure as well as -nitrification and denitrification that were modified based on the Century -model. Biological fixation was revised to distribute fixation more -realistically over the year (:ref:`Koven et al. 2013`). The fire model was -replaced with a model that includes representations of natural and -anthropogenic triggers and suppression as well as agricultural, -deforestation, and peat fires (:ref:`Li et al. 2012a,b`; :ref:`Li et al. 2013a`). The -biogenic volatile organic compounds model is updated to MEGAN2.1 -(:ref:`Guenther et al. 2012`). - -Additions to the model include a methane production, oxidation, and -emissions model (:ref:`Riley et al. 2011a`) and an extension of the crop model -to include interactive fertilization, organ pools (:ref:`Drewniak et al. -2013`), and irrigation (:ref:`Sacks et al. 2009`). Elements of the Variable -Infiltration Capacity (VIC) model are included as an alternative -optional runoff generation scheme (:ref:`Li et al. 2011`). There is also an -option to run with a multilayer canopy (:ref:`Bonan et al. 2012`). Multiple -urban density classes, rather than the single dominant urban density -class used in CLM4, are modeled in the urban land unit. Carbon -(:math:`{}^{13}`\ C and :math:`{}^{14}`\ C) isotopes are enabled (:ref:`Koven -et al. 2013`). Minor changes include a switch of the C3 Arctic grass and -shrub phenology from stress deciduous to seasonal deciduous and a change -in the glacier bare ice albedo to better reflect recent estimates. -Finally, the carbon and nitrogen cycle spinup is accelerated and -streamlined with a revised spinup method, though the spinup timescale -remains long. - -Finally, the predominantly low resolution input data for provided with -CLM4 to create CLM4 surface datasets is replaced with newer and higher -resolution input datasets where possible (see section :numref:`Surface Data` -for details). The default meteorological forcing dataset provided with CLM4 -(:ref:`Qian et al. 2006)` is replaced with the 1901-2010 -CRUNCEP forcing dataset (see Chapter :numref:`rst_Land-Only Mode`) for CLM4.5, -though users can also still use the :ref:`Qian et al. (2006)` -dataset or other alternative forcing datasets. - -CLM4.5 was released to the community in June 2013 along with the -Community Earth System Model version 1.2 (CESM1.2). +The motivations for the development of CLM4.5 were similar to those for CLM4: incorporate several recent scientific advances in the understanding and representation of land surface processes, expand model capabilities, and improve surface and atmospheric forcing datasets. + +Specifically, several parameterizations were revised to reflect new scientific understanding and in an attempt to reduce biases identified in CLM4 simulations including low soil carbon stocks especially in the Arctic, excessive tropical GPP and unrealistically low Arctic GPP, a dry soil bias in Arctic soils, unrealistically high LAI in the tropics, a transient 20\ :math:`{}^{th}` century carbon response that was inconsistent with observational estimates, and several other more minor problems or biases. + +The main modifications include updates to canopy processes including a revised canopy radiation scheme and canopy scaling of leaf processes, co-limitations on photosynthesis, revisions to photosynthetic parameters (:ref:`Bonan et al. 2011`), temperature acclimation of photosynthesis, and improved stability of the iterative solution in the photosynthesis and stomatal conductance model (:ref:`Sun et al. 2012`). Hydrology updates included modifications such that hydraulic properties of frozen soils are determined by liquid water content only rather than total water content and the introduction of an ice impedance function, and other corrections that increase the consistency between soil water state and water table position and allow for a perched water table above icy permafrost ground (:ref:`Swenson et al. 2012`). A new snow cover fraction parameterization is incorporated that reflects the hysteresis in fractional snow cover for a given snow depth between accumulation and melt phases (:ref:`Swenson and Lawrence, 2012`). The lake model in CLM4 was replaced with a completely revised and more realistic lake model (:ref:`Subin et al. 2012a`). A surface water store was introduced, replacing the wetland land unit and permitting prognostic wetland distribution modeling. The surface energy fluxes are calculated separately (:ref:`Swenson and Lawrence, 2012`) for snow-covered, water-covered, and snow/water-free portions of vegetated and crop land units, and snow-covered and snow-free portions of glacier land units. Globally constant river flow velocity is replaced with variable flow velocity based on mean grid cell slope. A vertically resolved soil biogeochemistry scheme is introduced with base decomposition rates modified by soil temperature, water, and oxygen limitations and also including vertical mixing of soil carbon and nitrogen due to bioturbation, cryoturbation, and diffusion (:ref:`Koven et al. 2013`). The litter and soil carbon and nitrogen pool structure as well as nitrification and denitrification that were modified based on the Century model. Biological fixation was revised to distribute fixation more realistically over the year (:ref:`Koven et al. 2013`). The fire model was replaced with a model that includes representations of natural and anthropogenic triggers and suppression as well as agricultural, deforestation, and peat fires (:ref:`Li et al. 2012a,b`; :ref:`Li et al. 2013a`). The biogenic volatile organic compounds model is updated to MEGAN2.1 (:ref:`Guenther et al. 2012`). + +Additions to the model include a methane production, oxidation, and emissions model (:ref:`Riley et al. 2011a`) and an extension of the crop model to include interactive fertilization, organ pools (:ref:`Drewniak et al. 2013`), and irrigation (:ref:`Sacks et al. 2009`). Elements of the Variable Infiltration Capacity (VIC) model are included as an alternative optional runoff generation scheme (:ref:`Li et al. 2011`). There is also an option to run with a multilayer canopy (:ref:`Bonan et al. 2012`). Multiple urban density classes, rather than the single dominant urban density class used in CLM4, are modeled in the urban land unit. Carbon (:math:`{}^{13}`\ C and :math:`{}^{14}`\ C) isotopes are enabled (:ref:`Koven et al. 2013`). Minor changes include a switch of the C3 Arctic grass and shrub phenology from stress deciduous to seasonal deciduous and a change in the glacier bare ice albedo to better reflect recent estimates. Finally, the carbon and nitrogen cycle spinup is accelerated and streamlined with a revised spinup method, though the spinup timescale remains long. + +Finally, the predominantly low resolution input data for provided with CLM4 to create CLM4 surface datasets is replaced with newer and higher resolution input datasets where possible (see section :numref:`Surface Data` for details). The default meteorological forcing dataset provided with CLM4 (:ref:`Qian et al. 2006)` is replaced with the 1901-2010 CRUNCEP forcing dataset (see Chapter :numref:`rst_Land-Only Mode`) for CLM4.5, though users can also still use the :ref:`Qian et al. (2006)` dataset or other alternative forcing datasets. + +CLM4.5 was released to the community in June 2013 along with the Community Earth System Model version 1.2 (CESM1.2). CLM5.0 ^^^^^^^^^^^^ -Developments for CLM5.0 build on the progress made in CLM4.5. Most major components of the model have been updated with particularly -notable changes made to soil and plant hydrology, snow density, river modeling, carbon and nitrogen cycling and coupling, -and crop modeling. -Much of the focus of development centered on a -push towards more mechanistic treatment of key processes, in addition to more comprehensive and explicit representation -of land use and land-cover change. Prior versions of CLM included relatively few options for physics parameterizations or structure. -In CLM5, where new parameterizations or model decisions were made, in most cases, the CLM4.5 parameterization was maintained so that users could switch back and forth between different parameterizations via namelist control where appropriate or desirable. Throughout the CLM5 Technical Descpription, in general only the default parameterization for any given process is described. Readers are referred to the CLM4.5 or CLM4 Technical Descriptions for detailed descriptions of non-default parameterizations. - -The hydrology updates include the introduction of a dry surface layer-based soil evaporation resistance parameterization :ref:`(Swenson and Lawrence, 2014)` and a revised canopy interception parameterization. Canopy interception is now divided into liquid and solid phases, with the intercepted snow subject to unloading events due to wind or above-freezing temperatures. The snow-covered fraction of the canopy is used within the canopy radiation and surface albedo calculation. Instead of applying a spatially uniform soil thickness, soil thickness can vary in space :ref:`(Brunke et al. 2016` and :ref:`Swenson and Lawrence, 2015)` and is set to values within a range of 0.4m to 8.5m depth, derived from a spatially explicit soil thickness data product :ref:`(Pelletier et al., 2016)`. The explicit treatment of soil thickness allows for the deprecation of the unconfined aquifer parameterization used in CLM4.5, which is replaced with a zero flux boundary condition and explicit modeling of both the saturated and unsaturated zones. The default model soil layer resolution is increased, especially within the top 3m, to more explicitly represent active layer thickness within the permafrost zone. Rooting profiles were used inconsistently in CLM4.5 with :ref:`Zeng (2001)` profiles used for water and :ref:`Jackson et al. (1996)` profiles used for carbon inputs. For CLM5, the Jackson et al. (1996) rooting profiles are used for both water and carbon. Roots are deepened for the broadleaf evergreen tropical tree and broadleaf deciduous tropical tree types. Finally, an adaptive time-stepping solution to the Richard's equation is introduced, which improves the accuracy and stability of the numerical soil water solution. The River Transport Model (RTM) is replaced with the Model for Scale Adaptive River Transport (MOSART, :ref:`Li et al., 2013b)` in which surface runoff is routed across hillslopes and then discharged along with subsurface runoff into a tributary subnetwork before entering the main channel. - -Several changes are included that are mainly targeted at improving the simulation of surface mass balance over ice -sheets. The fresh snow density parameterization is updated to more realistically capture temperature effects and to additionally account for wind effects on new snow density :ref:`(van Kampenhout et al., 2017)`. The maximum number of snow layers and snow amount is increased from 5 layers and 1m snow water equivalent to 12 layers and 10m snow water equivalent to allow for the formation of firn in regions of persistent snow-cover (e.g., glaciers and ice sheets) :ref:`(van Kampenhout et al., 2017)`. The CISM2 ice sheet model is included for Greenland by default. The ice sheet does not evolve for typical configurations, but ice sheet evolution can be turned on by choosing an appropriate compset. The introduction in CLM5 of the capability to -dynamically adjust landunit weights means that a glacier can initiate, grow, shrink, or disappear during -a simulation when ice evolution is active. That is, there are two-way feedbacks between CLM and CISM. Multiple elevation classes (10 elevation classes by default) and associated temperature, rain/snow partitioning, and downwelling longwave downscaling are used for glacier landunits to account for the strong topographic elevation heterogeneity over glaciers and ice sheets. - -A plant hydraulic stress routine is introduced which explicitly models water transport through the vegetation according to a simple hydraulic framework (Kennedy et al., to be submitted). The water supply equations are used to solve for vegetation water potential forced by transpiration demand and a set of layer-by-layer soil water potentials. Stomatal conductance, therefore, is a function of prognostic leaf water potential. Water stress is calculated as the ratio of attenuated stomatal conductance to maximum stomatal conductance. An emergent feature of the plant hydraulics is soil hydraulic redistribution. In CLM5, maximum stomatal conductance is obtained from the Medlyn conductance model :ref:`(Medlyn et al., 2011)`, rather than the Ball-Berry stomatal conductance model that was utilized in CLM4.5 and prior versions of the model. The Medlyn stomatal conductance model is preferred mainly for it's more realistic behavior at low humidity levels :ref:`(Rogers et al., 2017)`. The stress deciduous vegetation phenology trigger is augmented with a antecedent precipitation requirement :ref:`(Dahlin et al. 2015)`. - -Plant nutrient dynamics are substantially updated to resolve several deficiencies with the CLM4 and CLM4.5 nutrient cycling representation. The Fixation and Update of Nitrogen (FUN) model based on the work of :ref:`Fisher et al. (2010)`, :ref:`Brzostek et al. (2014)`, and :ref:`Shi et al. (2016)` is incorporated. The concept of FUN is that in most cases, N uptake requires the expenditure of energy in the form of carbon, and further, that there are numerous potential sources of N in the environment which a plant may exchange for carbon. The ratio of carbon expended to N acquired is therefore the cost, or exchange rate, of N acquisition. FUN calculates the rate of symbiotic N fixation, with this N passed straight to the plant, not the mineral N pool. Separately, CLM5 also calculates rates of symbiotic (or free living) N fixation as a function of evapotranspiration (:ref:`Cleveland et al. 1999 `), which -is added to the soil inorganic ammonium (NH\ :sub:`4`\ :sup:`+`) pool. The static plant carbon:nitrogen (C:N) ratios utilized in CLM4 and CLM4.5 are replaced with variable plant C:N ratios which -allows plants to adjust their C:N ratio, and therefore their leaf nitrogen content, with the cost of N uptake :ref:`(Ghimire et al. 2016)`. -The implementation of a flexible C:N ratio means that the model no longer relies on instantaneous downregulation -of potential photosynthesis rates based on soil mineral nitrogen availability to represent nutrient limitation. Furthermore, stomatal conductance -is now based on the N-limited photosynthesis rather than on potential photosynthesis. Finally, the Leaf Use of -Nitrogen for Assimilation (LUNA, :ref:`Xu et al., 2012` and :ref:`Ali et al., 2016)` model is incorporated. The LUNA model calculates -photosynthetic capacity based on optimization of the use of leaf nitrogen under different environmental conditions such that -light capture, carboxylation, and respiration are co-limiting. - -CLM5 applies a fixed allocation scheme for woody vegetation. The decision to use a fixed allocation scheme in CLM5, rather than a dynamic NPP-based allocation scheme, as was used in CLM4 and CLM4.5, was driven by the fact that observations indicate that biomass saturates with increasing productivity, in contrast to the behavior in CLM4 and CLM4.5 where biomass continuously increases with increasing productivity (:ref:`Negron-Juarez et al., 2015`). Soil carbon decomposition processes are unchanged in CLM5, but a new metric for apparent soil carbon turnover times (:ref:`Koven et al., 2017 `) suggested parameter changes that produce a weak intrinsic depth limitation on soil carbon turnover rates (rather than the strong depth limitaiton in CLM4.5) and that the thresholds for soil moisture limitation on soil carbon turnover rates in dry soils should be set at a wetter soil moisture level than that used in CLM4.5. - -Representation of human management of the land (agriculture, wood harvest) is augmented in several ways. -The CLM4.5 crop model is extended to operate globally through the addition of rice, sugarcane, -tropical varieties of corn and soybean :ref:`(Badger and Dirmeyer, 2015` and :ref:`Levis et al., 2016)`, -and perennial bioenergy crops :ref:`(Cheng et al., 2019)`. -These crop types are added to the existing temperate corn, temperate soybean, spring wheat, and cotton crop types. -Fertilization rates and irrigation equipped area updated annually based on crop type and geographic region through an input dataset. -The irrigation trigger is updated. Additional minor changes include crop phenological triggers that -vary by latitude for selected crop types, grain C and N is now removed at harvest to a 1-year product pool with -the carbon for the next season's crop seed removed from the grain carbon at harvest. -A fraction of leaf/livestem C and N from bioenergy crops is removed at harvest to the biofuel feedstock pools and added to the 1-year product pool. -Through the introduction of the capability to dynamically adjust landunit weights during a simulation, the crop model can now be run coincidentally -with prescribed land use, which significantly expands the capabilities of the model. Mass-based rather than area-based wood harvest is applied. Several heat stress indices for both urban and rural areas are calculated and output by default :ref:`(Buzan et al., 2015)`. A more sophisticated and realistic building space heating and air conditioning submodel that prognoses interior building air temperature and includes more realistic space heating and air conditioning wasteheat factors -is incorporated. +Developments for CLM5.0 build on the progress made in CLM4.5. Most major components of the model have been updated with particularly notable changes made to soil and plant hydrology, snow density, river modeling, carbon and nitrogen cycling and coupling, and crop modeling. Much of the focus of development centered on a push towards more mechanistic treatment of key processes, in addition to more comprehensive and explicit representation of land use and land-cover change. Prior versions of CLM included relatively few options for physics parameterizations or structure. In CLM5, where new parameterizations or model decisions were made, in most cases, the CLM4.5 parameterization was maintained so that users could switch back and forth between different parameterizations via namelist control where appropriate or desirable. Throughout the CLM5 Technical Descpription, in general only the default parameterization for any given process is described. Readers are referred to the CLM4.5 or CLM4 Technical Descriptions for detailed descriptions of non-default parameterizations. + +The hydrology updates include the introduction of a dry surface layer-based soil evaporation resistance parameterization :ref:`(Swenson and Lawrence, 2014)` and a revised canopy interception parameterization. Canopy interception is now divided into liquid and solid phases, with the intercepted snow subject to unloading events due to wind or above-freezing temperatures. The snow-covered fraction of the canopy is used within the canopy radiation and surface albedo calculation. Instead of applying a spatially uniform soil thickness, soil thickness can vary in space :ref:`(Brunke et al. 2016` and :ref:`Swenson and Lawrence, 2015)` and is set to values within a range of 0.4m to 8.5m depth, derived from a spatially explicit soil thickness data product :ref:`(Pelletier et al., 2016)`. The explicit treatment of soil thickness allows for the deprecation of the unconfined aquifer parameterization used in CLM4.5, which is replaced with a zero flux boundary condition and explicit modeling of both the saturated and unsaturated zones. The default model soil layer resolution is increased, especially within the top 3m, to more explicitly represent active layer thickness within the permafrost zone. Rooting profiles were used inconsistently in CLM4.5 with :ref:`Zeng (2001)` profiles used for water and :ref:`Jackson et al. (1996)` profiles used for carbon inputs. For CLM5, the Jackson et al. (1996) rooting profiles are used for both water and carbon. Roots are deepened for the broadleaf evergreen tropical tree and broadleaf deciduous tropical tree types. Finally, an adaptive time-stepping solution to the Richard's equation is introduced, which improves the accuracy and stability of the numerical soil water solution. The River Transport Model (RTM) is replaced with the Model for Scale Adaptive River Transport (MOSART, :ref:`Li et al., 2013b)` in which surface runoff is routed across hillslopes and then discharged along with subsurface runoff into a tributary subnetwork before entering the main channel. + +Several changes are included that are mainly targeted at improving the simulation of surface mass balance over ice sheets. The fresh snow density parameterization is updated to more realistically capture temperature effects and to additionally account for wind effects on new snow density :ref:`(van Kampenhout et al., 2017)`. The maximum number of snow layers and snow amount is increased from 5 layers and 1m snow water equivalent to 12 layers and 10m snow water equivalent to allow for the formation of firn in regions of persistent snow-cover (e.g., glaciers and ice sheets) :ref:`(van Kampenhout et al., 2017)`. The CISM2 ice sheet model is included for Greenland by default. The ice sheet does not evolve for typical configurations, but ice sheet evolution can be turned on by choosing an appropriate compset. The introduction in CLM5 of the capability to dynamically adjust landunit weights means that a glacier can initiate, grow, shrink, or disappear during a simulation when ice evolution is active. That is, there are two-way feedbacks between CLM and CISM. Multiple elevation classes (10 elevation classes by default) and associated temperature, rain/snow partitioning, and downwelling longwave downscaling are used for glacier landunits to account for the strong topographic elevation heterogeneity over glaciers and ice sheets. + +A plant hydraulic stress routine is introduced which explicitly models water transport through the vegetation according to a simple hydraulic framework (Kennedy et al., to be submitted). The water supply equations are used to solve for vegetation water potential forced by transpiration demand and a set of layer-by-layer soil water potentials. Stomatal conductance, therefore, is a function of prognostic leaf water potential. Water stress is calculated as the ratio of attenuated stomatal conductance to maximum stomatal conductance. An emergent feature of the plant hydraulics is soil hydraulic redistribution. In CLM5, maximum stomatal conductance is obtained from the Medlyn conductance model :ref:`(Medlyn et al., 2011)`, rather than the Ball-Berry stomatal conductance model that was utilized in CLM4.5 and prior versions of the model. The Medlyn stomatal conductance model is preferred mainly for it's more realistic behavior at low humidity levels :ref:`(Rogers et al., 2017)`. The stress deciduous vegetation phenology trigger is augmented with a antecedent precipitation requirement :ref:`(Dahlin et al. 2015)`. + +Plant nutrient dynamics are substantially updated to resolve several deficiencies with the CLM4 and CLM4.5 nutrient cycling representation. The Fixation and Update of Nitrogen (FUN) model based on the work of :ref:`Fisher et al. (2010)`, :ref:`Brzostek et al. (2014)`, and :ref:`Shi et al. (2016)` is incorporated. The concept of FUN is that in most cases, N uptake requires the expenditure of energy in the form of carbon, and further, that there are numerous potential sources of N in the environment which a plant may exchange for carbon. The ratio of carbon expended to N acquired is therefore the cost, or exchange rate, of N acquisition. FUN calculates the rate of symbiotic N fixation, with this N passed straight to the plant, not the mineral N pool. Separately, CLM5 also calculates rates of symbiotic (or free living) N fixation as a function of evapotranspiration (:ref:`Cleveland et al. 1999 `), which is added to the soil inorganic ammonium (NH\ :sub:`4`\ :sup:`+`) pool. The static plant carbon:nitrogen (C:N) ratios utilized in CLM4 and CLM4.5 are replaced with variable plant C:N ratios which allows plants to adjust their C:N ratio, and therefore their leaf nitrogen content, with the cost of N uptake :ref:`(Ghimire et al. 2016)`. The implementation of a flexible C:N ratio means that the model no longer relies on instantaneous downregulation of potential photosynthesis rates based on soil mineral nitrogen availability to represent nutrient limitation. Furthermore, stomatal conductance is now based on the N-limited photosynthesis rather than on potential photosynthesis. Finally, the Leaf Use of Nitrogen for Assimilation (LUNA, :ref:`Xu et al., 2012` and :ref:`Ali et al., 2016)` model is incorporated. The LUNA model calculates photosynthetic capacity based on optimization of the use of leaf nitrogen under different environmental conditions such that light capture, carboxylation, and respiration are co-limiting. + +CLM5 applies a fixed allocation scheme for woody vegetation. The decision to use a fixed allocation scheme in CLM5, rather than a dynamic NPP-based allocation scheme, as was used in CLM4 and CLM4.5, was driven by the fact that observations indicate that biomass saturates with increasing productivity, in contrast to the behavior in CLM4 and CLM4.5 where biomass continuously increases with increasing productivity (:ref:`Negron-Juarez et al., 2015`). Soil carbon decomposition processes are unchanged in CLM5, but a new metric for apparent soil carbon turnover times (:ref:`Koven et al., 2017 `) suggested parameter changes that produce a weak intrinsic depth limitation on soil carbon turnover rates (rather than the strong depth limitaiton in CLM4.5) and that the thresholds for soil moisture limitation on soil carbon turnover rates in dry soils should be set at a wetter soil moisture level than that used in CLM4.5. + +Representation of human management of the land (agriculture, wood harvest) is augmented in several ways. The CLM4.5 crop model is extended to operate globally through the addition of rice, sugarcane, tropical varieties of corn and soybean :ref:`(Badger and Dirmeyer, 2015` and :ref:`Levis et al., 2016)`, and perennial bioenergy crops :ref:`(Cheng et al., 2019)`. These crop types are added to the existing temperate corn, temperate soybean, spring wheat, and cotton crop types. Fertilization rates and irrigation equipped area updated annually based on crop type and geographic region through an input dataset. The irrigation trigger is updated. Additional minor changes include crop phenological triggers that vary by latitude for selected crop types, grain C and N is now removed at harvest to a 1-year product pool with the carbon for the next season's crop seed removed from the grain carbon at harvest. A fraction of leaf/livestem C and N from bioenergy crops is removed at harvest to the biofuel feedstock pools and added to the 1-year product pool. Through the introduction of the capability to dynamically adjust landunit weights during a simulation, the crop model can now be run coincidentally with prescribed land use, which significantly expands the capabilities of the model. Mass-based rather than area-based wood harvest is applied. Several heat stress indices for both urban and rural areas are calculated and output by default :ref:`(Buzan et al., 2015)`. A more sophisticated and realistic building space heating and air conditioning submodel that prognoses interior building air temperature and includes more realistic space heating and air conditioning wasteheat factors is incorporated. The fire model is the same as utilized in CLM4.5 except that a modified scheme is used to estimate the dependence of fire occurrence and spread on fuel wetness for non-peat fires outside cropland and tropical closed forests :ref:`(Li and Lawrence, 2017)` and the dependence of agricultural fires on fuel load is removed. -Included with the release of CLM5.0 is a functionally supported version of the Functionally-Assembled Terrestrial Ecosystem Simulator (FATES, :ref:`Fisher et al., 2015)`. A major motivation of FATES is to allow the prediction of biome boundaries directly from plant physiological traits via their competitive interactions. FATES is a cohort model of vegetation competition and co-existence, allowing a representation of the biosphere which accounts for the division of the land surface into successional stages, and for competition for light between height structured cohorts of representative trees of various plant functional types. FATES is not active by default in CLM5.0. +Included with the release of CLM5.0 is a functionally supported version of the Functionally-Assembled Terrestrial Ecosystem Simulator (FATES, :ref:`Fisher et al., 2015)`. A major motivation of FATES is to allow the prediction of biome boundaries directly from plant physiological traits via their competitive interactions. FATES is a cohort model of vegetation competition and co-existence, allowing a representation of the biosphere which accounts for the division of the land surface into successional stages, and for competition for light between height structured cohorts of representative trees of various plant functional types. FATES is not active by default in CLM5.0. -Note that the classical dynamic global vegetation model (CLM-DGVM) that has been available within CLM4 and CLM4.5 remains available, though it is largely untested. The technical description of the CLM-DGVM can be found within the CLM4.5 Technical Description (:ref:`Oleson et al. 2013)`. +Note that the classical dynamic global vegetation model (CLM-DGVM) that has been available within CLM4 and CLM4.5 remains available, though it is largely untested. The technical description of the CLM-DGVM can be found within the CLM4.5 Technical Description (:ref:`Oleson et al. 2013)`. -During the course of the development of CLM5.0, it became clear that the increasing complexity of the model combined with the increasing number and range -of model development projects required updates to the underlying CLM infrastructure. Many such software improvements -are included in CLM5 including a partial transition to an object-oriented modular software structure. Many hard coded model -parameters have been extracted into either the parameter file or the CLM namelist, which allows users to more readily calibrate the model for use at -specific locations or to conduct parameter sensitivity studies. As part of the effort to increase -the scientific utility of the code, in most instances older generation parameterizations (i.e., the parameterizations -available in CLM4 or CLM4.5) are retained under namelist switches, allowing the user to revert to CLM4.5 -from the same code base or to revert individual parameterizations where the old parameterizations are compatible with the new code. Finally, multiple vertical soil layer structures -are defined and it is relatively easy to add additional structures. +During the course of the development of CLM5.0, it became clear that the increasing complexity of the model combined with the increasing number and range of model development projects required updates to the underlying CLM infrastructure. Many such software improvements are included in CLM5 including a partial transition to an object-oriented modular software structure. Many hard coded model parameters have been extracted into either the parameter file or the CLM namelist, which allows users to more readily calibrate the model for use at specific locations or to conduct parameter sensitivity studies. As part of the effort to increase the scientific utility of the code, in most instances older generation parameterizations (i.e., the parameterizations available in CLM4 or CLM4.5) are retained under namelist switches, allowing the user to revert to CLM4.5 from the same code base or to revert individual parameterizations where the old parameterizations are compatible with the new code. Finally, multiple vertical soil layer structures are defined and it is relatively easy to add additional structures. Biogeophysical and Biogeochemical Processes ----------------------------------------------- -Biogeophysical and biogeochemical processes are simulated for each -subgrid land unit, column, and plant functional type (PFT) independently -and each subgrid unit maintains its own prognostic variables (see -section :numref:`Surface Heterogeneity and Data Structure` for definitions -of subgrid units). The same atmospheric -forcing is used to force all subgrid units within a grid cell. The -surface variables and fluxes required by the atmosphere are obtained by -averaging the subgrid quantities weighted by their fractional areas. The -processes simulated include (:numref:`Figure Land processes`): +Biogeophysical and biogeochemical processes are simulated for each subgrid land unit, column, and plant functional type (PFT) independently and each subgrid unit maintains its own prognostic variables (see section :numref:`Surface Heterogeneity and Data Structure` for definitions of subgrid units). The same atmospheric forcing is used to force all subgrid units within a grid cell. The surface variables and fluxes required by the atmosphere are obtained by averaging the subgrid quantities weighted by their fractional areas. The processes simulated include (:numref:`Figure Land processes`): -#. Surface characterization including land type heterogeneity and - ecosystem structure (Chapter :numref:`rst_Surface Characterization, Vertical Discretization, and Model Input Requirements`) +#. Surface characterization including land type heterogeneity and ecosystem structure (Chapter :numref:`rst_Surface Characterization, Vertical Discretization, and Model Input Requirements`) -#. Absorption, reflection, and transmittance of solar radiation (Chapter - :numref:`rst_Surface Albedos`, :numref:`rst_Radiative Fluxes`) +#. Absorption, reflection, and transmittance of solar radiation (Chapter :numref:`rst_Surface Albedos`, :numref:`rst_Radiative Fluxes`) #. Absorption and emission of longwave radiation (Chapter :numref:`rst_Radiative Fluxes`) -#. Momentum, sensible heat (ground and canopy), and latent heat (ground - evaporation, canopy evaporation, transpiration) fluxes (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) +#. Momentum, sensible heat (ground and canopy), and latent heat (ground evaporation, canopy evaporation, transpiration) fluxes (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) #. Heat transfer in soil and snow including phase change (Chapter :numref:`rst_Soil and Snow Temperatures`) #. Canopy hydrology (interception, throughfall, and drip) (Chapter :numref:`rst_Hydrology`) -#. Soil hydrology (surface runoff, infiltration, redistribution of water - within the column, sub-surface drainage, groundwater) (Chapter :numref:`rst_Hydrology`) +#. Soil hydrology (surface runoff, infiltration, redistribution of water within the column, sub-surface drainage, groundwater) (Chapter :numref:`rst_Hydrology`) -#. Snow hydrology (snow accumulation and melt, compaction, water - transfer between snow layers) (Chapter :numref:`rst_Snow Hydrology`) +#. Snow hydrology (snow accumulation and melt, compaction, water transfer between snow layers) (Chapter :numref:`rst_Snow Hydrology`) -#. Stomatal physiology, photosythetic capacity, and photosynthesis (Chapters :numref:`rst_Stomatal Resistance and Photosynthesis` and - :numref:`rst_Photosynthetic Capacity`) +#. Stomatal physiology, photosythetic capacity, and photosynthesis (Chapters :numref:`rst_Stomatal Resistance and Photosynthesis` and :numref:`rst_Photosynthetic Capacity`) #. Plant hydraulics (Chapter :numref:`rst_Plant Hydraulics`) @@ -611,13 +277,11 @@ processes simulated include (:numref:`Figure Land processes`): #. Fixation and uptake of nitrogen (Chapter :numref:`rst_FUN`) -#. External nitrogen cycling including deposition, - denitrification, leaching, and losses due to fire (Chapter :numref:`rst_External Nitrogen Cycle`) +#. External nitrogen cycling including deposition, denitrification, leaching, and losses due to fire (Chapter :numref:`rst_External Nitrogen Cycle`) #. Plant mortality (Chapter :numref:`rst_Plant Mortality`) -#. Fire ignition, suppression, spread, and emissions, including natural, deforestation, and - agricultural fire (Chapter :numref:`rst_Fire`) +#. Fire ignition, suppression, spread, and emissions, including natural, deforestation, and agricultural fire (Chapter :numref:`rst_Fire`) #. Methane production, oxidation, and emissions (Chapter :numref:`rst_Methane Model`) diff --git a/doc/source/tech_note/Isotopes/CLM50_Tech_Note_Isotopes.rst b/doc/source/tech_note/Isotopes/CLM50_Tech_Note_Isotopes.rst index 43692205b8..45689d613c 100644 --- a/doc/source/tech_note/Isotopes/CLM50_Tech_Note_Isotopes.rst +++ b/doc/source/tech_note/Isotopes/CLM50_Tech_Note_Isotopes.rst @@ -3,211 +3,111 @@ Carbon Isotopes =================== -CLM includes a fully prognostic representation of the fluxes, storage, -and isotopic discrimination of the carbon isotopes :sup:`13`\ C -and :sup:`14`\ C. The implementation of the C isotopes capability -takes advantage of the CLM hierarchical data structures, replicating the -carbon state and flux variable structures at the column and PFT level to -track total carbon and both C isotopes separately (see description of -data structure hierarchy in Chapter 2). For the most part, fluxes and -associated updates to carbon state variables for :sup:`13`\ C are -calculated directly from the corresponding total C fluxes. Separate -calculations are required in a few special cases, such as where isotopic -discrimination occurs, or where the necessary isotopic ratios are -undefined. The general approach for :sup:`13`\ C flux and state -variable calculation is described here, followed by a description of all -the places where special calculations are required. +CLM includes a fully prognostic representation of the fluxes, storage, and isotopic discrimination of the carbon isotopes :sup:`13`\ C and :sup:`14`\ C. The implementation of the C isotopes capability takes advantage of the CLM hierarchical data structures, replicating the carbon state and flux variable structures at the column and PFT level to track total carbon and both C isotopes separately (see description of data structure hierarchy in Chapter 2). For the most part, fluxes and associated updates to carbon state variables for :sup:`13`\ C are calculated directly from the corresponding total C fluxes. Separate calculations are required in a few special cases, such as where isotopic discrimination occurs, or where the necessary isotopic ratios are undefined. The general approach for :sup:`13`\ C flux and state variable calculation is described here, followed by a description of all the places where special calculations are required. General Form for Calculating :sup:`13`\ C and :sup:`14`\ C Flux -------------------------------------------------------------------------------- -In general, the flux of :sup:`13`\ C and corresponding to a given -flux of total C (:math:`{CF}_{13C}` and :math:`{CF}_{totC}`, -respectively) is determined by :math:`{CF}_{totC}`, the masses of -:sup:`13`\ C and total C in the upstream pools -(:math:`{CS}_{13C\_up}` and :math:`{CS}_{totC\_up}`, -respectively, i.e. the pools *from which* the fluxes of -:sup:`13`\ C and total C originate), and a fractionation factor, -:math:`{f}_{frac}`: +In general, the flux of :sup:`13`\ C and corresponding to a given flux of total C (:math:`{CF}_{13C}` and :math:`{CF}_{totC}`, respectively) is determined by :math:`{CF}_{totC}`, the masses of :sup:`13`\ C and total C in the upstream pools (:math:`{CS}_{13C\_up}` and :math:`{CS}_{totC\_up}`, respectively, i.e. the pools *from which* the fluxes of :sup:`13`\ C and total C originate), and a fractionation factor, :math:`{f}_{frac}`: .. math:: - :label: ZEqnNum629812 + :label: ZEqnNum629812 CF_{13C} =\left\{\begin{array}{l} {CF_{totC} \frac{CS_{13C\_ up} }{CS_{totC\_ up} } f_{frac} \qquad {\rm for\; }CS_{totC} \ne 0} \\ {0\qquad {\rm for\; }CS_{totC} =0} \end{array}\right\} -If the :math:`{f}_{frac}` = 1.0 (no fractionation), then the fluxes -:math:`{CF}_{13C}` and :math:`{CF}_{totC}` will be in simple -proportion to the masses :math:`{CS}_{13C\_up}` and -:math:`{CS}_{totC\_up}`. Values of :math:`{f}_{frac} < 1.0` indicate a discrimination against the heavier isotope -(:sup:`13`\ C) in the flux-generating process, while -:math:`{f}_{frac}` :math:`>` 1.0 would indicate a preference for the -heavier isotope. Currently, in all cases where Eq. is used to calculate -a :sup:`13`\ C flux, :math:`{f}_{frac}` is set to 1.0. - -For :sup:`1`\ :sup:`4`\ C, no fractionation is used in -either the initial photosynthetic step, nor in subsequent fluxes from -upstream to downstream pools; as discussed below, this is because -observations of :sup:`1`\ :sup:`4`\ C are typically -described in units that implicitly correct out the fractionation of -:sup:`1`\ :sup:`4`\ C by referencing them to -:sup:`1`\ :sup:`3`\ C ratios. +If the :math:`{f}_{frac}` = 1.0 (no fractionation), then the fluxes :math:`{CF}_{13C}` and :math:`{CF}_{totC}` will be in simple proportion to the masses :math:`{CS}_{13C\_up}` and :math:`{CS}_{totC\_up}`. Values of :math:`{f}_{frac} < 1.0` indicate a discrimination against the heavier isotope (:sup:`13`\ C) in the flux-generating process, while :math:`{f}_{frac}` :math:`>` 1.0 would indicate a preference for the heavier isotope. Currently, in all cases where Eq. is used to calculate a :sup:`13`\ C flux, :math:`{f}_{frac}` is set to 1.0. + +For :sup:`14`\ C, no fractionation is used in either the initial photosynthetic step, nor in subsequent fluxes from upstream to downstream pools; as discussed below, this is because observations of :sup:`14` C are typically described in units that implicitly correct out the fractionation of :sup:`14`\ C by referencing them to :sup:`13`\ C ratios. Isotope Symbols, Units, and Reference Standards ---------------------------------------------------- -Carbon has two primary stable isotopes, :sup:`12`\ C and -:sup:`13`\ C. :sup:`12`\ C is the most abundant, comprising -about 99% of all carbon. The isotope ratio of a compound, -:math:`{R}_{A}`, is the mass ratio of the rare isotope to the abundant isotope +Carbon has two primary stable isotopes, :sup:`12`\ C and :sup:`13`\ C. :sup:`12`\ C is the most abundant, comprising about 99% of all carbon. The isotope ratio of a compound, :math:`{R}_{A}`, is the mass ratio of the rare isotope to the abundant isotope .. math:: - :label: 30.2) + :label: 30.2) R_{A} =\frac{{}^{13} C_{A} }{{}^{12} C_{A} } . -Carbon isotope ratios are often expressed using delta notation, -:math:`\delta`. The :math:`\delta^{13}`\ C value of a -compound A, :math:`\delta^{13}`\ C\ :sub:`A`, is the -difference between the isotope ratio of the compound, -:math:`{R}_{A}`, and that of the Pee Dee Belemnite standard, :math:`{R}_{PDB}`, in parts per thousand +Carbon isotope ratios are often expressed using delta notation, :math:`\delta`. The :math:`\delta^{13}`\ C value of a compound A, :math:`\delta^{13}`\ C\ :sub:`A`, is the difference between the isotope ratio of the compound, :math:`{R}_{A}`, and that of the Pee Dee Belemnite standard, :math:`{R}_{PDB}`, in parts per thousand .. math:: - :label: 30.3) + :label: 30.3) \delta ^{13} C_{A} =\left(\frac{R_{A} }{R_{PDB} } -1\right)\times 1000 where :math:`{R}_{PDB}` = 0.0112372, and units of :math:`\delta` are per mil (‰). -Isotopic fractionation can be expressed in several ways. One expression -of the fractionation factor is with alpha (:math:`\alpha`) notation. -For example, the equilibrium fractionation between two reservoirs A and -B can be written as: +Isotopic fractionation can be expressed in several ways. One expression of the fractionation factor is with alpha (:math:`\alpha`) notation. For example, the equilibrium fractionation between two reservoirs A and B can be written as: .. math:: - :label: 30.4) + :label: 30.4) \alpha _{A-B} =\frac{R_{A} }{R_{B} } =\frac{\delta _{A} +1000}{\delta _{B} +1000} . This can also be expressed using epsilon notation (:math:`\epsilon`), where .. math:: - :label: 30.5) + :label: 30.5) \alpha _{A-B} =\frac{\varepsilon _{A-B} }{1000} +1 In other words, if :math:`{\epsilon }_{A-B} = 4.4` ‰ , then :math:`{\alpha}_{A-B} =1.0044`. -In addition to the stable isotopes :sup:`1`\ :sup:`2`\ C and :sup:`1`\ :sup:`3`\ C, the unstable isotope -:sup:`1`\ :sup:`4`\ C is included in CLM. :sup:`1`\ :sup:`4`\ C can also be described using the delta notation: +In addition to the stable isotopes :sup:`1`\ :sup:`2`\ C and :sup:`13`\ C, the unstable isotope :sup:`14`\ C is included in CLM. :sup:`14`\ C can also be described using the delta notation: .. math:: - :label: 30.6) + :label: 30.6) \delta ^{14} C=\left(\frac{A_{s} }{A_{abs} } -1\right)\times 1000 -However, observations of :sup:`1`\ :sup:`4`\ C are typically -fractionation-corrected using the following notation: +However, observations of :sup:`14`\ C are typically fractionation-corrected using the following notation: .. math:: - :label: 30.7) + :label: 30.7) \Delta {}^{14} C=1000\times \left(\left(1+\frac{\delta {}^{14} C}{1000} \right)\frac{0.975^{2} }{\left(1+\frac{\delta {}^{13} C}{1000} \right)^{2} } -1\right) -where :math:`\delta^{14}`\ C is the measured isotopic -fraction and :math:`\mathrm{\Delta}^{14}`\ C corrects for -mass-dependent isotopic fractionation processes (assumed to be 0.975 for -fractionation of :sup:`13`\ C by photosynthesis). CLM assumes a -background preindustrial atmospheric :sup:`14`\ C /C ratio of 10\ :sup:`-12`, which is used for A\ :sub::`abs`. -For the reference standard A\ :math:`{}_{abs}`, which is a plant tissue and has -a :math:`\delta^{13}`\ C value is :math:`\mathrm{-}`\ 25 ‰ due to photosynthetic discrimination, -:math:`\delta`\ :sup:`14`\ C = :math:`\mathrm{\Delta}`\ :sup:`14`\ C. For CLM, in order to use -the :sup:`14`\ C model independently of the :sup:`13`\ C -model, for the :sup:`14`\ C calculations, this fractionation is -set to zero, such that the 0.975 term becomes 1, the -:math:`\delta^{13}`\ C term (for the calculation of -:math:`\delta^{14}`\ C only) becomes 0, and thus -:math:`\delta^{14}`\ C = :math:`\mathrm{\Delta}`\ :sup:`14`\ C. +where :math:`\delta^{14}`\ C is the measured isotopic fraction and :math:`\mathrm{\Delta}^{14}`\ C corrects for mass-dependent isotopic fractionation processes (assumed to be 0.975 for fractionation of :sup:`13`\ C by photosynthesis). CLM assumes a background preindustrial atmospheric :sup:`14`\ C /C ratio of 10\ :sup:`-12`, which is used for A\ :sub::`abs`. For the reference standard A\ :math:`{}_{abs}`, which is a plant tissue and has a :math:`\delta^{13}`\ C value is :math:`\mathrm{-}`\ 25 ‰ due to photosynthetic discrimination, :math:`\delta`\ :sup:`14`\ C = :math:`\mathrm{\Delta}`\ :sup:`14`\ C. For CLM, in order to use the :sup:`14`\ C model independently of the :sup:`13`\ C model, for the :sup:`14`\ C calculations, this fractionation is set to zero, such that the 0.975 term becomes 1, the :math:`\delta^{13}`\ C term (for the calculation of :math:`\delta^{14}`\ C only) becomes 0, and thus :math:`\delta^{14}`\ C = :math:`\mathrm{\Delta}`\ :sup:`14`\ C. Carbon Isotope Discrimination During Photosynthesis -------------------------------------------------------- -Photosynthesis is modeled in CLM as a two-step process: diffusion of -CO\ :sub:`2` into the stomatal cavity, followed by enzymatic -fixation (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`). Each step is associated with a kinetic isotope -effect. The kinetic isotope effect during diffusion of -CO\ :sub:`2` through the stomatal opening is 4.4‰. The kinetic -isotope effect during fixation of CO\ :sub:`2` with Rubisco is -:math:`\sim`\ 30‰; however, since about 5-10% of carbon in C3 plants -reacts with phosphoenolpyruvate carboxylase (PEPC) (Melzer and O’Leary, -1987), the net kinetic isotope effect during fixation is -:math:`\sim`\ 27‰ for C3 plants. In C4 plant photosynthesis, only the -diffusion effect is important. The fractionation factor equations for C3 -and C4 plants are given below: +Photosynthesis is modeled in CLM as a two-step process: diffusion of CO\ :sub:`2` into the stomatal cavity, followed by enzymatic fixation (Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`). Each step is associated with a kinetic isotope effect. The kinetic isotope effect during diffusion of CO\ :sub:`2` through the stomatal opening is 4.4‰. The kinetic isotope effect during fixation of CO\ :sub:`2` with Rubisco is :math:`\sim`\ 30‰; however, since about 5-10% of carbon in C3 plants reacts with phosphoenolpyruvate carboxylase (PEPC) (Melzer and O'Leary, 1987), the net kinetic isotope effect during fixation is :math:`\sim`\ 27‰ for C3 plants. In C4 plant photosynthesis, only the diffusion effect is important. The fractionation factor equations for C3 and C4 plants are given below: For C4 plants, .. math:: - :label: 30.8) + :label: 30.8) \alpha _{psn} =1+\frac{4.4}{1000} For C3 plants, .. math:: - :label: 30.9) + :label: 30.9) \alpha _{psn} =1+\frac{4.4+22.6\frac{c_{i}^{*} }{pCO_{2} } }{1000} -where :math:`{\alpha }_{psn}` is the fractionation factor, and -:math:`c^*_i` and pCO\ :sub:`2` are the revised intracellular and -atmospheric CO\ :sub:`2` partial pressure, respectively. +where :math:`{\alpha }_{psn}` is the fractionation factor, and :math:`c^*_i` and pCO\ :sub:`2` are the revised intracellular and atmospheric CO\ :sub:`2` partial pressure, respectively. -As can be seen from the above equation, kinetic isotope effect during -fixation of CO\ :sub:`2` is dependent on the intracellular -CO\ :sub:`2` concentration, which in turn depends on the net -carbon assimilation. That is calculated during the photosynthesis -calculation as follows: +As can be seen from the above equation, kinetic isotope effect during fixation of CO\ :sub:`2` is dependent on the intracellular CO\ :sub:`2` concentration, which in turn depends on the net carbon assimilation. That is calculated during the photosynthesis calculation as follows: .. math:: - :label: 30.10) + :label: 30.10) c_{i} =pCO_{2} -a_{n} p\frac{\left(1.4g_{s} \right)+\left(1.6g_{b} \right)}{g_{b} g_{s} } -where :math:`a_n` is net carbon assimilation during photosynthesis, :math:`p` is -atmospheric pressure, :math:`g_b` is leaf boundary layer conductance, -and :math:`g_s` is leaf stomatal conductance. +where :math:`a_n` is net carbon assimilation during photosynthesis, :math:`p` is atmospheric pressure, :math:`g_b` is leaf boundary layer conductance, and :math:`g_s` is leaf stomatal conductance. -Isotopic fractionation code is compatible with multi-layered canopy -parameterization; i.e., it is possible to calculate varying -discrimination rates for each layer of a multi-layered canopy. However, -as with the rest of the photosynthesis model, the number of canopy -layers is currently set to one by default. +Isotopic fractionation code is compatible with multi-layered canopy parameterization; i.e., it is possible to calculate varying discrimination rates for each layer of a multi-layered canopy. However, as with the rest of the photosynthesis model, the number of canopy layers is currently set to one by default. :sup:`14`\ C radioactive decay and historical atmospheric :sup:`14`\ C and :sup:`13`\ C concentrations ------------------------------------------------------------------------------------------------------ -In the preindustrial biosphere, radioactive decay of :sup:`14`\ C -in carbon pools allows dating of long-term age since photosynthetic -uptake; while over the 20\ :math:`{}^{th}` century, radiocarbon in the -atmosphere was first diluted by radiocarbon-free fossil fuels and then -enriched by aboveground thermonuclear testing to approximately double -its long-term mean concentration. CLM includes both of these processes -to allow comparison of carbon that may vary on multiple timescales with -observed values. - -For radioactive decay, at each timestep all :sup:`14`\ C pools are -reduced at a rate of –log/:math:`\tau`, where :math:`\tau` is the -half-life (Libby half-life value of 5568 years). In order to rapidly -equilibrate the long-lived pools during accelerated decomposition -spinup, the radioactive decay of the accelerated pools is also -accelerated by the same degree as the decomposition, such that the -:sup:`14`\ C value of these pools is in equilibrium when taken out -of the spinup mode. - -For variation of atmospheric :sup:`14`\ C and :sup:`13`\ C over the historical -period, :math:`\mathrm{\Delta}`\ :sup:`14`\ C and :math:`\mathrm{\Delta}`\:sup:`13`\ C values can be set to -either fixed concentrations -or time-varying concentrations read in from a file. A default file is provided that spans the historical period (:ref:`Graven et al., 2017 `). For -:math:`\mathrm{\Delta}`\ :sup:`14`\ C, values are provided and read in for three latitude bands (30 :sup:`o`\ N-90 :sup:`o`\ N, 30 :sup:`o`\ S-30 :sup:`o`\ N, and 30 :sup:`o`\ S-90 :sup:`o`\ S). +In the preindustrial biosphere, radioactive decay of :sup:`14`\ C in carbon pools allows dating of long-term age since photosynthetic uptake; while over the 20\ :math:`{}^{th}` century, radiocarbon in the atmosphere was first diluted by radiocarbon-free fossil fuels and then enriched by aboveground thermonuclear testing to approximately double its long-term mean concentration. CLM includes both of these processes to allow comparison of carbon that may vary on multiple timescales with observed values. + +For radioactive decay, at each timestep all :sup:`14`\ C pools are reduced at a rate of –log/:math:`\tau`, where :math:`\tau` is the half-life (Libby half-life value of 5568 years). In order to rapidly equilibrate the long-lived pools during accelerated decomposition spinup, the radioactive decay of the accelerated pools is also accelerated by the same degree as the decomposition, such that the :sup:`14`\ C value of these pools is in equilibrium when taken out of the spinup mode. +For variation of atmospheric :sup:`14`\ C and :sup:`13`\ C over the historical period, :math:`\mathrm{\Delta}`\ :sup:`14`\ C and :math:`\mathrm{\Delta}`\ :sup:`13`\ C values can be set to either fixed concentrations or time-varying concentrations read in from a file. A default file is provided that spans the historical period (:ref:`Graven et al., 2017 `). For :math:`\mathrm{\Delta}`\ :sup:`14`\ C, values are provided and read in for three latitude bands (30°N--90°N, 30°S--30°N, and 30°S--90°S). diff --git a/doc/source/tech_note/Lake/CLM50_Tech_Note_Lake.rst b/doc/source/tech_note/Lake/CLM50_Tech_Note_Lake.rst index 40b1918f46..88cb77d737 100644 --- a/doc/source/tech_note/Lake/CLM50_Tech_Note_Lake.rst +++ b/doc/source/tech_note/Lake/CLM50_Tech_Note_Lake.rst @@ -3,110 +3,37 @@ Lake Model ============= -The lake model, denoted the *Lake, Ice, Snow, and Sediment Simulator* -(LISSS), is from :ref:`Subin et al. (2012a) `. -It includes extensive modifications to the lake code of -:ref:`Zeng et al. (2002) ` used in CLM -versions 2 through 4, which utilized concepts from the lake models of -:ref:`Bonan (1996) `, -:ref:`Henderson-Sellers (1985) `, -:ref:`Henderson-Sellers (1986) `, -:ref:`Hostetler and Bartlein (1990) `, -and the coupled lake-atmosphere model of :ref:`Hostetler et al. (1993) `, :ref:`Hostetler et al. (1993) `. -Lakes have spatially variable depth prescribed in the surface data (section -:ref:`External Data Lake`); the surface data optionally includes lake optical -extinction coeffient and horizontal fetch, currently only used for site -simulations. Lake physics includes freezing and thawing in the lake -body, resolved snow layers, and “soil” and bedrock layers below the lake -body. Temperatures and ice fractions are simulated for -:math:`N_{levlak} =10` layers (for global simulations) or -:math:`N_{levlak} =25` (for site simulations) with discretization -described in section :numref:`Vertical Discretization Lake`. Lake albedo is -described in section :numref:`Surface Albedo Lake`. Lake surface fluxes -(section :numref:`Surface Fluxes and Surface Temperature Lake`) generally -follow the formulations for non-vegetated surfaces, including the calculations -of aerodynamic resistances (section -:numref:`Sensible and Latent Heat Fluxes for Non-Vegetated Surfaces`); -however, the lake surface temperature -:math:`T_{g}` (representing an infinitesimal interface layer between -the top resolved lake layer and the atmosphere) is solved for -simultaneously with the surface fluxes. After surface fluxes are -evaluated, temperatures are solved simultaneously in the resolved snow -layers (if present), the lake body, and the soil and bedrock, using the -ground heat flux *G* as a top boundary condition. Snow, soil, and -bedrock models generally follow the formulations for non-vegetated -surfaces (Chapter :numref:`rst_Soil and Snow Temperatures`), with -modifications described below. +The lake model, denoted the *Lake, Ice, Snow, and Sediment Simulator* (LISSS), is from :ref:`Subin et al. (2012a) `. It includes extensive modifications to the lake code of :ref:`Zeng et al. (2002) ` used in CLM versions 2 through 4, which utilized concepts from the lake models of :ref:`Bonan (1996) `, :ref:`Henderson-Sellers (1985) `, :ref:`Henderson-Sellers (1986) `, :ref:`Hostetler and Bartlein (1990) `, and the coupled lake-atmosphere model of :ref:`Hostetler et al. (1993) `, :ref:`Hostetler et al. (1993) `. Lakes have spatially variable depth prescribed in the surface data (section :ref:`External Data Lake`); the surface data optionally includes lake optical extinction coeffient and horizontal fetch, currently only used for site simulations. Lake physics includes freezing and thawing in the lake body, resolved snow layers, and "soil" and bedrock layers below the lake body. Temperatures and ice fractions are simulated for :math:`N_{levlak} =10` layers (for global simulations) or :math:`N_{levlak} =25` (for site simulations) with discretization described in section :numref:`Vertical Discretization Lake`. Lake albedo is described in section :numref:`Surface Albedo Lake`. Lake surface fluxes (section :numref:`Surface Fluxes and Surface Temperature Lake`) generally follow the formulations for non-vegetated surfaces, including the calculations of aerodynamic resistances (section :numref:`Sensible and Latent Heat Fluxes for Non-Vegetated Surfaces`); however, the lake surface temperature :math:`T_{g}` (representing an infinitesimal interface layer between the top resolved lake layer and the atmosphere) is solved for simultaneously with the surface fluxes. After surface fluxes are evaluated, temperatures are solved simultaneously in the resolved snow layers (if present), the lake body, and the soil and bedrock, using the ground heat flux *G* as a top boundary condition. Snow, soil, and bedrock models generally follow the formulations for non-vegetated surfaces (Chapter :numref:`rst_Soil and Snow Temperatures`), with modifications described below. .. _Vertical Discretization Lake: Vertical Discretization --------------------------- -Currently, there is one lake modeled in each grid cell (with prescribed -or assumed depth *d*, extinction coefficient :math:`\eta`, and fetch -*f*), although this could be modified with changes to the CLM subgrid -decomposition algorithm in future model versions. As currently -implemented, the lake consists of 0-5 snow layers; water and ice layers -(10 for global simulations and 25 for site simulations) comprising the -“lake body;” 10 “soil” layers; and 5 bedrock layers. Each lake body -layer has a fixed water mass (set by the nominal layer thickness and the -liquid density), with frozen mass-fraction *I* a state variable. -Resolved snow layers are present if the snow thickness -:math:`z_{sno} \ge s_{\min }` , where *s*\ :sub:`min` = 4 cm by -default, and is adjusted for model timesteps other than 1800 s in order -to maintain numerical stability (section :numref:`Modifications to Snow Layer Logic Lake`). For global simulations -with 10 body layers, the default (50 m lake) body layer thicknesses are -given by: :math:`\Delta z_{i}` of 0.1, 1, 2, 3, 4, 5, 7, 7, 10.45, and -10.45 m, with node depths :math:`z_{i}` located at the center of each -layer (i.e., 0.05, 0.6, 2.1, 4.6, 8.1, 12.6, 18.6, 25.6, 34.325, 44.775 -m). For site simulations with 25 layers, the default thicknesses are -(m): 0.1 for layer 1; 0.25 for layers 2-5; 0.5 for layers 6-9; 0.75 for -layers 10-13; 2 for layers 14-15; 2.5 for layers 16-17; 3.5 for layers -18-21; and 5.225 for layers 22-25. For lakes with depth *d* -:math:`\neq` 50 m and *d* :math:`\ge` 1 m, the top -layer is kept at 10 cm and the other 9 layer thicknesses are adjusted to -maintain fixed proportions. For lakes with *d* :math:`<` 1 m, all layers -have equal thickness. Thicknesses of snow, soil, and bedrock layers -follow the scheme used over non-vegetated surfaces (Chapter :numref:`rst_Soil and Snow Temperatures`), with -modifications to the snow layer thickness rules to keep snow layers at -least as thick as *s*\ :sub:`min` (section :numref:`Modifications to Snow Layer Logic Lake`). +Currently, there is one lake modeled in each grid cell (with prescribed or assumed depth *d*, extinction coefficient :math:`\eta`, and fetch *f*), although this could be modified with changes to the CLM subgrid decomposition algorithm in future model versions. As currently implemented, the lake consists of 0-5 snow layers; water and ice layers (10 for global simulations and 25 for site simulations) comprising the "lake body;" 10 "soil" layers; and 5 bedrock layers. Each lake body layer has a fixed water mass (set by the nominal layer thickness and the liquid density), with frozen mass-fraction *I* a state variable. Resolved snow layers are present if the snow thickness :math:`z_{sno} \ge s_{\min }`, where *s*\ :sub:`min` = 4 cm by default, and is adjusted for model timesteps other than 1800 s in order to maintain numerical stability (section :numref:`Modifications to Snow Layer Logic Lake`). For global simulations with 10 body layers, the default (50 m lake) body layer thicknesses are given by: :math:`\Delta z_{i}` of 0.1, 1, 2, 3, 4, 5, 7, 7, 10.45, and 10.45 m, with node depths :math:`z_{i}` located at the center of each layer (i.e., 0.05, 0.6, 2.1, 4.6, 8.1, 12.6, 18.6, 25.6, 34.325, 44.775 m). For site simulations with 25 layers, the default thicknesses are (m): 0.1 for layer 1; 0.25 for layers 2-5; 0.5 for layers 6-9; 0.75 for layers 10-13; 2 for layers 14-15; 2.5 for layers 16-17; 3.5 for layers 18-21; and 5.225 for layers 22-25. For lakes with depth *d* :math:`\neq` 50 m and *d* :math:`\ge` 1 m, the top layer is kept at 10 cm and the other 9 layer thicknesses are adjusted to maintain fixed proportions. For lakes with *d* :math:`<` 1 m, all layers have equal thickness. Thicknesses of snow, soil, and bedrock layers follow the scheme used over non-vegetated surfaces (Chapter :numref:`rst_Soil and Snow Temperatures`), with modifications to the snow layer thickness rules to keep snow layers at least as thick as *s*\ :sub:`min` (section :numref:`Modifications to Snow Layer Logic Lake`). .. _External Data Lake: External Data ----------------- -As discussed in :ref:`Subin et al. (2012a, b) `, the -Global Lake and Wetland Database (:ref:`Lehner and Doll 2004`) -is currently used to prescribe lake fraction in each land model grid cell, -for a total of 2.3 million km\ :sup:`-2`. As in -:ref:`Subin et al. (2012a, b) `, the -:ref:`Kourzeneva et al. (2012)` global gridded dataset is currently -used to estimate a mean lake depth in each grid cell, based on interpolated -compilations of geographic information. +As discussed in :ref:`Subin et al. (2012a, b) `, the Global Lake and Wetland Database (:ref:`Lehner and Doll 2004`) is currently used to prescribe lake fraction in each land model grid cell, for a total of 2.3 million km\ :sup:`-2`. As in :ref:`Subin et al. (2012a, b) `, the :ref:`Kourzeneva et al. (2012)` global gridded dataset is currently used to estimate a mean lake depth in each grid cell, based on interpolated compilations of geographic information. .. _Surface Albedo Lake: Surface Albedo ------------------ -For direct radiation, the albedo *a* for lakes with ground temperature -:math:`{T}_{g}` (K) above freezing is given by (:ref:`Pivovarov, 1972`) +For direct radiation, the albedo *a* for lakes with ground temperature :math:`{T}_{g}` (K) above freezing is given by (:ref:`Pivovarov, 1972`) .. math:: :label: 12.1 a=\frac{0.5}{\cos z+0.15} -where *z* is the zenith angle. For diffuse radiation, the expression in -eq. is integrated over the full sky to yield *a* = 0.10. +where *z* is the zenith angle. For diffuse radiation, the expression in eq. is integrated over the full sky to yield *a* = 0.10. -For frozen lakes without resolved snow layers, the albedo at cold -temperatures *a*\ :sub:`0` is 0.60 for visible and 0.40 for near -infrared radiation. As the temperature at the ice surface, -:math:`{T}_{g}`, approaches freezing [ :math:`{T}_{f}` (K) (:numref:`Table Physical Constants`)], the albedo is relaxed towards 0.10 based on -:ref:`Mironov et al. (2010)`: +For frozen lakes without resolved snow layers, the albedo at cold temperatures *a*\ :sub:`0` is 0.60 for visible and 0.40 for near infrared radiation. As the temperature at the ice surface, :math:`{T}_{g}`, approaches freezing [ :math:`{T}_{f}` (K) (:numref:`Table Physical Constants`)], the albedo is relaxed towards 0.10 based on :ref:`Mironov et al. (2010)`: .. math:: :label: 12.2 @@ -115,11 +42,7 @@ infrared radiation. As the temperature at the ice surface, where *a* is restricted to be no less than that given in :eq:`12.1`. -For frozen lakes with resolved snow layers, the reflectance of the ice -surface is fixed at *a*\ :sub:`0`, and the snow reflectance is -calculated as over non-vegetated surfaces (Chapter :numref:`rst_Surface Albedos`). -These two reflectances are combined to obtain the snow-fraction-weighted albedo as -in over non-vegetated surfaces (Chapter :numref:`rst_Surface Albedos`). +For frozen lakes with resolved snow layers, the reflectance of the ice surface is fixed at *a*\ :sub:`0`, and the snow reflectance is calculated as over non-vegetated surfaces (Chapter :numref:`rst_Surface Albedos`). These two reflectances are combined to obtain the snow-fraction-weighted albedo as in over non-vegetated surfaces (Chapter :numref:`rst_Surface Albedos`). .. _Surface Fluxes and Surface Temperature Lake: @@ -131,75 +54,98 @@ Surface Fluxes and Surface Temperature Surface Properties ^^^^^^^^^^^^^^^^^^^^^^^^ -The fraction of shortwave radiation absorbed at the surface, -:math:`\beta`, depends on the lake state. If resolved snow layers are -present, then :math:`\beta` is set equal to the absorption fraction -predicted by the snow-optics submodel (Chapter :numref:`rst_Surface Albedos`) -for the top snow -layer. Otherwise, :math:`\beta` is set equal to the near infrared -fraction of the shortwave radiation reaching the surface simulated by -the atmospheric model or atmospheric data model used for offline -simulations (Chapter :numref:`rst_Land-only Mode`). The remainder of the shortwave radiation -fraction (1 :math:`{-}` :math:`\beta`) is absorbed in the lake -body or soil as described in section :numref:`Radiation Penetration`. - -The surface roughnesses are functions of the lake state and atmospheric -forcing. For frozen lakes ( :math:`T_{g} \le T_{f}` ) with resolved -snow layers, the momentum roughness length -:math:`z_{0m} =2.4 \times 10^{-3} {\rm m}` (as over non-vegetated -surfaces; Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), and the scalar roughness lengths -(*z*\ :sub:`0q` for latent heat; and *z*\ :sub:`0h`, for sensible heat) are given by -(:ref:`Zilitinkevich 1970`) +The fraction of shortwave radiation absorbed at the surface, :math:`\beta`, depends on the lake state. If resolved snow layers are present, then :math:`\beta` is set equal to the absorption fraction predicted by the snow-optics submodel (Chapter :numref:`rst_Surface Albedos`) for the top snow layer. Otherwise, :math:`\beta` is set equal to the near infrared fraction of the shortwave radiation reaching the surface simulated by the atmospheric model or atmospheric data model used for offline simulations (Chapter :numref:`rst_Land-only Mode`). The remainder of the shortwave radiation fraction (1 :math:`{-}` :math:`\beta`) is absorbed in the lake body or soil as described in section :numref:`Radiation Penetration`. -.. math:: - :label: 12.3 - - \begin{array}{l} {R_{0} =\frac{z_{0m} u_{*} }{\nu } ,} \\ {z_{0h} =z_{0q} =z_{0m} \exp \left\{-0.13R_{0} ^{0.45} \right\}} \end{array} +The surface roughnesses are functions of the lake state and atmospheric forcing. -where :math:`R_{0}` is the near-surface atmospheric roughness -Reynolds number, :math:`z_{0h}` is the roughness -length for sensible heat, :math:`z_{0q}` is the -roughness length for latent heat, :math:`\nu` (m\ :sup:`2` s\ :sup:`-1`) is the kinematic viscosity of air, and -:math:`u_{\*}` (m s\ :sup:`-1`) is the friction velocity in the -atmospheric surface layer. For frozen lakes without resolved snow -layers, :math:`z_{0m} =1\times 10^{-3} {\rm m}` (:ref:`Subin et al. (2012a) `), -and the scalar roughness lengths are given by . - -For unfrozen lakes, *z*\ :sub:`0m` is given by (:ref:`Subin et al. (2012a) `) +For unfrozen lakes (:math:`T_{g} > T_{f}`), :math:`z_{0m}` is given by (:ref:`Subin et al. (2012a) `) .. math:: - :label: 12.4 + :label: 12.3 z_{0m} =\max \left(\frac{\alpha \nu }{u_{*} } ,C\frac{u_{*} ^{2} }{g} \right) -where :math:`\alpha` = 0.1, :math:`\nu` is the kinematic viscosity -of air given below, *C* is the effective Charnock coefficient given -below, and *g* is the acceleration of gravity (:numref:`Table Physical Constants`). The kinematic -viscosity is given by +where :math:`\alpha` = 0.1, :math:`\nu` is the kinematic viscosity of air given below, *C* is the effective Charnock coefficient given below, :math:`u_{*}` is the friction velocity (m/s), and *g* is the acceleration of gravity (:numref:`Table Physical Constants`). The kinematic viscosity is given by .. math:: - :label: 12.5 + :label: 12.4 \nu =\nu _{0} \left(\frac{T_{g} }{T_{0} } \right)^{1.5} \frac{P_{0} }{P_{ref} } where -:math:`\nu _{0} =1.51\times 10^{-5} {\textstyle\frac{{\rm m}^{{\rm 2}} }{{\rm s}}}` +:math:`\nu _{0} =1.51\times 10^{-5} {\textstyle\frac{{\rm m}^{{\rm 2}} }{{\rm s}}}` , :math:`T_{0} ={\rm 293.15\; K}`, :math:`P_{0} =1.013\times 10^{5} {\rm \; Pa}` , and -:math:`P_{ref}` is the pressure at the atmospheric reference -height. The Charnock coefficient *C* is a function of the lake fetch *F* -(m), given in the surface data or set to 25 times the lake depth *d* by -default: +:math:`P_{ref}` is the pressure at the atmospheric reference height. The Charnock coefficient *C* is a function of the lake fetch *F* (m), given in the surface data or set to 25 times the lake depth *d* by default: .. math:: - :label: 12.6 + :label: 12.5 - \begin{array}{l} {C=C_{\min } +(C_{\max } -C_{\min } )\exp \left\{-\min \left(A,B\right)\right\}} \\ {A={\left(\frac{Fg}{u_{\*} ^{2} } \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right. \kern-\nulldelimiterspace} 3} } \mathord{\left/ {\vphantom {\left(\frac{Fg}{u_{\*} ^{2} } \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right. \kern-\nulldelimiterspace} 3} } f_{c} }} \right. \kern-\nulldelimiterspace} f_{c} } } \\ {B=\varepsilon \frac{\sqrt{dg} }{u} } \end{array} + \begin{array}{l} {C=C_{\min } +(C_{\max } -C_{\min } )\exp \left\{-\min \left(A,B\right)\right\}} \\ {A={\left(\frac{Fg}{u_{*} ^{2} } \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right.} 3} } \mathord{\left/ {\vphantom {\left(\frac{Fg}{u_{*} ^{2} } \right)^{{1\mathord{\left/ {\vphantom {1 3}} \right.} 3} } f_{c} }} \right.} f_{c} } } \\ {B=\varepsilon \frac{\sqrt{dg} }{u} } \end{array} where *A* and *B* define the fetch- and depth-limitation, respectively; :math:`C_{\min } =0.01` , :math:`C_{\max } =0.01`, -:math:`\varepsilon =1` , :math:`f_{c} =100` , and *u* (m -s\ :sup:`-1`) is the atmospheric forcing wind. +:math:`\varepsilon =1` , :math:`f_{c} =100` , and +*u* (m s\ :sup:`-1`) is the atmospheric forcing wind. + +The scalar roughness lengths +(:math:`z_{0q}` for latent heat and :math:`z_{0h}` for sensible heat) are given by +(:ref:`Subin et al. 2012a`) + +.. math:: + :label: 12.5a + + \begin{array}{l} {R_{0} =(\frac{z_{0m} u_{*} }{\nu })^{0.5} ,} \\ {z_{0h} =z_{0m} \exp \left\{-\frac{k} {Pr} (4 R_{0} ^{0.5} -3.2) \right\},} \\ {z_{0q} =z_{0m} \exp \left\{-\frac{k} {Sc} (4 R_{0} ^{0.5} - 4.2) \right\}}\end{array} + +where :math:`R_{0}` is the near-surface atmospheric roughness Reynolds number, :math:`k` is the von Karman constant (:numref:`Table Physical Constants`), :math:`Pr = 0.713` is the molecular Prandt number for air at neutral stability, :math:`Sc = 0.66` is the Schmidt number for water in air at neutral stability. +:math:`z_{0q}` and :math:`z_{0h}` are restricted to be no smaller than :math:`1 \times 10^{-10}`. + +For frozen lakes ( :math:`T_{g} \le T_{f}` ) without resolved snow +layers ( :math:`snl = 0` ), :math:`z_{0m} =z_{0m_{ice}} =2.3\times 10^{-3} {\rm m}` (:ref:`Meier et al. (2022) `). + +For frozen lakes with resolved +snow layers ( :math:`snl > 0` ), the momentum roughness length is evaluated based on accumulated snow melt :math:`M_{a} {\rm }` (:ref:`Meier et al. (2022) `). +For :math:`M_{a} >=1\times 10^{-5}` + +.. math:: + :label: 12.5b + + z_{0m} =\exp (b_{1} \tan ^{-1} \left[\frac{log_{10} (M_{a}) + 0.23)} {0.08}\right] + b_{4})\times 10^{-3} + +where :math:`M_{a}` is accumulated snow melt (meters water equivalent), :math:`b_{1} =1.4` and :math:`b_{4} =-0.31`. +For :math:`M_{a} <1\times 10^{-5}` + +.. math:: + :label: 12.5c + + z_{0m} =\exp (-b_{1} 0.5 \pi + b_{4})\times 10^{-3} + +Accumulated snow melt :math:`M_{a}` at the current time step :math:`t` is defined as + +.. math:: + :label: 12.5d + + M ^{t}_{a} = M ^{t-1}_{a} - (q ^{t}_{sno} \Delta t + q ^{t}_{snowmelt} \Delta t)\times 10^{-3} + +where :math:`M ^{t}_{a}` and :math:`M ^{t-1}_{a}` are the accumulated snowmelt at the current time step and previous time step, respectively (m), :math:`q ^{t}_{sno} \Delta t` is the freshly fallen snow (mm), and :math:`q ^{t}_{snowmelt} \Delta t` is the melted snow (mm). + +For frozen lakes without and with resolved snow layers, an initial guess for the scalar roughness lengths is derived by assuming :math:`\theta_{*} = 0 {\rm }` (:ref:`Meier et al. (2022) `) + +.. math:: + :label: 12.5e + + z_{0h}=z_{0q}=\frac{70 \nu}{u_{*}} + +where :math:`\nu=1.5 \times 10^{-5}` is the kinematic viscosity of air (m\ :sup:`2` s\ :sup:`-1`), and +:math:`u_{*}` is the friction velocity in the atmospheric surface layer (m s\ :sup:`-1`). +Thereafter, the scalar roughness lengths are updated within the stability iteration described in section :numref:`Surface Flux Solution Lake` as + +.. math:: + :label: 12.6 + + z_{0h}=z_{0q}=\frac{70 \nu}{u_{*}} \exp (-\beta {u_{*}} ^{0.5} |{\theta_{*}}| ^{0.25} ) + +where :math:`\beta` = 7.2, and :math:`\theta_{*}` is the potential temperature scale (section :numref:`Surface Flux Solution Lake`). .. _Surface Flux Solution Lake: @@ -217,10 +163,8 @@ where :math:`\vec{S}_{g}` \ is the absorbed solar radiation in the lake, :math:`\beta` is the fraction absorbed at the surface, :math:`\vec{L}_{g}` \ is the net emitted longwave radiation (+ upwards), :math:`H_{g}` \ is the sensible heat flux (+ upwards), -:math:`E_{g}` \ is the water vapor flux (+ upwards), and *G* is the -ground heat flux (+ downwards). All of these fluxes depend implicitly on -the temperature at the lake surface :math:`{T}_{g}`. -:math:`\lambda` converts :math:`E_{g}` to an energy flux based on +:math:`E_{g}` \ is the water vapor flux (+ upwards), and +*G* is the ground heat flux (+ downwards). All of these fluxes depend implicitly on the temperature at the lake surface :math:`{T}_{g}`. :math:`\lambda` converts :math:`E_{g}` to an energy flux based on .. math:: :label: 12.8 @@ -234,29 +178,22 @@ The sensible heat flux (W m\ :sup:`-2`) is H_{g} =-\rho _{atm} C_{p} \frac{\left(\theta _{atm} -T_{g} \right)}{r_{ah} } -where :math:`\rho _{atm}` is the density of moist air (kg -m\ :sup:`-3`) (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), :math:`C_{p}` is the specific heat -capacity of air (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical Constants`), -:math:`\theta _{atm}` is the atmospheric potential temperature (K) -(Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), :math:`T_{g}` is the lake surface temperature (K) (at an -infinitesimal interface just above the top resolved model layer: snow, -ice, or water), and :math:`r_{ah}` is the aerodynamic resistance to -sensible heat transfer (s m\ :sup:`-1`) (section :numref:`Monin-Obukhov Similarity Theory`). +where :math:`\rho _{atm}` is the density of moist air (kg m\ :sup:`-3`) (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), +:math:`C_{p}` is the specific heat capacity of air (J kg\ :sup:`-1` K\ :sup:`-1`) (:numref:`Table Physical Constants`), +:math:`\theta _{atm}` is the atmospheric potential temperature (K) (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), +:math:`T_{g}` is the lake surface temperature (K) (at an infinitesimal interface just above the top resolved model layer: snow, ice, or water), and +:math:`r_{ah}` is the aerodynamic resistance to sensible heat transfer (s m\ :sup:`-1`) (section :numref:`Monin-Obukhov Similarity Theory`). The water vapor flux (kg m\ :sup:`-2` s\ :sup:`-1`) is .. math:: - :label: 12.10 + :label: 12.10 E_{g} =-\frac{\rho _{atm} \left(q_{atm} -q_{sat}^{T_{g} } \right)}{r_{aw} } -where :math:`q_{atm}` is the atmospheric specific humidity (kg -kg\ :sup:`-1`) (section :numref:`Atmospheric Coupling`), -:math:`q_{sat}^{T_{g} }` \ is the saturated specific humidity -(kg kg\ :sup:`-1`) (section :numref:`Saturation Vapor Pressure`) at -the lake surface temperature :math:`T_{g}` , and :math:`r_{aw}` is the -aerodynamic resistance to water vapor transfer (s m\ :sup:`-1`) -(section :numref:`Monin-Obukhov Similarity Theory`). +where :math:`q_{atm}` is the atmospheric specific humidity (kg kg\ :sup:`-1`) (section :numref:`Atmospheric Coupling`), +:math:`q_{sat}^{T_{g} }` \ is the saturated specific humidity (kg kg\ :sup:`-1`) (section :numref:`Saturation Vapor Pressure`) at the lake surface temperature :math:`T_{g}`, and +:math:`r_{aw}` is the aerodynamic resistance to water vapor transfer (s m\ :sup:`-1`) (section :numref:`Monin-Obukhov Similarity Theory`). The zonal and meridional momentum fluxes are @@ -270,11 +207,8 @@ The zonal and meridional momentum fluxes are \tau _{y} =-\rho _{atm} \frac{v_{atm} }{r_{atm} } -where :math:`u_{atm}` and :math:`v_{atm}` are the zonal and -meridional atmospheric winds (m s\ :sup:`-1`) (section -:numref:`Atmospheric Coupling`), and -:math:`r_{am}` is the aerodynamic resistance for momentum (s -m\ :sup:`-1`) (section :numref:`Monin-Obukhov Similarity Theory`). +where :math:`u_{atm}` and :math:`v_{atm}` are the zonal and meridional atmospheric winds (m s\ :sup:`-1`) (section :numref:`Atmospheric Coupling`), and +:math:`r_{am}` is the aerodynamic resistance for momentum (s m\ :sup:`-1`) (section :numref:`Monin-Obukhov Similarity Theory`). The heat flux into the lake surface :math:`G` (W m\ :sup:`-2`) is @@ -283,19 +217,7 @@ The heat flux into the lake surface :math:`G` (W m\ :sup:`-2`) is G=\frac{2\lambda _{T} }{\Delta z_{T} } \left(T_{g} -T_{T} \right) -where :math:`\lambda _{T}` is the thermal conductivity (W -m\ :sup:`-1` K\ :sup:`-1`), :math:`\Delta z_{T}` is the -thickness (m), and :math:`T_{T}` is the temperature (K) of the top -resolved lake layer (snow, ice, or water). The top thermal conductivity -:math:`\lambda _{T}` of unfrozen lakes ( :math:`T_{g} >T_{f}` ) -includes conductivities due to molecular ( :math:`\lambda _{liq}` ) and -eddy (:math:`\lambda _{K}` ) diffusivities (section :numref:`Eddy Diffusivity and Thermal Conductivities`), as evaluated -in the top lake layer at the previous timestep, where -:math:`\lambda _{liq}` is the thermal conductivity of water (:numref:`Table Physical Constants`). For frozen lakes without resolved snow layers, -:math:`\lambda _{T} =\lambda _{ice}` . When resolved snow layers are -present, :math:`\lambda _{T}` \ is calculated based on the water -content, ice content, and thickness of the top snow layer, as for -non-vegetated surfaces. +where :math:`\lambda _{T}` is the thermal conductivity (W m\ :sup:`-1` K\ :sup:`-1`), :math:`\Delta z_{T}` is the thickness (m), and :math:`T_{T}` is the temperature (K) of the top resolved lake layer (snow, ice, or water). The top thermal conductivity :math:`\lambda _{T}` of unfrozen lakes ( :math:`T_{g} >T_{f}` ) includes conductivities due to molecular ( :math:`\lambda _{liq}` ) and eddy (:math:`\lambda _{K}` ) diffusivities (section :numref:`Eddy Diffusivity and Thermal Conductivities`), as evaluated in the top lake layer at the previous timestep, where :math:`\lambda _{liq}` is the thermal conductivity of water (:numref:`Table Physical Constants`). For frozen lakes without resolved snow layers, :math:`\lambda _{T} =\lambda _{ice}`. When resolved snow layers are present, :math:`\lambda _{T}` \ is calculated based on the water content, ice content, and thickness of the top snow layer, as for non-vegetated surfaces. The absorbed solar radiation :math:`\vec{S}_{g}` is @@ -304,14 +226,7 @@ The absorbed solar radiation :math:`\vec{S}_{g}` is \vec{S}_{g} =\sum _{\Lambda }S_{atm} \, \downarrow _{\Lambda }^{\mu } \left(1-\alpha _{g,\, \Lambda }^{\mu } \right) +S_{atm} \, \downarrow _{\Lambda } \left(1-\alpha _{g,\, \Lambda } \right) -where :math:`S_{atm} \, \downarrow _{\Lambda }^{\mu }` and -:math:`S_{atm} \, \downarrow _{\Lambda }` are the incident direct beam -and diffuse solar fluxes (W m\ :sup:`-2`) and :math:`\Lambda` -denotes the visible (:math:`<` 0.7\ :math:`\mu {\rm m}`) and -near-infrared (:math:`\ge` 0.7\ :math:`\mu {\rm m}`) wavebands (section -:numref:`Atmospheric Coupling`), and :math:`\alpha _{g,\, \Lambda }^{\mu }` and -:math:`\alpha _{g,\, \mu }` are the direct beam and diffuse lake -albedos (section :numref:`Surface Albedo Lake`). +where :math:`S_{atm} \, \downarrow _{\Lambda }^{\mu }` and :math:`S_{atm} \, \downarrow _{\Lambda }` are the incident direct beam and diffuse solar fluxes (W m\ :sup:`-2`) and :math:`\Lambda` denotes the visible (:math:`<` 0.7\ :math:`\mu {\rm m}`) and near-infrared (:math:`\ge` 0.7\ :math:`\mu {\rm m}`) wavebands (section :numref:`Atmospheric Coupling`), and :math:`\alpha _{g,\, \Lambda }^{\mu }` and :math:`\alpha _{g,\, \mu }` are the direct beam and diffuse lake albedos (section :numref:`Surface Albedo Lake`). The net emitted longwave radiation is @@ -320,10 +235,8 @@ The net emitted longwave radiation is \vec{L}_{g} =L_{g} \, \uparrow -L_{atm} \, \downarrow -where :math:`L_{g} \, \uparrow` is the upward longwave radiation from -the surface, :math:`L_{atm} \, \downarrow` is the downward atmospheric -longwave radiation (section :numref:`Atmospheric Coupling`). The upward -longwave radiation from the surface is +where :math:`L_{g} \, \uparrow` is the upward longwave radiation from the surface, +:math:`L_{atm} \, \downarrow` is the downward atmospheric longwave radiation (section :numref:`Atmospheric Coupling`). The upward longwave radiation from the surface is .. math:: :label: 12.16 @@ -331,26 +244,17 @@ longwave radiation from the surface is L\, \uparrow =\left(1-\varepsilon _{g} \right)L_{atm} \, \downarrow +\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{4} +4\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{3} \left(T_{g}^{n+1} -T_{g}^{n} \right) where :math:`\varepsilon _{g} =0.97` is the lake surface emissivity, -:math:`\sigma` is the Stefan-Boltzmann constant (W m\ :sup:`-2` K\ -:sup:`-4`) (:numref:`Table Physical Constants`), and -:math:`T_{g}^{n+1} -T_{g}^{n}` is the difference in lake surface -temperature between Newton-Raphson iterations (see below). +:math:`\sigma` is the Stefan-Boltzmann constant (W m\ :sup:`-2` K\ :sup:`-4`) (:numref:`Table Physical Constants`), and +:math:`T_{g}^{n+1} -T_{g}^{n}` is the difference in lake surface temperature between Newton-Raphson iterations (see below). -The sensible heat :math:`H_{g}` , the water vapor flux :math:`E_{g}` -through its dependence on the saturated specific humidity, the net -longwave radiation :math:`\vec{L}_{g}` , and the ground heat flux -:math:`G`, all depend on the lake surface temperature :math:`T_{g}` . -Newton-Raphson iteration is applied to solve for :math:`T_{g}` and the -surface fluxes as +The sensible heat :math:`H_{g}`, the water vapor flux :math:`E_{g}` through its dependence on the saturated specific humidity, the net longwave radiation :math:`\vec{L}_{g}`, and the ground heat flux :math:`G`, all depend on the lake surface temperature :math:`T_{g}`. Newton-Raphson iteration is applied to solve for :math:`T_{g}` and the surface fluxes as .. math:: :label: 12.17 \Delta T_{g} =\frac{\beta \overrightarrow{S}_{g} -\overrightarrow{L}_{g} -H_{g} -\lambda E_{g} -G}{\frac{\partial \overrightarrow{L}_{g} }{\partial T_{g} } +\frac{\partial H_{g} }{\partial T_{g} } +\frac{\partial \lambda E_{g} }{\partial T_{g} } +\frac{\partial G}{\partial T_{g} } } -where :math:`\Delta T_{g} =T_{g}^{n+1} -T_{g}^{n}` and the subscript -“n” indicates the iteration. Therefore, the surface temperature -:math:`T_{g}^{n+1}` can be written as +where :math:`\Delta T_{g} =T_{g}^{n+1} -T_{g}^{n}` and the subscript "n" indicates the iteration. Therefore, the surface temperature :math:`T_{g}^{n+1}` can be written as .. math:: :label: 12.18 @@ -379,26 +283,11 @@ where the partial derivatives are \frac{\partial G}{\partial T_{g} } =\frac{2\lambda _{T} }{\Delta z_{T} } . -The fluxes of momentum, sensible heat, and water vapor are solved for -simultaneously with lake surface temperature as follows. The -stability-related equations are the same as for non-vegetated surfaces -(section :numref:`Sensible and Latent Heat Fluxes for Non-Vegetated Surfaces`), -except that the surface roughnesses are here (weakly varying) functions -of the friction velocity :math:`u_{\*}` . To begin, *z*\ :sub:`0m` is set -based on the value calculated for the last timestep (for -:math:`T_{g} >T_{f}` ) or based on the values in section -:numref:`Surface Properties Lake` (otherwise), and the scalar roughness -lengths are set based on the relationships in section :numref:`Surface Properties Lake`. - -#. An initial guess for the wind speed :math:`V_{a}` including the - convective velocity :math:`U_{c}` is obtained from :eq:`5.24` assuming an - initial convective velocity :math:`U_{c} =0` m s\ :sup:`-1` for - stable conditions (:math:`\theta _{v,\, atm} -\theta _{v,\, s} \ge 0` - as evaluated from :eq:`5.50`) and :math:`U_{c} =0.5` for unstable - conditions (:math:`\theta _{v,\, atm} -\theta _{v,\, s} <0`). - -#. An initial guess for the Monin-Obukhov length :math:`L` is obtained - from the bulk Richardson number using :eq:`5.46` and :eq:`5.48`. +The fluxes of momentum, sensible heat, and water vapor are solved for simultaneously with lake surface temperature as follows. To begin, :math:`z_{0m}` and the scalar roughness lengths are set as described in section :numref:`Surface Properties Lake`. + +#. An initial guess for the wind speed :math:`V_{a}` including the convective velocity :math:`U_{c}` is obtained from :eq:`5.24` assuming an initial convective velocity :math:`U_{c} =0` m s\ :sup:`-1` for stable conditions (:math:`\theta _{v,\, atm} -\theta _{v,\, s} \ge 0` as evaluated from :eq:`5.50`) and :math:`U_{c} =0.5` for unstable conditions (:math:`\theta _{v,\, atm} -\theta _{v,\, s} <0`). + +#. An initial guess for the Monin-Obukhov length :math:`L` is obtained from the bulk Richardson number using :eq:`5.46` and :eq:`5.48`. #. The following system of equations is iterated four times: @@ -406,73 +295,49 @@ lengths are set based on the relationships in section :numref:`Surface Propertie #. Thermal conductivity :math:`\lambda _{T}` \ (above) -#. Friction velocity :math:`u_{\*}` (:eq:`5.32`, :eq:`5.33`, :eq:`5.34`, :eq:`5.35`) +#. Friction velocity :math:`u_{*}` (:eq:`5.32`, :eq:`5.33`, :eq:`5.34`, :eq:`5.35`) -#. Potential temperature scale :math:`\theta _{\*}` (:eq:`5.37` , :eq:`5.38`, :eq:`5.39`, :eq:`5.40`) +#. Potential temperature scale :math:`\theta _{*}` (:eq:`5.37`, :eq:`5.38`, :eq:`5.39`, :eq:`5.40`) -#. Humidity scale :math:`q_{\*}` (:eq:`5.41`, :eq:`5.42`, :eq:`5.43`, :eq:`5.44`) +#. Humidity scale :math:`q_{*}` (:eq:`5.41`, :eq:`5.42`, :eq:`5.43`, :eq:`5.44`) -#. Aerodynamic resistances :math:`r_{am}` , :math:`r_{ah}` , and - :math:`r_{aw}` (:eq:`5.55`, :eq:`5.56`, :eq:`5.57`) +#. Aerodynamic resistances :math:`r_{am}`, :math:`r_{ah}`, and :math:`r_{aw}` (:eq:`5.55`, :eq:`5.56`, :eq:`5.57`) -#. Lake surface temperature :math:`T_{g}^{n+1}` (:eq:`12.18`) +#. Lake surface temperature :math:`T_{g}^{n+1}` (:eq:`12.18`) -#. Heat of vaporization / sublimation :math:`\lambda` (:eq:`12.8`) +#. Heat of vaporization / sublimation :math:`\lambda` (:eq:`12.8`) -#. Sensible heat flux :math:`H_{g}` is updated for :math:`T_{g}^{n+1}` - (:eq:`12.9`) +#. Sensible heat flux :math:`H_{g}` is updated for :math:`T_{g}^{n+1}` (:eq:`12.9`) -#. Water vapor flux :math:`E_{g}` is updated for :math:`T_{g}^{n+1}` - as +#. Water vapor flux :math:`E_{g}` is updated for :math:`T_{g}^{n+1}` as .. math:: :label: 12.23 E_{g} =-\frac{\rho _{atm} }{r_{aw} } \left[q_{atm} -q_{sat}^{T_{g} } -\frac{\partial q_{sat}^{T_{g} } }{\partial T_{g} } \left(T_{g}^{n+1} -T_{g}^{n} \right)\right] -where the last term on the right side of equation is the change in -saturated specific humidity due to the change in :math:`T_{g}` between -iterations. +where the last term on the right side of equation :eq:`12.23` is the change in saturated specific humidity due to the change in :math:`T_{g}` between iterations. -#. Saturated specific humidity :math:`q_{sat}^{T_{g} }` and its - derivative :math:`\frac{dq_{sat}^{T_{g} } }{dT_{g} }` are updated - for :math:`T_{g}^{n+1}` (section :numref:`Monin-Obukhov Similarity Theory`). +#. Saturated specific humidity :math:`q_{sat}^{T_{g} }` and its derivative :math:`\frac{dq_{sat}^{T_{g} } }{dT_{g} }` are updated for :math:`T_{g}^{n+1}` (section :numref:`Monin-Obukhov Similarity Theory`). -#. Virtual potential temperature scale :math:`\theta _{v\*}` (:eq:`5.17`) +#. Virtual potential temperature scale :math:`\theta _{v*}` (:eq:`5.17`) -#. Wind speed including the convective velocity, :math:`V_{a}` (:eq:`5.24`) +#. Wind speed including the convective velocity, :math:`V_{a}` (:eq:`5.24`) #. Monin-Obukhov length :math:`L` (:eq:`5.49`) -#. Roughness lengths (:eq:`12.3`, :eq:`12.4`). +#. Roughness lengths (section :numref:`Surface Properties Lake`). -Once the four iterations for lake surface temperature have been yielded -a tentative solution :math:`T_{g} ^{{'} }` , several restrictions -are imposed in order to maintain consistency with the top lake model -layer temperature :math:`T_{T}` \ (:ref:`Subin et al. (2012a) `). +Once the four iterations for lake surface temperature have been yielded a tentative solution :math:`T_{g} ^{{'} }`, several restrictions are imposed in order to maintain consistency with the top lake model layer temperature :math:`T_{T}` \ (:ref:`Subin et al. (2012a) `). .. math:: :label: 12.24 \begin{array}{l} {{\rm 1)\; }T_{T} \le T_{f} T_{g} ^{{'} } >T_{m} \Rightarrow T_{g} =T_{T} ,} \\ {{\rm 3)\; }T_{m} >T_{g} ^{{'} } >T_{T} >T_{f} \Rightarrow T_{g} =T_{T} } \end{array} -where :math:`T_{m}` \ is the temperature of maximum liquid water -density, 3.85\ :sup:`o` C (:ref:`Hostetler and Bartlein (1990) `). The -first condition requires that, if there is any snow or ice present, the -surface temperature is restricted to be less than or equal to freezing. -The second and third conditions maintain convective stability in the top -lake layer. - -If eq. XXX is applied, the turbulent fluxes :math:`H_{g}` and -:math:`E_{g}` are re-evaluated. The emitted longwave radiation and -the momentum fluxes are re-evaluated in any case. The final ground heat -flux :math:`G` is calculated from the residual of the energy balance eq. -XXX in order to precisely conserve energy. XXX This ground heat flux -is taken as a prescribed flux boundary condition for the lake -temperature solution (section :numref:`Boundary Conditions Lake`). -An energy balance check is -included at each timestep to insure that eq. XXX is obeyed to within -0.1 W m\ :sup:`-2`. +where :math:`T_{m}` \ is the temperature of maximum liquid water density, 3.85°C (:ref:`Hostetler and Bartlein (1990) `). The first condition requires that, if there is any snow or ice present, the surface temperature is restricted to be less than or equal to freezing. The second and third conditions maintain convective stability in the top lake layer. + +If equation :eq:`12.24` is applied, the turbulent fluxes :math:`H_{g}` and :math:`E_{g}` are re-evaluated. The emitted longwave radiation and the momentum fluxes are re-evaluated in any case. The final ground heat flux :math:`G` is calculated from the residual of the energy balance (equation :eq:`12.7`) in order to precisely conserve energy. This ground heat flux is taken as a prescribed flux boundary condition for the lake temperature solution (section :numref:`Boundary Conditions Lake`). A check is included at each timestep to insure that energy balance is obeyed to within 0.1 W m\ :sup:`-2` (see :numref:`Energy Conservation Lake`). .. _Lake Temperature: @@ -484,97 +349,55 @@ Lake Temperature Introduction ^^^^^^^^^^^^^^^^^^ -The (optional-) snow, lake body (water and/or ice), soil, and bedrock -system is unified for the lake temperature solution. The governing -equation, similar to that for the snow-soil-bedrock system for vegetated -land units (Chapter :numref:`rst_Soil and Snow Temperatures`), is +The (optional-) snow, lake body (water and/or ice), soil, and bedrock system is unified for the lake temperature solution. The governing equation, similar to that for the snow-soil-bedrock system for vegetated land units (Chapter :numref:`rst_Soil and Snow Temperatures`), is .. math:: :label: 12.25 \tilde{c}_{v} \frac{\partial T}{\partial t} =\frac{\partial }{\partial z} \left(\tau \frac{\partial T}{\partial z} \right)-\frac{d\phi }{dz} -where :math:`\tilde{c}_{v}` is the volumetric heat capacity (J -m\ :sup:`-3` K\ :sup:`-1`), :math:`t` is time (s), *T* is -the temperature (K), :math:`\tau` is the thermal conductivity (W -m\ :sup:`-1` K\ :sup:`-1`), and :math:`\phi` is the solar -radiation (W m\ :sup:`-2`) penetrating to depth *z* (m). The -system is discretized into *N* layers, where +where :math:`\tilde{c}_{v}` is the volumetric heat capacity (J m\ :sup:`-3` K\ :sup:`-1`), +:math:`t` is time (s), +*T* is the temperature (K), +:math:`\tau` is the thermal conductivity (W m\ :sup:`-1` K\ :sup:`-1`), and +:math:`\phi` is the solar radiation (W m\ :sup:`-2`) penetrating to depth *z* (m). The system is discretized into *N* layers, where .. math:: :label: 12.26 N=n_{sno} +N_{levlak} +N_{levgrnd} , -:math:`n_{sno}` is the number of actively modeled snow layers at the -current timestep (Chapter :numref:`rst_Snow Hydrology`), and -:math:`N_{levgrnd}` \ is as for vegetated land units (Chapter -:numref:`rst_Soil and Snow Temperatures`). Energy is conserved as +:math:`n_{sno}` is the number of actively modeled snow layers at the current timestep (Chapter :numref:`rst_Snow Hydrology`), and +:math:`N_{levgrnd}` \ is as for vegetated land units (Chapter :numref:`rst_Soil and Snow Temperatures`). Energy is conserved as .. math:: :label: 12.27 \frac{d}{dt} \sum _{j=1}^{N}\left[\tilde{c}_{v,j} (t)\left(T_{j} -T_{f} \right)+L_{j} (t)\right] \Delta z_{j} =G+\left(1-\beta \right)\vec{S}_{g} -where :math:`\tilde{c}_{v,j} (t)`\ is the volumetric heat capacity of -the *j*\ th layer (section :numref:`Radiation Penetration`), -:math:`L_{j} (t)`\ is the latent heat -of fusion per unit volume of the *j*\ th layer (proportional to the mass -of liquid water present), and the right-hand side represents the net -influx of energy to the lake system. Note that -:math:`\tilde{c}_{v,j} (t)` can only change due to phase change (except -for changing snow layer mass, which, apart from energy required to melt -snow, represents an untracked energy flux in the land model, along with -advected energy associated with water flows in general), and this is -restricted to occur at :math:`T_{j} =T_{f}` \ in the snow-lake-soil -system, allowing eq. to be precisely enforced and justifying the -exclusion of :math:`c_{v,j}` from the time derivative in eq. . +where :math:`\tilde{c}_{v,j} (t)`\ is the volumetric heat capacity of the *j*\ th layer (section :numref:`Radiation Penetration`), :math:`L_{j} (t)`\ is the latent heat of fusion per unit volume of the *j*\ th layer (proportional to the mass of liquid water present), and the right-hand side represents the net influx of energy to the lake system. Note that :math:`\tilde{c}_{v,j} (t)` can only change due to phase change (except for changing snow layer mass, which, apart from energy required to melt snow, represents an untracked energy flux in the land model, along with advected energy associated with water flows in general), and this is restricted to occur at :math:`T_{j} =T_{f}` \ in the snow-lake-soil system, allowing eq. to be precisely enforced and justifying the exclusion of :math:`c_{v,j}` from the time derivative in eq.. .. _Overview of Changes from CLM4 2: Overview of Changes from CLM4 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Thermal conductivities include additional eddy diffusivity, beyond the -:ref:`Hostetler and Bartlein (1990)` formulation, -due to unresolved processes (:ref:`Fang and Stefan 1996`; -:ref:`Subin et al. (2012a) `). Lake water is now allowed to -freeze by an arbitrary fraction for each layer, which releases latent -heat and changes thermal properties. Convective mixing occurs for all -lakes, even if frozen. Soil and bedrock are included beneath the lake. -The full snow model is used if the snow thickness exceeds a threshold; -if there are resolved snow layers, radiation transfer is predicted by -the snow-optics submodel (Chapter :numref:`rst_Surface Albedos`), and the remaining radiation -penetrating the bottom snow layer is absorbed in the top layer of lake -ice; conversely, if there are no snow layers, the solar radiation -penetrating the bottom lake layer is absorbed in the top soil layer. The -lakes have variable depth, and all physics is assumed valid for -arbitrary depth, except for a depth-dependent enhanced mixing (section -:numref:`Eddy Diffusivity and Thermal Conductivities`). Finally, a previous sign error in the calculation of eddy -diffusivity (specifically, the Brunt-Väisälä frequency term; eq. ) was -corrected. +Thermal conductivities include additional eddy diffusivity, beyond the :ref:`Hostetler and Bartlein (1990)` formulation, due to unresolved processes (:ref:`Fang and Stefan 1996`; :ref:`Subin et al. (2012a) `). Lake water is now allowed to freeze by an arbitrary fraction for each layer, which releases latent heat and changes thermal properties. Convective mixing occurs for all lakes, even if frozen. Soil and bedrock are included beneath the lake. The full snow model is used if the snow thickness exceeds a threshold; if there are resolved snow layers, radiation transfer is predicted by the snow-optics submodel (Chapter :numref:`rst_Surface Albedos`), and the remaining radiation penetrating the bottom snow layer is absorbed in the top layer of lake ice; conversely, if there are no snow layers, the solar radiation penetrating the bottom lake layer is absorbed in the top soil layer. The lakes have variable depth, and all physics is assumed valid for arbitrary depth, except for a depth-dependent enhanced mixing (section :numref:`Eddy Diffusivity and Thermal Conductivities`). Finally, a previous sign error in the calculation of eddy diffusivity (specifically, the Brunt-Väisälä frequency term; eq. ) was corrected. .. _Boundary Conditions Lake: Boundary Conditions ^^^^^^^^^^^^^^^^^^^^^^^^^ -The top boundary condition, imposed at the top modeled layer -:math:`i=j_{top}` , where :math:`j_{top} =-n_{sno} +1`, is the downwards -surface flux *G* defined by the energy flux residual during the surface -temperature solution (section :numref:`Boundary Conditions Lake`). The bottom -boundary condition, imposed at :math:`i=N_{levlak} +N_{levgrnd}` , is zero flux. -The 2-m windspeed :math:`u_{2}` \ (m s\ :sup:`-1`) is used in the -calculation of eddy diffusivity: +The top boundary condition, imposed at the top modeled layer :math:`i=j_{top}`, where :math:`j_{top} =-n_{sno} +1`, is the downwards surface flux *G* defined by the energy flux residual during the surface temperature solution (section :numref:`Boundary Conditions Lake`). The bottom boundary condition, imposed at :math:`i=N_{levlak} +N_{levgrnd}`, is zero flux. The 2-m windspeed :math:`u_{2}` \ (m s\ :sup:`-1`) is used in the calculation of eddy diffusivity: .. math:: :label: 12.28 u_{2} =\frac{u_{*} }{k} \ln \left(\frac{2}{z_{0m} } \right)\ge 0.1. -where :math:`u_{*}` \ is the friction velocity calculated in section -:numref:`Boundary Conditions Lake` and *k* is the von Karman constant -(:numref:`Table Physical Constants`). +where :math:`u_{*}` \ is the friction velocity calculated in section :numref:`Boundary Conditions Lake` and +*k* is the von Karman constant (:numref:`Table Physical Constants`). .. _Eddy Diffusivity and Thermal Conductivities: @@ -588,19 +411,10 @@ The total eddy diffusivity :math:`K_{W}` (m\ :sup:`2` s\ :sup:`-1`) for liquid K_{W} = m_{d} \left(\kappa _{e} +K_{ed} +\kappa _{m} \right) -where :math:`\kappa _{e}` is due to wind-driven eddies -(:ref:`Hostetler and Bartlein (1990)`), -:math:`K_{ed}` is a modest enhanced diffusivity -intended to represent unresolved mixing processes -(:ref:`Fang and Stefan 1996`), -:math:`\kappa _{m} =\frac{\lambda _{liq} }{c_{liq} \rho _{liq} }` \ is -the molecular diffusivity of water (given by the ratio of its thermal -conductivity (W m\ :sup:`-1` K\ :sup:`-1`) to the product of -its heat capacity (J kg\ :sup:`-1` K\ :sup:`-1`) and density -(kg m\ :sup:`-3`), values given in :numref:`Table Physical Constants`), and :math:`m_{d}` -(unitless) is a factor which increases the overall diffusivity for large -lakes, intended to represent 3-dimensional mixing processes such as -caused by horizontal temperature gradients. As currently implemented, +where :math:`\kappa _{e}` is due to wind-driven eddies (:ref:`Hostetler and Bartlein (1990)`), +:math:`K_{ed}` is a modest enhanced diffusivity intended to represent unresolved mixing processes (:ref:`Fang and Stefan 1996`), +:math:`\kappa _{m} =\frac{\lambda _{liq} }{c_{liq} \rho _{liq} }` \ is the molecular diffusivity of water (given by the ratio of its thermal conductivity (W m\ :sup:`-1` K\ :sup:`-1`) to the product of its heat capacity (J kg\ :sup:`-1` K\ :sup:`-1`) and density (kg m\ :sup:`-3`), values given in :numref:`Table Physical Constants`), and +:math:`m_{d}` (unitless) is a factor which increases the overall diffusivity for large lakes, intended to represent 3-dimensional mixing processes such as caused by horizontal temperature gradients. As currently implemented, .. math:: :label: 12.30 @@ -616,18 +430,7 @@ The wind-driven eddy diffusion coefficient :math:`\kappa _{e,\, i}` (m\ :sup:`2` \kappa _{e,\, i} =\left\{\begin{array}{l} {\frac{kw^{*} z_{i} }{P_{0} \left(1+37Ri^{2} \right)} \exp \left(-k^{*} z_{i} \right)\qquad T_{g} >T_{f} } \\ {0\qquad T_{g} \le T_{f} } \end{array}\right\} -where :math:`P_{0} =1` is the neutral value of the turbulent Prandtl -number, :math:`z_{i}` is the node depth (m), the surface friction -velocity (m s\ :sup:`-1`) is :math:`w^{*} =0.0012u_{2}` , and -:math:`k^{*}` varies with latitude :math:`\phi` as -:math:`k^{*} =6.6u_{2}^{-1.84} \sqrt{\left|\sin \phi \right|}` . For the -bottom layer, -:math:`\kappa _{e,\, N_{levlak} } =\kappa _{e,N_{levlak} -1\, }` . As in -:ref:`Hostetler and Bartlein (1990)`, -the 2-m wind speed :math:`u_{2}` (m s\ :sup:`-1`) (eq. ) is used to evaluate -:math:`w^{*}` and :math:`k^{*}` rather than the 10-m wind used by -:ref:`Henderson-Sellers (1985) `. - +where :math:`P_{0} =1` is the neutral value of the turbulent Prandtl number, :math:`z_{i}` is the node depth (m), the surface friction velocity (m s\ :sup:`-1`) is :math:`w^{*} =0.0012u_{2}`, and :math:`k^{*}` varies with latitude :math:`\phi` as :math:`k^{*} =6.6u_{2}^{-1.84} \sqrt{\left|\sin \phi \right|}`. For the bottom layer, :math:`\kappa _{e,\, N_{levlak} } =\kappa _{e,N_{levlak} -1\, }`. As in :ref:`Hostetler and Bartlein (1990)`, the 2-m wind speed :math:`u_{2}` (m s\ :sup:`-1`) (eq. ) is used to evaluate :math:`w^{*}` and :math:`k^{*}` rather than the 10-m wind used by :ref:`Henderson-Sellers (1985) `. The Richardson number is @@ -643,93 +446,55 @@ where N^{2} =\frac{g}{\rho _{i} } \frac{\partial \rho }{\partial z} -and :math:`g` is the acceleration due to gravity (m s\ :sup:`-2`) -(:numref:`Table Physical Constants`), :math:`\rho _{i}` is the density of water (kg -m\ :sup:`-3`), and :math:`\frac{\partial \rho }{\partial z}` is -approximated as -:math:`\frac{\rho _{i+1} -\rho _{i} }{z_{i+1} -z_{i} }` . Note that -because here, *z* is increasing downwards (unlike in :ref:`Hostetler and Bartlein (1990)`), eq. contains no negative sign; this is a correction -from CLM4. The density of water is -(:ref:`Hostetler and Bartlein (1990)`) +and :math:`g` is the acceleration due to gravity (m s\ :sup:`-2`) (:numref:`Table Physical Constants`), :math:`\rho _{i}` is the density of water (kg m\ :sup:`-3`), and :math:`\frac{\partial \rho }{\partial z}` is approximated as :math:`\frac{\rho _{i+1} -\rho _{i} }{z_{i+1} -z_{i} }`. Note that because here, *z* is increasing downwards (unlike in :ref:`Hostetler and Bartlein (1990)`), eq. contains no negative sign; this is a correction from CLM4. The density of water is (:ref:`Hostetler and Bartlein (1990)`) .. math:: :label: 12.34 \rho _{i} =1000\left(1-1.9549\times 10^{-5} \left|T_{i} -277\right|^{1.68} \right). -The enhanced diffusivity :math:`K_{ed}` is given by -(:ref:`Fang and Stefan 1996`) +The enhanced diffusivity :math:`K_{ed}` is given by (:ref:`Fang and Stefan 1996`) .. math:: :label: 12.35 K_{ed} =1.04\times 10^{-8} \left(N^{2} \right)^{-0.43} ,N^{2} \ge 7.5\times 10^{-5} {\rm s}^{2} -where :math:`N^{2}` \ is calculated as in eq. except for the minimum -value imposed in . +where :math:`N^{2}` \ is calculated as in eq. except for the minimum value imposed in. -The thermal conductivity for the liquid water portion of lake body layer -*i*, :math:`\tau _{liq,i}` (W m\ :sup:`-1` K\ :sup:`-1`) is -given by +The thermal conductivity for the liquid water portion of lake body layer *i*, :math:`\tau _{liq,i}` (W m\ :sup:`-1` K\ :sup:`-1`) is given by .. math:: :label: 12.36 \tau _{liq,i} =K_{W} c_{liq} \rho _{liq} . -The thermal conductivity of the ice portion of lake body layer *i*, -:math:`\tau _{ice,eff}` \ (W m\ :sup:`-1` K\ :sup:`-1`), is -constant among layers, and is given by +The thermal conductivity of the ice portion of lake body layer *i*, :math:`\tau _{ice,eff}` \ (W m\ :sup:`-1` K\ :sup:`-1`), is constant among layers, and is given by .. math:: :label: 12.37 \tau _{ice,eff} =\tau _{ice} \frac{\rho _{ice} }{\rho _{liq} } -where :math:`\tau _{ice}` \ (:numref:`Table Physical Constants`) is the nominal thermal -conductivity of ice: :math:`\tau _{ice,eff}` \ is adjusted for the fact -that the nominal model layer thicknesses remain constant even while the -physical ice thickness exceeds the water thickness. +where :math:`\tau _{ice}` \ (:numref:`Table Physical Constants`) is the nominal thermal conductivity of ice: :math:`\tau _{ice,eff}` \ is adjusted for the fact that the nominal model layer thicknesses remain constant even while the physical ice thickness exceeds the water thickness. -The overall thermal conductivity :math:`\tau _{i}` for layer *i* with -ice mass-fraction :math:`I_{i}` is the harmonic mean of the liquid -and water fractions, assuming that they will be physically vertically -stacked, and is given by +The overall thermal conductivity :math:`\tau _{i}` for layer *i* with ice mass-fraction :math:`I_{i}` is the harmonic mean of the liquid and water fractions, assuming that they will be physically vertically stacked, and is given by .. math:: :label: 12.38 \tau _{i} =\frac{\tau _{ice,eff} \tau _{liq,i} }{\tau _{liq,i} I_{i} +\tau _{ice} \left(1-I_{i} \right)} . -The thermal conductivity of snow, soil, and bedrock layers above and -below the lake, respectively, are computed identically to those for -vegetated land units (Chapter :numref:`rst_Soil and Snow Temperatures`), except for the adjustment of thermal -conductivity for frost heave or excess ice (:ref:`Subin et al., 2012a, -Supporting Information`). +The thermal conductivity of snow, soil, and bedrock layers above and below the lake, respectively, are computed identically to those for vegetated land units (Chapter :numref:`rst_Soil and Snow Temperatures`), except for the adjustment of thermal conductivity for frost heave or excess ice (:ref:`Subin et al., 2012a, Supporting Information`). .. _Radiation Penetration: Radiation Penetration ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If there are no resolved snow layers, the surface absorption fraction :math:`\beta` is set according to the near-infrared fraction simulated -by the atmospheric model. This is apportioned to the surface energy -budget (section :numref:`Surface Properties Lake`), and thus no additional radiation is absorbed in -the top :math:`z_{a}` (currently 0.6 m) of unfrozen lakes, for which -the light extinction coefficient :math:`\eta` (m\ :sup:`-1`) -varies between lake columns (eq. ). For frozen lakes -(:math:`T_{g} \le T_{f}` ), the remaining :math:`\left(1-\beta \right)\vec{S}_{g}` fraction of surface absorbed -radiation that is not apportioned to the surface energy budget is -absorbed in the top lake body layer. This is a simplification, as lake -ice is partially transparent. If there are resolved snow layers, then -the snow optics submodel (Chapter :numref:`rst_Surface Albedos`) is used to calculate the snow layer -absorption (except for the absorption predicted for the top layer by the -snow optics submodel, which is assigned to the surface energy budget), -with the remainder penetrating snow layers absorbed in the top lake body -ice layer. - -For unfrozen lakes, the solar radiation remaining at depth -:math:`z>z_{a}` in the lake body is given by +If there are no resolved snow layers, the surface absorption fraction :math:`\beta` is set according to the near-infrared fraction simulated by the atmospheric model. This is apportioned to the surface energy budget (section :numref:`Surface Properties Lake`), and thus no additional radiation is absorbed in the top :math:`z_{a}` (currently 0.6 m) of unfrozen lakes, for which the light extinction coefficient :math:`\eta` (m\ :sup:`-1`) varies between lake columns (eq. ). For frozen lakes (:math:`T_{g} \le T_{f}` ), the remaining :math:`\left(1-\beta \right)\vec{S}_{g}` fraction of surface absorbed radiation that is not apportioned to the surface energy budget is absorbed in the top lake body layer. This is a simplification, as lake ice is partially transparent. If there are resolved snow layers, then the snow optics submodel (Chapter :numref:`rst_Surface Albedos`) is used to calculate the snow layer absorption (except for the absorption predicted for the top layer by the snow optics submodel, which is assigned to the surface energy budget), with the remainder penetrating snow layers absorbed in the top lake body ice layer. + +For unfrozen lakes, the solar radiation remaining at depth :math:`z>z_{a}` in the lake body is given by .. math:: :label: 12.39 @@ -744,14 +509,9 @@ For all lake body layers, the flux absorbed by the layer *i*, \phi _{i} =\left(1-\beta \vec{S}_{g} \right)\left[\exp \left\{-\eta \left(z_{i} -\frac{\Delta z_{i} }{2} -z_{a} \right)\right\}-\exp \left\{-\eta \left(z_{i} +\frac{\Delta z_{i} }{2} -z_{a} \right)\right\}\right] . -The argument of each exponent is constrained to be non-negative (so -:math:`\phi _{i}` = 0 for layers contained within :math:`{z}_{a}`). -The remaining flux exiting the bottom of layer :math:`i=N_{levlak}` is -absorbed in the top soil layer. +The argument of each exponent is constrained to be non-negative (so :math:`\phi _{i}` = 0 for layers contained within :math:`{z}_{a}`). The remaining flux exiting the bottom of layer :math:`i=N_{levlak}` is absorbed in the top soil layer. -The light extinction coefficient :math:`\eta` (m\ :sup:`-1`), if -not provided as external data, is a function of depth *d* (m) -(:ref:`Subin et al. (2012a) `): +The light extinction coefficient :math:`\eta` (m\ :sup:`-1`), if not provided as external data, is a function of depth *d* (m) (:ref:`Subin et al. (2012a) `): .. math:: :label: 12.41 @@ -763,79 +523,50 @@ not provided as external data, is a function of depth *d* (m) Heat Capacities ^^^^^^^^^^^^^^^^^^^^^ -The vertically-integrated heat capacity for each lake layer, -:math:`\text{c}_{v,i}` (J m\ :sup:`-2`) is determined by the mass-weighted average over the heat capacities for the -water and ice fractions: +The vertically-integrated heat capacity for each lake layer, :math:`\text{c}_{v,i}` (J m\ :sup:`-2`) is determined by the mass-weighted average over the heat capacities for the water and ice fractions: .. math:: :label: 12.42 c_{v,i} =\Delta z_{i} \rho _{liq} \left[c_{liq} \left(1-I_{i} \right)+c_{ice} I_{i} \right] . -Note that the density of water is used for both ice and water fractions, -as the thickness of the layer is fixed. +Note that the density of water is used for both ice and water fractions, as the thickness of the layer is fixed. -The total heat capacity :math:`c_{v,i}` for each soil, snow, and -bedrock layer (J m\ :sup:`-2`) is determined as for vegetated land -units (Chapter :numref:`rst_Soil and Snow Temperatures`), as the sum of the heat capacities for the water, ice, -and mineral constituents. +The total heat capacity :math:`c_{v,i}` for each soil, snow, and bedrock layer (J m\ :sup:`-2`) is determined as for vegetated land units (Chapter :numref:`rst_Soil and Snow Temperatures`), as the sum of the heat capacities for the water, ice, and mineral constituents. .. _Crank-Nicholson Solution Lake: Crank-Nicholson Solution ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The solution method for thermal diffusion is similar to that used for -soil (Chapter :numref:`rst_Soil and Snow Temperatures`), except that the -lake body layers are sandwiched between the snow and soil layers -(section :numref:`Introduction Lake`), and radiation flux is -absorbed throughout the lake layers. Before solution, layer temperatures -:math:`T_{i}` (K), thermal conductivities :math:`\tau _{i}` (W -m\ :sup:`-1` K\ :sup:`-1`), heat capacities :math:`c_{v,i}` -(J m\ :sup:`-2`), and layer and interface depths from all -components are transformed into a uniform set of vectors with length -:math:`N=n_{sno} +N_{levlak} +N_{levgrnd}` and consistent units to -simplify the solution. Thermal conductivities at layer interfaces are -calculated as the harmonic mean of the conductivities of the neighboring -layers: +The solution method for thermal diffusion is similar to that used for soil (Chapter :numref:`rst_Soil and Snow Temperatures`), except that the lake body layers are sandwiched between the snow and soil layers (section :numref:`Introduction Lake`), and radiation flux is absorbed throughout the lake layers. Before solution, layer temperatures :math:`T_{i}` (K), thermal conductivities :math:`\tau _{i}` (W m\ :sup:`-1` K\ :sup:`-1`), heat capacities :math:`c_{v,i}` (J m\ :sup:`-2`), and layer and interface depths from all components are transformed into a uniform set of vectors with length :math:`N=n_{sno} +N_{levlak} +N_{levgrnd}` and consistent units to simplify the solution. Thermal conductivities at layer interfaces are calculated as the harmonic mean of the conductivities of the neighboring layers: .. math:: :label: 12.43 \lambda _{i} =\frac{\tau _{i} \tau _{i+1} \left(z_{i+1} -z_{i} \right)}{\tau _{i} \left(z_{i+1} -\hat{z}_{i} \right)+\tau _{i+1} \left(\hat{z}_{i} -z_{i} \right)} , -where :math:`\lambda _{i}` is the conductivity at the interface between -layer *i* and layer *i +* 1, :math:`z_{i}` is the depth of the node of -layer *i*, and :math:`\hat{z}_{i}` is the depth of the interface below -layer *i*. Care is taken at the boundaries between snow and lake and -between lake and soil. The governing equation is discretized for each -layer as +where :math:`\lambda _{i}` is the conductivity at the interface between layer *i* and layer *i +* 1, +:math:`z_{i}` is the depth of the node of layer *i*, and +:math:`\hat{z}_{i}` is the depth of the interface below layer *i*. Care is taken at the boundaries between snow and lake and between lake and soil. The governing equation is discretized for each layer as .. math:: :label: 12.44 \frac{c_{v,i} }{\Delta t} \left(T_{i}^{n+1} -T_{i}^{n} \right)=F_{i-1} -F_{i} +\phi _{i} -where superscripts *n* + 1 and *n* denote values at the end and -beginning of the timestep :math:`\Delta t`, respectively, :math:`F_{i}` -(W m\ :sup:`-2`) is the downward heat flux at the bottom of layer -*i*, and :math:`\phi _{i}` is the solar radiation absorbed in layer -*i*. +where superscripts *n* + 1 and *n* denote values at the end and beginning of the timestep :math:`\Delta t`, respectively, +:math:`F_{i}` (W m\ :sup:`-2`) is the downward heat flux at the bottom of layer *i*, and +:math:`\phi _{i}` is the solar radiation absorbed in layer *i*. -Eq. is solved using the semi-implicit Crank-Nicholson Method, resulting -in a tridiagonal system of equations: +Eq. is solved using the semi-implicit Crank-Nicholson Method, resulting in a tridiagonal system of equations: .. math:: :label: 12.45 \begin{array}{l} {r_{i} =a_{i} T_{i-1}^{n+1} +b_{i} T_{i}^{n+1} +cT_{i+1}^{n+1} ,} \\ {a_{i} =-0.5\frac{\Delta t}{c_{v,i} } \frac{\partial F_{i-1} }{\partial T_{i-1}^{n} } ,} \\ {b_{i} =1+0.5\frac{\Delta t}{c_{v,i} } \left(\frac{\partial F_{i-1} }{\partial T_{i-1}^{n} } +\frac{\partial F_{i} }{\partial T_{i}^{n} } \right),} \\ {c_{i} =-0.5\frac{\Delta t}{c_{v,i} } \frac{\partial F_{i} }{\partial T_{i}^{n} } ,} \\ {r_{i} =T_{i}^{n} +0.5\frac{\Delta t}{c_{v,i} } \left(F_{i-1} -F_{i} \right)+\frac{\Delta t}{c_{v,i} } \phi _{i} .} \end{array} -The fluxes :math:`F_{i}` are defined as follows: for the top layer, -:math:`F_{j_{top} -1} =2G;a_{j_{top} } =0`, where *G* is defined as in -section :numref:`Boundary Conditions Lake` (the factor of 2 merely cancels -out the Crank-Nicholson 0.5 in the equation for :math:`r_{j_{top} }` ). -For the bottom layer, :math:`F_{N_{levlak} +N_{levgrnd} } =0`. -For all other layers: +The fluxes :math:`F_{i}` are defined as follows: for the top layer, :math:`F_{j_{top} -1} =2G;a_{j_{top} } =0`, where *G* is defined as in section :numref:`Boundary Conditions Lake` (the factor of 2 merely cancels out the Crank-Nicholson 0.5 in the equation for :math:`r_{j_{top} }` ). For the bottom layer, :math:`F_{N_{levlak} +N_{levgrnd} } =0`. For all other layers: .. math:: :label: 12.46 @@ -847,47 +578,32 @@ For all other layers: Phase Change ^^^^^^^^^^^^^^^^^^ -Phase change in the lake, snow, and soil is done similarly to that done -for the soil and snow for vegetated land units (Chapter :numref:`rst_Soil and Snow Temperatures`), except -without the allowance for freezing point depression in soil underlying -lakes. After the heat diffusion is calculated, phase change occurs in a -given layer if the temperature is below freezing and liquid water -remains, or if the temperature is above freezing and ice remains. +Phase change in the lake, snow, and soil is done similarly to that done for the soil and snow for vegetated land units (Chapter :numref:`rst_Soil and Snow Temperatures`), except without the allowance for freezing point depression in soil underlying lakes. After the heat diffusion is calculated, phase change occurs in a given layer if the temperature is below freezing and liquid water remains, or if the temperature is above freezing and ice remains. -If melting occurs, the available energy for melting, :math:`Q_{avail}` -(J m\ :sup:`-2`), is computed as +If melting occurs, the available energy for melting, :math:`Q_{avail}` (J m\ :sup:`-2`), is computed as .. math:: :label: 12.47 Q_{avail} =\left(T_{i} -T_{f} \right)c_{v,i} -where :math:`T_{i}` is the temperature of the layer after thermal -diffusion (section :numref:`Crank-Nicholson Solution Lake`), and -:math:`c_{v,i}` \ is as calculated in section -:numref:`Heat Capacities Lake`. The mass of melt in the layer *M* -(kg m\ :sup:`-2`) is given by +where :math:`T_{i}` is the temperature of the layer after thermal diffusion (section :numref:`Crank-Nicholson Solution Lake`), and +:math:`c_{v,i}` \ is as calculated in section :numref:`Heat Capacities Lake`. The mass of melt in the layer *M* (kg m\ :sup:`-2`) is given by .. math:: :label: 12.48 M=\min \left\{M_{ice} ,\frac{Q_{avail} }{H_{fus} } \right\} -where :math:`H_{fus}` (J kg\ :sup:`-1`) is the latent heat of -fusion of water (:numref:`Table Physical Constants`), and :math:`M_{ice}` is the mass of ice in -the layer: :math:`I_{i} \rho _{liq} \Delta z_{i}` for a lake body -layer, or simply the soil / snow ice content state variable -(:math:`w_{ice}` ) for a soil / snow layer. The heat remainder, -:math:`Q_{rem}` \ is given by +where :math:`H_{fus}` (J kg\ :sup:`-1`) is the latent heat of fusion of water (:numref:`Table Physical Constants`), and +:math:`M_{ice}` is the mass of ice in the layer: :math:`I_{i} \rho _{liq} \Delta z_{i}` for a lake body layer, or simply the soil / snow ice content state variable (:math:`w_{ice}` ) for a soil / snow layer. The heat remainder, :math:`Q_{rem}` \ is given by .. math:: :label: 12.49 Q_{rem} =Q_{avail} -MH_{fus} . -Finally, the mass of ice in the layer :math:`M_{ice}` is adjusted -downwards by :math:`M`, and the temperature :math:`T_{i}` of the -layer is adjusted to +Finally, the mass of ice in the layer :math:`M_{ice}` is adjusted downwards by :math:`M`, and the temperature :math:`T_{i}` of the layer is adjusted to .. math:: :label: 12.50 @@ -896,64 +612,25 @@ layer is adjusted to where :math:`c'_{v,i} =c_{v,i} +M\left(c_{liq} -c_{ice} \right)`. -If freezing occurs, :math:`Q_{avail}` is again given by but will be -negative. The melt :math:`M`, also negative, is given by +If freezing occurs, :math:`Q_{avail}` is again given by but will be negative. The melt :math:`M`, also negative, is given by .. math:: :label: 12.51 M=\max \left\{-M_{liq} ,\frac{Q_{avail} }{H_{fus} } \right\} -where :math:`M_{liq}` is the mass of water in the layer: -:math:`\left(1-I_{i} \right)\rho _{liq} \Delta z_{i}` for a lake body -layer, or the soil / snow water content state variable -(:math:`w_{liq}` ). The heat remainder :math:`Q_{rem}` is given by eq. -and will be negative or zero. Finally, :math:`M_{liq}` is adjusted -downwards by :math:`-M` and the temperature is reset according to eq. . - -In the presence of nonzero snow water :math:`W_{sno}` without resolved -snow layers over +where :math:`M_{liq}` is the mass of water in the layer: :math:`\left(1-I_{i} \right)\rho _{liq} \Delta z_{i}` for a lake body layer, or the soil / snow water content state variable (:math:`w_{liq}` ). The heat remainder :math:`Q_{rem}` is given by eq. and will be negative or zero. Finally, :math:`M_{liq}` is adjusted downwards by :math:`-M` and the temperature is reset according to eq.. -an unfrozen top lake layer, the available energy in the top lake layer -:math:`\left(T_{1} -T_{f} \right)c_{v,1}` is used to melt the snow. -Similar to above, :math:`W_{sno}` is either completely melted and the -remainder of heat returned to the top lake layer, or the available heat -is exhausted and the top lake layer is set to freezing. The snow -thickness is adjusted downwards in proportion to the amount of melt, -maintaining constant density. +In the presence of nonzero snow water :math:`W_{sno}` without resolved snow layers over an unfrozen top lake layer, the available energy in the top lake layer :math:`\left(T_{1} -T_{f} \right)c_{v,1}` is used to melt the snow. Similar to above, :math:`W_{sno}` is either completely melted and the remainder of heat returned to the top lake layer, or the available heat is exhausted and the top lake layer is set to freezing. The snow thickness is adjusted downwards in proportion to the amount of melt, maintaining constant density. .. _Convection Lake: Convection ^^^^^^^^^^^^^^^^ -Convective mixing is based on -:ref:`Hostetler et al.’s (1993, 1994)` coupled -lake-atmosphere model, adjusting the lake temperature after diffusion -and phase change to maintain a stable density profile. Unfrozen lakes -overturn when :math:`\rho _{i} >\rho _{i+1}` , in which case the layer -thickness weighted average temperature for layers 1 to :math:`i+1` is -applied to layers 1 to :math:`i+1` and the densities are updated. This -scheme is applied iteratively to layers :math:`1\le i`), as occasionally -these can be induced by -heat expelled from the sediments (not present in the original -:ref:`Hostetler et al. (1994)` model). Mixing proceeds -from the bottom upward in this -case (i.e., first mixing layers :math:`i=N_{levlak} -1` and -:math:`i=N_{levlak}` , then checking :math:`i=N_{levlak} -2` and -:math:`i=N_{levlak} -1` and mixing down to :math:`i=N_{levlak}` if -needed, and on to the top), so as not to mix in with warmer over-lying -layers.\ - -For frozen lakes, this algorithm is generalized to conserve total -enthalpy and ice content, and to maintain ice contiguous at the top of -the lake. Thus, an additional mixing criterion is added: the presence of -ice in a layer that is below a layer which is not completely frozen. -When this occurs, these two lake layers and all those above mix. Total -enthalpy *Q* is conserved as +Convective mixing is based on :ref:`Hostetler et al.'s (1993, 1994)` coupled lake-atmosphere model, adjusting the lake temperature after diffusion and phase change to maintain a stable density profile. Unfrozen lakes overturn when :math:`\rho _{i} >\rho _{i+1}`, in which case the layer thickness weighted average temperature for layers 1 to :math:`i+1` is applied to layers 1 to :math:`i+1` and the densities are updated. This scheme is applied iteratively to layers :math:`1\le i`), as occasionally these can be induced by heat expelled from the sediments (not present in the original :ref:`Hostetler et al. (1994)` model). Mixing proceeds from the bottom upward in this case (i.e., first mixing layers :math:`i=N_{levlak} -1` and :math:`i=N_{levlak}`, then checking :math:`i=N_{levlak} -2` and :math:`i=N_{levlak} -1` and mixing down to :math:`i=N_{levlak}` if needed, and on to the top), so as not to mix in with warmer over-lying layers.\ + +For frozen lakes, this algorithm is generalized to conserve total enthalpy and ice content, and to maintain ice contiguous at the top of the lake. Thus, an additional mixing criterion is added: the presence of ice in a layer that is below a layer which is not completely frozen. When this occurs, these two lake layers and all those above mix. Total enthalpy *Q* is conserved as .. math:: :label: 12.52 @@ -967,45 +644,27 @@ Once the average ice fraction :math:`I_{av}` is calculated from \begin{array}{l} {I_{av} =\frac{\sum _{j=1}^{i+1}I_{j} \Delta z_{j} }{Z_{i+1} } ,} \\ {Z_{i+1} =\sum _{j=1}^{i+1}\Delta z_{j} ,} \end{array} -the temperatures are calculated. A separate temperature is calculated -for the frozen (:math:`T_{froz}` ) and unfrozen (:math:`T_{unfr}` ) -fractions of the mixed layers. If the total heat content *Q* is positive -(e.g. some layers will be above freezing), then the extra heat is all -assigned to the unfrozen layers, while the fully frozen layers are kept -at freezing. Conversely, if :math:`Q < 0`, the heat deficit will all -be assigned to the ice, and the liquid layers will be kept at freezing. -For the layer that contains both ice and liquid (if present), a weighted -average temperature will have to be calculated. +the temperatures are calculated. A separate temperature is calculated for the frozen (:math:`T_{froz}` ) and unfrozen (:math:`T_{unfr}` ) fractions of the mixed layers. If the total heat content *Q* is positive (e.g. some layers will be above freezing), then the extra heat is all assigned to the unfrozen layers, while the fully frozen layers are kept at freezing. Conversely, if :math:`Q < 0`, the heat deficit will all be assigned to the ice, and the liquid layers will be kept at freezing. For the layer that contains both ice and liquid (if present), a weighted average temperature will have to be calculated. -If :math:`Q > 0`, then :math:`T_{froz} =T_{f}` , and :math:`T_{unfr}` -is given by +If :math:`Q > 0`, then :math:`T_{froz} =T_{f}`, and :math:`T_{unfr}` is given by .. math:: :label: 12.54 T_{unfr} =\frac{Q}{\rho _{liq} Z_{i+1} \left[\left(1-I_{av} \right)c_{liq} \right]} +T_{f} . -If :math:`Q < 0`, then :math:`T_{unfr} =T_{f}` , and :math:`T_{froz}` -is given by +If :math:`Q < 0`, then :math:`T_{unfr} =T_{f}`, and :math:`T_{froz}` is given by .. math:: :label: 12.55 T_{froz} =\frac{Q}{\rho _{liq} Z_{i+1} \left[I_{av} c_{ice} \right]} +T_{f} . -The ice is lumped together at the top. For each lake layer *j* from 1 -to *i* + 1, the ice fraction and temperature are set as follows, where -:math:`Z_{j} =\sum _{m=1}^{j}\Delta z_{m}` : +The ice is lumped together at the top. For each lake layer *j* from 1 to *i* + 1, the ice fraction and temperature are set as follows, where :math:`Z_{j} =\sum _{m=1}^{j}\Delta z_{m}` : -#. If :math:`Z_{j} \le Z_{i+1} I_{av}` , then :math:`I_{j} =1` and - :math:`T_{j} =T_{froz}` . +#. If :math:`Z_{j} \le Z_{i+1} I_{av}`, then :math:`I_{j} =1` and :math:`T_{j} =T_{froz}`. -#. Otherwise, if :math:`Z_{j-1} 1000{\rm m}`, -in which case additional precipitation and frost deposition is added to -:math:`q_{snwcp,\, ice}` . - -If there are resolved snow layers, the generalized “evaporation” -:math:`E_{g}` (i.e., evaporation, dew, frost, and sublimation) is -treated as over other land units, except that the allowed evaporation -from the ground is unlimited (though the top snow layer cannot lose more -water mass than it contains). If there are no resolved snow layers but -:math:`W_{sno} >0` and :math:`E_{g} >0`, sublimation -:math:`q_{sub,sno}` \ (kg m\ :sup:`-2` s\ :sup:`-1`) will be -given by +All precipitation reaches the ground, as there is no vegetated fraction. As for other land types, incident snowfall accumulates (with ice mass :math:`W_{sno}` and thickness :math:`z_{sno}` ) until its thickness exceeds a minimum thickness :math:`s_{\min }`, at which point a resolved snow layer is initiated, with water, ice, dissolved aerosol, snow-grain radius, etc., state variables tracked by the Snow Hydrology submodel (Chapter :numref:`rst_Snow Hydrology`). The density of fresh snow is assigned as for other land types (Chapter :numref:`rst_Snow Hydrology`). Solid precipitation is added immediately to the snow, while liquid precipitation is added to snow layers, if they exist, after accounting for dew, frost, and sublimation (below). If :math:`z_{sno}` exceeds :math:`s_{\min }` after solid precipitation is added but no snow layers are present, a new snow layer is initiated immediately, and then dew, frost, and sublimation are accounted for. Snow-capping is invoked if the snow depth :math:`z_{sno} >1000{\rm m}`, in which case additional precipitation and frost deposition is added to :math:`q_{snwcp,\, ice}`. + +If there are resolved snow layers, the generalized "evaporation" :math:`E_{g}` (i.e., evaporation, dew, frost, and sublimation) is treated as over other land units, except that the allowed evaporation from the ground is unlimited (though the top snow layer cannot lose more water mass than it contains). If there are no resolved snow layers but :math:`W_{sno} >0` and :math:`E_{g} >0`, sublimation :math:`q_{sub,sno}` \ (kg m\ :sup:`-2` s\ :sup:`-1`) will be given by .. math:: :label: 12.60 q_{sub,sno} =\min \left\{E_{g} ,\frac{W_{sno} }{\Delta t} \right\} . -If :math:`E_{g} <0,T_{g} \le T_{f}` , and there are no resolved snow -layers or the top snow layer is not unfrozen, then the rate of frost -production :math:`q_{frost} =\left|E_{g} \right|`. If :math:`E_{g} <0` -but the top snow layer has completely thawed during the Phase Change step -of the Lake Temperature solution (section :numref:`Phase Change Lake`), then -frost (or dew) is not allowed to accumulate (:math:`q_{frost} =0`), to -insure that the layer is eliminated by the Snow Hydrology -(Chapter :numref:`rst_Snow Hydrology`) code. (If :math:`T_{g} >T_{f}`, -then no snow is present (section :numref:`Surface Flux Solution Lake`), and -evaporation or dew deposition is balanced by :math:`q_{rgwl}` .) The -snowpack is updated for frost and sublimation: +If :math:`E_{g} <0,T_{g} \le T_{f}`, and there are no resolved snow layers or the top snow layer is not unfrozen, then the rate of frost production :math:`q_{frost} =\left|E_{g} \right|`. If :math:`E_{g} <0` but the top snow layer has completely thawed during the Phase Change step of the Lake Temperature solution (section :numref:`Phase Change Lake`), then frost (or dew) is not allowed to accumulate (:math:`q_{frost} =0`), to insure that the layer is eliminated by the Snow Hydrology (Chapter :numref:`rst_Snow Hydrology`) code. (If :math:`T_{g} >T_{f}`, then no snow is present (section :numref:`Surface Flux Solution Lake`), and evaporation or dew deposition is balanced by :math:`q_{rgwl}`.) The snowpack is updated for frost and sublimation: .. math:: :label: 12.61 W_{sno} =W_{sno} +\Delta t\left(q_{frost} -q_{sub,sno} \right) . -If there are resolved snow layers, then this update occurs using the Snow -Hydrology submodel (Chapter :numref:`rst_Snow Hydrology`). Otherwise, the -snow ice mass is updated directly, and :math:`z_{sno}` is adjusted by the same -proportion as the snow ice (i.e., maintaining the same density), unless -there was no snow before adding the frost, in which case the density is -assumed to be 250 kg m\ :sup:`-3`. +If there are resolved snow layers, then this update occurs using the Snow Hydrology submodel (Chapter :numref:`rst_Snow Hydrology`). Otherwise, the snow ice mass is updated directly, and :math:`z_{sno}` is adjusted by the same proportion as the snow ice (i.e., maintaining the same density), unless there was no snow before adding the frost, in which case the density is assumed to be 250 kg m\ :sup:`-3`. .. _Soil Hydrology Lake: Soil Hydrology ^^^^^^^^^^^^^^^^^^^^ -The combined water and ice soil volume fraction in a soil layer -:math:`\theta _{i}` is given by +The combined water and ice soil volume fraction in a soil layer :math:`\theta _{i}` is given by .. math:: :label: 12.62 \theta _{i} =\frac{1}{\Delta z_{i} } \left(\frac{w_{ice,i} }{\rho _{ice} } +\frac{w_{liq,i} }{\rho _{liq} } \right) . -If :math:`\theta _{i} <\theta _{sat,i}` , the pore volume fraction at -saturation (as may occur when ice melts), then the liquid water mass is -adjusted to +If :math:`\theta _{i} <\theta _{sat,i}`, the pore volume fraction at saturation (as may occur when ice melts), then the liquid water mass is adjusted to .. math:: :label: 12.63 w_{liq,i} =\left(\theta _{sat,i} \Delta z_{i} -\frac{w_{ice,i} }{\rho _{ice} } \right)\rho _{liq} . -Otherwise, if excess ice is melting and -:math:`w_{liq,i} >\theta _{sat,i} \rho _{liq} \Delta z_{i}` , then the -water in the layer is reset to +Otherwise, if excess ice is melting and :math:`w_{liq,i} >\theta _{sat,i} \rho _{liq} \Delta z_{i}`, then the water in the layer is reset to .. math:: :label: 12.64 w_{liq,i} = \theta _{sat,i} \rho _{liq} \Delta z_{i} -This allows excess ice to be initialized (and begin to be lost only -after the pore ice is melted, which is realistic if the excess ice is -found in heterogeneous chunks) but irreversibly lost when melt occurs. +This allows excess ice to be initialized (and begin to be lost only after the pore ice is melted, which is realistic if the excess ice is found in heterogeneous chunks) but irreversibly lost when melt occurs. .. _Modifications to Snow Layer Logic Lake: Modifications to Snow Layer Logic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A thickness difference :math:`z_{lsa} =s_{\min } -\tilde{s}_{\min }` -adjusts the minimum resolved snow layer thickness for lake columns as -compared to non-lake columns. The value of :math:`z_{lsa}` is chosen to -satisfy the CFL condition for the model timestep. By default, -:math:`\tilde{s}_{\min }` \ = 1 cm and :math:`s_{\min }` \ = 4 cm. See -:ref:`Subin et al. (2012a; including Supporting Information)` -for further discussion. - -The rules for combining and sub-dividing snow layers (section -:numref:`Snow Layer Combination and Subdivision`) are -adjusted for lakes to maintain minimum thicknesses of :math:`s_{\min }` -and to increase all target layer thicknesses by :math:`z_{lsa}` . The -rules for combining layers are modified by simply increasing layer -thickness thresholds by :math:`z_{lsa}` . The rules for dividing snow -layers are contained in a separate subroutine that is modified for -lakes, and is a function of the number of layers and the layer -thicknesses. There are two types of operations: (a) subdividing layers -in half, and (b) shifting some volume from higher layers to lower layers -(without increasing the layer number). For subdivisions of type (a), the -thickness thresholds triggering subdivision are increased by -:math:`2z_{lsa}` for lakes. For shifts of type (b), the thickness -thresholds triggering the shifts are increased by :math:`z_{lsa}` . At -the end of the modified subroutine, a snow ice and liquid balance check -are performed. - -In rare instances, resolved snow layers may be present over an unfrozen -top lake body layer. In this case, the snow layers may be eliminated if -enough heat is present in the top layer to melt the snow: see -:ref:`Subin et al. (2012a, Supporting Information) `. +A thickness difference :math:`z_{lsa} =s_{\min } -\tilde{s}_{\min }` adjusts the minimum resolved snow layer thickness for lake columns as compared to non-lake columns. The value of :math:`z_{lsa}` is chosen to satisfy the CFL condition for the model timestep. By default, :math:`\tilde{s}_{\min }` \ = 1 cm and :math:`s_{\min }` \ = 4 cm. See :ref:`Subin et al. (2012a; including Supporting Information)` for further discussion. + +The rules for combining and sub-dividing snow layers (section :numref:`Snow Layer Combination and Subdivision`) are adjusted for lakes to maintain minimum thicknesses of :math:`s_{\min }` and to increase all target layer thicknesses by :math:`z_{lsa}`. The rules for combining layers are modified by simply increasing layer thickness thresholds by :math:`z_{lsa}`. The rules for dividing snow layers are contained in a separate subroutine that is modified for lakes, and is a function of the number of layers and the layer thicknesses. There are two types of operations: (a) subdividing layers in half, and (b) shifting some volume from higher layers to lower layers (without increasing the layer number). For subdivisions of type (a), the thickness thresholds triggering subdivision are increased by :math:`2z_{lsa}` for lakes. For shifts of type (b), the thickness thresholds triggering the shifts are increased by :math:`z_{lsa}`. At the end of the modified subroutine, a snow ice and liquid balance check are performed. + +In rare instances, resolved snow layers may be present over an unfrozen top lake body layer. In this case, the snow layers may be eliminated if enough heat is present in the top layer to melt the snow: see :ref:`Subin et al. (2012a, Supporting Information) `. diff --git a/doc/source/tech_note/Land-Only_Mode/CLM50_Tech_Note_Land-Only_Mode.rst b/doc/source/tech_note/Land-Only_Mode/CLM50_Tech_Note_Land-Only_Mode.rst index 9fba6f187c..c233a0cb4b 100644 --- a/doc/source/tech_note/Land-Only_Mode/CLM50_Tech_Note_Land-Only_Mode.rst +++ b/doc/source/tech_note/Land-Only_Mode/CLM50_Tech_Note_Land-Only_Mode.rst @@ -3,80 +3,27 @@ Land-Only Mode ================ -In land-only mode (uncoupled to an atmospheric model), the atmospheric -forcing required by CLM (:numref:`Table Atmospheric input to land model`) -is supplied by observed datasets. The standard forcing provided with the -model is a 110-year (1901-2010) dataset provided by the Global Soil Wetness -Project (GSWP3; NEED A REFERENCE). The GSWP3 dataset has a spatial resolution of -0.5\ :sup:`o` X 0.5\ :sup:`o` and a temporal resolution of three -hours. - -An alternative forcing dataset is also available, CRUNCEP, a 110-year (1901-2010) dataset -(CRUNCEP; :ref:`Viovy 2011 `) that is a combination of two existing datasets; -the CRU TS3.2 0.5\ :sup:`o` X 0.5\ :sup:`o` monthly data covering the period -1901 to 2002 (:ref:`Mitchell and Jones 2005 `) -and the NCEP reanalysis 2.5\ :sup:`o` X 2.5\ :sup:`o` -6-hourly data covering the period 1948 to 2010. The CRUNCEP dataset has -been used to force CLM for studies of vegetation growth, -evapotranspiration, and gross primary production (:ref:`Mao et al. 2012 `, -:ref:`Mao et al. 2013 `, :ref:`Shi et al. 2013 `) -and for the TRENDY (trends in net land-atmosphere carbon exchange over the period -1980-2010) project (:ref:`Piao et al. 2012 `). Version 7 is available -here (:ref:`Viovy 2011 `). - -Here, the GSWP3 dataset, which does not include data for particular fields over oceans, -lakes, and Antarctica is modified. This missing data is filled with -:ref:`Qian et al. (2006) ` data from 1948 that is interpolated by the data atmosphere -model to the 0.5\ :sup:`o` GSWP3 grid. This allows the model -to be run over Antarctica and ensures data is available along coastlines -regardless of model resolution. - -The forcing data is ingested into a data atmosphere model in three -“streams”; precipitation (:math:`P`) (mm s\ :sup:`-1`), solar -radiation (:math:`S_{atm}` ) (W m\ :sup:`-2`), and four other -fields [atmospheric pressure :math:`P_{atm}` (Pa), atmospheric specific -humidity :math:`q_{atm}` (kg kg\ :sup:`-1`), atmospheric -temperature :math:`T_{atm}` (K), and atmospheric wind :math:`W_{atm}` -(m s\ :sup:`-1`)]. These are separate streams because they are -handled differently according to the type of field. In the GSWP3 -dataset, the precipitation stream is provided at three hour intervals and -the data atmosphere model prescribes the same precipitation rate for -each model time step within the three hour period. The four fields that -are grouped together in another stream (pressure, humidity, temperature, -and wind) are provided at three hour intervals and the data atmosphere -model linearly interpolates these fields to the time step of the model. - -The total solar radiation is also provided at three hour intervals. The -data is fit to the model time step using a diurnal function that depends -on the cosine of the solar zenith angle :math:`\mu` to provide a -smoother diurnal cycle of solar radiation and to ensure that all of the -solar radiation supplied by the three-hourly forcing data is actually -used. The solar radiation at model time step :math:`t_{M}` is +In land-only mode (uncoupled to an atmospheric model), the atmospheric forcing required by CLM (:numref:`Table Atmospheric input to land model`) is supplied by observed datasets. The standard forcing provided with the model is a 110-year (1901-2010) dataset provided by the Global Soil Wetness Project (GSWP3; NEED A REFERENCE). The GSWP3 dataset has a spatial resolution of 0.5° X 0.5° and a temporal resolution of three hours. + +An alternative forcing dataset is also available, CRUNCEP, a 110-year (1901-2010) dataset (CRUNCEP; :ref:`Viovy 2011 `) that is a combination of two existing datasets; the CRU TS3.2 0.5° X 0.5° monthly data covering the period 1901 to 2002 (:ref:`Mitchell and Jones 2005 `) and the NCEP reanalysis 2.5° X 2.5° 6-hourly data covering the period 1948 to 2010. The CRUNCEP dataset has been used to force CLM for studies of vegetation growth, evapotranspiration, and gross primary production (:ref:`Mao et al. 2012 `, :ref:`Mao et al. 2013 `, :ref:`Shi et al. 2013 `) and for the TRENDY (trends in net land-atmosphere carbon exchange over the period 1980-2010) project (:ref:`Piao et al. 2012 `). Version 7 is available here (:ref:`Viovy 2011 `). + +Here, the GSWP3 dataset, which does not include data for particular fields over oceans, lakes, and Antarctica is modified. This missing data is filled with :ref:`Qian et al. (2006) ` data from 1948 that is interpolated by the data atmosphere model to the 0.5° GSWP3 grid. This allows the model to be run over Antarctica and ensures data is available along coastlines regardless of model resolution. + +The forcing data is ingested into a data atmosphere model in three "streams"; precipitation (:math:`P`) (mm s\ :sup:`-1`), solar radiation (:math:`S_{atm}` ) (W m\ :sup:`-2`), and four other fields [atmospheric pressure :math:`P_{atm}` (Pa), atmospheric specific humidity :math:`q_{atm}` (kg kg\ :sup:`-1`), atmospheric temperature :math:`T_{atm}` (K), and atmospheric wind :math:`W_{atm}` (m s\ :sup:`-1`)]. These are separate streams because they are handled differently according to the type of field. In the GSWP3 dataset, the precipitation stream is provided at three hour intervals and the data atmosphere model prescribes the same precipitation rate for each model time step within the three hour period. The four fields that are grouped together in another stream (pressure, humidity, temperature, and wind) are provided at three hour intervals and the data atmosphere model linearly interpolates these fields to the time step of the model. + +The total solar radiation is also provided at three hour intervals. The data is fit to the model time step using a diurnal function that depends on the cosine of the solar zenith angle :math:`\mu` to provide a smoother diurnal cycle of solar radiation and to ensure that all of the solar radiation supplied by the three-hourly forcing data is actually used. The solar radiation at model time step :math:`t_{M}` is .. math:: :label: 31.1 - \begin{array}{lr} - S_{atm} \left(t_{M} \right)=\frac{\frac{\Delta t_{FD} }{\Delta t_{M} } S_{atm} \left(t_{FD} \right)\mu \left(t_{M} \right)}{\sum _{i=1}^{\frac{\Delta t_{FD} }{\Delta t_{M} } }\mu \left(t_{M_{i} } \right) } & \qquad {\rm for\; }\mu \left(t_{M} \right)>0.001 \\ - S_{atm} \left(t_{M} \right)=0 & \qquad {\rm for\; }\mu \left(t_{M} \right)\le 0.001 + \begin{array}{lr} + S_{atm} \left(t_{M} \right)=\frac{\frac{\Delta t_{FD} }{\Delta t_{M} } S_{atm} \left(t_{FD} \right)\mu \left(t_{M} \right)}{\sum _{i=1}^{\frac{\Delta t_{FD} }{\Delta t_{M} } }\mu \left(t_{M_{i} } \right) } & \qquad {\rm for\; }\mu \left(t_{M} \right)>0.001 \\ + S_{atm} \left(t_{M} \right)=0 & \qquad {\rm for\; }\mu \left(t_{M} \right)\le 0.001 \end{array} -where :math:`\Delta t_{FD}` is the time step of the forcing data (3 -hours :math:`\times` 3600 seconds hour\ :sup:`-1` = 10800 -seconds), :math:`\Delta t_{M}` is the model time step (seconds), -:math:`S_{atm} \left(t_{FD} \right)` is the three-hourly solar radiation -from the forcing data (W m\ :sup:`-2`), and -:math:`\mu \left(t_{M} \right)` is the cosine of the solar zenith angle -at model time step :math:`t_{M}` (section :numref:`Solar Zenith Angle`). The term in the -denominator of equation (1) is the sum of the cosine of the solar zenith -angle for each model time step falling within the three hour period. For -numerical purposes, :math:`\mu \left(t_{M_{i} } \right)\ge 0.001`. +where :math:`\Delta t_{FD}` is the time step of the forcing data (3 hours :math:`\times` 3600 seconds hour\ :sup:`-1` = 10800 seconds), :math:`\Delta t_{M}` is the model time step (seconds), :math:`S_{atm} \left(t_{FD} \right)` is the three-hourly solar radiation from the forcing data (W m\ :sup:`-2`), and :math:`\mu \left(t_{M} \right)` is the cosine of the solar zenith angle at model time step :math:`t_{M}` (section :numref:`Solar Zenith Angle`). The term in the denominator of equation :eq:`31.1` is the sum of the cosine of the solar zenith angle for each model time step falling within the three hour period. For numerical purposes, :math:`\mu \left(t_{M_{i} } \right)\ge 0.001`. -The total incident solar radiation :math:`S_{atm}` at the model time -step :math:`t_{M}` is then split into near-infrared and visible -radiation and partitioned into direct and diffuse according to factors -derived from one year’s worth of hourly CAM output from CAM version -cam3\_5\_55 as +The total incident solar radiation :math:`S_{atm}` at the model time step :math:`t_{M}` is then split into near-infrared and visible radiation and partitioned into direct and diffuse according to factors derived from one year's worth of hourly CAM output from CAM version cam3\_5\_55 as .. math:: :label: 31.2 @@ -98,16 +45,14 @@ cam3\_5\_55 as S_{atm} \, \downarrow _{nir} =\left(1-R_{nir} \right)\left[\left(1-\alpha \right)S_{atm} \right]. -where :math:`\alpha` , the ratio of visible to total incident solar -radiation, is assumed to be +where :math:`\alpha`, the ratio of visible to total incident solar radiation, is assumed to be .. math:: :label: 31.6 \alpha =\frac{S_{atm} \, \downarrow _{vis}^{\mu } +S_{atm} \, \downarrow _{vis}^{} }{S_{atm} } =0.5. -The ratio of direct to total incident radiation in the visible -:math:`R_{vis}` is +The ratio of direct to total incident radiation in the visible :math:`R_{vis}` is .. math:: :label: 31.7 @@ -121,21 +66,9 @@ and in the near-infrared :math:`R_{nir}` is R_{nir} =b_{0} +b_{1} \times \left(1-\alpha \right)S_{atm} +b_{2} \times \left[\left(1-\alpha \right)S_{atm} \right]^{2} +b_{3} \times \left[\left(1-\alpha \right)S_{atm} \right]^{3} \qquad 0.01\le R_{nir} \le 0.99 -where -:math:`a_{0} =0.17639,\, a_{1} =0.00380,\, a_{2} =-9.0039\times 10^{-6} ,\, a_{3} =8.1351\times 10^{-9}` -and -:math:`b_{0} =0.29548,b_{1} =0.00504,b_{2} =-1.4957\times 10^{-5} ,b_{3} =1.4881\times 10^{-8}` -are coefficients from polynomial fits to the CAM data. - -The additional atmospheric forcing variables required by :numref:`Table Atmospheric input to land model` are -derived as follows. The atmospheric reference height :math:`z'_{atm}` -(m) is set to 30 m. The directional wind components are derived as -:math:`u_{atm} =v_{atm} ={W_{atm} \mathord{\left/ {\vphantom {W_{atm} \sqrt{2} }} \right. \kern-\nulldelimiterspace} \sqrt{2} }` . -The potential temperature :math:`\overline{\theta _{atm} }` (K) is set -to the atmospheric temperature :math:`T_{atm}` . The atmospheric -longwave radiation :math:`L_{atm} \, \downarrow` (W m\ :sup:`-2`) -is derived from the atmospheric vapor pressure :math:`e_{atm}` and -temperature :math:`T_{atm}` (:ref:`Idso 1981`) as +where :math:`a_{0} =0.17639,\, a_{1} =0.00380,\, a_{2} =-9.0039\times 10^{-6},\, a_{3} =8.1351\times 10^{-9}` and :math:`b_{0} =0.29548,b_{1} =0.00504,b_{2} =-1.4957\times 10^{-5},b_{3} =1.4881\times 10^{-8}` are coefficients from polynomial fits to the CAM data. + +The additional atmospheric forcing variables required by :numref:`Table Atmospheric input to land model` are derived as follows. The atmospheric reference height :math:`z'_{atm}` (m) is set to 30 m. The directional wind components are derived as :math:`u_{atm} =v_{atm} ={W_{atm} \mathord{\left/ {\vphantom {W_{atm} \sqrt{2} }} \right.} \sqrt{2} }`. The potential temperature :math:`\overline{\theta _{atm} }` (K) is set to the atmospheric temperature :math:`T_{atm}`. The atmospheric longwave radiation :math:`L_{atm} \, \downarrow` (W m\ :sup:`-2`) is derived from the atmospheric vapor pressure :math:`e_{atm}` and temperature :math:`T_{atm}` (:ref:`Idso 1981`) as .. math:: :label: 31.9 @@ -149,10 +82,7 @@ where e_{atm} =\frac{P_{atm} q_{atm} }{0.622+0.378q_{atm} } -and :math:`\sigma` is the Stefan-Boltzmann constant (W m\ :sup:`-2` K\ :sup:`-4`) -(:numref:`Table Physical constants`). The fraction of -precipitation :math:`P` (mm s\ :sup:`-1`) falling as rain and/or -snow is +and :math:`\sigma` is the Stefan-Boltzmann constant (W m\ :sup:`-2` K\ :sup:`-4`) (:numref:`Table Physical constants`). The fraction of precipitation :math:`P` (mm s\ :sup:`-1`) falling as rain and/or snow is .. math:: :label: 31.11 @@ -171,85 +101,39 @@ where f_{P} =0<0.5\left(T_{atm} -T_{f} \right)<1. -The aerosol deposition rates :math:`D_{sp}` (14 rates as described in -:numref:`Table Atmospheric input to land model`) are provided by a -time-varying, globally-gridded aerosol deposition file developed by -:ref:`Lamarque et al. (2010) `. +The aerosol deposition rates :math:`D_{sp}` (14 rates as described in :numref:`Table Atmospheric input to land model`) are provided by a time-varying, globally-gridded aerosol deposition file developed by :ref:`Lamarque et al. (2010) `. -If the user wishes to provide atmospheric forcing data from another -source, the data format outlined above will need to be followed with the -following exceptions. The data atmosphere model will accept a -user-supplied relative humidity :math:`RH` (%) and derive specific -humidity :math:`q_{atm}` (kg kg\ :sup:`-1`) from +If the user wishes to provide atmospheric forcing data from another source, the data format outlined above will need to be followed with the following exceptions. The data atmosphere model will accept a user-supplied relative humidity :math:`RH` (%) and derive specific humidity :math:`q_{atm}` (kg kg\ :sup:`-1`) from .. math:: :label: 31.14 q_{atm} =\frac{0.622e_{atm} }{P_{atm} -0.378e_{atm} } -where the atmospheric vapor pressure :math:`e_{atm}` (Pa) is derived -from the water (:math:`T_{atm} >T_{f}` ) or ice -(:math:`T_{atm} \le T_{f}` ) saturation vapor pressure -:math:`e_{sat}^{T_{atm} }` as -:math:`e_{atm} =\frac{RH}{100} e_{sat}^{T_{atm} }` where :math:`T_{f}` -is the freezing temperature of water (K) (:numref:`Table Physical constants`), and -:math:`P_{atm}` is the pressure at height :math:`z_{atm}` (Pa). The -data atmosphere model will also accept a user-supplied dew point -temperature :math:`T_{dew}` (K) and derive specific humidity -:math:`q_{atm}` from +where the atmospheric vapor pressure :math:`e_{atm}` (Pa) is derived from the water (:math:`T_{atm} >T_{f}` ) or ice (:math:`T_{atm} \le T_{f}` ) saturation vapor pressure :math:`e_{sat}^{T_{atm} }` as :math:`e_{atm} =\frac{RH}{100} e_{sat}^{T_{atm} }` where :math:`T_{f}` is the freezing temperature of water (K) (:numref:`Table Physical constants`), and :math:`P_{atm}` is the pressure at height :math:`z_{atm}` (Pa). The data atmosphere model will also accept a user-supplied dew point temperature :math:`T_{dew}` (K) and derive specific humidity :math:`q_{atm}` from .. math:: :label: 31.15 q_{atm} = \frac{0.622e_{sat}^{T_{dew} } }{P_{atm} -0.378e_{sat}^{T_{dew} } } . -Here, :math:`e_{sat}^{T}` , the saturation vapor pressure as a function -of temperature, is derived from :ref:`Lowe’s (1977) ` polynomials. If not -provided by the user, the atmospheric pressure :math:`P_{atm}` (Pa) is -set equal to the standard atmospheric pressure :math:`P_{std} =101325` -Pa, and surface pressure :math:`P_{srf}` (Pa) is set equal -to\ :math:`P_{atm}` . - -The user may provide the total direct and diffuse solar radiation, -:math:`S_{atm} \, \downarrow ^{\mu }` and -:math:`S_{atm} \, \downarrow` . These will be time-interpolated using -the procedure described above and then each term equally apportioned -into the visible and near-infrared wavebands (e.g., -:math:`S_{atm} \, \downarrow _{vis}^{\mu } =0.5S_{atm} \, \downarrow ^{\mu }` , -:math:`S_{atm} \, \downarrow _{nir}^{\mu } =0.5S_{atm} \, \downarrow ^{\mu }` ). +Here, :math:`e_{sat}^{T}`, the saturation vapor pressure as a function of temperature, is derived from :ref:`Lowe's (1977) ` polynomials. If not provided by the user, the atmospheric pressure :math:`P_{atm}` (Pa) is set equal to the standard atmospheric pressure :math:`P_{std} =101325` Pa, and surface pressure :math:`P_{srf}` (Pa) is set equal to\ :math:`P_{atm}`. + +The user may provide the total direct and diffuse solar radiation, :math:`S_{atm} \, \downarrow ^{\mu }` and :math:`S_{atm} \, \downarrow`. These will be time-interpolated using the procedure described above and then each term equally apportioned into the visible and near-infrared wavebands (e.g., :math:`S_{atm} \, \downarrow _{vis}^{\mu } =0.5S_{atm} \, \downarrow ^{\mu }`, :math:`S_{atm} \, \downarrow _{nir}^{\mu } =0.5S_{atm} \, \downarrow ^{\mu }` ). .. _Anomaly Forcing: Anomaly Forcing ----------------------------- -The 'Anomaly Forcing' atmospheric forcing mode provides a means to drive -CLM with projections of future climate conditions without the need for -large, high-frequency datasets. From an existing climate simulation -spanning both the historical and future time periods, a set of anomalies -are created by removing a climatological seasonal cycle based on the end -of the historical period from each year of the future time period of the -simulation. These anomalies can then be applied to a repeating -high-frequency forcing dataset of finite duration (e.g. 10 years). State -and flux forcing variables are adjusted using additive and multiplicative -anomalies, respectively: +The 'Anomaly Forcing' atmospheric forcing mode provides a means to drive CLM with projections of future climate conditions without the need for large, high-frequency datasets. From an existing climate simulation spanning both the historical and future time periods, a set of anomalies are created by removing a climatological seasonal cycle based on the end of the historical period from each year of the future time period of the simulation. These anomalies can then be applied to a repeating high-frequency forcing dataset of finite duration (e.g. 10 years). State and flux forcing variables are adjusted using additive and multiplicative anomalies, respectively: .. math:: :label: 31.16 - \begin{array}{lr} - S^{'} = S + k_{anomaly} & \quad {\rm state \ variable} \\ - F^{'} = f \times k_{anomaly} & \quad {\rm flux \ variable} + \begin{array}{lr} + S^{'} = S + k_{anomaly} & \quad {\rm state \ variable} \\ + F^{'} = f \times k_{anomaly} & \quad {\rm flux \ variable} \end{array} -where :math:`S^{'}` is the adjusted atmospheric state variable, :math:`S` -is the state variable from the high-frequency reference atmospheric -forcing dataset, and :math:`k_{anomaly}` is an additive anomaly. -Similarly, :math:`F^{'}` is the adjusted atmospheric flux variable, -:math:`F` is the flux variable from the high-frequency reference -atmospheric forcing dataset, and :math:`k_{anomaly}` is a -multiplicative anomaly. State variables are temperature :math:`T_{atm}`, -pressure :math:`P_{atm}`, humidity :math:`q_{atm}`, and wind -:math:`W_{atm}`. Flux variables are precipitation :math:`P`, atmospheric -shortwave radiation :math:`S_{atm} \, \downarrow`, and atmospheric -longwave radiation :math:`L_{atm} \, \downarrow`. +where :math:`S^{'}` is the adjusted atmospheric state variable, :math:`S` is the state variable from the high-frequency reference atmospheric forcing dataset, and :math:`k_{anomaly}` is an additive anomaly. Similarly, :math:`F^{'}` is the adjusted atmospheric flux variable, :math:`F` is the flux variable from the high-frequency reference atmospheric forcing dataset, and :math:`k_{anomaly}` is a multiplicative anomaly. State variables are temperature :math:`T_{atm}`, pressure :math:`P_{atm}`, humidity :math:`q_{atm}`, and wind :math:`W_{atm}`. Flux variables are precipitation :math:`P`, atmospheric shortwave radiation :math:`S_{atm} \, \downarrow`, and atmospheric longwave radiation :math:`L_{atm} \, \downarrow`. diff --git a/doc/source/tech_note/MOSART/CLM50_Tech_Note_MOSART.rst b/doc/source/tech_note/MOSART/CLM50_Tech_Note_MOSART.rst index 439de8a7a1..3c10cd4507 100644 --- a/doc/source/tech_note/MOSART/CLM50_Tech_Note_MOSART.rst +++ b/doc/source/tech_note/MOSART/CLM50_Tech_Note_MOSART.rst @@ -8,40 +8,14 @@ Model for Scale Adaptive River Transport (MOSART) Overview --------- -MOSART is a river transport model designed for applications across local, -regional and global scales :ref:`(Li et al., 2013b) `. A -major purpose of MOSART is to provide freshwater input for the ocean -model in coupled Earth System Models. MOSART also provides an effective -way of evaluating and diagnosing the soil hydrology simulated by land -surface models through direct comparison of the simulated river flow -with observations of natural streamflow at gauging stations -:ref:`(Li et al., 2015a)`. Moreover, MOSART provides a -modeling framework for representing riverine transport and transformation -of energy and biogeochemical fluxes under both natural and human-influenced -conditions ( :ref:`(Li et al., 2015b) `. +MOSART is a river transport model designed for applications across local, regional and global scales :ref:`(Li et al., 2013b) `. A major purpose of MOSART is to provide freshwater input for the ocean model in coupled Earth System Models. MOSART also provides an effective way of evaluating and diagnosing the soil hydrology simulated by land surface models through direct comparison of the simulated river flow with observations of natural streamflow at gauging stations :ref:`(Li et al., 2015a)`. Moreover, MOSART provides a modeling framework for representing riverine transport and transformation of energy and biogeochemical fluxes under both natural and human-influenced conditions ( :ref:`(Li et al., 2015b) `. .. _Routing Processes: Routing Processes ------------------ -MOSART divides each spatial unit such as a lat/lon grid or watershed into -three categories of hydrologic units (as shown in -:numref:`Figure MOSART conceptual diagram`): hillslopes -that convert both surface and subsurface runoff into tributaries, -tributaries that discharge into a single main channel, and the main channel -that connects the local spatial unit with upstream/downstream units through the -river network. MOSART assumes that all the tributaries within a spatial unit -can be treated as a single hypothetical sub-network channel with a transport -capacity equivalent to all the tributaries combined. Correspondingly, three -routing processes are represented in MOSART: 1) hillslope routing: in each -spatial unit, surface runoff is routed as overland flow into the sub-network -channel, while subsurface runoff generated in the spatial unit directly enters -the sub-network channel; 2) sub-network channel routing: the sub-network channel -receives water from the hillslopes, routes water through the channel and discharges -it into the main channel; 3) main channel routing: the main channel receives water -from the sub-network channel and/or inflow, if any, from the upstream spatial units, -and discharges the water to its downstream spatial unit or the ocean. +MOSART divides each spatial unit such as a lat/lon grid or watershed into three categories of hydrologic units (as shown in :numref:`Figure MOSART conceptual diagram`): hillslopes that convert both surface and subsurface runoff into tributaries, tributaries that discharge into a single main channel, and the main channel that connects the local spatial unit with upstream/downstream units through the river network. MOSART assumes that all the tributaries within a spatial unit can be treated as a single hypothetical sub-network channel with a transport capacity equivalent to all the tributaries combined. Correspondingly, three routing processes are represented in MOSART: 1) hillslope routing: in each spatial unit, surface runoff is routed as overland flow into the sub-network channel, while subsurface runoff generated in the spatial unit directly enters the sub-network channel; 2) sub-network channel routing: the sub-network channel receives water from the hillslopes, routes water through the channel and discharges it into the main channel; 3) main channel routing: the main channel receives water from the sub-network channel and/or inflow, if any, from the upstream spatial units, and discharges the water to its downstream spatial unit or the ocean. .. Figure 14.1. MOSART conceptual diagram @@ -51,146 +25,62 @@ and discharges the water to its downstream spatial unit or the ocean. :width: 800px :height: 400px -MOSART only routes positive runoff, although negative runoff can be generated -occasionally by the land model (e.g., :math:`q_{gwl}`). Negative runoff in any -runoff component including :math:`q_{sur}`, :math:`q_{sub}`, :math:`q_{gwl}` -is not routed through MOSART, but instead is mapped directly from the spatial unit -where it is generated at any time step to the coupler. - -In MOSART, the travel velocities of water across hillslopes, sub-network and main -channel are all estimated using Manning’s equation with different levels of -simplifications. Generally the Manning’s equation is in the form of +MOSART only routes positive runoff, although negative runoff can be generated occasionally by the land model (e.g., :math:`q_{gwl}`). Negative runoff in any runoff component including :math:`q_{sur}`, :math:`q_{sub}`, :math:`q_{gwl}` is not routed through MOSART, but instead is mapped directly from the spatial unit where it is generated at any time step to the coupler. + +In MOSART, the travel velocities of water across hillslopes, sub-network and main channel are all estimated using Manning's equation with different levels of simplifications. Generally the Manning's equation is in the form of .. math:: :label: 14.1 - + V = \frac{R^{\frac{2}{3}} S_{f}}{n} -where :math:`V` is the travel velocity (m s :sup:`-1` ), :math:`R` is the hydraulic -radius (m). :math:`S_{f}` is the friction slope that accounts for the effects -of gravity, friction, inertia and other forces on the water. If the channel slope -is steep enough, the gravity force dominates over the others so one can approximate -:math:`S_{f}` by the channel bed slope :math:`S` , which is the key assumption -underpinning the kinematic wave method. :math:`n` is the Manning’s roughness -coefficient, which is mainly controlled by surface roughness and sinuosity of the -flow path. +where :math:`V` is the travel velocity (m s :sup:`-1` ), :math:`R` is the hydraulic radius (m). :math:`S_{f}` is the friction slope that accounts for the effects of gravity, friction, inertia and other forces on the water. If the channel slope is steep enough, the gravity force dominates over the others so one can approximate :math:`S_{f}` by the channel bed slope :math:`S`, which is the key assumption underpinning the kinematic wave method. :math:`n` is the Manning's roughness coefficient, which is mainly controlled by surface roughness and sinuosity of the flow path. -If the water surface is sufficiently large or the water depth :math:`h` is -sufficiently shallow, the hydraulic radius can be approximated by the water depth. -This is the case for both hillslope and sub-network channel routing. +If the water surface is sufficiently large or the water depth :math:`h` is sufficiently shallow, the hydraulic radius can be approximated by the water depth. This is the case for both hillslope and sub-network channel routing. .. math:: :label: 14.2 - + R_{h} = h_{h} R_{t} = h_{t} -Here :math:`R_{h}` (m) and :math:`R_{t}` (m) are hydraulic radius for hillslope and -sub-network channel routing respectively, and :math:`h_{h}` (m) and :math:`h_{t}` -(m) are water depth during hillslope and sub-network channel routing respectively. - -For the main channel, the hydraulic radius is given by +Here :math:`R_{h}` (m) and :math:`R_{t}` (m) are hydraulic radius for hillslope and sub-network channel routing respectively, and :math:`h_{h}` (m) and :math:`h_{t}` (m) are water depth during hillslope and sub-network channel routing respectively. + +For the main channel, the hydraulic radius is given by .. math:: :label: 14.3 - + R_{r} = \frac{A_{r}}{P_{r}} -where :math:`A_{r}` (m :sup:`2` ) is the wetted area defined as the part of the -channel cross-section area below the water surface, :math:`P_{r}` (m) is the -wetted perimeter, the perimeter confined in the wetted area. - -For hillslopes, sub-network and main channels, a common continuity equation can -be written as +where :math:`A_{r}` (m :sup:`2` ) is the wetted area defined as the part of the channel cross-section area below the water surface, :math:`P_{r}` (m) is the wetted perimeter, the perimeter confined in the wetted area. + +For hillslopes, sub-network and main channels, a common continuity equation can be written as .. math:: :label: 14.4 - - \frac{dS}{dt} = Q_{in} - Q_{out} + R + \frac{dS}{dt} = Q_{in} - Q_{out} + R -where :math:`Q_{in}` (m :sup:`3` s :sup:`-1` ) is the main channel flow from -the upstream grid(s) into the main channel of the current grid, which is zero for -hillslope and sub-network routing. :math:`Q_{out}` (m :sup:`3` s :sup:`-1` ) is -the outflow rate from hillslope into the sub-network, from the sub-network into -the main channel, or from the current main channel to the main channel of its -downstream grid (if not the outlet grid) or ocean (if the current grid is the -basin outlet). :math:`R` (m :sup:`3` s :sup:`-1` ) is a source term, which -could be the surface -runoff generation rate for hillslopes, or lateral inflow (from hillslopes) into -sub-network channel or water-atmosphere exchange fluxes such as precipitation -and evaporation. It is assumed that surface runoff is generated uniformly -across all the hillslopes. Currently, MOSART does not exchange water with -the atmosphere or return water to the land model so its function is strictly -to transport water from runoff generation through the hillslope, tributaries, -and main channels to the basin outlets. +where :math:`Q_{in}` (m :sup:`3` s :sup:`-1` ) is the main channel flow from the upstream grid(s) into the main channel of the current grid, which is zero for hillslope and sub-network routing. :math:`Q_{out}` (m :sup:`3` s :sup:`-1` ) is the outflow rate from hillslope into the sub-network, from the sub-network into the main channel, or from the current main channel to the main channel of its downstream grid (if not the outlet grid) or ocean (if the current grid is the basin outlet). :math:`R` (m :sup:`3` s :sup:`-1` ) is a source term, which could be the surface runoff generation rate for hillslopes, or lateral inflow (from hillslopes) into sub-network channel or water-atmosphere exchange fluxes such as precipitation and evaporation. It is assumed that surface runoff is generated uniformly across all the hillslopes. Currently, MOSART does not exchange water with the atmosphere or return water to the land model so its function is strictly to transport water from runoff generation through the hillslope, tributaries, and main channels to the basin outlets. .. _Numerical Solution MOSART: Numerical Solution ---------------------------- -The numerical implementation of MOSART is mainly based on a subcycling -scheme and a local time-stepping algorithm. There are two levels of -subcycling. For convenience, we denote :math:`T_{inputs}` (s), -:math:`T_{mosart}` (s), :math:`T_{hillslope}` (s) and -:math:`T_{channel}` (s) as the time steps of runoff inputs (from CLM -to MOSART via the flux coupler), MOSART routing, hillslope routing, and -channel routing, respectively. The first level of subcycling is between -the runoff inputs and MOSART routing. If :math:`T_{inputs}` is 10800s -and :math:`T_{mosart}` is 3600s, three MOSART time steps will be -invoked each time the runoff inputs are updated. The second level of -subcycling is between the hillslope routing and channel routing. This -is to account for the fact that the travel velocity of water across -hillslopes is usually much slower than that in the channels. -:math:`T_{hillslope}` is usually set as the same as :math:`T_{mosart}`, -but within each time step of hillslope routing there are a few time -steps for channel routing, i.e., -:math:`T_{hillslope} = D_{levelH2R} \cdot T_{channel}`. The local -time-stepping algorithm is to account for the fact that the travel -velocity of water is much faster in some river channels (e.g., with -steeper bed slope, narrower channel width) than others. That is, for -each channel (either a sub-network or main channel), the final time -step of local channel routing is given as -:math:`T_{local}=T_{channel}/D_{local}`. :math:`D_{local}` is -currently estimated empirically as a function of local channel slope, -width, length and upstream drainage area. If MOSART crashes due to a -numerical issue, we recommend increasing :math:`D_{levelH2R}` and, if -the issue remains, reducing :math:`T_{mosart}`. +The numerical implementation of MOSART is mainly based on a subcycling scheme and a local time-stepping algorithm. There are two levels of subcycling. For convenience, we denote :math:`T_{inputs}` (s), :math:`T_{mosart}` (s), :math:`T_{hillslope}` (s) and :math:`T_{channel}` (s) as the time steps of runoff inputs (from CLM to MOSART via the flux coupler), MOSART routing, hillslope routing, and channel routing, respectively. The first level of subcycling is between the runoff inputs and MOSART routing. If :math:`T_{inputs}` is 10800s and :math:`T_{mosart}` is 3600s, three MOSART time steps will be invoked each time the runoff inputs are updated. The second level of subcycling is between the hillslope routing and channel routing. This is to account for the fact that the travel velocity of water across hillslopes is usually much slower than that in the channels. :math:`T_{hillslope}` is usually set as the same as :math:`T_{mosart}`, but within each time step of hillslope routing there are a few time steps for channel routing, i.e., :math:`T_{hillslope} = D_{levelH2R} \cdot T_{channel}`. The local time-stepping algorithm is to account for the fact that the travel velocity of water is much faster in some river channels (e.g., with steeper bed slope, narrower channel width) than others. That is, for each channel (either a sub-network or main channel), the final time step of local channel routing is given as :math:`T_{local}=T_{channel}/D_{local}`. :math:`D_{local}` is currently estimated empirically as a function of local channel slope, width, length and upstream drainage area. If MOSART crashes due to a numerical issue, we recommend increasing :math:`D_{levelH2R}` and, if the issue remains, reducing :math:`T_{mosart}`. .. _Parameters and Input Data: Parameters and Input Data --------------------------------- -MOSART is supported by a comprehensive, global hydrography dataset at 0.5 -:sup:`o` resolution. As such, the fundamental spatial unit of MOSART is a 0.5 -:sup:`o` lat/lon grid. The topographic parameters (such as flow direction, -channel length, topographic and channel slopes, etc.) were derived using the -Dominant River Tracing (DRT) algorithm (:ref:`Wu et al., 2011` ; -:ref:`Wu et al. 2012 `). The DRT algorithm produces the topographic -parameters in a scale-consistent way to preserve/upscale the key features of -a baseline high-resolution hydrography dataset at multiple coarser spatial -resolutions. Here the baseline high-resolution hydrography dataset is the -1km resolution Hydrological data and maps based on SHuttle Elevation -Derivatives at multiple Scales (HydroSHEDS) -(:ref:`Lehner and Döll, 2004 ` ; -:ref:`Lehner et al., 2008 `). The channel geometry -parameters, e.g., bankfull width and depth, were estimated from empirical -hydraulic geometry relationships as functions of the mean annual discharge. -The Manning roughness coefficients for overland and channel flow were -calculated as functions of landcover and water depth. For more details -on the methodology to derive channel geometry and the Manning’s roughness -coefficients, please refer to -:ref:`Getirana et al. (2012) ` . The full list of -parameters included in this global hydrography dataset is provided in -:numref:`Table MOSART Parameters`. Evaluation of global simulations -by MOSART using the aforementioned parameters is described in -:ref:`Li et al. (2015b) ` . +MOSART is supported by a comprehensive, global hydrography dataset at 0.5 ° resolution. As such, the fundamental spatial unit of MOSART is a 0.5 ° lat/lon grid. The topographic parameters (such as flow direction, channel length, topographic and channel slopes, etc.) were derived using the Dominant River Tracing (DRT) algorithm (:ref:`Wu et al., 2011`; :ref:`Wu et al. 2012 `). The DRT algorithm produces the topographic parameters in a scale-consistent way to preserve/upscale the key features of a baseline high-resolution hydrography dataset at multiple coarser spatial resolutions. Here the baseline high-resolution hydrography dataset is the 1km resolution Hydrological data and maps based on SHuttle Elevation Derivatives at multiple Scales (HydroSHEDS) (:ref:`Lehner and Döll, 2004 `; :ref:`Lehner et al., 2008 `). The channel geometry parameters, e.g., bankfull width and depth, were estimated from empirical hydraulic geometry relationships as functions of the mean annual discharge. The Manning roughness coefficients for overland and channel flow were calculated as functions of landcover and water depth. For more details on the methodology to derive channel geometry and the Manning's roughness coefficients, please refer to :ref:`Getirana et al. (2012) `. The full list of parameters included in this global hydrography dataset is provided in :numref:`Table MOSART Parameters`. Evaluation of global simulations by MOSART using the aforementioned parameters is described in :ref:`Li et al. (2015b) `. .. _Table MOSART Parameters: -.. table:: List of parameters in the global hydrography dataset +.. table:: List of parameters in the global hydrography dataset +-------------------------+---------------+------------------------------------------------------------------------------------------------------------------------------------+ | Name | Unit | Description | @@ -217,31 +107,19 @@ by MOSART using the aforementioned parameters is described in +-------------------------+---------------+------------------------------------------------------------------------------------------------------------------------------------+ | :math:`W_{t}` | m | The average bankfull width of tributary channels | +-------------------------+---------------+------------------------------------------------------------------------------------------------------------------------------------+ - | :math:`n_{r}` | \- | Manning’s roughness coefficient for channel flow routing | + | :math:`n_{r}` | \- | Manning's roughness coefficient for channel flow routing | +-------------------------+---------------+------------------------------------------------------------------------------------------------------------------------------------+ - | :math:`n_{h}` | \- | Manning’s roughness coefficient for overland flow routing | + | :math:`n_{h}` | \- | Manning's roughness coefficient for overland flow routing | +-------------------------+---------------+------------------------------------------------------------------------------------------------------------------------------------+ - - Difference between CLM5.0 and CLM4.5 ------------------------------------- -1. Routing methods: RTM, a linear reservoir method, is used in CLM4.5 for -river routing, whilst in CLM5.0, MOSART is an added option for river routing -based on the more physically-based kinematic wave method. - -2. Runoff treatment: In RTM runoff is routed regardless of its sign so -negative streamflow can be simulated at times. MOSART routes only non-negative -runoff and always produces positive streamflow, which is important for -future extensions to model riverine heat and biogeochemical fluxes. +1. Routing methods: RTM, a linear reservoir method, is used in CLM4.5 for river routing, whilst in CLM5.0, MOSART is an added option for river routing based on the more physically-based kinematic wave method. -3. Input parameters: RTM in CLM4.5 only requires one layer of a spatially varying -variable of channel velocity, whilst MOSART in CLM5.0 requires 13 parameters that -are all available globally at 0.5 :sup:`o` resolution. +2. Runoff treatment: In RTM runoff is routed regardless of its sign so negative streamflow can be simulated at times. MOSART routes only non-negative runoff and always produces positive streamflow, which is important for future extensions to model riverine heat and biogeochemical fluxes. -4. Outputs: RTM only produces streamflow simulation, whilst MOSART -additionally simulates the time-varying channel velocities, channel water depth, and -channel surface water variations. +3. Input parameters: RTM in CLM4.5 only requires one layer of a spatially varying variable of channel velocity, whilst MOSART in CLM5.0 requires 13 parameters that are all available globally at 0.5 ° resolution. +4. Outputs: RTM only produces streamflow simulation, whilst MOSART additionally simulates the time-varying channel velocities, channel water depth, and channel surface water variations. diff --git a/doc/source/tech_note/Methane/CLM50_Tech_Note_Methane.rst b/doc/source/tech_note/Methane/CLM50_Tech_Note_Methane.rst index 7c89f857e3..90be6e6ad0 100644 --- a/doc/source/tech_note/Methane/CLM50_Tech_Note_Methane.rst +++ b/doc/source/tech_note/Methane/CLM50_Tech_Note_Methane.rst @@ -3,187 +3,80 @@ Methane Model ================= -The representation of processes in the methane biogeochemical model -integrated in CLM [CLM4Me; (:ref:`Riley et al. 2011a`)] -is based on several previously published models -(:ref:`Cao et al. 1996`; :ref:`Petrescu et al. 2010`; -:ref:`Tianet al. 2010`; :ref:`Walter et al. 2001`; -:ref:`Wania et al. 2010`; :ref:`Zhang et al. 2002`; -:ref:`Zhuang et al. 2004`). Although the model has similarities -with these precursor models, a number of new process representations and -parameterization have been integrated into CLM. - -Mechanistically modeling net surface CH\ :sub:`4` emissions requires -representing a complex and interacting series of processes. We first -(section :numref:`Methane Model Structure and Flow`) describe the overall -model structure and flow of -information in the CH\ :sub:`4` model, then describe the methods -used to represent: CH\ :sub:`4` mass balance; CH\ :sub:`4` -production; ebullition; aerenchyma transport; CH\ :sub:`4` -oxidation; reactive transport solution, including boundary conditions, -numerical solution, water table interface, etc.; seasonal inundation -effects; and impact of seasonal inundation on CH\ :sub:`4` -production. +The representation of processes in the methane biogeochemical model integrated in CLM [CLM4Me; (:ref:`Riley et al. 2011a`)] is based on several previously published models (:ref:`Cao et al. 1996`; :ref:`Petrescu et al. 2010`; :ref:`Tianet al. 2010`; :ref:`Walter et al. 2001`; :ref:`Wania et al. 2010`; :ref:`Zhang et al. 2002`; :ref:`Zhuang et al. 2004`). Although the model has similarities with these precursor models, a number of new process representations and parameterization have been integrated into CLM. + +Mechanistically modeling net surface CH\ :sub:`4` emissions requires representing a complex and interacting series of processes. We first (section :numref:`Methane Model Structure and Flow`) describe the overall model structure and flow of information in the CH\ :sub:`4` model, then describe the methods used to represent: CH\ :sub:`4` mass balance; CH\ :sub:`4` production; ebullition; aerenchyma transport; CH\ :sub:`4` oxidation; reactive transport solution, including boundary conditions, numerical solution, water table interface, etc.; seasonal inundation effects; and impact of seasonal inundation on CH\ :sub:`4` production. .. _Methane Model Structure and Flow: Methane Model Structure and Flow ------------------------------------- -The driver routine for the methane biogeochemistry calculations (ch4, in -ch4Mod.F) controls the initialization of boundary conditions, -inundation, and impact of redox conditions; calls to routines to -calculate CH\ :sub:`4` production, oxidation, transport through -aerenchyma, ebullition, and the overall mass balance (for unsaturated -and saturated soils and, if desired, lakes); resolves changes to -CH\ :sub:`4` calculations associated with a changing inundated -fraction; performs a mass balance check; and calculates the average -gridcell CH\ :sub:`4` production, oxidation, and exchanges with -the atmosphere. +The driver routine for the methane biogeochemistry calculations (ch4, in ch4Mod.F) controls the initialization of boundary conditions, inundation, and impact of redox conditions; calls to routines to calculate CH\ :sub:`4` production, oxidation, transport through aerenchyma, ebullition, and the overall mass balance (for unsaturated and saturated soils and, if desired, lakes); resolves changes to CH\ :sub:`4` calculations associated with a changing inundated fraction; performs a mass balance check; and calculates the average gridcell CH\ :sub:`4` production, oxidation, and exchanges with the atmosphere. .. _Governing Mass-Balance Relationship: Governing Mass-Balance Relationship ---------------------------------------- -The model (:numref:`Figure Methane Schematic`) accounts for CH\ :sub:`4` -production in the anaerobic fraction of soil (*P*, mol m\ :sup:`-3` -s\ :sup:`-1`), ebullition (*E*, mol m\ :sup:`-3` s\ :sup:`-1`), -aerenchyma transport (*A*, mol m\ :sup:`-3` s\ :sup:`-1`), aqueous and -gaseous diffusion (:math:`{F}_{D}`, mol m\ :sup:`-2` s\ :sup:`-1`), and -oxidation (*O*, mol m\ :sup:`-3` s\ :sup:`-1`) via a transient reaction -diffusion equation: +The model (:numref:`Figure Methane Schematic`) accounts for CH\ :sub:`4` production in the anaerobic fraction of soil (*P*, mol m\ :sup:`-3` s\ :sup:`-1`), ebullition (*E*, mol m\ :sup:`-3` s\ :sup:`-1`), aerenchyma transport (*A*, mol m\ :sup:`-3` s\ :sup:`-1`), aqueous and gaseous diffusion (:math:`{F}_{D}`, mol m\ :sup:`-2` s\ :sup:`-1`), and oxidation (*O*, mol m\ :sup:`-3` s\ :sup:`-1`) via a transient reaction diffusion equation: .. math:: :label: 24.1 \frac{\partial \left(RC\right)}{\partial t} =\frac{\partial F_{D} }{\partial z} +P\left(z,t\right)-E\left(z,t\right)-A\left(z,t\right)-O\left(z,t\right) -Here *z* (m) represents the vertical dimension, *t* (s) is time, and *R* -accounts for gas in both the aqueous and gaseous -phases:\ :math:`R = \epsilon _{a} +K_{H} \epsilon _{w}`, with -:math:`\epsilon _{a}`, :math:`\epsilon _{w}`, and :math:`K_{H}` (-) the air-filled porosity, water-filled -porosity, and partitioning coefficient for the species of interest, -respectively, and :math:`C` represents CH\ :sub:`4` or O\ :sub:`2` concentration with respect to water volume (mol m\ :sup:`-3`). - -An analogous version of equation is concurrently solved for -O\ :sub:`2`, but with the following differences relative to -CH\ :sub:`4`: *P* = *E* = 0 (i.e., no production or ebullition), -and the oxidation sink includes the O\ :sub:`2` demanded by -methanotrophs, heterotroph decomposers, nitrifiers, and autotrophic root -respiration. - -As currently implemented, each gridcell contains an inundated and a -non-inundated fraction. Therefore, equation is solved four times for -each gridcell and time step: in the inundated and non-inundated -fractions, and for CH\ :sub:`4` and O\ :sub:`2`. If desired, -the CH\ :sub:`4` and O\ :sub:`2` mass balance equation is -solved again for lakes (Chapter 9). For non-inundated areas, the water -table interface is defined at the deepest transition from greater than -95% saturated to less than 95% saturated that occurs above frozen soil -layers. The inundated fraction is allowed to change at each time step, -and the total soil CH\ :sub:`4` quantity is conserved by evolving -CH\ :sub:`4` to the atmosphere when the inundated fraction -decreases, and averaging a portion of the non-inundated concentration -into the inundated concentration when the inundated fraction increases. +Here *z* (m) represents the vertical dimension, *t* (s) is time, and *R* accounts for gas in both the aqueous and gaseous phases:\ :math:`R = \epsilon _{a} +K_{H} \epsilon _{w}`, with :math:`\epsilon _{a}`, :math:`\epsilon _{w}`, and :math:`K_{H}` (-) the air-filled porosity, water-filled porosity, and partitioning coefficient for the species of interest, respectively, and :math:`C` represents CH\ :sub:`4` or O\ :sub:`2` concentration with respect to water volume (mol m\ :sup:`-3`). + +An analogous version of equation :eq:`24.1` is concurrently solved for O\ :sub:`2`, but with the following differences relative to CH\ :sub:`4`: *P* = *E* = 0 (i.e., no production or ebullition), and the oxidation sink includes the O\ :sub:`2` demanded by methanotrophs, heterotroph decomposers, nitrifiers, and autotrophic root respiration. + +As currently implemented, each gridcell contains an inundated and a non-inundated fraction. Therefore, equation :eq:`24.1` is solved four times for each gridcell and time step: in the inundated and non-inundated fractions, and for CH\ :sub:`4` and O\ :sub:`2`. If desired, the CH\ :sub:`4` and O\ :sub:`2` mass balance equation is solved again for lakes (Chapter 9). For non-inundated areas, the water table interface is defined at the deepest transition from greater than 95% saturated to less than 95% saturated that occurs above frozen soil layers. The inundated fraction is allowed to change at each time step, and the total soil CH\ :sub:`4` quantity is conserved by evolving CH\ :sub:`4` to the atmosphere when the inundated fraction decreases, and averaging a portion of the non-inundated concentration into the inundated concentration when the inundated fraction increases. .. _Figure Methane Schematic: .. figure:: image1.png - Schematic representation of biological and physical - processes integrated in CLM that affect the net CH\ :sub:`4` - surface flux (:ref:`Riley et al. 2011a`). (left) - Fully inundated portion of a - CLM gridcell and (right) variably saturated portion of a gridcell. + Schematic representation of biological and physical processes integrated in CLM that affect the net CH\ :sub:`4` + surface flux (:ref:`Riley et al. 2011a`). (left) + Fully inundated portion of a CLM gridcell and (right) variably saturated portion of a gridcell. .. _CH4 Production: CH\ :sub:`4` Production ---------------------------------- -Because CLM does not currently specifically represent wetland plant -functional types or soil biogeochemical processes, we used -gridcell-averaged decomposition rates as proxies. Thus, the upland -(default) heterotrophic respiration is used to estimate the wetland -decomposition rate after first dividing off the O\ :sub:`2` -limitation. The O\ :sub:`2` consumption associated with anaerobic -decomposition is then set to the unlimited version so that it will be -reduced appropriately during O\ :sub:`2` competition. -CH\ :sub:`4` production at each soil level in the anaerobic -portion (i.e., below the water table) of the column is related to the -gridcell estimate of heterotrophic respiration from soil and litter -(R\ :sub:`H`; mol C m\ :sup:`-2` s\ :sub:`-1`) corrected for its soil temperature -(:math:`{T}_{s}`) dependence, soil temperature through a -:math:`{A}_{10}` factor (:math:`f_{T}`), pH (:math:`f_{pH}`), -redox potential (:math:`f_{pE}`), and a factor accounting for the -seasonal inundation fraction (*S*, described below): +Because CLM does not currently specifically represent wetland plant functional types or soil biogeochemical processes, we used gridcell-averaged decomposition rates as proxies. Thus, the upland (default) heterotrophic respiration is used to estimate the wetland decomposition rate after first dividing off the O\ :sub:`2` limitation. The O\ :sub:`2` consumption associated with anaerobic decomposition is then set to the unlimited version so that it will be reduced appropriately during O\ :sub:`2` competition. CH\ :sub:`4` production at each soil level in the anaerobic portion (i.e., below the water table) of the column is related to the gridcell estimate of heterotrophic respiration from soil and litter (R\ :sub:`H`; mol C m\ :sup:`-2` s\ :sub:`-1`) corrected for its soil temperature (:math:`{T}_{s}`) dependence, soil temperature through a :math:`{A}_{10}` factor (:math:`f_{T}`), pH (:math:`f_{pH}`), redox potential (:math:`f_{pE}`), and a factor accounting for the seasonal inundation fraction (*S*, described below): .. math:: :label: 24.2 P=R_{H} f_{CH_{4} } f_{T} f_{pH} f_{pE} S. -Here, :math:`f_{CH_{4} }` is the baseline ratio between CO\ :sub:`2` -and CH\ :sub:`4` production (all parameters values are given in -:numref:`Table Methane Parameter descriptions`). Currently, :math:`f_{CH_{4} }` -is modified to account for our assumptions that methanogens may have a -higher Q\ :math:`{}_{10}` than aerobic decomposers; are not N limited; -and do not have a low-moisture limitation. - -When the single BGC soil level is used in CLM (Chapter :numref:`rst_Decomposition`), the -temperature factor, :math:`f_{T}` , is set to 0 for temperatures equal -to or below freezing, even though CLM allows heterotrophic respiration -below freezing. However, if the vertically resolved BGC soil column is -used, CH\ :sub:`4` production continues below freezing because -liquid water stress limits decomposition. The base temperature for the -:math:`{Q}_{10}` factor, :math:`{T}_{B}`, is 22\ :sup:`o` C and effectively -modified the base :math:`f_{CH_{4}}` value. - -For the single-layer BGC version, :math:`{R}_{H}` is distributed -among soil levels by assuming that 50% is associated with the roots -(using the CLM PFT-specific rooting distribution) and the rest is evenly -divided among the top 0.28 m of soil (to be consistent with CLM’s soil -decomposition algorithm). For the vertically resolved BGC version, the -prognosed distribution of :math:`{R}_{H}` is used to estimate CH\ :sub:`4` production. - -The factor :math:`f_{pH}` is nominally set to 1, although a static -spatial map of *pH* can be used to determine this factor -(:ref:`Dunfield et al. 1993`) by applying: +Here, :math:`f_{CH_{4} }` is the baseline ratio between CO\ :sub:`2` and CH\ :sub:`4` production (all parameters values are given in :numref:`Table Methane Parameter descriptions`). Currently, :math:`f_{CH_{4} }` is modified to account for our assumptions that methanogens may have a higher Q\ :math:`{}_{10}` than aerobic decomposers; are not N limited; and do not have a low-moisture limitation. + +When the single BGC soil level is used in CLM (Chapter :numref:`rst_Decomposition`), the temperature factor, :math:`f_{T}`, is set to 0 for temperatures equal to or below freezing, even though CLM allows heterotrophic respiration below freezing. However, if the vertically resolved BGC soil column is used, CH\ :sub:`4` production continues below freezing because liquid water stress limits decomposition. The base temperature for the :math:`{Q}_{10}` factor, :math:`{T}_{B}`, is 22°C and effectively modified the base :math:`f_{CH_{4}}` value. + +For the single-layer BGC version, :math:`{R}_{H}` is distributed among soil levels by assuming that 50% is associated with the roots (using the CLM PFT-specific rooting distribution) and the rest is evenly divided among the top 0.28 m of soil (to be consistent with CLM's soil decomposition algorithm). For the vertically resolved BGC version, the prognosed distribution of :math:`{R}_{H}` is used to estimate CH\ :sub:`4` production. + +The factor :math:`f_{pH}` is nominally set to 1, although a static spatial map of *pH* can be used to determine this factor (:ref:`Dunfield et al. 1993`) by applying: .. math:: :label: 24.3 f_{pH} =10^{-0.2235pH^{2} +2.7727pH-8.6} . -The :math:`f_{pE}` factor assumes that alternative electron acceptors -are reduced with an e-folding time of 30 days after inundation. The -default version of the model applies this factor to horizontal changes -in inundated area but not to vertical changes in the water table depth -in the upland fraction of the gridcell. We consider both :math:`f_{pH}` -and :math:`f_{pE}` to be poorly constrained in the model and identify -these controllers as important areas for model improvement. +The :math:`f_{pE}` factor assumes that alternative electron acceptors are reduced with an e-folding time of 30 days after inundation. The default version of the model applies this factor to horizontal changes in inundated area but not to vertical changes in the water table depth in the upland fraction of the gridcell. We consider both :math:`f_{pH}` and :math:`f_{pE}` to be poorly constrained in the model and identify these controllers as important areas for model improvement. -As a non-default option to account for CH\ :sub:`4` production in -anoxic microsites above the water table, we apply the Arah and Stephen -(1998) estimate of anaerobic fraction: +As a non-default option to account for CH\ :sub:`4` production in anoxic microsites above the water table, we apply the Arah and Stephen (1998) estimate of anaerobic fraction: .. math:: :label: 24.4 \varphi =\frac{1}{1+\eta C_{O_{2} } } . -Here, :math:`\phi` is the factor by which production is inhibited -above the water table (compared to production as calculated in equation -, :math:`C_{O_{2}}` (mol m\ :sup:`-3`) is the bulk soil oxygen -concentration, and :math:`\eta` = 400 mol m\ :sup:`-3`. +Here, :math:`\varphi` is the factor by which production is inhibited above the water table (compared to production as calculated in equation :eq:`24.2`, :math:`C_{O_{2}}` (mol m\ :sup:`-3`) is the bulk soil oxygen concentration, and :math:`\eta` = 400 mol m\ :sup:`-3`. -The O\ :sub:`2` required to facilitate the vertically resolved -heterotrophic decomposition and root respiration is estimated assuming 1 -mol O\ :sub:`2` is required per mol CO\ :sub:`2` produced. -The model also calculates the O\ :sub:`2` required during -nitrification, and the total O\ :sub:`2` demand is used in the -O\ :sub:`2` mass balance solution. +The O\ :sub:`2` required to facilitate the vertically resolved heterotrophic decomposition and root respiration is estimated assuming 1 mol O\ :sub:`2` is required per mol CO\ :sub:`2` produced. The model also calculates the O\ :sub:`2` required during nitrification, and the total O\ :sub:`2` demand is used in the O\ :sub:`2` mass balance solution. .. _Table Methane Parameter descriptions: @@ -225,16 +118,10 @@ O\ :sub:`2` mass balance solution. | | :math:`R_{o,\max }` | 1.25 x 10\ :math:`{}^{-5}` | 1.25\ :math:`\times`\ 10\ :math:`{}^{-6}` - 1.25\ :math:`\times`\ 10\ :math:`{}^{-4}` | mol m\ :sup:`-3` s\ :sup:`-1` | Maximum oxidation rate (wetlands) | +--------------+----------------------------+----------------------------------------------+--------------------------------------------------------------------------------------------------+---------------------------------------------+--------------------------------------------------------------------------------------------+ - Ebullition --------------- -Briefly, the simulated aqueous CH\ :sub:`4` concentration in each -soil level is used to estimate the expected equilibrium gaseous partial -pressure (:math:`C_{e}` ), as a function of temperature and depth below -the water table, by first estimating the Henry’s law partitioning -coefficient (:math:`k_{h}^{C}` ) by the method described in -:ref:`Wania et al. (2010)`: +Briefly, the simulated aqueous CH\ :sub:`4` concentration in each soil level is used to estimate the expected equilibrium gaseous partial pressure (:math:`C_{e}` ), as a function of temperature and depth below the water table, by first estimating the Henry's law partitioning coefficient (:math:`k_{h}^{C}` ) by the method described in :ref:`Wania et al. (2010)`: .. math:: :label: 24.5 @@ -251,75 +138,36 @@ coefficient (:math:`k_{h}^{C}` ) by the method described in C_{e} =\frac{C_{w} R_{g} T}{\theta _{s} k_{H}^{C} p} -where :math:`C_{H}` \ is a constant, :math:`R_{g}` is the universal -gas constant, :math:`k_{H}^{s}` is Henry’s law partitioning coefficient -at standard temperature (:math:`T^{s}` ),\ :math:`C_{w}` \ is local -aqueous CH\ :sub:`4` concentration, and *p* is pressure. - -The local pressure is calculated as the sum of the ambient pressure, -water pressure down to the local depth, and pressure from surface -ponding (if applicable). When the CH\ :sub:`4` partial pressure -exceeds 15% of the local pressure (Baird et al. 2004; Strack et al. -2006; Wania et al. 2010), bubbling occurs to remove CH\ :sub:`4` -to below this value, modified by the fraction of CH\ :sub:`4` in -the bubbles [taken as 57%; (:ref:`Kellner et al. 2006`; -:ref:`Wania et al. 2010`)]. -Bubbles are immediately added to the surface flux for saturated columns -and are placed immediately above the water table interface in -unsaturated columns. +where :math:`C_{H}` \ is a constant, :math:`R_{g}` is the universal gas constant, :math:`k_{H}^{s}` is Henry's law partitioning coefficient at standard temperature (:math:`T^{s}` ),\ :math:`C_{w}` \ is local aqueous CH\ :sub:`4` concentration, and *p* is pressure. + +The local pressure is calculated as the sum of the ambient pressure, water pressure down to the local depth, and pressure from surface ponding (if applicable). When the CH\ :sub:`4` partial pressure exceeds 15% of the local pressure (:ref:`Baird et al. 2004`; :ref:`Strack et al. 2006`; :ref:`Wania et al. 2010`), bubbling occurs to remove CH\ :sub:`4` to below this value, modified by the fraction of CH\ :sub:`4` in the bubbles [taken as 57%; (:ref:`Kellner et al. 2006`; :ref:`Wania et al. 2010`)]. Bubbles are immediately added to the surface flux for saturated columns and are placed immediately above the water table interface in unsaturated columns. .. _Aerenchyma Transport: Aerenchyma Transport ------------------------- -Aerenchyma transport is modeled in CLM as gaseous diffusion driven by a -concentration gradient between the specific soil layer and the -atmosphere and, if specified, by vertical advection with the -transpiration stream. There is evidence that pressure driven flow can -also occur, but we did not include that mechanism in the current model. +Aerenchyma transport is modeled in CLM as gaseous diffusion driven by a concentration gradient between the specific soil layer and the atmosphere and, if specified, by vertical advection with the transpiration stream. There is evidence that pressure driven flow can also occur, but we did not include that mechanism in the current model. The diffusive transport through aerenchyma (*A*, mol m\ :sup:`-2` s\ :sup:`-1`) from each soil layer is represented in the model as: .. math:: :label: 24.8 - A=\frac{C\left(z\right)-C_{a} }{{\raise0.7ex\hbox{$ r_{L} z $}\!\mathord{\left/ {\vphantom {r_{L} z D}} \right. \kern-\nulldelimiterspace}\!\lower0.7ex\hbox{$ D $}} +r_{a} } pT\rho _{r} , - -where *D* is the free-air gas diffusion coefficient (m:sup:`2` s\ :sup:`-1`); *C(z)* (mol m\ :sup:`-3`) is the gaseous -concentration at depth *z* (m); :math:`r_{L}` is the ratio of root -length to depth; *p* is the porosity (-); *T* is specific aerenchyma -area (m:sup:`2` m\ :sup:`-2`); :math:`{r}_{a}` is the -aerodynamic resistance between the surface and the atmospheric reference -height (s m:sup:`-1`); and :math:`\rho _{r}` is the rooting -density as a function of depth (-). The gaseous concentration is -calculated with Henry’s law as described in equation . - -Based on the ranges reported in :ref:`Colmer (2003)`, we have chosen -baseline aerenchyma porosity values of 0.3 for grass and crop PFTs and 0.1 for -tree and shrub PFTs: + A=\frac{C\left(z\right)-C_{a} }{{\raise0.7ex\hbox{$ r_{L} z $}\!\mathord{\left/ {\vphantom {r_{L} z D}} \right.}\!\lower0.7ex\hbox{$ D $}} +r_{a} } pT\rho _{r} , + +where *D* is the free-air gas diffusion coefficient (m\ :sup:`2` s\ :sup:`-1`); *C(z)* (mol m\ :sup:`-3`) is the gaseous concentration at depth *z* (m); :math:`r_{L}` is the ratio of root length to depth; *p* is the porosity (-); *T* is specific aerenchyma area (m\ :sup:`2` m\ :sup:`-2`); :math:`{r}_{a}` is the aerodynamic resistance between the surface and the atmospheric reference height (s m\ :sup:`-1`); and :math:`\rho _{r}` is the rooting density as a function of depth (-). The gaseous concentration is calculated with Henry's law as described in equation :eq:`24.7`. + +Based on the ranges reported in :ref:`Colmer (2003)`, we have chosen baseline aerenchyma porosity values of 0.3 for grass and crop PFTs and 0.1 for tree and shrub PFTs: .. math:: :label: 24.9 T=\frac{4 f_{N} N_{a}}{0.22} \pi R^{2} . -Here :math:`N_{a}` is annual net primary production (NPP, mol -m\ :sup:`-2` s\ :sup:`-1`); *R* is the aerenchyma radius -(2.9 :math:`\times`\ 10\ :sup:`-3` m); :math:`{f}_{N}` is the -belowground fraction of annual NPP; and the 0.22 factor represents the -amount of C per tiller. O\ :sub:`2` can also diffuse in from the -atmosphere to the soil layer via the reverse of the same pathway, with -the same representation as Equation but with the gas diffusivity of -oxygen. - -CLM also simulates the direct emission of CH\ :sub:`4` from leaves -to the atmosphere via transpiration of dissolved methane. We calculate -this flux (:math:`F_{CH_{4} -T}` ; mol m\ :math:`{}^{-}`\ :sup:`2` -s\ :sup:`-1`) using the simulated soil water methane concentration -(:math:`C_{CH_{4} ,j}` (mol m\ :sup:`-3`)) in each soil layer *j* -and the CLM predicted transpiration (:math:`F_{T}` ) for each PFT, -assuming that no methane was oxidized inside the plant tissue: +Here :math:`N_{a}` is annual net primary production (NPP, mol m\ :sup:`-2` s\ :sup:`-1`); *R* is the aerenchyma radius (2.9 :math:`\times`\ 10\ :sup:`-3` m); :math:`{f}_{N}` is the belowground fraction of annual NPP; and the 0.22 factor represents the amount of C per tiller. O\ :sub:`2` can also diffuse in from the atmosphere to the soil layer via the reverse of the same pathway, with the same representation as Equation :eq:`24.8` but with the gas diffusivity of oxygen. + +CLM also simulates the direct emission of CH\ :sub:`4` from leaves to the atmosphere via transpiration of dissolved methane. We calculate this flux (:math:`F_{CH_{4} -T}`; mol m\ :math:`{}^{-}`\ :sup:`2` s\ :sup:`-1`) using the simulated soil water methane concentration (:math:`C_{CH_{4},j}` (mol m\ :sup:`-3`)) in each soil layer *j* and the CLM predicted transpiration (:math:`F_{T}` ) for each PFT, assuming that no methane was oxidized inside the plant tissue: .. math:: :label: 24.10 @@ -331,247 +179,123 @@ assuming that no methane was oxidized inside the plant tissue: CH\ :sub:`4` Oxidation --------------------------------- -CLM represents CH\ :sub:`4` oxidation with double Michaelis-Menten -kinetics (:ref:`Arah and Stephen 1998`; :ref:`Segers 1998`), -dependent on both the gaseous CH\ :sub:`4` and O\ :sub:`2` concentrations: +CLM represents CH\ :sub:`4` oxidation with double Michaelis-Menten kinetics (:ref:`Arah and Stephen 1998`; :ref:`Segers 1998`), dependent on both the gaseous CH\ :sub:`4` and O\ :sub:`2` concentrations: .. math:: :label: 24.11 R_{oxic} =R_{o,\max } \left[\frac{C_{CH_{4} } }{K_{CH_{4} } +C_{CH_{4} } } \right]\left[\frac{C_{O_{2} } }{K_{O_{2} } +C_{O_{2} } } \right]Q_{10} F_{\vartheta } -where :math:`K_{CH_{4} }` and :math:`K_{O_{2} }` \ are the half -saturation coefficients (mol m\ :sup:`-3`) with respect to -CH\ :sub:`4` and O\ :sub:`2` concentrations, respectively; -:math:`R_{o,\max }` is the maximum oxidation rate (mol -m\ :sup:`-3` s\ :sup:`-1`); and :math:`{Q}_{10}` -specifies the temperature dependence of the reaction with a base -temperature set to 12 :sup:`o` C. The soil moisture limitation -factor :math:`F_{\theta }` is applied above the water table to -represent water stress for methanotrophs. Based on the data in -:ref:`Schnell and King (1996)`, we take -:math:`F_{\theta } = {e}^{-P/{P}_{c}}`, where *P* is the soil moisture -potential and :math:`{P}_{c} = -2.4 \times {10}^{5}` mm. +where :math:`K_{CH_{4} }` and :math:`K_{O_{2} }` \ are the half saturation coefficients (mol m\ :sup:`-3`) with respect to CH\ :sub:`4` and O\ :sub:`2` concentrations, respectively; :math:`R_{o,\max }` is the maximum oxidation rate (mol m\ :sup:`-3` s\ :sup:`-1`); and :math:`{Q}_{10}` specifies the temperature dependence of the reaction with a base temperature set to 12 °C. The soil moisture limitation factor :math:`F_{\theta }` is applied above the water table to represent water stress for methanotrophs. Based on the data in :ref:`Schnell and King (1996)`, we take :math:`F_{\theta } = {e}^{-P/{P}_{c}}`, where *P* is the soil moisture potential and :math:`{P}_{c} = -2.4 \times {10}^{5}` mm. .. _Reactive Transport Solution: Reactive Transport Solution -------------------------------- -The solution to equation is solved in several sequential steps: resolve -competition for CH\ :sub:`4` and O\ :sub:`2` (section -:numref:`Competition for CH4and O2`); add the ebullition flux into the -layer directly above the water -table or into the atmosphere; calculate the overall CH\ :sub:`4` -or O\ :sub:`2` source term based on production, aerenchyma -transport, ebullition, and oxidation; establish boundary conditions, -including surface conductance to account for snow, ponding, and -turbulent conductances and bottom flux condition -(section :numref:`CH4 and O2 Source Terms`); calculate diffusivity -(section :numref:`Aqueous and Gaseous Diffusion`); and solve the resulting -mass balance using a tridiagonal solver (section -:numref:`Crank-Nicholson Solution Methane`). +The solution to equation :eq:`24.11` is solved in several sequential steps: resolve competition for CH\ :sub:`4` and O\ :sub:`2` (section :numref:`Competition for CH4and O2`); add the ebullition flux into the layer directly above the water table or into the atmosphere; calculate the overall CH\ :sub:`4` or O\ :sub:`2` source term based on production, aerenchyma transport, ebullition, and oxidation; establish boundary conditions, including surface conductance to account for snow, ponding, and turbulent conductances and bottom flux condition (section :numref:`CH4 and O2 Source Terms`); calculate diffusivity (section :numref:`Aqueous and Gaseous Diffusion`); and solve the resulting mass balance using a tridiagonal solver (section :numref:`Crank-Nicholson Solution Methane`). .. _Competition for CH4and O2: Competition for CH\ :sub:`4` and O\ :sub:`2` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For each time step, the unlimited CH\ :sub:`4` and -O\ :sub:`2` demands in each model depth interval are computed. If -the total demand over a time step for one of the species exceeds the -amount available in a particular control volume, the demand from each -process associated with the sink is scaled by the fraction required to -ensure non-negative concentrations. Since the methanotrophs are limited -by both CH\ :sub:`4` and O\ :sub:`2`, the stricter -limitation is applied to methanotroph oxidation, and then the -limitations are scaled back for the other processes. The competition is -designed so that the sinks must not exceed the available concentration -over the time step, and if any limitation exists, the sinks must sum to -this value. Because the sinks are calculated explicitly while the -transport is semi-implicit, negative concentrations can occur after the -tridiagonal solution. When this condition occurs for O\ :sub:`2`, -the concentrations are reset to zero; if it occurs for -CH\ :sub:`4`, the surface flux is adjusted and the concentration -is set to zero if the adjustment is not too large. +For each time step, the unlimited CH\ :sub:`4` and O\ :sub:`2` demands in each model depth interval are computed. If the total demand over a time step for one of the species exceeds the amount available in a particular control volume, the demand from each process associated with the sink is scaled by the fraction required to ensure non-negative concentrations. Since the methanotrophs are limited by both CH\ :sub:`4` and O\ :sub:`2`, the stricter limitation is applied to methanotroph oxidation, and then the limitations are scaled back for the other processes. The competition is designed so that the sinks must not exceed the available concentration over the time step, and if any limitation exists, the sinks must sum to this value. Because the sinks are calculated explicitly while the transport is semi-implicit, negative concentrations can occur after the tridiagonal solution. When this condition occurs for O\ :sub:`2`, the concentrations are reset to zero; if it occurs for CH\ :sub:`4`, the surface flux is adjusted and the concentration is set to zero if the adjustment is not too large. .. _CH4 and O2 Source Terms: CH\ :sub:`4` and O\ :sub:`2` Source Terms ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The overall CH\ :sub:`4` net source term consists of production, -oxidation at the base of aerenchyma, transport through aerenchyma, -methanotrophic oxidation, and ebullition (either to the control volume -above the water table if unsaturated or directly to the atmosphere if -saturated). For O\ :sub:`2` below the top control volume, the net -source term consists of O\ :sub:`2` losses from methanotrophy, SOM -decomposition, and autotrophic respiration, and an O\ :sub:`2` -source through aerenchyma. +The overall CH\ :sub:`4` net source term consists of production, oxidation at the base of aerenchyma, transport through aerenchyma, methanotrophic oxidation, and ebullition (either to the control volume above the water table if unsaturated or directly to the atmosphere if saturated). For O\ :sub:`2` below the top control volume, the net source term consists of O\ :sub:`2` losses from methanotrophy, SOM decomposition, and autotrophic respiration, and an O\ :sub:`2` source through aerenchyma. .. _Aqueous and Gaseous Diffusion: Aqueous and Gaseous Diffusion ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For gaseous diffusion, we adopted the temperature dependence of -molecular free-air diffusion coefficients (:math:`{D}_{0}` -(m:sup:`2` s\ :sup:`-1`)) as described by -:ref:`Lerman (1979) ` and applied by -:ref:`Wania et al. (2010)` -(:numref:`Table Temperature dependence of aqueous and gaseous diffusion`). +For gaseous diffusion, we adopted the temperature dependence of molecular free-air diffusion coefficients (:math:`{D}_{0}` (m\ :sup:`2` s\ :sup:`-1`)) as described by :ref:`Lerman (1979) ` and applied by :ref:`Wania et al. (2010)` (:numref:`Table Temperature dependence of aqueous and gaseous diffusion`). .. _Table Temperature dependence of aqueous and gaseous diffusion: .. table:: Temperature dependence of aqueous and gaseous diffusion coefficients for CH\ :sub:`4` and O\ :sub:`2` +----------------------------------------------------------+----------------------------------------------------------+--------------------------------------------------------+ - | :math:`{D}_{0}` (m\ :sup:`2` s\ :sup:`-1`) | CH\ :sub:`4` | O\ :sub:`2` | + | :math:`{D}_{0}` (cm\ :sup:`2` s\ :sup:`-1`) | CH\ :sub:`4` | O\ :sub:`2` | +==========================================================+==========================================================+========================================================+ | Aqueous | 0.9798 + 0.02986\ *T* + 0.0004381\ *T*\ :sup:`2` | 1.172+ 0.03443\ *T* + 0.0005048\ *T*\ :sup:`2` | +----------------------------------------------------------+----------------------------------------------------------+--------------------------------------------------------+ - | Gaseous | 0.1875 + 0.0013\ *T* | 0.1759 + 0.0011\ *T* | + | Gaseous | 0.1875 + 0.0013\ *T* | 0.1759 + 0.00117\ *T* | +----------------------------------------------------------+----------------------------------------------------------+--------------------------------------------------------+ -Gaseous diffusivity in soils also depends on the molecular diffusivity, -soil structure, porosity, and organic matter content. -:ref:`Moldrup et al. (2003)`, using observations across a -range of unsaturated mineral soils, showed that the relationship between -effective diffusivity (:math:`D_{e}` (m:sup:`2` s\ :sup:`-1`)) and soil -properties can be represented as: +Gaseous diffusivity in soils also depends on the molecular diffusivity, soil structure, porosity, and organic matter content. :ref:`Moldrup et al. (2003)`, using observations across a range of unsaturated mineral soils, showed that the relationship between effective diffusivity (:math:`D_{e}` (m\ :sup:`2` s\ :sup:`-1`)) and soil properties can be represented as: .. math:: :label: 24.12 - D_{e} =D_{0} \theta _{a}^{2} \left(\frac{\theta _{a} }{\theta _{s} } \right)^{{\raise0.7ex\hbox{$ 3 $}\!\mathord{\left/ {\vphantom {3 b}} \right. \kern-\nulldelimiterspace}\!\lower0.7ex\hbox{$ b $}} } , + D_{e} =D_{0} \theta _{a}^{2} \left(\frac{\theta _{a} }{\theta _{s} } \right)^{{\raise0.7ex\hbox{$ 3 $}\!\mathord{\left/ {\vphantom {3 b}} \right.}\!\lower0.7ex\hbox{$ b $}} } , -where :math:`\theta _{a}` and :math:`\theta _{s}` are the air-filled -and total (saturated water-filled) porosities (-), respectively, and *b* -is the slope of the water retention curve (-). However, :ref:`Iiyama and -Hasegawa (2005)` have shown that the original Millington-Quirk -(:ref:`Millington and Quirk 1961`) relationship matched -measurements more closely in unsaturated peat soils: +where :math:`\theta _{a}` and :math:`\theta _{s}` are the air-filled and total (saturated water-filled) porosities (-), respectively, and *b* is the slope of the water retention curve (-). However, :ref:`Iiyama and Hasegawa (2005)` have shown that the original Millington-Quirk (:ref:`Millington and Quirk 1961`) relationship matched measurements more closely in unsaturated peat soils: .. math:: :label: 24.13 - D_{e} =D_{0} \frac{\theta _{a} ^{{\raise0.7ex\hbox{$ 10 $}\!\mathord{\left/ {\vphantom {10 3}} \right. \kern-\nulldelimiterspace}\!\lower0.7ex\hbox{$ 3 $}} } }{\theta _{s} ^{2} } + D_{e} =D_{0} \frac{\theta _{a} ^{{\raise0.7ex\hbox{$ 10 $}\!\mathord{\left/ {\vphantom {10 3}} \right.}\!\lower0.7ex\hbox{$ 3 $}} } }{\theta _{s} ^{2} } -In CLM, we applied equation for soils with zero organic matter content -and equation for soils with more than 130 kg m\ :sup:`-3` organic -matter content. A linear interpolation between these two limits is -applied for soils with SOM content below 130 kg m\ :sup:`-3`. For -aqueous diffusion in the saturated part of the soil column, we applied -(:ref:`Moldrup et al. (2003)`): +In CLM, we applied equation :eq:`24.12` for soils with zero organic matter content and equation :eq:`24.13` for soils with more than 130 kg m\ :sup:`-3` organic matter content. A linear interpolation between these two limits is applied for soils with SOM content below 130 kg m\ :sup:`-3`. For aqueous diffusion in the saturated part of the soil column, we applied (:ref:`Moldrup et al. (2003)`): .. math:: :label: 24.14 D_{e} =D_{0} \theta _{s} ^{2} . -To simplify the solution, we assumed that gaseous diffusion dominates -above the water table interface and aqueous diffusion below the water -table interface. Descriptions, baseline values, and dimensions for -parameters specific to the CH\ :sub:`4` model are given in -:numref:`Table Methane Parameter descriptions`. For freezing or frozen -soils below the water table, diffusion is limited to the remaining -liquid (CLM allows for some freezing point depression), and the diffusion -coefficients are scaled by the -volume-fraction of liquid. For unsaturated soils, Henry’s law -equilibrium is assumed at the interface with the water table. +To simplify the solution, we assumed that gaseous diffusion dominates above the water table interface and aqueous diffusion below the water table interface. Descriptions, baseline values, and dimensions for parameters specific to the CH\ :sub:`4` model are given in :numref:`Table Methane Parameter descriptions`. For freezing or frozen soils below the water table, diffusion is limited to the remaining liquid (CLM allows for some freezing point depression), and the diffusion coefficients are scaled by the volume-fraction of liquid. For unsaturated soils, Henry's law equilibrium is assumed at the interface with the water table. .. _Boundary Conditions: Boundary Conditions ^^^^^^^^^^^^^^^^^^^^^^^^^^ -We assume the CH\ :sub:`4` and O\ :sub:`2` surface fluxes -can be calculated from an effective conductance and a gaseous -concentration gradient between the atmospheric concentration and either -the gaseous concentration in the first soil layer (unsaturated soils) or -in equilibrium with the water (saturated -soil\ :math:`w\left(C_{1}^{n} -C_{a} \right)` and -:math:`w\left(C_{1}^{n+1} -C_{a} \right)` for the fully explicit and -fully implicit cases, respectively (however, see -:ref:`Tang and Riley (2013)` -for a more complete representation of this process). Here, *w* is the -surface boundary layer conductance as calculated in the existing CLM -surface latent heat calculations. If the top layer is not fully -saturated, the :math:`\frac{D_{m1} }{\Delta x_{m1} }` term is replaced -with a series combination: -:math:`\left[\frac{1}{w} +\frac{\Delta x_{1} }{D_{1} } \right]^{-1}` , -and if the top layer is saturated, this term is replaced with -:math:`\left[\frac{K_{H} }{w} +\frac{\frac{1}{2} \Delta x_{1} }{D_{1} } \right]^{-1}` , -where :math:`{K}_{H}` is the Henry’s law equilibrium constant. - -When snow is present, a resistance is added to account for diffusion -through the snow based on the Millington-Quirk expression :eq:`24.13` -and CLM’s prediction of the liquid water, ice, and air fractions of each -snow layer. When the soil is ponded, the diffusivity is assumed to be -that of methane in pure water, and the resistance as the ratio of the -ponding depth to diffusivity. The overall conductance is taken as the -series combination of surface, snow, and ponding resistances. We assume -a zero flux gradient at the bottom of the soil column. +We assume the CH\ :sub:`4` and O\ :sub:`2` surface fluxes can be calculated from an effective conductance and a gaseous concentration gradient between the atmospheric concentration and either the gaseous concentration in the first soil layer (unsaturated soils) or in equilibrium with the water (saturated soil\ :math:`w\left(C_{1}^{n} -C_{a} \right)` and :math:`w\left(C_{1}^{n+1} -C_{a} \right)` for the fully explicit and fully implicit cases, respectively (however, see :ref:`Tang and Riley (2013)` for a more complete representation of this process). Here, *w* is the surface boundary layer conductance as calculated in the existing CLM surface latent heat calculations. If the top layer is not fully saturated, the :math:`\frac{D_{m1} }{\Delta x_{m1} }` term is replaced with a series combination: :math:`\left[\frac{1}{w} +\frac{\Delta x_{1} }{D_{1} } \right]^{-1}`, and if the top layer is saturated, this term is replaced with :math:`\left[\frac{K_{H} }{w} +\frac{\frac{1}{2} \Delta x_{1} }{D_{1} } \right]^{-1}`, where :math:`{K}_{H}` is the Henry's law equilibrium constant. + +When snow is present, a resistance is added to account for diffusion through the snow based on the Millington-Quirk expression :eq:`24.13` and CLM's prediction of the liquid water, ice, and air fractions of each snow layer. When the soil is ponded, the diffusivity is assumed to be that of methane in pure water, and the resistance as the ratio of the ponding depth to diffusivity. The overall conductance is taken as the series combination of surface, snow, and ponding resistances. We assume a zero flux gradient at the bottom of the soil column. .. _Crank-Nicholson Solution Methane: Crank-Nicholson Solution ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Equation is solved using a Crank-Nicholson solution -(:ref:`Press et al. 1992`), -which combines fully explicit and implicit representations of the mass -balance. The fully explicit decomposition of equation can be written as +Equation :eq:`24.1` is solved using a Crank-Nicholson solution (:ref:`Press et al., 1992`), which combines fully explicit and implicit representations of the mass balance. The fully explicit decomposition of equation :eq:`24.1` can be written as .. math:: :label: 24.15 \frac{R_{j}^{n+1} C_{j}^{n+1} -R_{j}^{n} C_{j}^{n} }{\Delta t} =\frac{1}{\Delta x_{j} } \left[\frac{D_{p1}^{n} }{\Delta x_{p1}^{} } \left(C_{j+1}^{n} -C_{j}^{n} \right)-\frac{D_{m1}^{n} }{\Delta x_{m1}^{} } \left(C_{j}^{n} -C_{j-1}^{n} \right)\right]+S_{j}^{n} , -where *j* refers to the cell in the vertically discretized soil column -(increasing downward), *n* refers to the current time step, -:math:`\Delta`\ *t* is the time step (s), *p1* is *j+½*, *m1* is *j-½*, -and :math:`S_{j}^{n}` is the net source at time step *n* and position -*j*, i.e., -:math:`S_{j}^{n} =P\left(j,n\right)-E\left(j,n\right)-A\left(j,n\right)-O\left(j,n\right)`. -The diffusivity coefficients are calculated as harmonic means of values -from the adjacent cells. Equation is solved for gaseous and aqueous -concentrations above and below the water table, respectively. The *R* -term ensure the total mass balance in both phases is properly accounted -for. An analogous relationship can be generated for the fully implicit -case by replacing *n* by *n+1* on the *C* and *S* terms of equation . -Using an average of the fully implicit and fully explicit relationships -gives: +where *j* refers to the cell in the vertically discretized soil column (increasing downward), *n* refers to the current time step, :math:`\Delta`\ *t* is the time step (s), *p1* is *j+½*, *m1* is *j-½*, and :math:`S_{j}^{n}` is the net source at time step *n* and position *j*, i.e., :math:`S_{j}^{n} =P\left(j,n\right)-E\left(j,n\right)-A\left(j,n\right)-O\left(j,n\right)`. The diffusivity coefficients are calculated as harmonic means of values from the adjacent cells. Equation :eq:`24.15` is solved for gaseous and aqueous concentrations above and below the water table, respectively. The *R* term ensure the total mass balance in both phases is properly accounted for. An analogous relationship can be generated for the fully implicit case by replacing *n* by *n+1* on the *C* and *S* terms of equation :eq:`24.15`. Using an average of the fully implicit and fully explicit relationships gives: .. math:: :label: 24.16 \begin{array}{l} {-\frac{1}{2\Delta x_{j} } \frac{D_{m1}^{} }{\Delta x_{m1}^{} } C_{j-1}^{n+1} +\left[\frac{R_{j}^{n+1} }{\Delta t} +\frac{1}{2\Delta x_{j} } \left(\frac{D_{p1}^{} }{\Delta x_{p1}^{} } +\frac{D_{m1}^{} }{\Delta x_{m1}^{} } \right)\right]C_{j}^{n+1} -\frac{1}{2\Delta x_{j} } \frac{D_{p1}^{} }{\Delta x_{p1}^{} } C_{j+1}^{n+1} =} \\ {\frac{R_{j}^{n} }{\Delta t} +\frac{1}{2\Delta x_{j} } \left[\frac{D_{p1}^{} }{\Delta x_{p1}^{} } \left(C_{j+1}^{n} -C_{j}^{n} \right)-\frac{D_{m1}^{} }{\Delta x_{m1}^{} } \left(C_{j}^{n} -C_{j-1}^{n} \right)\right]+\frac{1}{2} \left[S_{j}^{n} +S_{j}^{n+1} \right]} \end{array}, -Equation is solved with a standard tridiagonal solver, i.e.: +Equation :eq:`24.16` is solved with a standard tridiagonal solver, i.e.: .. math:: :label: 24.17 aC_{j-1}^{n+1} +bC_{j}^{n+1} +cC_{j+1}^{n+1} =r, -with coefficients specified in equation . +with coefficients specified in equation :eq:`24.16`. -Two methane balance checks are performed at each timestep to insure that -the diffusion solution and the time-varying aggregation over inundated -and non-inundated areas strictly conserves methane molecules (except for -production minus consumption) and carbon atoms. +Two methane balance checks are performed at each timestep to insure that the diffusion solution and the time-varying aggregation over inundated and non-inundated areas strictly conserves methane molecules (except for production minus consumption) and carbon atoms. .. _Interface between water table and unsaturated zone: Interface between water table and unsaturated zone ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -We assume Henry’s Law equilibrium at the interface between the saturated -and unsaturated zone and constant flux from the soil element below the -interface to the center of the soil element above the interface. In this -case, the coefficients are the same as described above, except for the -soil element above the interface: +We assume Henry's Law equilibrium at the interface between the saturated and unsaturated zone and constant flux from the soil element below the interface to the center of the soil element above the interface. In this case, the coefficients are the same as described above, except for the soil element above the interface: .. math:: \frac{D_{p1} }{\Delta x_{p1} } =\left[K_{H} \frac{\Delta x_{j} }{2D_{j} } +\frac{\Delta x_{j+1} }{2D_{j+1} } \right]^{-1} @@ -598,51 +322,26 @@ and the soil element below the interface: Inundated Fraction Prediction ---------------------------------- -A simplified dynamic representation of spatial inundation -based on recent work by :ref:`Prigent et al. (2007)` is used. Prigent et al. (2007) described a -multi-satellite approach to estimate the global monthly inundated -fraction (:math:`{F}_{i}`) over an equal area grid -(0.25 :math:`\circ` \ :math:`\times`\ 0.25\ :math:`\circ` at the equator) -from 1993 - 2000. They suggested that the IGBP estimate for inundation -could be used as a measure of sensitivity of their detection approach at -low inundation. We therefore used the sum of their satellite-derived -:math:`{F}_{i}` and the constant IGBP estimate when it was less than -10% to perform a simple inversion for the inundated fraction for methane -production (:math:`{f}_{s}`). The method optimized two parameters -(:math:`{fws}_{slope}` and :math:`{fws}_{intercept}`) for each -grid cell in a simple model based on simulated total water storage -(:math:`{TWS}`): +A simplified dynamic representation of spatial inundation based on recent work by :ref:`Prigent et al. (2007)` is used. :ref:`Prigent et al. (2007)` described a multi-satellite approach to estimate the global monthly inundated fraction (:math:`{F}_{i}`) over an equal area grid (0.25 :math:`\circ` \ :math:`\times`\ 0.25\ :math:`\circ` at the equator) from 1993 - 2000. They suggested that the IGBP estimate for inundation could be used as a measure of sensitivity of their detection approach at low inundation. We therefore used the sum of their satellite-derived :math:`{F}_{i}` and the constant IGBP estimate when it was less than 10% to perform a simple inversion for the inundated fraction for methane production (:math:`{f}_{s}`). The method optimized two parameters (:math:`{fws}_{slope}` and :math:`{fws}_{intercept}`) for each grid cell in a simple model based on simulated total water storage (:math:`{TWS}`): .. math:: :label: 24.20 f_{s} =fws_{slope} TWS + fws_{intercept} . -These parameters were evaluated at the -0.5\ :sup:`o` resolution, and aggregated for -coarser simulations. Ongoing work in the hydrology -submodel of CLM may alleviate the need for this crude simplification of -inundated fraction in future model versions. +These parameters were evaluated at the 0.5° resolution, and aggregated for coarser simulations. Ongoing work in the hydrology submodel of CLM may alleviate the need for this crude simplification of inundated fraction in future model versions. .. _Seasonal Inundation: Seasonal Inundation ------------------------ -A simple scaling factor is used to mimic the impact of -seasonal inundation on CH\ :sub:`4` production (see appendix B in -:ref:`Riley et al. (2011a)` for a discussion of this -simplified expression): +A simple scaling factor is used to mimic the impact of seasonal inundation on CH\ :sub:`4` production (see appendix B in :ref:`Riley et al. (2011a)` for a discussion of this simplified expression): .. math:: :label: 24.21 S=\frac{\beta \left(f-\bar{f}\right)+\bar{f}}{f} ,S\le 1. -Here, *f* is the instantaneous inundated fraction, :math:`\bar{f}` is -the annual average inundated fraction (evaluated for the previous -calendar year) weighted by heterotrophic respiration, and -:math:`\beta` is the anoxia factor that relates the fully anoxic -decomposition rate to the fully oxygen-unlimited decomposition rate, all -other conditions being equal. +Here, *f* is the instantaneous inundated fraction, :math:`\bar{f}` is the annual average inundated fraction (evaluated for the previous calendar year) weighted by heterotrophic respiration, and :math:`\beta` is the anoxia factor that relates the fully anoxic decomposition rate to the fully oxygen-unlimited decomposition rate, all other conditions being equal. diff --git a/doc/source/tech_note/Photosynthesis/CLM50_Tech_Note_Photosynthesis.rst b/doc/source/tech_note/Photosynthesis/CLM50_Tech_Note_Photosynthesis.rst index e4f4f63836..8c0899cc17 100644 --- a/doc/source/tech_note/Photosynthesis/CLM50_Tech_Note_Photosynthesis.rst +++ b/doc/source/tech_note/Photosynthesis/CLM50_Tech_Note_Photosynthesis.rst @@ -6,9 +6,7 @@ Stomatal Resistance and Photosynthesis Summary of CLM5.0 updates relative to the CLM4.5 ----------------------------------------------------- -We describe here the complete photosynthesis and stomatal conductance parameterizations that -appear in CLM5.0. Corresponding information for CLM4.5 appeared in the -CLM4.5 Technical Note (:ref:`Oleson et al. 2013 `). +We describe here the complete photosynthesis and stomatal conductance parameterizations that appear in CLM5.0. Corresponding information for CLM4.5 appeared in the CLM4.5 Technical Note (:ref:`Oleson et al. 2013 `). CLM5 includes the following new changes to photosynthesis and stomatal conductance: @@ -18,74 +16,30 @@ CLM5 includes the following new changes to photosynthesis and stomatal conductan - Leaf N concentration and the fraction of leaf N in Rubisco used to calculate :math:`V_{cmax25}` are determined by the LUNA model (Chapter :numref:`rst_Photosynthetic Capacity`) -- Water stress is applied by the hydraulic conductance model (Chapter :numref:`rst_Plant Hydraulics`) - +- Water stress is applied by the hydraulic conductance model (Chapter :numref:`rst_Plant Hydraulics`) Introduction ----------------------- -Leaf stomatal resistance, which is needed for the water vapor flux -(Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), -is coupled to leaf photosynthesis similar to Collatz et al. -(:ref:`1991 `, :ref:`1992 `). These equations are solved separately for sunlit and -shaded leaves using average absorbed photosynthetically active radiation -for sunlit and shaded leaves -[:math:`\phi ^{sun}` ,\ :math:`\phi ^{sha}` W m\ :sup:`-2` -(section :numref:`Solar Fluxes`)] to give sunlit and shaded stomatal resistance -(:math:`r_{s}^{sun}` ,\ :math:`r_{s}^{sha}` s m\ :sup:`-1`) and -photosynthesis (:math:`A^{sun}` ,\ :math:`A^{sha}` µmol CO\ :sub:`2` m\ :sup:`-2` s\ :sup:`-1`). Canopy -photosynthesis is :math:`A^{sun} L^{sun} +A^{sha} L^{sha}` , where -:math:`L^{sun}` and :math:`L^{sha}` are the sunlit and shaded leaf -area indices (section :numref:`Solar Fluxes`). Canopy conductance is -:math:`\frac{1}{r_{b} +r_{s}^{sun} } L^{sun} +\frac{1}{r_{b} +r_{s}^{sha} } L^{sha}` , -where :math:`r_{b}` is the leaf boundary layer resistance (section -:numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`). +Leaf stomatal resistance, which is needed for the water vapor flux (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`), is coupled to leaf photosynthesis similar to Collatz et al. (:ref:`1991 `, :ref:`1992 `). These equations are solved separately for sunlit and shaded leaves using average absorbed photosynthetically active radiation for sunlit and shaded leaves [:math:`\phi ^{sun}`,\ :math:`\phi ^{sha}` W m\ :sup:`-2` (section :numref:`Solar Fluxes`)] to give sunlit and shaded stomatal resistance (:math:`r_{s}^{sun}`,\ :math:`r_{s}^{sha}` s m\ :sup:`-1`) and photosynthesis (:math:`A^{sun}`,\ :math:`A^{sha}` µmol CO\ :sub:`2` m\ :sup:`-2` s\ :sup:`-1`). Canopy photosynthesis is :math:`A^{sun} L^{sun} +A^{sha} L^{sha}`, where :math:`L^{sun}` and :math:`L^{sha}` are the sunlit and shaded leaf area indices (section :numref:`Solar Fluxes`). Canopy conductance is :math:`\frac{1}{r_{b} +r_{s}^{sun} } L^{sun} +\frac{1}{r_{b} +r_{s}^{sha} } L^{sha}`, where :math:`r_{b}` is the leaf boundary layer resistance (section :numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`). .. _Stomatal resistance: Stomatal resistance ----------------------- -CLM5 calculates stomatal conductance using the Medlyn stomatal conductance model (:ref:`Medlyn et al. 2011`). -Previous versions of CLM calculated leaf stomatal resistance using the Ball-Berry conductance -model as described by :ref:`Collatz et al. (1991)` and implemented in global -climate models (:ref:`Sellers et al. 1996`). The Medlyn model -calculates stomatal conductance (i.e., the inverse of resistance) based on net leaf -photosynthesis, the leaf-to-air vapor pressure difference, and the CO\ :sub:`2` concentration at the leaf surface. -Leaf stomatal resistance is: +CLM5 calculates stomatal conductance using the Medlyn stomatal conductance model (:ref:`Medlyn et al. 2011`). Previous versions of CLM calculated leaf stomatal resistance using the Ball-Berry conductance model as described by :ref:`Collatz et al. (1991)` and implemented in global climate models (:ref:`Sellers et al. 1996`). The Medlyn model calculates stomatal conductance (i.e., the inverse of resistance) based on net leaf photosynthesis, the leaf-to-air vapor pressure difference, and the CO\ :sub:`2` concentration at the leaf surface. Leaf stomatal resistance is: .. math:: - :label: 9.1 - - \frac{1}{r_{s} } =g_{s} = g_{o} + 1.6(1 + \frac{g_{1} }{\sqrt{D_{s}}}) \frac{A_{n} }{{c_{s} \mathord{\left/ {\vphantom {c_{s} P_{atm} }} \right. \kern-\nulldelimiterspace} P_{atm} } } - -where :math:`r_{s}` is leaf stomatal resistance (s m\ :sup:`2` -:math:`\mu`\ mol\ :sup:`-1`), :math:`g_{o}` is the minimum stomatal conductance -(:math:`\mu` mol m :sup:`-2` s\ :sup:`-1`), :math:`A_{n}` is leaf net -photosynthesis (:math:`\mu`\ mol CO\ :sub:`2` m\ :sup:`-2` -s\ :sup:`-1`), :math:`c_{s}` is the CO\ :sub:`2` partial -pressure at the leaf surface (Pa), :math:`P_{atm}` is the atmospheric -pressure (Pa), and :math:`D_{s}=(e_{i}-e{_s})/1000` is the leaf-to-air vapor pressure difference at the leaf surface (kPa) -where :math:`e_{i}` is the saturation vapor pressure (Pa) evaluated at the leaf temperature -:math:`T_{v}` , and :math:`e_{s}` is the vapor pressure at the leaf surface (Pa). -:math:`g_{1}` is a plant functional type dependent parameter (:numref:`Table Plant functional type (PFT) stomatal conductance parameters`) -and are the same as those used in the CABLE model (:ref:`de Kauwe et al. 2015 `). - -The value for :math:`g_{o}=100` :math:`\mu` mol m :sup:`-2` s\ :sup:`-1` for -C\ :sub:`3` and C\ :sub:`4` plants. -Photosynthesis is calculated for sunlit (:math:`A^{sun}`) and shaded -(:math:`A^{sha}`) leaves to give :math:`r_{s}^{sun}` and -:math:`r_{s}^{sha}`. Additionally, soil water influences stomatal -resistance through plant hydraulic stress, detailed in -the :ref:`rst_Plant Hydraulics` chapter. - -Resistance is converted from units of -s m\ :sup:`2` :math:`\mu` mol\ :sup:`-1` to s m\ :sup:`-1` as: -1 s m\ :sup:`-1` = :math:`1\times 10^{-9} R_{gas} \frac{\theta _{atm} }{P_{atm} }` -:math:`\mu` mol\ :sup:`-1` m\ :sup:`2` s, -where :math:`R_{gas}` is the universal gas constant (J K\ :sup:`-1` -kmol\ :sup:`-1`) (:numref:`Table Physical constants`) and :math:`\theta _{atm}` is the -atmospheric potential temperature (K). + :label: 9.1 + + \frac{1}{r_{s} } =g_{s} = g_{o} + 1.6(1 + \frac{g_{1} }{\sqrt{D_{s}}}) \frac{A_{n} }{{c_{s} \mathord{\left/ {\vphantom {c_{s} P_{atm} }} \right.} P_{atm} } } + +where :math:`r_{s}` is leaf stomatal resistance (s m\ :sup:`2` :math:`\mu`\ mol\ :sup:`-1`), :math:`g_{o}` is the minimum stomatal conductance (:math:`\mu` mol m :sup:`-2` s\ :sup:`-1`), :math:`A_{n}` is leaf net photosynthesis (:math:`\mu`\ mol CO\ :sub:`2` m\ :sup:`-2` s\ :sup:`-1`), :math:`c_{s}` is the CO\ :sub:`2` partial pressure at the leaf surface (Pa), :math:`P_{atm}` is the atmospheric pressure (Pa), and :math:`D_{s}=(e_{i}-e{_s})/1000` is the leaf-to-air vapor pressure difference at the leaf surface (kPa) where :math:`e_{i}` is the saturation vapor pressure (Pa) evaluated at the leaf temperature :math:`T_{v}`, and :math:`e_{s}` is the vapor pressure at the leaf surface (Pa). :math:`g_{1}` is a plant functional type dependent parameter (:numref:`Table Plant functional type (PFT) stomatal conductance parameters`) and are the same as those used in the CABLE model (:ref:`de Kauwe et al. 2015 `). + +The value for :math:`g_{o}=100` :math:`\mu` mol m :sup:`-2` s\ :sup:`-1` for C\ :sub:`3` and C\ :sub:`4` plants. Photosynthesis is calculated for sunlit (:math:`A^{sun}`) and shaded (:math:`A^{sha}`) leaves to give :math:`r_{s}^{sun}` and :math:`r_{s}^{sha}`. Additionally, soil water influences stomatal resistance through plant hydraulic stress, detailed in the :ref:`rst_Plant Hydraulics` chapter. + +Resistance is converted from units of s m\ :sup:`2` :math:`\mu` mol\ :sup:`-1` to s m\ :sup:`-1` as: 1 s m\ :sup:`-1` = :math:`1\times 10^{-9} R_{gas} \frac{\theta _{atm} }{P_{atm} }` :math:`\mu` mol\ :sup:`-1` m\ :sup:`2` s, where :math:`R_{gas}` is the universal gas constant (J K\ :sup:`-1` kmol\ :sup:`-1`) (:numref:`Table Physical constants`) and :math:`\theta _{atm}` is the atmospheric potential temperature (K). .. _Table Plant functional type (PFT) stomatal conductance parameters: @@ -142,146 +96,82 @@ atmospheric potential temperature (K). +----------------------------------+-------------------+ | Switchgrass | 1.79 | +----------------------------------+-------------------+ - + .. _Photosynthesis: Photosynthesis ------------------ -Photosynthesis in C\ :sub:`3` plants is based on the model of -:ref:`Farquhar et al. (1980)`. Photosynthesis in C\ :sub:`4` plants is -based on the model of :ref:`Collatz et al. (1992)`. :ref:`Bonan et al. (2011)` -describe the implementation, modified here. In its simplest form, leaf -net photosynthesis after accounting for respiration (:math:`R_{d}` ) is +Photosynthesis in C\ :sub:`3` plants is based on the model of :ref:`Farquhar et al. (1980)`. Photosynthesis in C\ :sub:`4` plants is based on the model of :ref:`Collatz et al. (1992)`. :ref:`Bonan et al. (2011)` describe the implementation, modified here. In its simplest form, leaf net photosynthesis after accounting for respiration (:math:`R_{d}` ) is .. math:: :label: 9.2 A_{n} =\min \left(A_{c} ,A_{j} ,A_{p} \right)-R_{d} . -The RuBP carboxylase (Rubisco) limited rate of carboxylation -:math:`A_{c}` (:math:`\mu` \ mol CO\ :sub:`2` m\ :sup:`-2` -s\ :sup:`-1`) is +The RuBP carboxylase (Rubisco) limited rate of carboxylation :math:`A_{c}` (:math:`\mu` \ mol CO\ :sub:`2` m\ :sup:`-2` s\ :sup:`-1`) is .. math:: :label: 9.3 - A_{c} =\left\{\begin{array}{l} {\frac{V_{c\max } \left(c_{i} -\Gamma _{\*} \right)}{c_{i} +K_{c} \left(1+{o_{i} \mathord{\left/ {\vphantom {o_{i} K_{o} }} \right. \kern-\nulldelimiterspace} K_{o} } \right)} \qquad {\rm for\; C}_{{\rm 3}} {\rm \; plants}} \\ {V_{c\max } \qquad \qquad \qquad {\rm for\; C}_{{\rm 4}} {\rm \; plants}} \end{array}\right\}\qquad \qquad c_{i} -\Gamma _{\*} \ge 0. + A_{c} =\left\{\begin{array}{l} {\frac{V_{c\max } \left(c_{i} -\Gamma _{*} \right)}{c_{i} +K_{c} \left(1+{o_{i} \mathord{\left/ {\vphantom {o_{i} K_{o} }} \right.} K_{o} } \right)} \qquad {\rm for\; C}_{{\rm 3}} {\rm \; plants}} \\ {V_{c\max } \qquad \qquad \qquad {\rm for\; C}_{{\rm 4}} {\rm \; plants}} \end{array}\right\}\qquad \qquad c_{i} -\Gamma _{*} \ge 0. -The maximum rate of carboxylation allowed by the capacity to regenerate -RuBP (i.e., the light-limited rate) :math:`A_{j}` (:math:`\mu` \ mol -CO\ :sub:`2` m\ :sup:`-2` s\ :sup:`-1`) is +The maximum rate of carboxylation allowed by the capacity to regenerate RuBP (i.e., the light-limited rate) :math:`A_{j}` (:math:`\mu` \ mol CO\ :sub:`2` m\ :sup:`-2` s\ :sup:`-1`) is .. math:: :label: 9.4 - A_{j} =\left\{\begin{array}{l} {\frac{J_{x}\left(c_{i} -\Gamma _{\*} \right)}{4c_{i} +8\Gamma _{\*} } \qquad \qquad {\rm for\; C}_{{\rm 3}} {\rm \; plants}} \\ {\alpha (4.6\phi )\qquad \qquad {\rm for\; C}_{{\rm 4}} {\rm \; plants}} \end{array}\right\}\qquad \qquad c_{i} -\Gamma _{\*} \ge 0. + A_{j} =\left\{\begin{array}{l} {\frac{J_{x}\left(c_{i} -\Gamma _{*} \right)}{4c_{i} +8\Gamma _{*} } \qquad \qquad {\rm for\; C}_{{\rm 3}} {\rm \; plants}} \\ {\alpha (4.6\phi )\qquad \qquad {\rm for\; C}_{{\rm 4}} {\rm \; plants}} \end{array}\right\}\qquad \qquad c_{i} -\Gamma _{*} \ge 0. -The product-limited rate of carboxylation for C\ :sub:`3` plants -and the PEP carboxylase-limited rate of carboxylation for -C\ :sub:`4` plants :math:`A_{p}` (:math:`\mu` \ mol -CO\ :sub:`2` m\ :sup:`-2` s\ :sup:`-1`) is +The product-limited rate of carboxylation for C\ :sub:`3` plants and the PEP carboxylase-limited rate of carboxylation for C\ :sub:`4` plants :math:`A_{p}` (:math:`\mu` \ mol CO\ :sub:`2` m\ :sup:`-2` s\ :sup:`-1`) is .. math:: - :label: 9.5 + :label: 9.5 A_{p} =\left\{\begin{array}{l} {3T_{p\qquad } \qquad \qquad {\rm for\; C}_{{\rm 3}} {\rm \; plants}} \\ {k_{p} \frac{c_{i} }{P_{atm} } \qquad \qquad \qquad {\rm for\; C}_{{\rm 4}} {\rm \; plants}} \end{array}\right\}. -In these equations, :math:`c_{i}` is the internal leaf -CO\ :sub:`2` partial pressure (Pa) and :math:`o_{i} =0.20P_{atm}` -is the O\ :sub:`2` partial pressure (Pa). :math:`K_{c}` and -:math:`K_{o}` are the Michaelis-Menten constants (Pa) for -CO\ :sub:`2` and O\ :sub:`2`. :math:`\Gamma _{\*}` (Pa) is -the CO\ :sub:`2` compensation point. :math:`V_{c\max }` is the -maximum rate of carboxylation (µmol m\ :sup:`-2` -s\ :sup:`-1`, Chapter :numref:`rst_Photosynthetic Capacity`) -and :math:`J_{x}` is the electron transport rate (µmol -m\ :sup:`-2` s\ :sup:`-1`). :math:`T_{p}` is the triose -phosphate utilization rate (µmol m\ :sup:`-2` s\ :sup:`-1`), -taken as :math:`T_{p} =0.167V_{c\max }` so that -:math:`A_{p} =0.5V_{c\max }` for C\ :sub:`3` plants (as in -:ref:`Collatz et al. 1992 `). For C\ :sub:`4` plants, the light-limited -rate :math:`A_{j}` varies with :math:`\phi` in relation to the quantum -efficiency (:math:`\alpha =0.05` mol CO\ :sub:`2` -mol\ :sup:`-1` photon). :math:`\phi` is the absorbed -photosynthetically active radiation (W m\ :sup:`-2`) (section :numref:`Solar Fluxes`) -, which is converted to photosynthetic photon flux assuming 4.6 -:math:`\mu` \ mol photons per joule. :math:`k_{p}` is the initial slope -of C\ :sub:`4` CO\ :sub:`2` response curve. - -For C\ :sub:`3` plants, the electron transport rate depends on the -photosynthetically active radiation absorbed by the leaf. A common -expression is the smaller of the two roots of the equation +In these equations, :math:`c_{i}` is the internal leaf CO\ :sub:`2` partial pressure (Pa) and :math:`o_{i} =0.20P_{atm}` is the O\ :sub:`2` partial pressure (Pa). :math:`K_{c}` and :math:`K_{o}` are the Michaelis-Menten constants (Pa) for CO\ :sub:`2` and O\ :sub:`2`. :math:`\Gamma _{*}` (Pa) is the CO\ :sub:`2` compensation point. :math:`V_{c\max }` is the maximum rate of carboxylation (µmol m\ :sup:`-2` s\ :sup:`-1`, Chapter :numref:`rst_Photosynthetic Capacity`) and :math:`J_{x}` is the electron transport rate (µmol m\ :sup:`-2` s\ :sup:`-1`). :math:`T_{p}` is the triose phosphate utilization rate (µmol m\ :sup:`-2` s\ :sup:`-1`), taken as :math:`T_{p} =0.167V_{c\max }` so that :math:`A_{p} =0.5V_{c\max }` for C\ :sub:`3` plants (as in :ref:`Collatz et al. 1992 `). For C\ :sub:`4` plants, the light-limited rate :math:`A_{j}` varies with :math:`\phi` in relation to the quantum efficiency (:math:`\alpha =0.05` mol CO\ :sub:`2` mol\ :sup:`-1` photon). :math:`\phi` is the absorbed photosynthetically active radiation (W m\ :sup:`-2`) (section :numref:`Solar Fluxes`), which is converted to photosynthetic photon flux assuming 4.6 :math:`\mu` \ mol photons per joule. :math:`k_{p}` is the initial slope of C\ :sub:`4` CO\ :sub:`2` response curve. + +For C\ :sub:`3` plants, the electron transport rate depends on the photosynthetically active radiation absorbed by the leaf. A common expression is the smaller of the two roots of the equation .. math:: :label: 9.6 \Theta _{PSII} J_{x}^{2} -\left(I_{PSII} +J_{\max } \right)J_{x}+I_{PSII} J_{\max } =0 -where :math:`J_{\max }` is the maximum potential rate of electron -transport (:math:`\mu`\ mol m\ :sup:`-2` s\ :sup:`-1`, Chapter :numref:`rst_Photosynthetic Capacity`), -:math:`I_{PSII}` is the light utilized in electron transport by -photosystem II (µmol m\ :sup:`-2` s\ :sup:`-1`), and -:math:`\Theta _{PSII}` is a curvature parameter. For a given amount of -photosynthetically active radiation absorbed by a leaf (:math:`\phi`, W -m\ :sup:`-2`), converted to photosynthetic photon flux density -with 4.6 :math:`\mu`\ mol J\ :sup:`-1`, the light utilized in -electron transport is +where :math:`J_{\max }` is the maximum potential rate of electron transport (:math:`\mu`\ mol m\ :sup:`-2` s\ :sup:`-1`, Chapter :numref:`rst_Photosynthetic Capacity`), :math:`I_{PSII}` is the light utilized in electron transport by photosystem II (µmol m\ :sup:`-2` s\ :sup:`-1`), and :math:`\Theta _{PSII}` is a curvature parameter. For a given amount of photosynthetically active radiation absorbed by a leaf (:math:`\phi`, W m\ :sup:`-2`), converted to photosynthetic photon flux density with 4.6 :math:`\mu`\ mol J\ :sup:`-1`, the light utilized in electron transport is .. math:: :label: 9.7 I_{PSII} =0.5\Phi _{PSII} (4.6\phi ) -where :math:`\Phi _{PSII}` is the quantum yield of photosystem II, and -the term 0.5 arises because one photon is absorbed by each of the two -photosystems to move one electron. Parameter values are -:math:`\Theta _{PSII}` \ = 0.7 and :math:`\Phi _{PSII}` \ = 0.85. In -calculating :math:`A_{j}` (for both C\ :sub:`3` and -C\ :sub:`4` plants), :math:`\phi =\phi ^{sun}` for sunlit leaves -and :math:`\phi =\phi ^{sha}` for shaded leaves. +where :math:`\Phi _{PSII}` is the quantum yield of photosystem II, and the term 0.5 arises because one photon is absorbed by each of the two photosystems to move one electron. Parameter values are :math:`\Theta _{PSII}` \ = 0.7 and :math:`\Phi _{PSII}` \ = 0.85. In calculating :math:`A_{j}` (for both C\ :sub:`3` and C\ :sub:`4` plants), :math:`\phi =\phi ^{sun}` for sunlit leaves and :math:`\phi =\phi ^{sha}` for shaded leaves. -The model uses co-limitation as described by :ref:`Collatz et al. (1991, 1992) -`. The actual gross photosynthesis rate, :math:`A`, is given by the -smaller root of the equations +The model uses co-limitation as described by :ref:`Collatz et al. (1991, 1992) `. The actual gross photosynthesis rate, :math:`A`, is given by the smaller root of the equations .. math:: :label: 9.8 \begin{array}{rcl} {\Theta _{cj} A_{i}^{2} -\left(A_{c} +A_{j} \right)A_{i} +A_{c} A_{j} } & {=} & {0} \\ {\Theta _{ip} A^{2} -\left(A_{i} +A_{p} \right)A+A_{i} A_{p} } & {=} & {0} \end{array} . -Values are :math:`\Theta _{cj} =0.98` and :math:`\Theta _{ip} =0.95` for -C\ :sub:`3` plants; and :math:`\Theta _{cj} =0.80`\ and -:math:`\Theta _{ip} =0.95` for C\ :sub:`4` plants. -:math:`A_{i}` is the intermediate co-limited photosynthesis. -:math:`A_{n} =A-R_{d}` . +Values are :math:`\Theta _{cj} =0.98` and :math:`\Theta _{ip} =0.95` for C\ :sub:`3` plants; and :math:`\Theta _{cj} =0.80`\ and :math:`\Theta _{ip} =0.95` for C\ :sub:`4` plants. :math:`A_{i}` is the intermediate co-limited photosynthesis. :math:`A_{n} =A-R_{d}`. -The parameters :math:`K_{c}`, :math:`K_{o}`, and :math:`\Gamma` -depend on temperature. Values at 25 :sup:`o` \ C are -:math:`K_{c25} ={\rm 4}0{\rm 4}.{\rm 9}\times 10^{-6} P_{atm}`, -:math:`K_{o25} =278.4\times 10^{-3} P_{atm}`, and -:math:`\Gamma _{25} {\rm =42}.75\times 10^{-6} P_{atm}`. -:math:`V_{c\max }`, :math:`J_{\max }`, :math:`T_{p}`, :math:`k_{p}`, -and :math:`R_{d}` also vary with temperature. +The parameters :math:`K_{c}`, :math:`K_{o}`, and :math:`\Gamma` depend on temperature. Values at 25 °C are :math:`K_{c25} ={\rm 4}0{\rm 4}.{\rm 9}\times 10^{-6} P_{atm}`, :math:`K_{o25} =278.4\times 10^{-3} P_{atm}`, and :math:`\Gamma _{25} {\rm =42}.75\times 10^{-6} P_{atm}`. :math:`V_{c\max }`, :math:`J_{\max }`, :math:`T_{p}`, :math:`k_{p}`, and :math:`R_{d}` also vary with temperature. -:math:`J_{\max 25}` at 25 :sup:`\o`\ C: is calculated by the LUNA model (Chapter :numref:`rst_Photosynthetic Capacity`) +:math:`J_{\max 25}` at 25 :sup:`\o`\ C: is calculated by the LUNA model (Chapter :numref:`rst_Photosynthetic Capacity`) Parameter values at 25 :sup:`\o`\ C are calculated from :math:`V_{c\max }` \ at 25 -:sup:`\o`\ C:, including: +:sup:`\o`\ C:, including: :math:`T_{p25} =0.167V_{c\max 25}`, and :math:`R_{d25} =0.015V_{c\max 25}` (C\ :sub:`3`) and -:math:`R_{d25} =0.025V_{c\max 25}` (C\ :sub:`4`). +:math:`R_{d25} =0.025V_{c\max 25}` (C\ :sub:`4`). For C\ :sub:`4` plants, :math:`k_{p25} =20000\; V_{c\max 25}`. -However, when the biogeochemistry is active (the default mode), :math:`R_{d25}` is -calculated from leaf nitrogen as described in (Chapter :numref:`rst_Plant Respiration`) +However, when the biogeochemistry is active (the default mode), :math:`R_{d25}` is calculated from leaf nitrogen as described in (Chapter :numref:`rst_Plant Respiration`) -The parameters :math:`V_{c\max 25}`, -:math:`J_{\max 25}`, :math:`T_{p25}`, :math:`k_{p25}`, and -:math:`R_{d25}` are scaled over the canopy for sunlit and shaded leaves -(section :numref:`Canopy scaling`). In C\ :sub:`3` plants, these are adjusted for leaf temperature, -:math:`T_{v}` (K), as: +The parameters :math:`V_{c\max 25}`, :math:`J_{\max 25}`, :math:`T_{p25}`, :math:`k_{p25}`, and :math:`R_{d25}` are scaled over the canopy for sunlit and shaded leaves (section :numref:`Canopy scaling`). In C\ :sub:`3` plants, these are adjusted for leaf temperature, :math:`T_{v}` (K), as: .. math:: :label: 9.9 @@ -300,16 +190,7 @@ and f_{H} \left(T_{v} \right)=\frac{1+\exp \left(\frac{298.15\Delta S-\Delta H_{d} }{298.15\times 0.001R_{gas} } \right)}{1+\exp \left(\frac{\Delta ST_{v} -\Delta H_{d} }{0.001R_{gas} T_{v} } \right)} . -:numref:`Table Temperature dependence parameters for C3 photosynthesis` -lists parameter values for :math:`\Delta H_{a}` and -:math:`\Delta H_{d}` . :math:`\Delta S` is calculated -separately for :math:`V_{c\max }` and :math:`J_{max }` -to allow for temperature acclimation of photosynthesis (see equation :eq:`9.16`), -and :math:`\Delta S` is 490 J mol :sup:`-1` K :sup:`-1` for :math:`R_d` -(:ref:`Bonan et al. 2011`, :ref:`Lombardozzi et al. 2015`). -Because :math:`T_{p}` as implemented here varies with -:math:`V_{c\max }` , :math:`T_{p}` uses the same temperature parameters as -:math:`V_{c\max}` . For C\ :sub:`4` plants, +:numref:`Table Temperature dependence parameters for C3 photosynthesis` lists parameter values for :math:`\Delta H_{a}` and :math:`\Delta H_{d}`. :math:`\Delta S` is calculated separately for :math:`V_{c\max }` and :math:`J_{max }` to allow for temperature acclimation of photosynthesis (see equation :eq:`9.16`), and :math:`\Delta S` is 490 J mol :sup:`-1` K :sup:`-1` for :math:`R_d` (:ref:`Bonan et al. 2011`, :ref:`Lombardozzi et al. 2015`). Because :math:`T_{p}` as implemented here varies with :math:`V_{c\max }`, :math:`T_{p}` uses the same temperature parameters as :math:`V_{c\max}`. For C\ :sub:`4` plants, .. math:: :label: 9.12 @@ -319,16 +200,15 @@ Because :math:`T_{p}` as implemented here varies with with :math:`Q_{10} =2`, :math:`s_{1} =0.3`\ K\ :sup:`-1` :math:`s_{2} =313.15` K, -:math:`s_{3} =0.2`\ K\ :sup:`-1`, and :math:`s_{4} =288.15` K. -Additionally, +:math:`s_{3} =0.2`\ K\ :sup:`-1`, and +:math:`s_{4} =288.15` K. Additionally, .. math:: :label: 9.13 R_{d} =R_{d25} \left\{\frac{Q_{10} ^{(T_{v} -298.15)/10} }{1+\exp \left[s_{5} \left(T_{v} -s_{6} \right)\right]} \right\} -with :math:`Q_{10} =2`, :math:`s_{5} =1.3` -K\ :sup:`-1` and :math:`s_{6} =328.15`\ K, and +with :math:`Q_{10} =2`, :math:`s_{5} =1.3` K\ :sup:`-1` and :math:`s_{6} =328.15`\ K, and .. math:: :label: 9.14 @@ -356,54 +236,41 @@ with :math:`Q_{10} =2`. +------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | :math:`K_{o}` | 36380 | – | +------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ - | :math:`\Gamma _{\*}` | 37830 | – | + | :math:`\Gamma _{*}` | 37830 | – | +------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ -In the model, acclimation is -implemented as in :ref:`Kattge and Knorr (2007) `. In this parameterization, -:math:`V_{c\max }` and :math:`J_{\max }` vary with the plant growth temperature. This is -achieved by allowing :math:`\Delta S`\ to vary with growth temperature -according to +In the model, acclimation is implemented as in :ref:`Kattge and Knorr (2007) `. In this parameterization, :math:`V_{c\max }` and :math:`J_{\max }` vary with the plant growth temperature. This is achieved by allowing :math:`\Delta S`\ to vary with growth temperature according to .. math:: :label: 9.15 \begin{array}{l} {\Delta S=668.39-1.07(T_{10} -T_{f} )\qquad \qquad {\rm for\; }V_{c\max } } \\ {\Delta S=659.70-0.75(T_{10} -T_{f} )\qquad \qquad {\rm for\; }J_{\max } } \end{array} -The effect is to cause the temperature optimum of :math:`V_{c\max }` -and :math:`J_{\max }` to increase with warmer temperatures. -Additionally, the -ratio :math:`J_{\max 25} /V_{c\max 25}` at 25 :sup:`o`\ C decreases with growth temperature as +The effect is to cause the temperature optimum of :math:`V_{c\max }` and :math:`J_{\max }` to increase with warmer temperatures. Additionally, the ratio :math:`J_{\max 25} /V_{c\max 25}` at 25 °C decreases with growth temperature as .. math:: :label: 9.16 J_{\max 25} /V_{c\max 25} =2.59-0.035(T_{10} -T_{f} ). -In these acclimation functions, :math:`T_{10}` is the 10-day mean air -temperature (K) and :math:`T_{f}` is the freezing point of water (K). -For lack of data, :math:`T_{p}` acclimates similar to :math:`V_{c\max }`. Acclimation is restricted over the temperature -range :math:`T_{10} -T_{f} \ge 11`\ :sup:`o`\ C and :math:`T_{10} -T_{f} \le 35`\ :sup:`o`\ C. +In these acclimation functions, :math:`T_{10}` is the 10-day mean air temperature (K) and :math:`T_{f}` is the freezing point of water (K). For lack of data, :math:`T_{p}` acclimates similar to :math:`V_{c\max }`. Acclimation is restricted over the temperature range :math:`T_{10} -T_{f} \ge` 11°C and :math:`T_{10} -T_{f} \le` 35°C. .. _Canopy scaling: Canopy scaling -------------------------------------------- -When LUNA is on, the :math:`V_{c\max 25}` for sun leaves is scaled to the shaded leaves -:math:`J_{\max 25}` , :math:`T_{p25}` , :math:`k_{p25}`, and -:math:`R_{d25}` scale similarly. - +When LUNA is on, the :math:`V_{c\max 25}` for sun leaves is scaled to the shaded leaves :math:`J_{\max 25}`, :math:`T_{p25}`, :math:`k_{p25}`, and :math:`R_{d25}` scale similarly. .. math:: :label: 9.17 - \begin{array}{rcl} - {V_{c\max 25 sha}} & {=} & {V_{c\max 25 sha} \frac{i_{v,sha}}{i_{v,sun}}} \\ + \begin{array}{rcl} + {V_{c\max 25 sha}} & {=} & {V_{c\max 25 sha} \frac{i_{v,sha}}{i_{v,sun}}} \\ {J_{\max 25 sha}} & {=} & {J_{\max 25 sun} \frac{i_{v,sha}}{i_{v,sun}}} \\ - {T_{p sha}} & {=} & {T_{p sun} \frac{i_{v,sha}}{i_{v,sun}}} \end{array} + {T_{p sha}} & {=} & {T_{p sun} \frac{i_{v,sha}}{i_{v,sun}}} \end{array} -Where :math:`i_{v,sun}` and :math:`i_{v,sha}` are the leaf-to-canopy scaling coefficients of the twostream radiation model, calculated as +Where :math:`i_{v,sun}` and :math:`i_{v,sha}` are the leaf-to-canopy scaling coefficients of the twostream radiation model, calculated as .. math:: :label: 9.18 @@ -411,22 +278,16 @@ Where :math:`i_{v,sun}` and :math:`i_{v,sha}` are the leaf-to-canopy scaling coe i_{v,sun} = \frac{(1 - e^{-(k_{n,ext}+k_{b,ext})*lai_e)} / (k_{n,ext}+k_{b,ext})}{f_{sun}*lai_e}\\ i_{v,sha} = \frac{(1 - e^{-(k_{n,ext}+k_{b,ext})*lai_e)} / (k_{n,ext}+k_{b,ext})}{(1 - f_{sun})*lai_e} -k_{n,ext} is the extinction coefficient for N through the canopy (0.3). k_{b,ext} is the direct beam extinction coefficient calculated in the surface albedo routine, and :math:`f_{sun}` is the fraction of sunlit leaves, both derived from Chapter :numref:`rst_Surface Albedos`. +k_{n,ext} is the extinction coefficient for N through the canopy (0.3). k_{b,ext} is the direct beam extinction coefficient calculated in the surface albedo routine, and :math:`f_{sun}` is the fraction of sunlit leaves, both derived from Chapter :numref:`rst_Surface Albedos`. -When LUNA is off, scaling defaults to the mechanism used in CLM4.5. +When LUNA is off, scaling defaults to the mechanism used in CLM4.5. .. _Numerical implementation photosynthesis: Numerical implementation ---------------------------- -The CO\ :sub:`2` partial pressure at the leaf surface, -:math:`c_{s}` (Pa), and the vapor pressure at the leaf surface, -:math:`e_{s}` (Pa), needed for the stomatal resistance model in -equation :eq:`9.1`, and the internal leaf CO\ :sub:`2` partial pressure -:math:`c_{i}` (Pa), needed for the photosynthesis model in equations :eq:`9.3`-:eq:`9.5`, -are calculated assuming there is negligible capacity to store -CO\ :sub:`2` and water vapor at the leaf surface so that +The CO\ :sub:`2` partial pressure at the leaf surface, :math:`c_{s}` (Pa), and the vapor pressure at the leaf surface, :math:`e_{s}` (Pa), needed for the stomatal resistance model in equation :eq:`9.1`, and the internal leaf CO\ :sub:`2` partial pressure :math:`c_{i}` (Pa), needed for the photosynthesis model in equations :eq:`9.3`-:eq:`9.5`, are calculated assuming there is negligible capacity to store CO\ :sub:`2` and water vapor at the leaf surface so that .. math:: :label: 9.19 @@ -436,31 +297,18 @@ CO\ :sub:`2` and water vapor at the leaf surface so that and the transpiration fluxes are related as .. math:: - :label: 9.20 + :label: 9.20 \frac{e_{a} -e_{i} }{r_{b} +r_{s} } =\frac{e_{a} -e_{s} }{r_{b} } =\frac{e_{s} -e_{i} }{r_{s} } -where :math:`r_{b}` is leaf boundary layer resistance (s -m\ :sup:`2` :math:`\mu` \ mol\ :sup:`-1`) (section :numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`), the -terms 1.4 and 1.6 are the ratios of diffusivity of CO\ :sub:`2` to -H\ :sub:`2`\ O for the leaf boundary layer resistance and stomatal -resistance, -:math:`c_{a} ={\rm CO}_{{\rm 2}} \left({\rm mol\; mol}^{{\rm -1}} \right)`, :math:`P_{atm}` -is the atmospheric pressure (Pa), :math:`e_{i}` is the -saturation vapor pressure (Pa) evaluated at the leaf temperature -:math:`T_{v}` , and :math:`e_{a}` is the vapor pressure of air (Pa). -The vapor pressure of air in the plant canopy :math:`e_{a}` (Pa) is -determined from +where :math:`r_{b}` is leaf boundary layer resistance (s m\ :sup:`2` :math:`\mu` \ mol\ :sup:`-1`) (section :numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`), the terms 1.4 and 1.6 are the ratios of diffusivity of CO\ :sub:`2` to H\ :sub:`2`\ O for the leaf boundary layer resistance and stomatal resistance, :math:`c_{a} ={\rm CO}_{{\rm 2}} \left({\rm mol\; mol}^{{\rm -1}} \right)`, :math:`P_{atm}` is the atmospheric pressure (Pa), :math:`e_{i}` is the saturation vapor pressure (Pa) evaluated at the leaf temperature :math:`T_{v}`, and :math:`e_{a}` is the vapor pressure of air (Pa). The vapor pressure of air in the plant canopy :math:`e_{a}` (Pa) is determined from .. math:: :label: 9.21 e_{a} =\frac{P_{atm} q_{s} }{0.622} -where :math:`q_{s}` is the specific humidity of canopy air (kg -kg\ :sup:`-1`, section :numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`). -Equations :eq:`9.19` and :eq:`9.20` are solved for -:math:`c_{s}` and :math:`e_{s}` +where :math:`q_{s}` is the specific humidity of canopy air (kg kg\ :sup:`-1`, section :numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`). Equations :eq:`9.19` and :eq:`9.20` are solved for :math:`c_{s}` and :math:`e_{s}` .. math:: :label: 9.34 @@ -472,19 +320,14 @@ Equations :eq:`9.19` and :eq:`9.20` are solved for e_{s} =\frac{e_{a} r_{s} +e_{i} r_{b} }{r_{b} +r_{s} } -In terms of conductance with -:math:`g_{s} =1/r_{s}` and :math:`g_{b} =1/r_{b}` +In terms of conductance with :math:`g_{s} =1/r_{s}` and :math:`g_{b} =1/r_{b}` .. math:: :label: 9.36 e_{s} =\frac{e_{a} g_{b} +e_{i} g_{s} }{g_{b} +g_{s} } . - -Substitution of equation :eq:`9.36` into equation :eq:`9.1` gives an expression for the stomatal -resistance -(:math:`r_{s}`) as a function of photosynthesis -(:math:`A_{n}` ) +Substitution of equation :eq:`9.36` into equation :eq:`9.1` gives an expression for the stomatal resistance (:math:`r_{s}`) as a function of photosynthesis (:math:`A_{n}` ) .. math:: :label: 9.37 @@ -497,9 +340,7 @@ where :label: 9.38 \begin{array}{l} a = 1 \\ - b = -[2(g_{o} * 10^{-6} + d) + \frac{(g_{1}d)^{2}}{g_{b}*10^{-6}D_{l}}] \\ - c = (g_{o}*10^{-6})^{2} + [2g_{o}*10^{-6} + d (1-\frac{g_{1}^{2}} {D_{l}})]d \end{array} and @@ -511,22 +352,12 @@ and D_{l} = \frac {max(e_{i} - e_{a},50)} {1000} - -Stomatal conductance, as solved by equation :eq:`9.36` (mol m :sup:`-2` s :sup:`-1`), is the larger of the two roots that satisfy the -quadratic equation. Values for :math:`c_{i}` are given by +Stomatal conductance, as solved by equation :eq:`9.36` (mol m :sup:`-2` s :sup:`-1`), is the larger of the two roots that satisfy the quadratic equation. Values for :math:`c_{i}` are given by .. math:: :label: 9.40 c_{i} =c_{a} -\left(1.4r_{b} +1.6r_{s} \right)P_{atm} A{}_{n} -The equations for :math:`c_{i}` , :math:`c_{s}` , :math:`r_{s}` , and -:math:`A_{n}` are solved iteratively until :math:`c_{i}` converges. -:ref:`Sun et al. (2012)` pointed out that the CLM4 numerical approach does not -always converge. Therefore, the model uses a hybrid algorithm that -combines the secant method and Brent’s method to solve for -:math:`c_{i}` . The equation set is solved separately for sunlit -(:math:`A_{n}^{sun}` , :math:`r_{s}^{sun}` ) and shaded -(:math:`A_{n}^{sha}` , :math:`r_{s}^{sha}` ) leaves. - +The equations for :math:`c_{i}`, :math:`c_{s}`, :math:`r_{s}`, and :math:`A_{n}` are solved iteratively until :math:`c_{i}` converges. :ref:`Sun et al. (2012)` pointed out that the CLM4 numerical approach does not always converge. Therefore, the model uses a hybrid algorithm that combines the secant method and Brent's method to solve for :math:`c_{i}`. The equation set is solved separately for sunlit (:math:`A_{n}^{sun}`, :math:`r_{s}^{sun}` ) and shaded (:math:`A_{n}^{sha}`, :math:`r_{s}^{sha}` ) leaves. diff --git a/doc/source/tech_note/Photosynthetic_Capacity/CLM50_Tech_Note_Photosynthetic_Capacity.rst b/doc/source/tech_note/Photosynthetic_Capacity/CLM50_Tech_Note_Photosynthetic_Capacity.rst index c42d7971a8..b918cdf063 100755 --- a/doc/source/tech_note/Photosynthetic_Capacity/CLM50_Tech_Note_Photosynthetic_Capacity.rst +++ b/doc/source/tech_note/Photosynthetic_Capacity/CLM50_Tech_Note_Photosynthetic_Capacity.rst @@ -3,175 +3,135 @@ Photosynthetic Capacity ======================= -The photosynthetic capacity is represented by two key parameters: 1) the maximum rate of carboxylation at -25 :sup:`o`\ C, :math:`V_{\text{c,max25}}`; and 2) the maximum rate of electron transport at -25 :sup:`o`\ C, :math:`J_{\text{max25}}` . They are predicted by a mechanistic model of leaf -utilization of nitrogen for assimilation (LUNA V1.0) (:ref:`Ali et al. 2016`) based on an optimality hypothesis to nitrogen allocation -among light capture, electron transport, carboxylation, respiration and storage. -Specifically, the model allocates the nitrogen by maximizing the daily -net photosynthetic carbon gain under following two key assumptions: - -- nitrogen allocated for light capture, electron transport and carboxylation are co-limiting; +The photosynthetic capacity is represented by two key parameters: 1) the maximum rate of carboxylation at 25 °C, :math:`V_{\text{c,max25}}`; and 2) the maximum rate of electron transport at 25 °C, :math:`J_{\text{max25}}`. They are predicted by a mechanistic model of leaf utilization of nitrogen for assimilation (LUNA V1.0) (:ref:`Ali et al. 2016`) based on an optimality hypothesis to nitrogen allocation among light capture, electron transport, carboxylation, respiration and storage. Specifically, the model allocates the nitrogen by maximizing the daily net photosynthetic carbon gain under following two key assumptions: + +- nitrogen allocated for light capture, electron transport and carboxylation are co-limiting; + - respiratory nitrogen is allocated to maintain dark respiration determined by :math:`V_{\text{c,max}}`. -Compared to traditional photosynthetic capacity models, a key advantage of LUNA is that the model is able to predict the potential -acclimation of photosynthetic capacities at different environmental conditions as determined by temperature, radiation, -CO :sub:`2` concentrations, day length, and humidity. +Compared to traditional photosynthetic capacity models, a key advantage of LUNA is that the model is able to predict the potential acclimation of photosynthetic capacities at different environmental conditions as determined by temperature, radiation, CO :sub:`2` concentrations, day length, and humidity. .. _Model inputs and parameter estimations: Model inputs and parameter estimations ------------------------------------------------------- -The LUNA model includes the following four unitless parameters: +The LUNA model includes the following four unitless parameters: - :math:`J_{maxb0}` , which specifies the baseline proportion of nitrogen allocated for electron transport; -- :math:`J_{maxb1}` , which determines response of electron transport rate to light availability; -- :math:`t_{c,j0}` , which defines the baseline ratio of Rubisco-limited rate to light-limited rate; -- :math:`H` , which determines the response of electron transport rate to relative humidity. - -The above four parameters are estimated by fitting the LUNA model to a global compilation of >800 obervations -located at different biomes, canopy locations, and time of the year from 1993-2013 (Ali et al. 2015). The model inputs -are area-based leaf nitrogen content, leaf mass per unit leaf area and the driving environmental conditions (average of past 10 days) -including temperature, CO :sub:`2` concentrations, daily mean and maximum radiation, relative humidity and day length. -The estimated values in CLM5 for the listed parameters are 0.0311, 0.1745, 0.8054, and 6.0999, repectively. In LUNA V1.0, the estimated -parameter values are for C3 natural vegetations. In view that potentially large differences in photosythetic capacity could exist -between crops and natural vegetations due to human selection and genetic modifications, in CLM5, -the LUNA model are used only for C3 natural vegetations. The photosynthetic capacity for crops and C4 plants are thus -still kept the same as CLM4.5. Namely, it is estimated based on the leaf nitrogen content, fixed RUBISCO allocations for -:math:`V_{c\max 25}` and an adjusting factor to account for the impact of day length. In CLM5, the model simulates both sun-lit and shaded leaves; -however, because the sun-lit and shaded leaves can changes through the day based on the sun angles, -we do not differentiate the photosynthetic capacity difference for sun-lit or shaded leaves. +- :math:`J_{maxb1}` , which determines response of electron transport rate to light availability; +- :math:`t_{c,j0}` , which defines the baseline ratio of Rubisco-limited rate to light-limited rate; +- :math:`H` , which determines the response of electron transport rate to relative humidity. +The above four parameters are estimated by fitting the LUNA model to a global compilation of >800 obervations located at different biomes, canopy locations, and time of the year from 1993-2013 (Ali et al. 2015). The model inputs are area-based leaf nitrogen content, leaf mass per unit leaf area and the driving environmental conditions (average of past 10 days) including temperature, CO :sub:`2` concentrations, daily mean and maximum radiation, relative humidity and day length. The estimated values in CLM5 for the listed parameters are 0.0311, 0.17, 0.8054, and 6.0999, repectively. In LUNA V1.0, the estimated parameter values are for C3 natural vegetations. In view that potentially large differences in photosythetic capacity could exist between crops and natural vegetations due to human selection and genetic modifications, in CLM5, the LUNA model are used only for C3 natural vegetations. The photosynthetic capacity for crops and C4 plants are thus still kept the same as CLM4.5. Namely, it is estimated based on the leaf nitrogen content, fixed RUBISCO allocations for :math:`V_{c\max 25}` and an adjusting factor to account for the impact of day length. In CLM5, the model simulates both sun-lit and shaded leaves; however, because the sun-lit and shaded leaves can changes through the day based on the sun angles, we do not differentiate the photosynthetic capacity difference for sun-lit or shaded leaves. .. _Model structure: Model structure ---------------------------------------------------------- +.. _Plant Nitrogen: + Plant Nitrogen '''''''''''''''''''''''''' -The structure of the LUNA model is adapted from :ref:`Xu et al. (2012)`, where the plant nitrogen at the leaf level ( :math:`\text{LNC}_{a}`; gN/ m :sup:`2` leaf) is divided into -four pools: structural nitrogen( :math:`N_{\text{str}}`; gN/m :sup:`2` leaf), -photosynthetic nitrogen ( :math:`N_{\text{psn}}`; gN/m :sup:`2` leaf), -storage nitrogen( :math:`N_{\text{store}}`; gN/m :sup:`2` leaf), -and respiratory nitrogen ( :math:`N_{\text{resp}}`; gN/m :sup:`2` leaf). -Namely, +The structure of the LUNA model is adapted from :ref:`Xu et al. (2012)`, where the plant nitrogen at the leaf level ( :math:`\text{LNC}_{a}`; gN/ m :sup:`2` leaf) is divided into four pools: structural nitrogen( :math:`N_{\text{str}}`; gN/m :sup:`2` leaf), photosynthetic nitrogen ( :math:`N_{\text{psn}}`; gN/m :sup:`2` leaf), storage nitrogen( :math:`N_{\text{store}}`; gN/m :sup:`2` leaf), and respiratory nitrogen ( :math:`N_{\text{resp}}`; gN/m :sup:`2` leaf). Namely, .. math:: :label: 10.1) \text{LNC}_{a} = N_{\text{psn}} + N_{\text{str}}+ N_{\text{store}} + N_{\text{resp}}. -The photosynthetic nitrogen, :math:`N_{\text{psn}}`, is further divided into -nitrogen for light capture ( :math:`N_{\text{lc}}`; gN/m :sup:`2` leaf), -nitrogen for electron transport ( :math:`N_{\text{et}}`; gN/m :sup:`2` leaf), -and nitrogen for carboxylation ( :math:`N_{\text{cb}}`; gN/m :sup:`2` leaf). -Namely, +The photosynthetic nitrogen, :math:`N_{\text{psn}}`, is further divided into nitrogen for light capture ( :math:`N_{\text{lc}}`; gN/m :sup:`2` leaf), nitrogen for electron transport ( :math:`N_{\text{et}}`; gN/m :sup:`2` leaf), and nitrogen for carboxylation ( :math:`N_{\text{cb}}`; gN/m :sup:`2` leaf). Namely, .. math:: :label: 10.2) N_{\text{psn}} =N_{\text{et}} + N_{\text{cb}} + N_{\text{lc}}. -The structural nitrogen, :math:`N_{\text{str}}`, is calculated as the -multiplication of leaf mass per unit area (:math:`\text{LMA}`; g biomass/m :sup:`2` leaf), and the structural nitrogen content (:math:`\text{SNC}`; gN/g biomass). Namely, +The structural nitrogen, :math:`N_{\text{str}}`, is calculated as the multiplication of leaf mass per unit area (:math:`\text{LMA}`; g biomass/m :sup:`2` leaf), and the structural nitrogen content (:math:`\text{SNC}`; gN/g biomass). Namely, .. math:: :label: 10.3) N_{\text{str}} = \text{SNC} \cdot \text{LMA} -where :math:`\text{SNC}` is set to be fixed at 0.002 (gN/g biomass), based on data on C:N ratio from dead wood (White etal.,2000), -and :math:`\text{LMA}` is the inverse of specific leaf area at the canopy top (:math:`SLA_{\text{0}}`), a PFT-level parameter (:numref:`Table Plant functional type (PFT) leaf N parameters`). +where :math:`\text{SNC}` is set to be fixed at 0.004 (gN/g biomass), based on data on C:N ratio from dead wood (White etal.,2000), and :math:`\text{LMA}` is the inverse of specific leaf area at the canopy top (:math:`SLA_{\text{0}}`), a PFT-level parameter (:numref:`Table Plant functional type (PFT) leaf N parameters`). .. _Table Plant functional type (PFT) leaf N parameters: .. table:: Plant functional type (PFT) leaf N parameters. - +----------------------------------+--------------------------+--------------------------+ - | PFT | :math:`SLA_{\text{0}}` | :math:`N_{\text{cb}}` | - +==================================+==========================+==========================+ - | NET Temperate | 0.0100 | 0.0509 | - +----------------------------------+--------------------------+--------------------------+ - | NET Boreal | 0.0100 | 0.0466 | - +----------------------------------+--------------------------+--------------------------+ - | NDT Boreal | 0.0202 | 0.0546 | - +----------------------------------+--------------------------+--------------------------+ - | BET Tropical | 0.0190 | 0.0461 | - +----------------------------------+--------------------------+--------------------------+ - | BET temperate | 0.0190 | 0.0515 | - +----------------------------------+--------------------------+--------------------------+ - | BDT tropical | 0.0308 | 0.0716 | - +----------------------------------+--------------------------+--------------------------+ - | BDT temperate | 0.0308 | 0.1007 | - +----------------------------------+--------------------------+--------------------------+ - | BDT boreal | 0.0308 | 0.1007 | - +----------------------------------+--------------------------+--------------------------+ - | BES temperate | 0.0180 | 0.0517 | - +----------------------------------+--------------------------+--------------------------+ - | BDS temperate | 0.0307 | 0.0943 | - +----------------------------------+--------------------------+--------------------------+ - | BDS boreal | 0.0307 | 0.0943 | - +----------------------------------+--------------------------+--------------------------+ - | C\ :sub:`3` arctic grass | 0.0402 | 0.1365 | - +----------------------------------+--------------------------+--------------------------+ - | C\ :sub:`3` grass | 0.0402 | 0.1365 | - +----------------------------------+--------------------------+--------------------------+ - | C\ :sub:`4` grass | 0.0385 | 0.0900 | - +----------------------------------+--------------------------+--------------------------+ - | Temperate Corn | 0.0500 | 0.2930 | - +----------------------------------+--------------------------+--------------------------+ - | Spring Wheat | 0.0350 | 0.4102 | - +----------------------------------+--------------------------+--------------------------+ - | Temperate Soybean | 0.0350 | 0.4102 | - +----------------------------------+--------------------------+--------------------------+ - | Cotton | 0.0350 | 0.4102 | - +----------------------------------+--------------------------+--------------------------+ - | Rice | 0.0350 | 0.4102 | - +----------------------------------+--------------------------+--------------------------+ - | Sugarcane | 0.0500 | 0.2930 | - +----------------------------------+--------------------------+--------------------------+ - | Tropical Corn | 0.0500 | 0.2930 | - +----------------------------------+--------------------------+--------------------------+ - | Tropical Soybean | 0.0350 | 0.4102 | - +----------------------------------+--------------------------+--------------------------+ - | Miscanthus | 0.0570 | 0.2930 | - +----------------------------------+--------------------------+--------------------------+ - | Switchgrass | 0.0490 | 0.2930 | - +----------------------------------+--------------------------+--------------------------+ - -Notes: :math:`SLA_{\text{0}}` is the specific leaf area at the canopy top (m :sup:`2` leaf/g biomass), -and :math:`N_{\text{cb}}` is the fraction of leaf nitrogen in Rubisco (g N in Rubisco g :sup:`-1` N) - -We assume that plants optimize their nitrogen allocations (i.e., :math:`N_{\text{store}}`, :math:`N_{\text{resp}}`, :math:`N_{\text{lc}}`, :math:`N_{\text{et}}`, :math:`N_{\text{cb}}`) to maximize the photosynthetic carbon gain, defined as -the gross photosynthesis ( :math:`A` ) minus the maintenance respiration for -photosynthetic enzymes ( :math:`R_{\text{psn}}` ), under specific -environmental conditions and given plant's strategy of leaf nitrogen -use. Namely, the solutions of nitrogen allocations \{ :math:`N_{\text{store}}`, :math:`N_{\text{resp}}`, :math:`N_{\text{lc}}`, :math:`N_{\text{et}}`, :math:`N_{\text{cb}}` \} can be estimated as follows, + +----------------------------------+--------------------------+ + | PFT | :math:`SLA_{\text{0}}` | + +==================================+==========================+ + | NET Temperate | 0.01000 | + +----------------------------------+--------------------------+ + | NET Boreal | 0.01000 | + +----------------------------------+--------------------------+ + | NDT Boreal | 0.02018 | + +----------------------------------+--------------------------+ + | BET Tropical | 0.01900 | + +----------------------------------+--------------------------+ + | BET temperate | 0.01900 | + +----------------------------------+--------------------------+ + | BDT tropical | 0.03080 | + +----------------------------------+--------------------------+ + | BDT temperate | 0.03080 | + +----------------------------------+--------------------------+ + | BDT boreal | 0.03080 | + +----------------------------------+--------------------------+ + | BES temperate | 0.01798 | + +----------------------------------+--------------------------+ + | BDS temperate | 0.03072 | + +----------------------------------+--------------------------+ + | BDS boreal | 0.02800 | + +----------------------------------+--------------------------+ + | C\ :sub:`3` arctic grass | 0.02100 | + +----------------------------------+--------------------------+ + | C\ :sub:`3` grass | 0.04024 | + +----------------------------------+--------------------------+ + | C\ :sub:`4` grass | 0.03846 | + +----------------------------------+--------------------------+ + | Temperate Corn | 0.05000 | + +----------------------------------+--------------------------+ + | Spring Wheat | 0.03500 | + +----------------------------------+--------------------------+ + | Temperate Soybean | 0.03500 | + +----------------------------------+--------------------------+ + | Cotton | 0.03500 | + +----------------------------------+--------------------------+ + | Rice | 0.03500 | + +----------------------------------+--------------------------+ + | Sugarcane | 0.05000 | + +----------------------------------+--------------------------+ + | Tropical Corn | 0.05000 | + +----------------------------------+--------------------------+ + | Tropical Soybean | 0.03500 | + +----------------------------------+--------------------------+ + | Miscanthus | 0.03500 | + +----------------------------------+--------------------------+ + | Switchgrass | 0.03500 | + +----------------------------------+--------------------------+ + +Notes: :math:`SLA_{\text{0}}` is the specific leaf area at the canopy top (m :sup:`2` leaf/g biomass) + +We assume that plants optimize their nitrogen allocations (i.e., :math:`N_{\text{store}}`, :math:`N_{\text{resp}}`, :math:`N_{\text{lc}}`, :math:`N_{\text{et}}`, :math:`N_{\text{cb}}`) to maximize the photosynthetic carbon gain, defined as the gross photosynthesis ( :math:`A` ) minus the maintenance respiration for photosynthetic enzymes ( :math:`R_{\text{psn}}` ), under specific environmental conditions and given plant's strategy of leaf nitrogen use. Namely, the solutions of nitrogen allocations \{ :math:`N_{\text{store}}`, :math:`N_{\text{resp}}`, :math:`N_{\text{lc}}`, :math:`N_{\text{et}}`, :math:`N_{\text{cb}}` \} can be estimated as follows, .. math:: :label: 10.4) \left\{\hat{N}_{\text{{store}}}, \hat{N}_{\text{{resp}}}, \hat{\mathrm{N}}_{\text{lc}}, \hat{N}_{\text{et}}, \hat{\mathrm{N}}_{\text{cb}} - \right\} = \underset{\mathrm{N}_{\text{store}}\,+\,\mathrm{N}_{\text{resp}}\,+\,\mathrm{N}_{\text{lc}}\,+\,\mathrm{N}_{\text{et}}\,+\,\mathrm{N}_{\text{cb}}\,<\text{FNC}_{\mathrm{a}}}{\text{argmax}} (A-R_{\text{psn}}), + \right\} = \underset{\mathrm{N}_{\text{store}}\,+\,\mathrm{N}_{\text{resp}}\,+\,\mathrm{N}_{\text{lc}}\,+\,\mathrm{N}_{\text{et}}\,+\,\mathrm{N}_{\text{cb}}\,<\text{FNC}_{\mathrm{a}}}{\text{argmax}} (A-R_{\text{psn}}), -where :math:`\text{FNC}_{a}` is the functional nitrogen content defined as the total leaf nitrogen content ( :math:`\text{LNC}_{a}`) minus the structural nitrogen content ( :math:`N_{\text{str}}` ). +where :math:`\text{FNC}_{a}` is the functional nitrogen content defined as the total leaf nitrogen content ( :math:`\text{LNC}_{a}`) minus the structural nitrogen content ( :math:`N_{\text{str}}` ). -The gross photosynthesis, :math:`A`, was calculated with a coupled leaf gas exchange model based on the :ref:`Farquhar et al. (1980)` model of -photosynthesis and Ball--Berry-type stomatal conductance model (Ball et al. 1987). The maintenance respiration for photosynthetic enzymes, :math:`R_{\text{psn}}`, is -calculated by the multiplication of total photosynthetic nitrogen ( :math:`N_{\text{psn}}` ) and the maintenance respiration cost for photosynthetic enzymes. +The gross photosynthesis, :math:`A`, was calculated with a coupled leaf gas exchange model based on the :ref:`Farquhar et al. (1980)` model of photosynthesis and Ball--Berry-type stomatal conductance model (Ball et al. 1987). The maintenance respiration for photosynthetic enzymes, :math:`R_{\text{psn}}`, is calculated by the multiplication of total photosynthetic nitrogen ( :math:`N_{\text{psn}}` ) and the maintenance respiration cost for photosynthetic enzymes. Maximum electron transport rate ''''''''''''''''''''''''''''''''' -In the LUNA model, the maximum electron transport rate -( :math:`J_{\text{max}}`; :math:`{\mu} mol` electron / m :sup:`2`/s) -is simulated to have a baseline allocation of nitrogen and additional -nitrogen allocation to change depending on the average daytime -photosynthetic active radiation (PAR; :math:`{\mu} mol` electron / m :sup:`2`/s), day length (hours) and air humidity. -Specifically, the LUNA model has +In the LUNA model, the maximum electron transport rate ( :math:`J_{\text{max}}`; :math:`{\mu} mol` electron / m :sup:`2`/s) is simulated to have a baseline allocation of nitrogen and additional nitrogen allocation to change depending on the average daytime photosynthetic active radiation (PAR; :math:`{\mu} mol` electron / m :sup:`2`/s), day length (hours) and air humidity. Specifically, the LUNA model has .. math:: :label: 10.5) @@ -187,30 +147,14 @@ The baseline electron transport rate, :math:`J_{\text{max}0}`, is calculated as J_{\text{max}0} = J_{\text{max}b0}{\text{FNC}}_{\mathrm{a}}{\text{NUE}}_{J_{\text{{max}}}} - -where :math:`J_{\text{max}b0}` (unitless) is the baseline proportion of nitrogen -allocated for electron transport rate. :math:`{\text{NUE}}_{J_{\text{{max}}}}` ( :math:`{\mu} mol` electron /s/g N) -is the nitrogen use efficiency of :math:`J_{\text{{max}}}`. :math:`J_{\text{max}b1}` (unitless) is a coefficient determining the response of the electron -transport rate to amount of absorbed light (i.e., :math:`\alpha \text{PAR}`). -:math:`f\left(\text{day length} \right)` is a function specifies the impact of day -length (hours) on :math:`J_{\text{max}}` in view that longer day length has been demonstrated by previous studies to alter :math:`V_{\mathrm{c}\text{max}25}` and -:math:`J_{\text{max}25}` (Bauerle et al. 2012; Comstock and Ehleringer 1986) through photoperiod sensing and regulation (e.g., Song et al. 2013). -Following Bauerle et al. (2012), :math:`f\left(\text{day length} \right)` is simulated as follows, +where :math:`J_{\text{max}b0}` (unitless) is the baseline proportion of nitrogen allocated for electron transport rate. :math:`{\text{NUE}}_{J_{\text{{max}}}}` ( :math:`{\mu} mol` electron /s/g N) is the nitrogen use efficiency of :math:`J_{\text{{max}}}`. :math:`J_{\text{max}b1}` (unitless) is a coefficient determining the response of the electron transport rate to amount of absorbed light (i.e., :math:`\alpha \text{PAR}`). :math:`f\left(\text{day length} \right)` is a function specifies the impact of day length (hours) on :math:`J_{\text{max}}` in view that longer day length has been demonstrated by previous studies to alter :math:`V_{\mathrm{c}\text{max}25}` and :math:`J_{\text{max}25}` (Bauerle et al. 2012; Comstock and Ehleringer 1986) through photoperiod sensing and regulation (e.g., Song et al. 2013). Following Bauerle et al. (2012), :math:`f\left(\text{day length} \right)` is simulated as follows, .. math:: :label: 10.7) f\left(\text{day length} \right) = \left(\frac{\text{day length}}{12} \right)^{2}. -:math:`f\left(\text{humidity} \right)` represents the impact of air humidity on -:math:`J_{\text{{max}}}`. We assume that higher humidity leads to higher -:math:`J_{\text{{max}}}` with less water limiation on stomta opening and that low -relative humidity has a stronger impact on nitrogen allocation due to greater -water limitation. When relative humidity (RH; unitless) is too low, we assume -that plants are physiologically unable to reallocate nitrogen. We therefore -assume that there exists a critical value of relative humidity ( :math:`RH_{0} = -0.25`; unitless), below which there is no optimal nitrogen allocation. Based -on the above assumptions, we have +:math:`f\left(\text{humidity} \right)` represents the impact of air humidity on :math:`J_{\text{{max}}}`. We assume that higher humidity leads to higher :math:`J_{\text{{max}}}` with less water limiation on stomta opening and that low relative humidity has a stronger impact on nitrogen allocation due to greater water limitation. When relative humidity (RH; unitless) is too low, we assume that plants are physiologically unable to reallocate nitrogen. We therefore assume that there exists a critical value of relative humidity ( :math:`RH_{0} = 0.25`; unitless), below which there is no optimal nitrogen allocation. Based on the above assumptions, we have .. math:: :label: 10.8) @@ -219,24 +163,16 @@ on the above assumptions, we have \right) = \left(1-\mathrm{e}^{\left(-H \frac{\text{max}\left(\text{RH}-{\text{RH}}_{0}, 0 \right)}{1-\text{RH}_{0}} \right)} \right), - where :math:`H` (unitless) specifies the impact of relative humidity on electron transport rate. -The efficiency of light energy absorption (unitless), :math:`\alpha`, is calculated -depending on the amount of nitrogen allocated for light capture, -:math:`\mathrm{N}_{\text{lc}}`. Following Niinemets and Tenhunen (1997), the LUNA model has, +The efficiency of light energy absorption (unitless), :math:`\alpha`, is calculated depending on the amount of nitrogen allocated for light capture, :math:`\mathrm{N}_{\text{lc}}`. Following Niinemets and Tenhunen (1997), the LUNA model has, .. math:: :label: 10.9) \alpha =\frac{0.292}{1+\frac{0.076}{\mathrm{N}_{\text{lc}}C_{b}}} - -where 0.292 is the conversion factor from photon to electron. :math:`C_{b}` -is the conversion factor (1.78) from nitrogen to chlorophyll. After we -estimate :math:`J_{\text{{max}}}`, the actual electron transport rate with -the daily maximum radiation ( :math:`J_{x}`) can be calculated using the -empirical expression of leaf (1937), +where 0.292 is the conversion factor from photon to electron. :math:`C_{b}` is the conversion factor (1.78) from nitrogen to chlorophyll. After we estimate :math:`J_{\text{{max}}}`, the actual electron transport rate with the daily maximum radiation ( :math:`J_{x}`) can be calculated using the empirical expression of leaf (1937), .. math:: :label: 10.10) @@ -244,127 +180,82 @@ empirical expression of leaf (1937), J_{x} = \frac{\alpha \text{PAR}_{\text{max}}} {\left(1 + \frac{\alpha^{2}{\text{PAR}}_{\text{{max}}}^{2}}{J_{\text{{max}}}^{2}} \right)^{0.5}} - -where :math:`\text{PAR}_{\text{{max}}}` ( :math:`\mu mol`/m :sup:`2`/s) is the -maximum photosynthetically active radiation during the day. +where :math:`\text{PAR}_{\text{{max}}}` ( :math:`\mu mol`/m :sup:`2`/s) is the maximum photosynthetically active radiation during the day. Maximum rate of carboxylation '''''''''''''''''''''''''''''' -The maximum rate of carboxylation at 25\ :sup:`o`\ C varies with -foliage nitrogen concentration and specific leaf area and is calculated -as in :ref:`Thornton and Zimmermann (2007)`. At 25ºC, +The maximum rate of carboxylation at 25°C varies with foliage nitrogen concentration and specific leaf area and is calculated as in :ref:`Thornton and Zimmermann (2007)`. At 25°C, .. math:: :label: 10.11) V_{c\max 25} = N_{cb} NUE_{V_{c\max 25}} -where :math:`N_{cb}` is nitrogen for carboxylation (g N m\ :sup:`-2` leaf, -:numref:`Table Plant functional type (PFT) leaf N parameters`), -and :math:`NUE_{V_{c\max 25}}` = 47.3 x 6.25 and is the nitrogen use efficiency for :math:`V_{c\max 25}`. -The constant 47.3 is the specific Rubisco activity ( :math:`\mu` mol CO\ :sub:`2` g\ :sup:`-1` Rubisco s\ :sup:`-1`) -measured at 25\ :sup:`o`\ C, and the constant 6.25 is the nitrogen binding factor for Rubisco -(g Rubisco g\ :sup:`-1` N; :ref:`Rogers 2014`). +where :math:`N_{cb}` is nitrogen for carboxylation (g N m\ :sup:`-2` leaf, :numref:`Table Plant functional type (PFT) leaf N parameters`), and :math:`NUE_{V_{c\max 25}}` = 47.3 x 6.25 and is the nitrogen use efficiency for :math:`V_{c\max 25}`. The constant 47.3 is the specific Rubisco activity ( :math:`\mu` mol CO\ :sub:`2` g\ :sup:`-1` Rubisco s\ :sup:`-1`) measured at 25°C, and the constant 6.25 is the nitrogen binding factor for Rubisco (g Rubisco g\ :sup:`-1` N; :ref:`Rogers 2014`). -:math:`V_{c\max 25}` additionally varies with daylength (:math:`DYL`) -using the function :math:`f(DYL)`, which introduces seasonal variation -to :math:`V_{c\max }` +:math:`V_{c\max 25}` additionally varies with daylength (:math:`DYL`) using the function :math:`f(DYL)`, which introduces seasonal variation to :math:`V_{c\max }` .. math:: - :label: 10.12) + :label: 10.12) f\left(DYL\right)=\frac{\left(DYL\right)^{2} }{\left(DYL_{\max } \right)^{2} } -with :math:`0.01\le f\left(DYL\right)\le 1`. Daylength (seconds) is -given by +with :math:`0.01\le f\left(DYL\right)\le 1`. Daylength (seconds) is given by .. math:: - :label: 10.13) + :label: 10.13) DYL=2\times 13750.9871\cos ^{-1} \left[\frac{-\sin \left(lat\right)\sin \left(decl\right)}{\cos \left(lat\right)\cos \left(decl\right)} \right] -where :math:`lat` (latitude) and :math:`decl` (declination angle) are -from section :numref:`Solar Zenith Angle`. Maximum daylength (:math:`DYL_{\max }` ) is calculated -similarly but using the maximum declination angle for present-day -orbital geometry (:math:`\pm`\ 23.4667º [:math:`\pm`\ 0.409571 radians], -positive for Northern Hemisphere latitudes and negative for Southern -Hemisphere). +where :math:`lat` (latitude) and :math:`decl` (declination angle) are from section :numref:`Solar Zenith Angle`. Maximum daylength (:math:`DYL_{\max }` ) is calculated similarly but using the maximum declination angle for present-day orbital geometry (:math:`\pm`\ 23.4667° [:math:`\pm`\ 0.409571 radians], positive for Northern Hemisphere latitudes and negative for Southern Hemisphere). Implementation of Photosynthetic Capacity '''''''''''''''''''''''''''''''''''''''''' -Based on :ref:`Farquhar et al. (1980)` and Wullschleger (1993), we can calculate the -electron-limited photosynthetic rate under daily maximum radiation ( :math:`W_{jx}`) -and the Rubisco-limited photosynthetic rate ( :math:`W_{\mathrm{c}}`) as follows, - +Based on :ref:`Farquhar et al. (1980)` and Wullschleger (1993), we can calculate the electron-limited photosynthetic rate under daily maximum radiation ( :math:`W_{jx}`) and the Rubisco-limited photosynthetic rate ( :math:`W_{\mathrm{c}}`) as follows, .. math:: :label: 10.14) - W_{J_{x}} = K_{j}J_{x} , + W_{J_{x}} = K_{j}J_{x} , .. math:: :label: 10.15) W_{\mathrm{c}} = K_{\mathrm{c}} V_{{\mathrm{c}, \text{max}}}, - -where :math:`K_{j}` and :math:`K_{\mathrm{c}}` as the conversion factors for -:math:`J_{x}` and :math:`V_{{\mathrm{c}, \text{max}}}` ( :math:`V_{{\mathrm{c}, \text{max}}}` to -:math:`W_{\mathrm{c}}` and :math:`J_{x}` to :math:`W_{J_{x}}`), respectively. Based on -:ref:`Xu et al. (2012)`, Maire et al. (2012) and Walker et al. (2014), we -assume that :math:`W_{\mathrm{c}}` is proportional to -:math:`W_{J_{x}}`. Specifically, we have +where :math:`K_{j}` and :math:`K_{\mathrm{c}}` as the conversion factors for :math:`J_{x}` and :math:`V_{{\mathrm{c}, \text{max}}}` ( :math:`V_{{\mathrm{c}, \text{max}}}` to :math:`W_{\mathrm{c}}` and :math:`J_{x}` to :math:`W_{J_{x}}`), respectively. Based on :ref:`Xu et al. (2012)`, Maire et al. (2012) and Walker et al. (2014), we assume that :math:`W_{\mathrm{c}}` is proportional to :math:`W_{J_{x}}`. Specifically, we have .. math:: :label: 10.16) W_{\mathrm{c}}=t_{\alpha}t_{\mathrm{c}, j0}W_{J_{x}} - -where :math:`t_{\mathrm{c}, j0}` is the baseline ratio of :math:`W_{\mathrm{c}}` to -:math:`W_{J_{x}}`. We recognize that this ratio may change depending on the -nitrogen use efficiency of carboxylation and electron transport (Ainsworth and Rogers 2007), -therefore the LUNA model has the modification factor, :math:`t_{\alpha}`, to adjust baseline -the ratio depending on the nitrogen use efficiency for electron vs carboxylation (:ref:`Ali et al. 2016`). +where :math:`t_{\mathrm{c}, j0}` is the baseline ratio of :math:`W_{\mathrm{c}}` to :math:`W_{J_{x}}`. We recognize that this ratio may change depending on the nitrogen use efficiency of carboxylation and electron transport (Ainsworth and Rogers 2007), therefore the LUNA model has the modification factor, :math:`t_{\alpha}`, to adjust baseline the ratio depending on the nitrogen use efficiency for electron vs carboxylation (:ref:`Ali et al. 2016`). Total Respiration ''''''''''''''''''' -Following :ref:`Collatz et al. (1991)`, the total respiration ( :math:`R_{\mathrm{t}}`) is -calculated in proportion to :math:`V_{\text{c,max}}`, +Following :ref:`Collatz et al. (1991)`, the total respiration ( :math:`R_{\mathrm{t}}`) is calculated in proportion to :math:`V_{\text{c,max}}`, .. math:: :label: 10.17) R_{\mathrm{t}} = 0.015 V_{\text{c,max}}. - Accounting for the daytime and nighttime temperature, the daily respirations is calculated as follows, - .. math:: :label: 10.18) - R_{\text{td}}={R}_{\mathrm{t}} [D_{\text{day}} + D_{\text{night}} f_{\mathrm{r}}{(T_{\text{night}})/f_{\mathrm{r}}{(T_{\text{day}})}}], - -where :math:`D_{\text{day}}` and :math:`D_{\text{night}}` are daytime and -nighttime durations in seconds. :math:`f_{\mathrm{r}}(T_{\text{night}})` and -:math:`f_{\mathrm{r}}(T_{\text{day}})` are the temperature response functions for -respiration (see Appendix B in :ref:`Ali et al. (2016)` for details). - +where :math:`D_{\text{day}}` and :math:`D_{\text{night}}` are daytime and nighttime durations in seconds. :math:`f_{\mathrm{r}}(T_{\text{night}})` and :math:`f_{\mathrm{r}}(T_{\text{day}})` are the temperature response functions for respiration (see Appendix B in :ref:`Ali et al. (2016)` for details). .. _Numerical scheme: Numerical scheme --------------------------------------------------------- -The LUNA model searches for the "optimal" nitrogen allocations for maximum net photosynthetic carbon gain -by incrementally increase the nitrogen allocated for light capture (i.e., :math:`N_{\text{lc}}`) (see :ref:`Ali et al. (2016)` for details). -We assume that plants only optimize the nitrogen allocation when they can grow (i.e., GPP>0.0). -If GPP become zero under stress, then the LUNA model assume a certain amount of enzyme will decay at daily rates of 0.1, -in view that the half-life time for photosynthetic enzymes are short (~7 days) (Suzuki et al. 2001). -To avoid unrealistic low values of photosynthetic capacity, the decay is only limited to 50 percent of the original enzyme levels. +The LUNA model searches for the "optimal" nitrogen allocations for maximum net photosynthetic carbon gain by incrementally increase the nitrogen allocated for light capture (i.e., :math:`N_{\text{lc}}`) (see :ref:`Ali et al. (2016)` for details). We assume that plants only optimize the nitrogen allocation when they can grow (i.e., GPP>0.0). If GPP become zero under stress, then the LUNA model assume a certain amount of enzyme will decay at daily rates of 0.1, in view that the half-life time for photosynthetic enzymes are short (~7 days) (Suzuki et al. 2001). To avoid unrealistic low values of photosynthetic capacity, the decay is only limited to 50 percent of the original enzyme levels. diff --git a/doc/source/tech_note/Plant_Hydraulics/CLM50_Tech_Note_Plant_Hydraulics.rst b/doc/source/tech_note/Plant_Hydraulics/CLM50_Tech_Note_Plant_Hydraulics.rst index f33a6d96b5..18a4fe1fdc 100644 --- a/doc/source/tech_note/Plant_Hydraulics/CLM50_Tech_Note_Plant_Hydraulics.rst +++ b/doc/source/tech_note/Plant_Hydraulics/CLM50_Tech_Note_Plant_Hydraulics.rst @@ -13,30 +13,24 @@ Roots Vertical Root Distribution --------------------------- -The root fraction :math:`r_{i}` in each soil layer depends on the plant -functional type +The root fraction :math:`r_{i}` in each soil layer depends on the plant functional type .. math:: :label: 11.1 - r_{i} = - \begin{array}{lr} - \left(\beta^{z_{h,\, i-1} \cdot 100} - \beta^{z_{h,\, i} \cdot 100} \right) & \qquad {\rm for\; }1 \le i \le N_{levsoi} + r_{i} = + \begin{array}{lr} + \left(\beta^{z_{h,\, i-1} \cdot 100} - \beta^{z_{h,\, i} \cdot 100} \right) & \qquad {\rm for\; }1 \le i \le N_{levsoi} \end{array} -where :math:`z_{h,\, i}` (m) is the depth from the soil surface to the -interface between layers :math:`i` and :math:`i+1` (:math:`z_{h,\, 0}` , -the soil surface) (section :numref:`Vertical Discretization`), the factor of 100 -converts from m to cm, and :math:`\beta` is a plant-dependent root -distribution parameter adopted from :ref:`Jackson et al. (1996)` -(:numref:`Table Plant functional type root distribution parameters`). +where :math:`z_{h,\, i}` (m) is the depth from the soil surface to the interface between layers :math:`i` and :math:`i+1` (:math:`z_{h,\, 0}`, the soil surface) (section :numref:`Vertical Discretization`), the factor of 100 converts from m to cm, and :math:`\beta` is a plant-dependent root distribution parameter adopted from :ref:`Jackson et al. (1996)` (:numref:`Table Plant functional type root distribution parameters`). .. rootfr(p,lev) = ( & beta ** (col%zi(c,lev-1)*m_to_cm) - & beta ** (col%zi(c,lev)*m_to_cm) ) -.. 0, 0.976, 0.943, 0.943, 0.993, 0.966, 0.993, 0.966, 0.943, 0.964, 0.964, - 0.914, 0.914, 0.943, 0.943, 0.943, 0.943, 0.943, 0.943, 0.943, 0.943, +.. 0, 0.976, 0.943, 0.943, 0.993, 0.966, 0.993, 0.966, 0.943, 0.964, 0.964, + 0.914, 0.914, 0.943, 0.943, 0.943, 0.943, 0.943, 0.943, 0.943, 0.943, .. _Table Plant functional type root distribution parameters: @@ -101,24 +95,20 @@ distribution parameter adopted from :ref:`Jackson et al. (1996) +----------------------------------+------------------+ | Switchgrass I | 0.943 | +----------------------------------+------------------+ - + .. _Root Spacing: Root Spacing ----------------------------- -To determine the conductance along the soil to root pathway (section -:numref:`Soil-to-root`) an estimate of the spacing between the roots within -a soil layer is required. The distance between roots :math:`dx_{root,i}` (m) -is calculated by assuming that roots are distributed uniformly throughout -the soil (:ref:`Gardner 1960`) +To determine the conductance along the soil to root pathway (section :numref:`Soil-to-root`) an estimate of the spacing between the roots within a soil layer is required. The distance between roots :math:`dx_{root,i}` (m) is calculated by assuming that roots are distributed uniformly throughout the soil (:ref:`Gardner 1960`) .. math:: :label: 11.12 dx_{root,i} = \left(\pi \cdot L_i\right)^{- \frac{1}{2}} -where :math:`L_{i}` is the root length density (m m :sup:`-3`) +where :math:`L_{i}` is the root length density (m m :sup:`-3`) .. math:: :label: 11.13 @@ -132,11 +122,9 @@ where :math:`L_{i}` is the root length density (m m :sup:`-3`) B_{root,i} = \frac{c\_to\_b \cdot C_{fineroot} \cdot r_{i}}{dz_{i}} -where :math:`c\_to\_b = 2` (kg biomass kg carbon :sup:`-1`) and -:math:`C_{fineroot}` is the amount of fine root carbon (kg m :sup:`-2`). +where :math:`c\_to\_b = 2` (kg biomass kg carbon :sup:`-1`) and :math:`C_{fineroot}` is the amount of fine root carbon (kg m :sup:`-2`). -:math:`\rho_{root}` is the root density (kg m :sup:`-3`), and -:math:`{CA}_{root}` is the fine root cross sectional area (m :sup:`2`) +:math:`\rho_{root}` is the root density (kg m :sup:`-3`), and :math:`{CA}_{root}` is the fine root cross sectional area (m :sup:`2`) .. math:: :label: 11.15 @@ -150,20 +138,9 @@ where :math:`r_{root}` is the root radius (m). Plant Hydraulic Stress ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The Plant Hydraulic Stress (PHS) routine explicitly models water transport -through the vegetation according to a simple hydraulic framework following -Darcy's Law for porous media flow equations influenced by -:ref:`Bonan et al. (2014) `, -:ref:`Chuang et al. (2006) `, -:ref:`Sperry et al. (1998) `, -:ref:`Sperry and Love (2015) `, -:ref:`Williams et al (1996) `. - -PHS solves for the vegetation water potential that matches water supply with -transpiration demand. Water supply is modeled according to the circuit analog -in :numref:`Figure Plant hydraulic circuit`. Transpiration demand is modeled -relative to maximum transpiration by a transpiration loss function dependent -on leaf water potential. +The Plant Hydraulic Stress (PHS) routine explicitly models water transport through the vegetation according to a simple hydraulic framework following Darcy's Law for porous media flow equations influenced by :ref:`Bonan et al. (2014) `, :ref:`Chuang et al. (2006) `, :ref:`Sperry et al. (1998) `, :ref:`Sperry and Love (2015) `, :ref:`Williams et al (1996) `. + +PHS solves for the vegetation water potential that matches water supply with transpiration demand. Water supply is modeled according to the circuit analog in :numref:`Figure Plant hydraulic circuit`. Transpiration demand is modeled relative to maximum transpiration by a transpiration loss function dependent on leaf water potential. .. _Figure Plant hydraulic circuit: @@ -176,47 +153,29 @@ on leaf water potential. Plant Water Supply ----------------------- -The supply equations are used to solve for vegetation water potential forced -by transpiration demand and the set of layer-by-layer soil water potentials. -The water supply is discretized into segments: soil-to-root, root-to-stem, and -stem-to-leaf. There are typically several (1-49) soil-to-root flows operating -in parallel, one per soil layer. There are two stem-to-leaf flows operating in -parallel, corresponding to the sunlit and shaded "leaves". +The supply equations are used to solve for vegetation water potential forced by transpiration demand and the set of layer-by-layer soil water potentials. The water supply is discretized into segments: soil-to-root, root-to-stem, and stem-to-leaf. There are typically several (1-49) soil-to-root flows operating in parallel, one per soil layer. There are two stem-to-leaf flows operating in parallel, corresponding to the sunlit and shaded "leaves". -In general the water fluxes (e.g. soil-to-root, root-to-stem, etc.) are -modeled according to Darcy's Law for porous media flow as: +In general the water fluxes (e.g. soil-to-root, root-to-stem, etc.) are modeled according to Darcy's Law for porous media flow as: .. math:: :label: 11.101 q = kA\left( \psi_1 - \psi_2 \right) -:math:`q` is the flux of water (mmH\ :sub:`2`\ O/s) spanning the segment -between :math:`\psi_1` and :math:`\psi_2` +:math:`q` is the flux of water (mmH\ :sub:`2`\ O/s) spanning the segment between :math:`\psi_1` and :math:`\psi_2` :math:`k` is the hydraulic conductance (s\ :sup:`-1`\ ) -:math:`A` is the area basis (m\ :sup:`2`\ /m\ :sup:`2`\ ) relating the -conducting area basis to ground area - -:math:`\psi_1 - \psi_2` is the gradient in water potential (mmH\ :sub:`2`\ O) -across the segment - -The segments in :numref:`Figure Plant hydraulic circuit` have variable resistance, -as the water potentials become lower, hydraulic conductance decreases. This is -captured by multiplying the maximum segment conductance by a sigmoidal function -capturing the percent loss of conductivity. The function uses two parameters to -fit experimental vulnerability curves: the water potential at 50% loss of -conductivity (:math:`p50`) and a shape fitting parameter (:math:`c_k`). +:math:`A` is the area basis (m\ :sup:`2`\ /m\ :sup:`2`\ ) relating the conducting area basis to ground area :math:`\psi_1 - \psi_2` is the gradient in water potential (mmH\ :sub:`2`\ O) across the segment The segments in :numref:`Figure Plant hydraulic circuit` have variable resistance, as the water potentials become lower, hydraulic conductance decreases. This is captured by multiplying the maximum segment conductance by a sigmoidal function capturing the percent loss of conductivity. The function uses two parameters to fit experimental vulnerability curves: the water potential at 50% loss of conductivity (:math:`p50`) and a shape fitting parameter (:math:`c_k`). .. math:: :label: 11.102 - + k=k_{max}\cdot 2^{-\left(\dfrac{\psi_1}{p50}\right)^{c_k}} -:math:`k_{max}` is the maximum segment conductance (s\ :sup:`-1`\ ) +:math:`k_{max}` is the maximum segment conductance (s\ :sup:`-1`\ ) -:math:`p50` is the water potential at 50% loss of conductivity (mmH\ :sub:`2`\ O) +:math:`p50` is the water potential at 50% loss of conductivity (mmH\ :sub:`2`\ O) :math:`\psi_1` is the water potential of the lower segment terminus (mmH\ :sub:`2`\ O) @@ -225,40 +184,26 @@ conductivity (:math:`p50`) and a shape fitting parameter (:math:`c_k`). Stem-to-leaf '''''''''''''''''''''''' -The area basis and conductance parameterization varies by segment. There -are two stem-to-leaf fluxes in parallel, from stem to sunlit leaf and from -stem to shaded leaf (:math:`q_{1a}` and :math:`q_{1a}`). The water flux from -stem-to-leaf is the product of the segment conductance, the conducting area -basis, and the water potential gradient from stem to leaf. Stem-to-leaf -conductance is defined as the maximum conductance multiplied by the percent -of maximum conductance, as calculated by the sigmoidal vulnerability curve. -The maximum conductance is a PFT parameter representing the maximum -conductance of water from stem to leaf per unit leaf area. This parameter -can be defined separately for sunlit and shaded segments and should already -include the appropriate length scaling (in other words this is a conductance, -not conductivity). The water potential gradient is the difference between -leaf water potential and stem water potential. There is no gravity term, -assuming a negligible difference in height across the segment. The area -basis is the leaf area index (either sunlit or shaded). - -.. math:: +The area basis and conductance parameterization varies by segment. There are two stem-to-leaf fluxes in parallel, from stem to sunlit leaf and from stem to shaded leaf (:math:`q_{1a}` and :math:`q_{1a}`). The water flux from stem-to-leaf is the product of the segment conductance, the conducting area basis, and the water potential gradient from stem to leaf. Stem-to-leaf conductance is defined as the maximum conductance multiplied by the percent of maximum conductance, as calculated by the sigmoidal vulnerability curve. The maximum conductance is a PFT parameter representing the maximum conductance of water from stem to leaf per unit leaf area. This parameter can be defined separately for sunlit and shaded segments and should already include the appropriate length scaling (in other words this is a conductance, not conductivity). The water potential gradient is the difference between leaf water potential and stem water potential. There is no gravity term, assuming a negligible difference in height across the segment. The area basis is the leaf area index (either sunlit or shaded). + +.. math:: :label: 11.103 - q_{1a}=k_{1a}\cdot\mbox{LAI}_{sun}\cdot\left(\psi_{stem}-\psi_{sunleaf} \right) + q_{1a}=k_{1a}\cdot\mbox{LAI}_{sun}\cdot\left(\psi_{stem}-\psi_{sunleaf} \right) -.. math:: +.. math:: :label: 11.104 - q_{1b}=k_{1b}\cdot\mbox{LAI}_{shade}\cdot\left(\psi_{stem}-\psi_{shadeleaf} \right) + q_{1b}=k_{1b}\cdot\mbox{LAI}_{shade}\cdot\left(\psi_{stem}-\psi_{shadeleaf} \right) -.. math:: +.. math:: :label: 11.105 - k_{1a}=k_{1a,max}\cdot 2^{-\left(\dfrac{\psi_{stem}}{p50_1}\right)^{c_k}} + k_{1a}=k_{1a,max}\cdot 2^{-\left(\dfrac{\psi_{stem}}{p50_1}\right)^{c_k}} .. math:: :label: 11.106 - + k_{1b}=k_{1b,max}\cdot 2^{-\left(\dfrac{\psi_{stem}}{p50_1}\right)^{c_k}} Variables: @@ -292,22 +237,11 @@ Parameters: Root-to-stem '''''''''''''''''''''''' -There is one root-to-stem flux. This represents a flux from the root collar -to the upper branch reaches. The water flux from root-to-stem is the product -of the segment conductance, the conducting area basis, and the water -potential gradient from root to stem. Root-to-stem conductance is defined -as the maximum conductance multiplied by the percent of maximum conductance, -as calculated by the sigmoidal vulnerability curve (two parameters). The -maximum conductance is defined as the maximum root-to-stem conductivity per -unit stem area (PFT parameter) divided by the length of the conducting path, -which is taken to be the vegetation height. The area basis is the stem area -index. The gradient in water potential is the difference between the root -water potential and the stem water potential less the difference in -gravitational potential. +There is one root-to-stem flux. This represents a flux from the root collar to the upper branch reaches. The water flux from root-to-stem is the product of the segment conductance, the conducting area basis, and the water potential gradient from root to stem. Root-to-stem conductance is defined as the maximum conductance multiplied by the percent of maximum conductance, as calculated by the sigmoidal vulnerability curve (two parameters). The maximum conductance is defined as the maximum root-to-stem conductivity per unit stem area (PFT parameter) divided by the length of the conducting path, which is taken to be the vegetation height. The area basis is the stem area index. The gradient in water potential is the difference between the root water potential and the stem water potential less the difference in gravitational potential. .. math:: :label: 11.107 - + q_2=k_2 \cdot SAI \cdot \left( \psi_{root} - \psi_{stem} - \Delta \psi_z \right) .. math:: @@ -340,63 +274,38 @@ Parameters: Soil-to-root '''''''''''''''''''''''' -There are several soil-to-root fluxes operating in parallel (one for each -root-containing soil layer). Each represents a flux from the given soil -layer to the root collar. The water flux from soil-to-root is the product -of the segment conductance, the conducting area basis, and the water -potential gradient from soil to root. The area basis is a proxy for root -area index, defined as the summed leaf and stem area index multiplied by -the root-to-shoot ratio (PFT parameter) multiplied by the layer root -fraction. The root fraction comes from an empirical root profile (section -:numref:`Vertical Root Distribution`). - -The gradient in water potential is the difference between the soil water -potential and the root water potential less the difference in gravitational -potential. There is only one root water potential to which all soil layers -are connected in parallel. A soil-to-root flux can be either positive -(vegetation water uptake) or negative (water deposition), depending on the -relative values of the root and soil water potentials. This allows for the -occurrence of hydraulic redistribution where water moves through vegetation -tissue from one soil layer to another. - -Soil-to-root conductance is the result of two resistances in series, first -across the soil-root interface and then through the root tissue. The root -tissue conductance is defined as the maximum conductance multiplied by the -percent of maximum conductance, as calculated by the sigmoidal vulnerability -curve. The maximum conductance is defined as the maximum root-tissue -conductivity (PFT parameter) divided by the length of the conducting path, -which is taken to be the soil layer depth plus lateral root length. - -The soil-root interface conductance is defined as the soil conductivity -divided by the conducting length from soil to root. The soil conductivity -varies by soil layer and is calculated based on soil potential and soil -properties, via the Brooks-Corey theory. The conducting length is determined -from the characteristic root spacing (section :numref:`Root Spacing`). +There are several soil-to-root fluxes operating in parallel (one for each root-containing soil layer). Each represents a flux from the given soil layer to the root collar. The water flux from soil-to-root is the product of the segment conductance, the conducting area basis, and the water potential gradient from soil to root. The area basis is a proxy for root area index, defined as the summed leaf and stem area index multiplied by the root-to-shoot ratio (PFT parameter) multiplied by the layer root fraction. The root fraction comes from an empirical root profile (section :numref:`Vertical Root Distribution`). + +The gradient in water potential is the difference between the soil water potential and the root water potential less the difference in gravitational potential. There is only one root water potential to which all soil layers are connected in parallel. A soil-to-root flux can be either positive (vegetation water uptake) or negative (water deposition), depending on the relative values of the root and soil water potentials. This allows for the occurrence of hydraulic redistribution where water moves through vegetation tissue from one soil layer to another. + +Soil-to-root conductance is the result of two resistances in series, first across the soil-root interface and then through the root tissue. The root tissue conductance is defined as the maximum conductance multiplied by the percent of maximum conductance, as calculated by the sigmoidal vulnerability curve. The maximum conductance is defined as the maximum root-tissue conductivity (PFT parameter) divided by the length of the conducting path, which is taken to be the soil layer depth plus lateral root length. + +The soil-root interface conductance is defined as the soil conductivity divided by the conducting length from soil to root. The soil conductivity varies by soil layer and is calculated based on soil potential and soil properties, via the Brooks-Corey theory. The conducting length is determined from the characteristic root spacing (section :numref:`Root Spacing`). .. math:: :label: 11.109 - q_{3,i}=k_{3,i} \cdot RAI \cdot \left(\psi_{soil,i}-\psi_{root} + \Delta\psi_{z,i} \right) + q_{3,i}=k_{3,i} \cdot \left(\psi_{soil,i}-\psi_{root} + \Delta\psi_{z,i} \right) .. math:: :label: 11.110 - RAI=\left(LAI+SAI \right) \cdot r_i \cdot f_{root-leaf} + k_{3,i}=\dfrac{k_{r,i} \cdot k_{s,i}}{k_{r,i}+k_{s,i}} .. math:: :label: 11.111 - k_{3,i}=\dfrac{k_{r,i} \cdot k_{s,i}}{k_{r,i}+k_{s,i}} + k_{r,i}=\dfrac{k_{3,max}}{z_{3,i}} \cdot RAI \cdot 2^{-\left(\dfrac{\psi_{soil,i}}{p50_3}\right)^{c_k}} .. math:: :label: 11.112 - k_{r,i}=\dfrac{k_{3,max}}{z_{3,i}} \cdot 2^{-\left(\dfrac{\psi_{soil,i}}{p50_3}\right)^{c_k}} + RAI=\left(LAI+SAI \right) \cdot r_i \cdot f_{root-leaf} .. math:: :label: 11.113 - k_{s,i} = \dfrac{k_{soil,i}}{dx_{root,i}} + k_{s,i} = \dfrac{k_{soil,i}}{dx_{root,i}} Variables: @@ -406,7 +315,7 @@ Variables: :math:`LAI` = total leaf area index (m2/m2) -:math:`SAI` = stem area index (m2/m2) +:math:`SAI` = stem area index (m2/m2) :math:`\psi_{soil,i}` = water potential in soil layer :math:`i` (mmH\ :sub:`2`\ O) @@ -431,35 +340,27 @@ Parameters: Plant Water Demand ----------------------- -Plant water demand depends on stomatal conductance, which is described in section :numref:`Stomatal resistance`. -Here we describe the influence of PHS and the coupling of vegetation water demand and supply. -PHS models vegetation water demand as transpiration attenuated by a transpiration loss function based on leaf water potential. -Sunlit leaf transpiration is modeled as the maximum sunlit leaf transpiration multiplied by the percent of maximum transpiration as modeled by the sigmoidal loss function. -The same follows for shaded leaf transpiration. -Maximum stomatal conductance is calculated from the Medlyn model :ref:`(Medlyn et al. 2011) ` absent water stress and used to calculate the maximum transpiration (see section :numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`). -Water stress is calculated as the ratio of attenuated stomatal conductance to maximum stomatal conductance. -Water stress is calculated with distinct values for sunlit and shaded leaves. -Vegetation water stress is calculated based on leaf water potential and is used to attenuate photosynthesis (see section :numref:`Photosynthesis`) +Plant water demand depends on stomatal conductance, which is described in section :numref:`Stomatal resistance`. Here we describe the influence of PHS and the coupling of vegetation water demand and supply. PHS models vegetation water demand as transpiration attenuated by a transpiration loss function based on leaf water potential. Sunlit leaf transpiration is modeled as the maximum sunlit leaf transpiration multiplied by the percent of maximum transpiration as modeled by the sigmoidal loss function. The same follows for shaded leaf transpiration. Maximum stomatal conductance is calculated from the Medlyn model :ref:`(Medlyn et al. 2011) ` absent water stress and used to calculate the maximum transpiration (see section :numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`). Water stress is calculated as the ratio of attenuated stomatal conductance to maximum stomatal conductance. Water stress is calculated with distinct values for sunlit and shaded leaves. Vegetation water stress is calculated based on leaf water potential and is used to attenuate photosynthesis (see section :numref:`Photosynthesis`) .. math:: :label: 11.201 - E_{sun} = E_{sun,max} \cdot 2^{-\left(\dfrac{\psi_{sunleaf}}{p50_e}\right)^{c_k}} + E_{sun} = E_{sun,max} \cdot 2^{-\left(\dfrac{\psi_{sunleaf}}{p50_e}\right)^{c_k}} .. math:: :label: 11.202 - E_{shade} = E_{shade,max} \cdot 2^{-\left(\dfrac{\psi_{shadeleaf}}{p50_e}\right)^{c_k}} + E_{shade} = E_{shade,max} \cdot 2^{-\left(\dfrac{\psi_{shadeleaf}}{p50_e}\right)^{c_k}} .. math:: :label: 11.203 - \beta_{t,sun} = \dfrac{g_{s,sun}}{g_{s,sun,\beta_t=1}} + \beta_{t,sun} = \dfrac{g_{s,sun}}{g_{s,sun,\beta_t=1}} .. math:: :label: 11.204 - \beta_{t,shade} = \dfrac{g_{s,shade}}{g_{s,shade,\beta_t=1}} + \beta_{t,shade} = \dfrac{g_{s,shade}}{g_{s,shade,\beta_t=1}} :math:`E_{sun}` = sunlit leaf transpiration (mm/s) @@ -471,11 +372,11 @@ Vegetation water stress is calculated based on leaf water potential and is used :math:`\psi_{sunleaf}` = sunlit leaf water potential (mmH\ :sub:`2`\ O) -:math:`\psi_{shadeleaf}` = shaded leaf water potential (mmH\ :sub:`2`\ O) +:math:`\psi_{shadeleaf}` = shaded leaf water potential (mmH\ :sub:`2`\ O) -:math:`\beta_{t,sun}` = sunlit transpiration water stress (-) +:math:`\beta_{t,sun}` = sunlit transpiration water stress (-) -:math:`\beta_{t,shade}` = shaded transpiration water stress (-) +:math:`\beta_{t,shade}` = shaded transpiration water stress (-) :math:`g_{s,sun}` = stomatal conductance of water corresponding to :math:`E_{sun}` @@ -490,7 +391,7 @@ Vegetation water stress is calculated based on leaf water potential and is used Vegetation Water Potential ----------------------------- -Both plant water supply and demand are functions of vegetation water potential. PHS explicitly models root, stem, shaded leaf, and sunlit leaf water potential at each timestep. PHS iterates to find the vegetation water potential :math:`\psi` (vector) that satisfies continuity between the non-linear vegetation water supply and demand (equations :eq:`11.103`, :eq:`11.104`, :eq:`11.107`, :eq:`11.109`, :eq:`11.201`, :eq:`11.202`). +Both plant water supply and demand are functions of vegetation water potential. PHS explicitly models root, stem, shaded leaf, and sunlit leaf water potential at each timestep. PHS iterates to find the vegetation water potential :math:`\psi` (vector) that satisfies continuity between the non-linear vegetation water supply and demand (equations :eq:`11.103`, :eq:`11.104`, :eq:`11.107`, :eq:`11.109`, :eq:`11.201`, :eq:`11.202`). .. math:: :label: 11.301 @@ -510,20 +411,12 @@ Both plant water supply and demand are functions of vegetation water potential. PHS finds the water potentials that match supply and demand. In the plant water transport equations :eq:`11.302`, the demand terms (left-hand side) are decreasing functions of absolute leaf water potential. As absolute leaf water potential becomes larger, water stress increases, causing a decrease in transpiration demand. The supply terms (right-hand side) are increasing functions of absolute leaf water potential. As absolute leaf water potential becomes larger, the gradients in water potential increase, causing an increase in vegetation water supply. PHS takes a Newton's method approach to iteratively solve for the vegetation water potentials that satisfy continuity :eq:`11.302`. - - .. _PHS Numerical Implementation: Numerical Implementation -------------------------------- -The four plant water potential nodes are ( :math:`\psi_{root}`, :math:`\psi_{xylem}`, :math:`\psi_{shadeleaf}`, :math:`\psi_{sunleaf}`). -The fluxes between each pair of nodes are labeled in Figure 1. -:math:`E_{sun}` and :math:`E_{sha}` are the transpiration from sunlit and shaded leaves, respectively. -We use the circuit-analog model to calculate the vegetation water potential ( :math:`\psi`) for the four plant nodes, forced by soil matric potential and unstressed transpiration. -The unstressed transpiration is acquired by running the photosynthesis model with :math:`\beta_t=1`. -The unstressed transpiration flux is attenuated based on the leaf-level vegetation water potential. -Using the attenuated transpiration, we solve for :math:`g_{s,stressed}` and output :math:`\beta_t=\dfrac{g_{s,stressed}}{g_{s,unstressed}}`. +The four plant water potential nodes are ( :math:`\psi_{root}`, :math:`\psi_{xylem}`, :math:`\psi_{shadeleaf}`, :math:`\psi_{sunleaf}`). The fluxes between each pair of nodes are labeled in Figure 1. :math:`E_{sun}` and :math:`E_{sha}` are the transpiration from sunlit and shaded leaves, respectively. We use the circuit-analog model to calculate the vegetation water potential ( :math:`\psi`) for the four plant nodes, forced by soil matric potential and unstressed transpiration. The unstressed transpiration is acquired by running the photosynthesis model with :math:`\beta_t=1`. The unstressed transpiration flux is attenuated based on the leaf-level vegetation water potential. Using the attenuated transpiration, we solve for :math:`g_{s,stressed}` and output :math:`\beta_t=\dfrac{g_{s,stressed}}{g_{s,unstressed}}`. The continuity of water flow through the system yields four equations @@ -537,25 +430,23 @@ The continuity of water flow through the system yields four equations q_2&=\sum_{i=1}^{nlevsoi}{q_{3,i}} \end{aligned} - -We seek the set of vegetation water potential values, +We seek the set of vegetation water potential values, .. math:: :label: 11.402 - \psi=\left[ \begin {array}{c} + \psi=\left[ \begin {array}{c} \psi_{sunleaf}\cr\psi_{shadeleaf}\cr\psi_{stem}\cr\psi_{root} - \end {array} \right] + \end {array} \right] -that satisfies these equations, as forced by the soil moisture and atmospheric state. -Each flux on the schematic can be represented in terms of the relevant water potentials. Defining the transpiration fluxes: +that satisfies these equations, as forced by the soil moisture and atmospheric state. Each flux on the schematic can be represented in terms of the relevant water potentials. Defining the transpiration fluxes: .. math:: :label: 11.403 \begin{aligned} E_{sun} &= E_{sun,max} \cdot 2^{-\left(\dfrac{\psi_{sunleaf}}{p50_e}\right)^{c_k}} \\ - E_{shade} &= E_{shade,max} \cdot 2^{-\left(\dfrac{\psi_{shadeleaf}}{p50_e}\right)^{c_k}} + E_{shade} &= E_{shade,max} \cdot 2^{-\left(\dfrac{\psi_{shadeleaf}}{p50_e}\right)^{c_k}} \end{aligned} Defining the water supply fluxes: @@ -570,11 +461,7 @@ Defining the water supply fluxes: q_{soil}&=\sum_{i=1}^{nlevsoi}{q_{3,i}}=\sum_{i=1}^{nlevsoi}{k_{3,i}\cdot RAI\cdot\left(\psi_{soil,i}-\psi_{root} + \Delta\psi_{z,i} \right)} \end{aligned} -We're looking to find the vector :math:`\psi` -that fits with soil and atmospheric forcings while satisfying water flow continuity. -Due to the model non-linearity, we use a linearized explicit approach, iterating with Newton's method. -The initial guess is the solution for :math:`\psi` (vector) from the previous time step. -The general framework, from iteration `m` to `m+1` is: +We're looking to find the vector :math:`\psi` that fits with soil and atmospheric forcings while satisfying water flow continuity. Due to the model non-linearity, we use a linearized explicit approach, iterating with Newton's method. The initial guess is the solution for :math:`\psi` (vector) from the previous time step. The general framework, from iteration `m` to `m+1` is: .. math:: :label: 11.405 @@ -646,11 +533,11 @@ Introducing the notation: \Delta\psi_{shadeleaf} \cr \Delta\psi_{stem} \cr \Delta\psi_{root} - \end {array} \right] + \end {array} \right] .. math:: :label: 11.412 - + A= \left[ \begin {array}{cccc} \dfrac{\delta q_{1a}}{\delta \psi_{sun}}-\dfrac{\delta E_{sun}}{\delta \psi_{sun}}&0&\dfrac{\delta q_{1a}}{\delta \psi_{stem}}&0\cr @@ -685,40 +572,30 @@ Now we compute all the entries for :math:`A` and :math:`b` based on the soil moi \psi_{m+1}=\psi_m+\Delta\psi -We iterate until :math:`b\to 0`, signifying water flux balance through the system. The result is a final set of water potentials ( :math:`\psi_{root}`, :math:`\psi_{xylem}`, :math:`\psi_{shadeleaf}`, :math:`\psi_{sunleaf}`) satisfying non-divergent water flux through the system. -The magnitude of the water flux is driven by soil matric potential and unstressed ( :math:`\beta_t=1`) transpiration. +We iterate until :math:`b\to 0`, signifying water flux balance through the system. The result is a final set of water potentials ( :math:`\psi_{root}`, :math:`\psi_{xylem}`, :math:`\psi_{shadeleaf}`, :math:`\psi_{sunleaf}`) satisfying non-divergent water flux through the system. The magnitude of the water flux is driven by soil matric potential and unstressed ( :math:`\beta_t=1`) transpiration. -We use the transpiration solution (corresponding to the final solution for :math:`\psi`) to compute stomatal conductance. The stomatal conductance is then used to compute :math:`\beta_t`. +We use the transpiration solution (corresponding to the final solution for :math:`\psi`) to compute stomatal conductance. The stomatal conductance is then used to compute :math:`\beta_t`. .. math:: :label: 11.416 - \beta_{t,sun} = \dfrac{g_{s,sun}}{g_{s,sun,\beta_t=1}} + \beta_{t,sun} = \dfrac{g_{s,sun}}{g_{s,sun,\beta_t=1}} .. math:: :label: 11.417 - \beta_{t,shade} = \dfrac{g_{s,shade}}{g_{s,shade,\beta_t=1}} + \beta_{t,shade} = \dfrac{g_{s,shade}}{g_{s,shade,\beta_t=1}} -The :math:`\beta_t` values are used in the Photosynthesis module (see section :numref:`Photosynthesis`) to apply water stress. -The solution for :math:`\psi` is saved as a new variable (vegetation water potential) and is indicative of plant water status. -The soil-to-root fluxes :math:`\left( q_{3,1},q_{3,2},\mbox{...},q_{3,n}\right)` are used as the soil transpiration sink in the Richards' equation subsurface flow equations (see section :numref:`Soil Water`). +The :math:`\beta_t` values are used in the Photosynthesis module (see section :numref:`Photosynthesis`) to apply water stress. The solution for :math:`\psi` is saved as a new variable (vegetation water potential) and is indicative of plant water status. The soil-to-root fluxes :math:`\left( q_{3,1},q_{3,2},\mbox{...},q_{3,n}\right)` are used as the soil transpiration sink in the Richards' equation subsurface flow equations (see section :numref:`Soil Water`). .. _Flow Diagram of Leaf Flux Calculations: Flow Diagram of Leaf Flux Calculations: ------------------------------------------- -PHS runs nested in the loop that solves for sensible and latent heat fluxes and temperature for vegetated surfaces (see section :numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`). -The scheme iterates for convergence of leaf temperature (:math:`T_l`), transpiration water stress (:math:`\beta_t`), and intercellular CO2 concentration (:math:`c_i`). -PHS is forced by maximum transpiration (absent water stress, :math:`\beta_t=1`), whereby we first solve for assimilation, stomatal conductance, and intercellular CO2 with :math:`\beta_{t,sun}` and :math:`\beta_{t,shade}` both set to 1. -This involves iterating to convergence of :math:`c_i` (see section :numref:`Photosynthesis`). +PHS runs nested in the loop that solves for sensible and latent heat fluxes and temperature for vegetated surfaces (see section :numref:`Sensible and Latent Heat Fluxes and Temperature for Vegetated Surfaces`). The scheme iterates for convergence of leaf temperature (:math:`T_l`), transpiration water stress (:math:`\beta_t`), and intercellular CO2 concentration (:math:`c_i`). PHS is forced by maximum transpiration (absent water stress, :math:`\beta_t=1`), whereby we first solve for assimilation, stomatal conductance, and intercellular CO2 with :math:`\beta_{t,sun}` and :math:`\beta_{t,shade}` both set to 1. This involves iterating to convergence of :math:`c_i` (see section :numref:`Photosynthesis`). -Next, using the solutions for :math:`E_{sun,max}` and :math:`E_{shade,max}`, PHS solves for :math:`\psi`, :math:`\beta_{t,sun}`, and :math:`\beta_{t,shade}`. -The values for :math:`\beta_{t,sun}`, and :math:`\beta_{t,shade}` are inputs to the photosynthesis routine, which now solves for attenuated photosynthesis and stomatal conductance (reflecting water stress). -Again this involves iterating to convergence of :math:`c_i`. -Non-linearities between :math:`\beta_t` and transpiration require also iterating to convergence of :math:`\beta_t`. -The outermost level of iteration works towards convergence of leaf temperature, reflecting leaf surface energy balance. +Next, using the solutions for :math:`E_{sun,max}` and :math:`E_{shade,max}`, PHS solves for :math:`\psi`, :math:`\beta_{t,sun}`, and :math:`\beta_{t,shade}`. The values for :math:`\beta_{t,sun}`, and :math:`\beta_{t,shade}` are inputs to the photosynthesis routine, which now solves for attenuated photosynthesis and stomatal conductance (reflecting water stress). Again this involves iterating to convergence of :math:`c_i`. Non-linearities between :math:`\beta_t` and transpiration require also iterating to convergence of :math:`\beta_t`. The outermost level of iteration works towards convergence of leaf temperature, reflecting leaf surface energy balance. .. _Figure PHS Flow Diagram: diff --git a/doc/source/tech_note/Plant_Mortality/CLM50_Tech_Note_Plant_Mortality.rst b/doc/source/tech_note/Plant_Mortality/CLM50_Tech_Note_Plant_Mortality.rst index 4c2cb71a26..a60b6000b5 100644 --- a/doc/source/tech_note/Plant_Mortality/CLM50_Tech_Note_Plant_Mortality.rst +++ b/doc/source/tech_note/Plant_Mortality/CLM50_Tech_Note_Plant_Mortality.rst @@ -3,501 +3,459 @@ Plant Mortality =================== -Plant mortality as described here applies to perennial vegetation types, -and is intended to represent the death of individuals from a stand of -plants due to the aggregate of processes such as wind throw, insect -attack, disease, extreme temperatures or drought, and age-related -decline in vigor. These processes are referred to in aggregate as -“gap-phase” mortality. Mortality due to fire and anthropogenic land -cover change are treated separately (see Chapters :numref:`rst_Fire` and :numref:`rst_Transient Landcover Change`, -respectively). +Plant mortality as described here applies to perennial vegetation types, and is intended to represent the death of individuals from a stand of plants due to the aggregate of processes such as wind throw, insect attack, disease, extreme temperatures or drought, and age-related decline in vigor. These processes are referred to in aggregate as "gap-phase" mortality. Mortality due to fire and anthropogenic land cover change are treated separately (see Chapters :numref:`rst_Fire` and :numref:`rst_Transient Landcover Change`, respectively). Mortality Fluxes Leaving Vegetation Pools ---------------------------------------------- -Whole-plant mortality is parameterized very simply, assuming a mortality -rate of 2% yr\ :sup:`-1` for all vegetation types. This is clearly -a gross oversimplification of an important process, and additional work -is required to better constrain this process in different climate zones -(:ref:`Keller et al. 2004`; :ref:`Sollins 1982`), for different species mixtures -(:ref:`Gomes et al. 2003`), and for different size and age classes (:ref:`Busing -2005`; :ref:`Law et al. 2003`). Literature values for forest mortality rates -range from at least 0.7% to 3.0% yr\ :sup:`-1`. Taking the annual -rate of mortality (*am*, proportion yr\ :sup:`-1`) as 0.02, a -mortality rate per second (*m*) is calculated as -:math:`m={am\mathord{\left/ {\vphantom {am \left(365\cdot 86400\right)}} \right. \kern-\nulldelimiterspace} \left(365\cdot 86400\right)}` . -All vegetation carbon and nitrogen pools for display, storage, and -transfer are affected at rate *m*, with mortality fluxes out of -vegetation pools eventually merged to the column level and deposited in -litter pools. Mortality (*mort*) fluxes out of displayed vegetation -carbon and nitrogen pools are - -.. math:: - :label: 33.1) +Whole-plant mortality is parameterized very simply, assuming a mortality rate of 2% yr\ :sup:`-1` for all vegetation types. This is clearly a gross oversimplification of an important process, and additional work is required to better constrain this process in different climate zones (:ref:`Keller et al. 2004`; :ref:`Sollins 1982`), for different species mixtures (:ref:`Gomes et al. 2003`), and for different size and age classes (:ref:`Busing 2005`; :ref:`Law et al. 2003`). Literature values for forest mortality rates range from at least 0.7% to 3.0% yr\ :sup:`-1`. Taking the annual rate of mortality (*am*, proportion yr\ :sup:`-1`) as 0.02, a mortality rate per second (*m*) is calculated as :math:`m={am\mathord{\left/ {\vphantom {am \left(365\cdot 86400\right)}} \right.} \left(365\cdot 86400\right)}`. All vegetation carbon and nitrogen pools for display, storage, and transfer are affected at rate *m*, with mortality fluxes out of vegetation pools eventually merged to the column level and deposited in litter pools. Mortality (*mort*) fluxes out of displayed vegetation carbon and nitrogen pools are + +.. math:: + :label: 33.1) CF_{leaf\_ mort} =CS_{leaf} m .. math:: - :label: 33.2) + :label: 33.2) CF_{froot\_ mort} =CS_{froot} m .. math:: - :label: 33.3) + :label: 33.3) CF_{livestem\_ mort} =CS_{livestem} m .. math:: - :label: 33.4) + :label: 33.4) CF_{deadstem\_ mort} =CS_{deadstem} m .. math:: - :label: 33.5) + :label: 33.5) CF_{livecroot\_ mort} =CS_{livecroot} m .. math:: - :label: 33.6) + :label: 33.6) CF_{deadcroot\_ mort} =CS_{deadcroot} m .. math:: - :label: 33.7) + :label: 33.7) NF_{leaf\_ mort} =NS_{leaf} m .. math:: - :label: 33.8) + :label: 33.8) NF_{froot\_ mort} =NS_{froot} m .. math:: - :label: 33.9) + :label: 33.9) NF_{livestem\_ mort} =NS_{livestem} m .. math:: - :label: 33.10) + :label: 33.10) NF_{deadstem\_ mort} =NS_{deadstem} m .. math:: - :label: 33.11) + :label: 33.11) NF_{livecroot\_ mort} =NS_{livecroot} m .. math:: - :label: 33.12) + :label: 33.12) NF_{deadcroot\_ mort} =NS_{deadcroot} m .. math:: - :label: 33.13) + :label: 33.13) NF_{retrans\_ mort} =NS_{retrans} m. -where CF are carbon fluxes, CS is carbon storage, NF are nitrogen -fluxes, NS is nitrogen storage, *croot* refers to coarse roots, *froot* -refers to fine roots, and *retrans* refers to retranslocated. +where CF are carbon fluxes, CS is carbon storage, NF are nitrogen fluxes, NS is nitrogen storage, *croot* refers to coarse roots, *froot* refers to fine roots, and *retrans* refers to retranslocated. Mortality fluxes out of carbon and nitrogen storage (*stor)* pools are .. math:: - :label: 33.14) + :label: 33.14) CF_{leaf\_ stor\_ mort} =CS_{leaf\_ stor} m .. math:: - :label: 33.15) + :label: 33.15) CF_{froot\_ stor\_ mort} =CS_{froot\_ stor} m .. math:: - :label: 33.16) + :label: 33.16) CF_{livestem\_ stor\_ mort} =CS_{livestem\_ stor} m .. math:: - :label: 33.17) + :label: 33.17) CF_{deadstem\_ stor\_ mort} =CS_{deadstem\_ stor} m .. math:: - :label: 33.18) + :label: 33.18) CF_{livecroot\_ stor\_ mort} =CS_{livecroot\_ stor} m .. math:: - :label: 33.19) + :label: 33.19) CF_{deadcroot\_ stor\_ mort} =CS_{deadcroot\_ stor} m .. math:: - :label: 33.20) + :label: 33.20) CF_{gresp\_ stor\_ mort} =CS_{gresp\_ stor} m .. math:: - :label: 33.21) + :label: 33.21) NF_{leaf\_ stor\_ mort} =NS_{leaf\_ stor} m .. math:: - :label: 33.22) + :label: 33.22) NF_{froot\_ stor\_ mort} =NS_{froot\_ stor} m .. math:: - :label: 33.23) + :label: 33.23) NF_{livestem\_ stor\_ mort} =NS_{livestem\_ stor} m .. math:: - :label: 33.24) + :label: 33.24) NF_{deadstem\_ stor\_ mort} =NS_{deadstem\_ stor} m .. math:: - :label: 33.25) + :label: 33.25) NF_{livecroot\_ stor\_ mort} =NS_{livecroot\_ stor} m .. math:: - :label: 33.26) + :label: 33.26) NF_{deadcroot\_ stor\_ mort} =NS_{deadcroot\_ stor} m where *gresp* refers to growth respiration. -Mortality fluxes out of carbon and nitrogen transfer (*xfer)* growth -pools are +Mortality fluxes out of carbon and nitrogen transfer (*xfer)* growth pools are .. math:: - :label: 33.27) + :label: 33.27) CF_{leaf\_ xfer\_ mort} =CS_{leaf\_ xfer} m .. math:: - :label: 33.28) + :label: 33.28) CF_{froot\_ xfer\_ mort} =CS_{froot\_ xfer} m .. math:: - :label: 33.29) + :label: 33.29) CF_{livestem\_ xfer\_ mort} =CS_{livestem\_ xfer} m .. math:: - :label: 33.30) + :label: 33.30) CF_{deadstem\_ xfer\_ mort} =CS_{deadstem\_ xfer} m .. math:: - :label: 33.31) + :label: 33.31) CF_{livecroot\_ xfer\_ mort} =CS_{livecroot\_ xfer} m .. math:: - :label: 33.32) + :label: 33.32) CF_{deadcroot\_ xfer\_ mort} =CS_{deadcroot\_ xfer} m .. math:: - :label: 33.33) + :label: 33.33) CF_{gresp\_ xfer\_ mort} =CS_{gresp\_ xfer} m .. math:: - :label: 33.34) + :label: 33.34) NF_{leaf\_ xfer\_ mort} =NS_{leaf\_ xfer} m .. math:: - :label: 33.35) + :label: 33.35) NF_{froot\_ xfer\_ mort} =NS_{froot\_ xfer} m .. math:: - :label: 33.36) + :label: 33.36) NF_{livestem\_ xfer\_ mort} =NS_{livestem\_ xfer} m .. math:: - :label: 33.37) + :label: 33.37) NF_{deadstem\_ xfer\_ mort} =NS_{deadstem\_ xfer} m .. math:: - :label: 33.38) + :label: 33.38) NF_{livecroot\_ xfer\_ mort} =NS_{livecroot\_ xfer} m .. math:: - :label: 33.39) + :label: 33.39) NF_{deadcroot\_ xfer\_ mort} =NS_{deadcroot\_ xfer} m Mortality Fluxes Merged to the Column Level ------------------------------------------------ -Analogous to the treatment of litterfall fluxes, mortality fluxes -leaving the vegetation pools are merged to the column level according to -the weighted distribution of PFTs on the column (:math:`wcol_{p}` ), and -deposited in litter and coarse woody debris pools, which are defined at -the column level. Carbon and nitrogen fluxes from mortality of displayed -leaf and fine root into litter pools are calculated as +Analogous to the treatment of litterfall fluxes, mortality fluxes leaving the vegetation pools are merged to the column level according to the weighted distribution of PFTs on the column (:math:`wcol_{p}` ), and deposited in litter and coarse woody debris pools, which are defined at the column level. Carbon and nitrogen fluxes from mortality of displayed leaf and fine root into litter pools are calculated as .. math:: - :label: 33.40) + :label: 33.40) CF_{leaf\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{leaf\_ mort} f_{lab\_ leaf,p} wcol_{p} .. math:: - :label: 33.41) + :label: 33.41) CF_{leaf\_ mort,lit2} =\sum _{p=0}^{npfts}CF_{leaf\_ mort} f_{cel\_ leaf,p} wcol_{p} .. math:: - :label: 33.42) + :label: 33.42) CF_{leaf\_ mort,lit3} =\sum _{p=0}^{npfts}CF_{leaf\_ mort} f_{lig\_ leaf,p} wcol_{p} .. math:: - :label: 33.43) + :label: 33.43) CF_{froot\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{froot\_ mort} f_{lab\_ froot,p} wcol_{p} .. math:: - :label: 33.44) + :label: 33.44) CF_{froot\_ mort,lit2} =\sum _{p=0}^{npfts}CF_{froot\_ mort} f_{cel\_ froot,p} wcol_{p} .. math:: - :label: 33.45) + :label: 33.45) CF_{froot\_ mort,lit3} =\sum _{p=0}^{npfts}CF_{froot\_ mort} f_{lig\_ froot,p} wcol_{p} .. math:: - :label: 33.46) + :label: 33.46) NF_{leaf\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{leaf\_ mort} f_{lab\_ leaf,p} wcol_{p} .. math:: - :label: 33.47) + :label: 33.47) NF_{leaf\_ mort,lit2} =\sum _{p=0}^{npfts}NF_{leaf\_ mort} f_{cel\_ leaf,p} wcol_{p} .. math:: - :label: 33.48) + :label: 33.48) NF_{leaf\_ mort,lit3} =\sum _{p=0}^{npfts}NF_{leaf\_ mort} f_{lig\_ leaf,p} wcol_{p} .. math:: - :label: 33.49) + :label: 33.49) NF_{froot\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{froot\_ mort} f_{lab\_ froot,p} wcol_{p} .. math:: - :label: 33.50) + :label: 33.50) NF_{froot\_ mort,lit2} =\sum _{p=0}^{npfts}NF_{froot\_ mort} f_{cel\_ froot,p} wcol_{p} .. math:: - :label: 33.51) + :label: 33.51) NF_{froot\_ mort,lit3} =\sum _{p=0}^{npfts}NF_{froot\_ mort} f_{lig\_ froot,p} wcol_{p} . -where *lab* refers to labile, *cel* refers to cellulose, and *lig* -refers to lignin. Carbon and nitrogen mortality fluxes from displayed -live and dead stem and coarse root pools are merged to the column level -and deposited in the coarse woody debris (*cwd*) pools: +where *lab* refers to labile, *cel* refers to cellulose, and *lig* refers to lignin. Carbon and nitrogen mortality fluxes from displayed live and dead stem and coarse root pools are merged to the column level and deposited in the coarse woody debris (*cwd*) pools: .. math:: - :label: 33.52) + :label: 33.52) CF_{livestem\_ mort,cwd} =\sum _{p=0}^{npfts}CF_{livestem\_ mort} wcol_{p} .. math:: - :label: 33.53) + :label: 33.53) CF_{deadstem\_ mort,cwd} =\sum _{p=0}^{npfts}CF_{deadstem\_ mort} wcol_{p} .. math:: - :label: 33.54) + :label: 33.54) CF_{livecroot\_ mort,cwd} =\sum _{p=0}^{npfts}CF_{livecroot\_ mort} wcol_{p} .. math:: - :label: 33.55) + :label: 33.55) CF_{deadcroot\_ mort,cwd} =\sum _{p=0}^{npfts}CF_{deadcroot\_ mort} wcol_{p} .. math:: - :label: 33.56) + :label: 33.56) NF_{livestem\_ mort,cwd} =\sum _{p=0}^{npfts}NF_{livestem\_ mort} wcol_{p} .. math:: - :label: 33.57) + :label: 33.57) NF_{deadstem\_ mort,cwd} =\sum _{p=0}^{npfts}NF_{deadstem\_ mort} wcol_{p} .. math:: - :label: 33.58) + :label: 33.58) NF_{livecroot\_ mort,cwd} =\sum _{p=0}^{npfts}NF_{livecroot\_ mort} wcol_{p} .. math:: - :label: 33.59) + :label: 33.59) NF_{deadcroot\_ mort,cwd} =\sum _{p=0}^{npfts}NF_{deadcroot\_ mort} wcol_{p} -All vegetation storage and transfer pools for carbon and nitrogen are -assumed to exist as labile pools within the plant (e.g. as carbohydrate -stores, in the case of carbon pools). This assumption applies to storage -and transfer pools for both non-woody and woody tissues. The mortality -fluxes from these pools are therefore assumed to be deposited in the -labile litter pools (:math:`{CS}_{lit1}`, :math:`{NS}_{lit1}`), -after being merged to the column level. Carbon mortality fluxes out of -storage and transfer pools are: +All vegetation storage and transfer pools for carbon and nitrogen are assumed to exist as labile pools within the plant (e.g. as carbohydrate stores, in the case of carbon pools). This assumption applies to storage and transfer pools for both non-woody and woody tissues. The mortality fluxes from these pools are therefore assumed to be deposited in the labile litter pools (:math:`{CS}_{lit1}`, :math:`{NS}_{lit1}`), after being merged to the column level. Carbon mortality fluxes out of storage and transfer pools are: .. math:: - :label: 33.60) + :label: 33.60) CF_{leaf\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{leaf\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.61) + :label: 33.61) CF_{froot\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{froot\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.62) + :label: 33.62) CF_{livestem\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{livestem\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.63) + :label: 33.63) CF_{deadstem\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{deadstem\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.64) + :label: 33.64) CF_{livecroot\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{livecroot\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.65) + :label: 33.65) CF_{deadcroot\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{deadcroot\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.66) + :label: 33.66) CF_{gresp\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{gresp\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.67) + :label: 33.67) CF_{leaf\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{leaf\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.68) + :label: 33.68) CF_{froot\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{froot\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.69) + :label: 33.69) CF_{livestem\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{livestem\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.70) + :label: 33.70) CF_{deadstem\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{deadstem\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.71) + :label: 33.71) CF_{livecroot\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{livecroot\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.72) + :label: 33.72) CF_{deadcroot\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{deadcroot\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.73) + :label: 33.73) CF_{gresp\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}CF_{gresp\_ xfer\_ mort} wcol_{p} . -Nitrogen mortality fluxes out of storage and transfer pools, including -the storage pool for retranslocated nitrogen, are calculated as: +Nitrogen mortality fluxes out of storage and transfer pools, including the storage pool for retranslocated nitrogen, are calculated as: .. math:: - :label: 33.74) + :label: 33.74) NF_{leaf\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{leaf\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.75) + :label: 33.75) NF_{froot\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{froot\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.76) + :label: 33.76) NF_{livestem\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{livestem\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.77) + :label: 33.77) NF_{deadstem\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{deadstem\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.78) + :label: 33.78) NF_{livecroot\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{livecroot\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.79) + :label: 33.79) NF_{deadcroot\_ stor\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{deadcroot\_ stor\_ mort} wcol_{p} .. math:: - :label: 33.80) + :label: 33.80) NF_{retrans\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{retrans\_ mort} wcol_{p} .. math:: - :label: 33.81) + :label: 33.81) NF_{leaf\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{leaf\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.82) + :label: 33.82) NF_{froot\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{froot\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.83) + :label: 33.83) NF_{livestem\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{livestem\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.84) + :label: 33.84) NF_{deadstem\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{deadstem\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.85) + :label: 33.85) NF_{livecroot\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{livecroot\_ xfer\_ mort} wcol_{p} .. math:: - :label: 33.86) + :label: 33.86) NF_{deadcroot\_ xfer\_ mort,lit1} =\sum _{p=0}^{npfts}NF_{deadcroot\_ xfer\_ mort} wcol_{p} . - diff --git a/doc/source/tech_note/Plant_Respiration/CLM50_Tech_Note_Plant_Respiration.rst b/doc/source/tech_note/Plant_Respiration/CLM50_Tech_Note_Plant_Respiration.rst index 8ec6f1d1fb..588e7e0060 100644 --- a/doc/source/tech_note/Plant_Respiration/CLM50_Tech_Note_Plant_Respiration.rst +++ b/doc/source/tech_note/Plant_Respiration/CLM50_Tech_Note_Plant_Respiration.rst @@ -9,17 +9,12 @@ CLM5 includes changes to plant respiration including Autotrophic Respiration ---------------------------- -The model treats maintenance and growth respiration fluxes separately, -even though it is difficult to measure them as separate fluxes (Lavigne -and Ryan, 1997; Sprugel et al., 1995). Maintenance respiration is -defined as the carbon cost to support the metabolic activity of existing -live tissue, while growth respiration is defined as the additional -carbon cost for the synthesis of new growth. +The model treats maintenance and growth respiration fluxes separately, even though it is difficult to measure them as separate fluxes (Lavigne and Ryan, 1997; Sprugel et al., 1995). Maintenance respiration is defined as the carbon cost to support the metabolic activity of existing live tissue, while growth respiration is defined as the additional carbon cost for the synthesis of new growth. Maintenance Respiration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Atkin et al. (2016) propose a model for leaf respiration that is based on the leaf nitrogen content per unit area (:math:`NS_{narea}` (gN m :sup:`2` leaf), with an intercept parameter that is PFT dependant, and an acclimation term that depends upon the average temperature of the previous 10 day period :math:`t_{2m,10days}`, in Celsius. +Atkin et al. (2016) propose a model for leaf respiration that is based on the leaf nitrogen content per unit area (:math:`NS_{narea}` (gN m :sup:`2` leaf), with an intercept parameter that is PFT dependant, and an acclimation term that depends upon the average temperature of the previous 10 day period :math:`t_{2m,10days}`, in Celsius. .. math:: :label: 17.46) @@ -29,33 +24,28 @@ Atkin et al. (2016) propose a model for leaf respiration that is based on the le The temperature dependance of leaf maintenance (dark) respiration is described in Chapter :numref:`rst_Stomatal Resistance and Photosynthesis`. .. math:: - :label: 17.47) + :label: 17.47) CF_{mr\_ livestem} \_ =NS_{livestem} MR_{base} MR_{Q10} ^{(T_{2m} -20)/10} .. math:: - :label: 17.48) + :label: 17.48) CF_{mr\_ livecroot} \_ =NS_{livecroot} MR_{base} MR_{Q10} ^{(T_{2m} -20)/10} .. math:: - :label: 17.49) + :label: 17.49) CF_{mr\_ froot} \_ =\sum _{j=1}^{nlevsoi}NS_{froot} rootfr_{j} MR_{base} MR_{Q10} ^{(Ts_{j} -20)/10} -where :math:`MR_{q10}` (= 2.0) is -the temperature sensitivity for maintenance respiration, -:math:`T_{2m}` (:sup:`o`\ C) is the air temperature at 2m -height, :math:`Ts_{j}`* (:sup:`o`\ C) is the soil -temperature at level *j*, and :math:`rootfr_{j}` is the fraction -of fine roots distributed in soil level *j*. +where :math:`MR_{q10}` (= 2.0) is the temperature sensitivity for maintenance respiration, :math:`T_{2m}` (°C) is the air temperature at 2m height, :math:`Ts_{j}`* (°C) is the soil temperature at level *j*, and :math:`rootfr_{j}` is the fraction of fine roots distributed in soil level *j*. .. table:: Atkin leaf respiration model intercept values. - ======================== ============= + ======================== ============= Plant functional type :math:`i_{atkin}` - ======================== ============= - NET Temperate 1.499 + ======================== ============= + NET Temperate 1.499 NET Boreal 1.499 NDT Boreal 1.499 BET Tropical 1.756 @@ -65,53 +55,28 @@ of fine roots distributed in soil level *j*. BDT boreal 1.756 BES temperate 2.075 BDS temperate 2.075 - BDS boreal 2.075 + BDS boreal 2.075 C\ :sub:`3` arctic grass 2.196 C\ :sub:`3` grass 2.196 C\ :sub:`4` grass 2.196 - ======================== ============= + ======================== ============= -Note that, for woody vegetation, maintenance respiration costs are not -calculated for the dead stem and dead coarse root components. These -components are assumed to consist of dead xylem cells, with no metabolic -function. By separating the small live component of the woody tissue -(ray parenchyma, phloem, and sheathing lateral meristem cells) from the -larger fraction of dead woody tissue, it is reasonable to assume a -common base maintenance respiration rate for all live tissue types. +Note that, for woody vegetation, maintenance respiration costs are not calculated for the dead stem and dead coarse root components. These components are assumed to consist of dead xylem cells, with no metabolic function. By separating the small live component of the woody tissue (ray parenchyma, phloem, and sheathing lateral meristem cells) from the larger fraction of dead woody tissue, it is reasonable to assume a common base maintenance respiration rate for all live tissue types. The total maintenance respiration cost is then given as: .. math:: - :label: 17.50) + :label: 17.50) CF_{mr} =CF_{mr\_ leaf} +CF_{mr\_ froot} +CF_{mr\_ livestem} +CF_{mr\_ livecroot} . +.. _Growth Respiration: + Growth Respiration ^^^^^^^^^^^^^^^^^^^^^^^^^ -Growth respiration is calculated as a factor of 0.11 times the total -carbon allocation to new growth (:math:`CF_{growth}`, after allocating carbon for N acquisition, -Chapter :numref:`rst_FUN`.) on a given timestep, based on construction costs -for a range of woody and non-woody tissues, with estimates of the growth -respiration flux revised downswards following (Atkin et al. 2017). For new -carbon and nitrogen allocation that enters storage pools for subsequent -display, it is not clear what fraction of the associated growth -respiration should occur at the time of initial allocation, and what -fraction should occur later, at the time of display of new growth from -storage. Eddy covariance estimates of carbon fluxes in forest ecosystems -suggest that the growth respiration associated with transfer of -allocated carbon and nitrogen from storage into displayed tissue is not -significant (Churkina et al., 2003), and so it is assumed in CLM that -all of the growth respiration cost is incurred at the time of initial -allocation, regardless of the fraction of allocation that is displayed -immediately (i.e. regardless of the value of :math:`f_{cur}`, -section 13.5). This behavior is parameterized in such a way that if -future research suggests that some fraction of the growth respiration -cost should be incurred at the time of display from storage, a simple -parameter modification will effect the change. [1]_ +Growth respiration is calculated as a factor of 0.11 times the total carbon allocation to new growth (:math:`CF_{growth}`, after allocating carbon for N acquisition, Chapter :numref:`rst_FUN`.) on a given timestep, based on construction costs for a range of woody and non-woody tissues, with estimates of the growth respiration flux revised downswards following (Atkin et al. 2017). For new carbon and nitrogen allocation that enters storage pools for subsequent display, it is not clear what fraction of the associated growth respiration should occur at the time of initial allocation, and what fraction should occur later, at the time of display of new growth from storage. Eddy covariance estimates of carbon fluxes in forest ecosystems suggest that the growth respiration associated with transfer of allocated carbon and nitrogen from storage into displayed tissue is not significant (Churkina et al., 2003), and so it is assumed in CLM that all of the growth respiration cost is incurred at the time of initial allocation, regardless of the fraction of allocation that is displayed immediately (i.e. regardless of the value of :math:`f_{cur}`, section 13.5). This behavior is parameterized in such a way that if future research suggests that some fraction of the growth respiration cost should be incurred at the time of display from storage, a simple parameter modification will effect the change. [1]_ .. [1] - Parameter :math:`\text{grpnow}` in routines CNGResp and CNAllocation, currently set to 1.0, could be changed to a smaller - value to transfer some portion (1 - :math:`\text{grpnow}` ) of the growth respiration forward in time to occur at the time of growth - display from storage. + Parameter :math:`\text{grpnow}` in routines CNGResp and CNAllocation, currently set to 1.0, could be changed to a smaller value to transfer some portion (1 - :math:`\text{grpnow}` ) of the growth respiration forward in time to occur at the time of growth display from storage. diff --git a/doc/source/tech_note/Radiative_Fluxes/CLM50_Tech_Note_Radiative_Fluxes.rst b/doc/source/tech_note/Radiative_Fluxes/CLM50_Tech_Note_Radiative_Fluxes.rst index 5e368456ba..6bf7c0d9d3 100644 --- a/doc/source/tech_note/Radiative_Fluxes/CLM50_Tech_Note_Radiative_Fluxes.rst +++ b/doc/source/tech_note/Radiative_Fluxes/CLM50_Tech_Note_Radiative_Fluxes.rst @@ -3,11 +3,7 @@ Radiative Fluxes =================== -The net radiation at the surface is -:math:`\left(\vec{S}_{v} +\vec{S}_{g} \right)-\left(\vec{L}_{v} +\vec{L}_{g} \right)`, -where :math:`\vec{S}` is the net solar flux absorbed by the vegetation -(“v”) and the ground (“g”) and :math:`\vec{L}` is the net longwave flux -(positive toward the atmosphere) (W m\ :sup:`-2`). +The net radiation at the surface is :math:`\left(\vec{S}_{v} +\vec{S}_{g} \right)-\left(\vec{L}_{v} +\vec{L}_{g} \right)`, where :math:`\vec{S}` is the net solar flux absorbed by the vegetation ("v") and the ground ("g") and :math:`\vec{L}` is the net longwave flux (positive toward the atmosphere) (W m\ :sup:`-2`). .. _Solar Fluxes: @@ -17,35 +13,22 @@ Solar Fluxes :numref:`Figure Radiation Schematic` illustrates the direct beam and diffuse fluxes in the canopy. :math:`I\, \uparrow _{\Lambda }^{\mu }` and -:math:`I\, \uparrow _{\Lambda }` are the upward diffuse fluxes, per -unit incident direct beam and diffuse flux (section :numref:`Canopy Radiative Transfer`). +:math:`I\, \uparrow _{\Lambda }` are the upward diffuse fluxes, per unit incident direct beam and diffuse flux (section :numref:`Canopy Radiative Transfer`). :math:`I\, \downarrow _{\Lambda }^{\mu }` and -:math:`I\, \downarrow _{\Lambda }` \ are the downward diffuse fluxes -below the vegetation per unit incident direct beam and diffuse radiation -(section :numref:`Canopy Radiative Transfer`). The direct beam flux -transmitted through the canopy, per -unit incident flux, is :math:`e^{-K\left(L+S\right)}` . -:math:`\vec{I}_{\Lambda }^{\mu }` and :math:`\vec{I}_{\Lambda }^{}` -are the fluxes absorbed by the vegetation, per unit incident direct beam -and diffuse radiation (section :numref:`Canopy Radiative Transfer`). -:math:`\alpha _{g,\, \Lambda }^{\mu }` and -:math:`\alpha _{g,\, \Lambda }` are the direct beam and diffuse ground -albedos (section :numref:`Ground Albedos`). :math:`L` and :math:`S` are the exposed leaf area -index and stem area index (section :numref:`Phenology and vegetation burial by snow`). -:math:`K` is the optical -depth of direct beam per unit leaf and stem area (section :numref:`Canopy Radiative Transfer`). +:math:`I\, \downarrow _{\Lambda }` \ are the downward diffuse fluxes below the vegetation per unit incident direct beam and diffuse radiation (section :numref:`Canopy Radiative Transfer`). The direct beam flux transmitted through the canopy, per unit incident flux, is :math:`e^{-K\left(L+S\right)}`. +:math:`\vec{I}_{\Lambda }^{\mu }` and :math:`\vec{I}_{\Lambda }^{}` are the fluxes absorbed by the vegetation, per unit incident direct beam and diffuse radiation (section :numref:`Canopy Radiative Transfer`). +:math:`\alpha _{g,\, \Lambda }^{\mu }` and +:math:`\alpha _{g,\, \Lambda }` are the direct beam and diffuse ground albedos (section :numref:`Ground Albedos`). +:math:`L` and :math:`S` are the exposed leaf area index and stem area index (section :numref:`Phenology and vegetation burial by snow`). +:math:`K` is the optical depth of direct beam per unit leaf and stem area (section :numref:`Canopy Radiative Transfer`). .. _Figure Radiation Schematic: .. figure:: image1.png - Schematic diagram of (a) direct beam radiation, (b) diffuse - solar radiation, and (c) longwave radiation absorbed, transmitted, and - reflected by vegetation and ground. - -For clarity, terms involving :math:`T^{n+1} -T^{n}` are not shown in -(c). + Schematic diagram of (a) direct beam radiation, (b) diffuse solar radiation, and (c) longwave radiation absorbed, transmitted, and reflected by vegetation and ground. +For clarity, terms involving :math:`T^{n+1} -T^{n}` are not shown in (c). The total solar radiation absorbed by the vegetation and ground is @@ -59,13 +42,7 @@ The total solar radiation absorbed by the vegetation and ground is \begin{array}{l} {\vec{S}_{g} =\sum _{\Lambda }S_{atm} \, \downarrow _{\Lambda }^{\mu } e^{-K\left(L+S\right)} \left(1-\alpha _{g,\, \Lambda }^{\mu } \right) +} \\ {\qquad \left(S_{atm} \, \downarrow _{\Lambda }^{\mu } I\downarrow _{\Lambda }^{\mu } +S_{atm} \downarrow _{\Lambda } I\downarrow _{\Lambda } \right)\left(1-\alpha _{g,\, \Lambda } \right)} \end{array} -where :math:`S_{atm} \, \downarrow _{\Lambda }^{\mu }` and -:math:`S_{atm} \, \downarrow _{\Lambda }` are the incident direct beam -and diffuse solar fluxes (W m\ :sup:`-2`). For non-vegetated -surfaces, :math:`e^{-K\left(L+S\right)} =1`, -:math:`\overrightarrow{I}_{\Lambda }^{\mu } =\overrightarrow{I}_{\Lambda } =0`, -:math:`I\, \downarrow _{\Lambda }^{\mu } =0`, and -:math:`I\, \downarrow _{\Lambda } =1`, so that +where :math:`S_{atm} \, \downarrow _{\Lambda }^{\mu }` and :math:`S_{atm} \, \downarrow _{\Lambda }` are the incident direct beam and diffuse solar fluxes (W m\ :sup:`-2`). For non-vegetated surfaces, :math:`e^{-K\left(L+S\right)} =1`, :math:`\overrightarrow{I}_{\Lambda }^{\mu } =\overrightarrow{I}_{\Lambda } =0`, :math:`I\, \downarrow _{\Lambda }^{\mu } =0`, and :math:`I\, \downarrow _{\Lambda } =1`, so that .. math:: :label: 4.3 @@ -81,117 +58,67 @@ Solar radiation is conserved as where the latter term in parentheses is reflected solar radiation. -Photosynthesis and transpiration depend non-linearly on solar radiation, -via the light response of stomata. The canopy is treated as two leaves -(sunlit and shaded) and the solar radiation in the visible waveband -(:math:`<` 0.7 µm) absorbed by the vegetation is apportioned to the -sunlit and shaded leaves (section :numref:`Canopy Radiative Transfer`). -The absorbed photosynthetically -active (visible waveband) radiation averaged over the sunlit canopy (per -unit plant area) is +Photosynthesis and transpiration depend non-linearly on solar radiation, via the light response of stomata. The canopy is treated as two leaves (sunlit and shaded) and the solar radiation in the visible waveband (:math:`<` 0.7 µm) absorbed by the vegetation is apportioned to the sunlit and shaded leaves (section :numref:`Canopy Radiative Transfer`). The absorbed photosynthetically active (visible waveband) radiation averaged over the sunlit canopy (per unit plant area) is .. math:: :label: 4.5 - \phi ^{sun} ={\left(\vec{I}_{sun,vis}^{\mu } S_{atm} \downarrow _{vis}^{\mu } +\vec{I}_{sun,vis}^{} S_{atm} \downarrow _{vis}^{} \right)\mathord{\left/ {\vphantom {\left(\vec{I}_{sun,vis}^{\mu } S_{atm} \downarrow _{vis}^{\mu } +\vec{I}_{sun,vis}^{} S_{atm} \downarrow _{vis}^{} \right) L^{sun} }} \right. \kern-\nulldelimiterspace} L^{sun} } + \phi ^{sun} ={\left(\vec{I}_{sun,vis}^{\mu } S_{atm} \downarrow _{vis}^{\mu } +\vec{I}_{sun,vis}^{} S_{atm} \downarrow _{vis}^{} \right)\mathord{\left/ {\vphantom {\left(\vec{I}_{sun,vis}^{\mu } S_{atm} \downarrow _{vis}^{\mu } +\vec{I}_{sun,vis}^{} S_{atm} \downarrow _{vis}^{} \right) L^{sun} }} \right.} L^{sun} } -and the absorbed radiation for the average shaded leaf (per unit plant -area) is +and the absorbed radiation for the average shaded leaf (per unit plant area) is .. math:: :label: 4.6 - \phi ^{sha} ={\left(\vec{I}_{sha,vis}^{\mu } S_{atm} \downarrow _{vis}^{\mu } +\vec{I}_{sha,vis}^{} S_{atm} \downarrow _{vis}^{} \right)\mathord{\left/ {\vphantom {\left(\vec{I}_{sha,vis}^{\mu } S_{atm} \downarrow _{vis}^{\mu } +\vec{I}_{sha,vis}^{} S_{atm} \downarrow _{vis}^{} \right) L^{sha} }} \right. \kern-\nulldelimiterspace} L^{sha} } + \phi ^{sha} ={\left(\vec{I}_{sha,vis}^{\mu } S_{atm} \downarrow _{vis}^{\mu } +\vec{I}_{sha,vis}^{} S_{atm} \downarrow _{vis}^{} \right)\mathord{\left/ {\vphantom {\left(\vec{I}_{sha,vis}^{\mu } S_{atm} \downarrow _{vis}^{\mu } +\vec{I}_{sha,vis}^{} S_{atm} \downarrow _{vis}^{} \right) L^{sha} }} \right.} L^{sha} } -with :math:`L^{sun}` and :math:`L^{sha}` the sunlit and shaded plant -area index, respectively. The sunlit plant area index is +with :math:`L^{sun}` and :math:`L^{sha}` the sunlit and shaded plant area index, respectively. The sunlit plant area index is .. math:: :label: 4.7 L^{sun} =\frac{1-e^{-K(L+S)} }{K} -and the shaded leaf area index is :math:`L^{sha} =(L+S)-L^{sun}` . In -calculating :math:`L^{sun}` , +and the shaded leaf area index is :math:`L^{sha} =(L+S)-L^{sun}`. In calculating :math:`L^{sun}`, .. math:: :label: 4.8 K=\frac{G\left(\mu \right)}{\mu } -where :math:`G\left(\mu \right)` and :math:`\mu` are parameters in the -two-stream approximation (section :numref:`Canopy Radiative Transfer`). - -The model uses the two-stream approximation to calculate radiative -transfer of direct and diffuse radiation through a canopy that is -differentiated into leaves that are sunlit and those that are shaded -(section :numref:`Canopy Radiative Transfer`). The two-stream equations -are integrated over all plant -area (leaf and stem area) in the canopy. The model has an optional -(though not supported) multi-layer canopy, as described by -:ref:`Bonan et al. (2012) `. -The multi-layer model is only intended to address the -non-linearity of light profiles, photosynthesis, and stomatal -conductance in the plant canopy. - -In the multi-layer canopy, canopy-integrated radiative fluxes are -calculated from the two-stream approximation. The model additionally -derives the light profile with depth in the canopy by taking the -derivatives of the absorbed radiative fluxes with respect to plant area -index (:math:`L'=L+S`) and evaluating them incrementally through the -canopy with cumulative plant area index (:math:`x`). The terms -:math:`{d\vec{I}_{sun,\Lambda }^{\mu } (x)\mathord{\left/ {\vphantom {d\vec{I}_{sun,\Lambda }^{\mu } (x) dL'}} \right. \kern-\nulldelimiterspace} dL'}` -and -:math:`{d\vec{I}_{sun,\Lambda }^{} (x)\mathord{\left/ {\vphantom {d\vec{I}_{sun,\Lambda }^{} (x) dL'}} \right. \kern-\nulldelimiterspace} dL'}` -are the direct beam and diffuse solar radiation, respectively, absorbed -by the sunlit fraction of the canopy (per unit plant area) at a depth -defined by the cumulative plant area index :math:`x`; -:math:`{d\vec{I}_{sha,\Lambda }^{\mu } (x)\mathord{\left/ {\vphantom {d\vec{I}_{sha,\Lambda }^{\mu } (x) dL'}} \right. \kern-\nulldelimiterspace} dL'}` \ and -:math:`{d\vec{I}_{sha,\Lambda }^{} (x)\mathord{\left/ {\vphantom {d\vec{I}_{sha,\Lambda }^{} (x) dL'}} \right. \kern-\nulldelimiterspace} dL'}` -are the corresponding fluxes for the shaded fraction of the canopy at -depth :math:`x`. These fluxes are normalized by the sunlit or shaded -fraction at depth :math:`x`, defined by -:math:`f_{sun} =\exp \left(-Kx\right)`, to give fluxes per unit sunlit -or shaded plant area at depth :math:`x`. +where :math:`G\left(\mu \right)` and :math:`\mu` are parameters in the two-stream approximation (section :numref:`Canopy Radiative Transfer`). + +The model uses the two-stream approximation to calculate radiative transfer of direct and diffuse radiation through a canopy that is differentiated into leaves that are sunlit and those that are shaded (section :numref:`Canopy Radiative Transfer`). The two-stream equations are integrated over all plant area (leaf and stem area) in the canopy. The model has an optional (though not supported) multi-layer canopy, as described by :ref:`Bonan et al. (2012) `. The multi-layer model is only intended to address the non-linearity of light profiles, photosynthesis, and stomatal conductance in the plant canopy. + +In the multi-layer canopy, canopy-integrated radiative fluxes are calculated from the two-stream approximation. The model additionally derives the light profile with depth in the canopy by taking the derivatives of the absorbed radiative fluxes with respect to plant area index (:math:`L'=L+S`) and evaluating them incrementally through the canopy with cumulative plant area index (:math:`x`). The terms :math:`{d\vec{I}_{sun,\Lambda }^{\mu } (x)\mathord{\left/ {\vphantom {d\vec{I}_{sun,\Lambda }^{\mu } (x) dL'}} \right.} dL'}` and :math:`{d\vec{I}_{sun,\Lambda }^{} (x)\mathord{\left/ {\vphantom {d\vec{I}_{sun,\Lambda }^{} (x) dL'}} \right.} dL'}` are the direct beam and diffuse solar radiation, respectively, absorbed by the sunlit fraction of the canopy (per unit plant area) at a depth defined by the cumulative plant area index :math:`x`; :math:`{d\vec{I}_{sha,\Lambda }^{\mu } (x)\mathord{\left/ {\vphantom {d\vec{I}_{sha,\Lambda }^{\mu } (x) dL'}} \right.} dL'}` \ and :math:`{d\vec{I}_{sha,\Lambda }^{} (x)\mathord{\left/ {\vphantom {d\vec{I}_{sha,\Lambda }^{} (x) dL'}} \right.} dL'}` are the corresponding fluxes for the shaded fraction of the canopy at depth :math:`x`. These fluxes are normalized by the sunlit or shaded fraction at depth :math:`x`, defined by :math:`f_{sun} =\exp \left(-Kx\right)`, to give fluxes per unit sunlit or shaded plant area at depth :math:`x`. .. _Longwave Fluxes: Longwave Fluxes ------------------- -The net longwave radiation (W m\ :sup:`-2`) (positive toward the -atmosphere) at the surface is +The net longwave radiation (W m\ :sup:`-2`) (positive toward the atmosphere) at the surface is .. math:: :label: 4.9 \vec{L}=L\, \uparrow -L_{atm} \, \downarrow -where :math:`L\, \uparrow` is the upward longwave radiation from the -surface and :math:`L_{atm} \, \downarrow` is the downward atmospheric -longwave radiation (W m\ :sup:`-2`). The radiative temperature -:math:`T_{rad}` (K) is defined from the upward longwave radiation as +where :math:`L\, \uparrow` is the upward longwave radiation from the surface and :math:`L_{atm} \, \downarrow` is the downward atmospheric longwave radiation (W m\ :sup:`-2`). The radiative temperature :math:`T_{rad}` (K) is defined from the upward longwave radiation as .. math:: :label: 4.10 - T_{rad} =\left(\frac{L\, \uparrow }{\sigma } \right)^{{1\mathord{\left/ {\vphantom {1 4}} \right. \kern-\nulldelimiterspace} 4} } + T_{rad} =\left(\frac{L\, \uparrow }{\sigma } \right)^{{1\mathord{\left/ {\vphantom {1 4}} \right.} 4} } -where :math:`\sigma` is the Stefan-Boltzmann constant (W\ m\ :sup:`-2` K\ :sup:`-4`) (:numref:`Table Physical constants`). With reference to -:numref:`Figure Radiation Schematic`, the upward longwave radiation from the surface to the atmosphere is +where :math:`\sigma` is the Stefan-Boltzmann constant (W\ m\ :sup:`-2` K\ :sup:`-4`) (:numref:`Table Physical constants`). With reference to :numref:`Figure Radiation Schematic`, the upward longwave radiation from the surface to the atmosphere is .. math:: :label: 4.11 \begin{array}{l} {L\, \uparrow =\delta _{veg} L_{vg} \, \uparrow +\left(1-\delta _{veg} \right)\left(1-\varepsilon _{g} \right)L_{atm} \, \downarrow +} \\ {\qquad \left(1-\delta _{veg} \right)\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{4} +4\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{3} \left(T_{g}^{n+1} -T_{g}^{n} \right)} \end{array} -where :math:`L_{vg} \, \uparrow` is the upward longwave radiation from -the vegetation/soil system for exposed leaf and stem area -:math:`L+S\ge 0.05`, :math:`\delta _{veg}` is a step function and is -zero for :math:`L+S<0.05` and one otherwise, :math:`\varepsilon _{g}` -is the ground emissivity, and :math:`T_{g}^{n+1}` and -:math:`T_{g}^{n}` are the snow/soil surface temperatures at the current -and previous time steps, respectively (:ref:`rst_Soil and Snow Temperatures`). +where :math:`L_{vg} \, \uparrow` is the upward longwave radiation from the vegetation/soil system for exposed leaf and stem area :math:`L+S\ge 0.05`, :math:`\delta _{veg}` is a step function and is zero for :math:`L+S<0.05` and one otherwise, :math:`\varepsilon _{g}` is the ground emissivity, and :math:`T_{g}^{n+1}` and :math:`T_{g}^{n}` are the snow/soil surface temperatures at the current and previous time steps, respectively (:ref:`rst_Soil and Snow Temperatures`). For non-vegetated surfaces, the above equation reduces to @@ -200,14 +127,9 @@ For non-vegetated surfaces, the above equation reduces to L\, \uparrow =\left(1-\varepsilon _{g} \right)L_{atm} \, \downarrow +\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{4} +4\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{3} \left(T_{g}^{n+1} -T_{g}^{n} \right) -where the first term is the atmospheric longwave radiation reflected by -the ground, the second term is the longwave radiation emitted by the -ground, and the last term is the increase (decrease) in longwave -radiation emitted by the ground due to an increase (decrease) in ground -temperature. +where the first term is the atmospheric longwave radiation reflected by the ground, the second term is the longwave radiation emitted by the ground, and the last term is the increase (decrease) in longwave radiation emitted by the ground due to an increase (decrease) in ground temperature. -For vegetated surfaces, the upward longwave radiation from the surface -reduces to +For vegetated surfaces, the upward longwave radiation from the surface reduces to .. math:: :label: 4.13 @@ -221,25 +143,7 @@ where \begin{array}{l} {L_{vg} \, \uparrow =\left(1-\varepsilon _{g} \right)\left(1-\varepsilon _{v} \right)\left(1-\varepsilon _{v} \right)L_{atm} \, \downarrow } \\ {\qquad \qquad +\varepsilon _{v} \left[1+\left(1-\varepsilon _{g} \right)\left(1-\varepsilon _{v} \right)\right]\sigma \left(T_{v}^{n} \right)^{3} \left[T_{v}^{n} +4\left(T_{v}^{n+1} -T_{v}^{n} \right)\right]} \\ {\qquad \qquad +\varepsilon _{g} \left(1-\varepsilon _{v} \right)\sigma \left(T_{g}^{n} \right)^{4} } \\ {\qquad =\left(1-\varepsilon _{g} \right)\left(1-\varepsilon _{v} \right)\left(1-\varepsilon _{v} \right)L_{atm} \, \downarrow } \\ {\qquad \qquad +\varepsilon _{v} \sigma \left(T_{v}^{n} \right)^{4} } \\ {\qquad \qquad +\varepsilon _{v} \left(1-\varepsilon _{g} \right)\left(1-\varepsilon _{v} \right)\sigma \left(T_{v}^{n} \right)^{4} } \\ {\qquad \qquad +4\varepsilon _{v} \sigma \left(T_{v}^{n} \right)^{3} \left(T_{v}^{n+1} -T_{v}^{n} \right)} \\ {\qquad \qquad +4\varepsilon _{v} \left(1-\varepsilon _{g} \right)\left(1-\varepsilon _{v} \right)\sigma \left(T_{v}^{n} \right)^{3} \left(T_{v}^{n+1} -T_{v}^{n} \right)} \\ {\qquad \qquad +\varepsilon _{g} \left(1-\varepsilon _{v} \right)\sigma \left(T_{g}^{n} \right)^{4} } \end{array} -where :math:`\varepsilon _{v}` is the vegetation emissivity and -:math:`T_{v}^{n+1}` and :math:`T_{v}^{n}` are the vegetation -temperatures at the current and previous time steps, respectively -(:ref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`). -The first term in the equation above is the atmospheric -longwave radiation that is transmitted through the canopy, reflected by -the ground, and transmitted through the canopy to the atmosphere. The -second term is the longwave radiation emitted by the canopy directly to -the atmosphere. The third term is the longwave radiation emitted -downward from the canopy, reflected by the ground, and transmitted -through the canopy to the atmosphere. The fourth term is the increase -(decrease) in longwave radiation due to an increase (decrease) in canopy -temperature that is emitted by the canopy directly to the atmosphere. -The fifth term is the increase (decrease) in longwave radiation due to -an increase (decrease) in canopy temperature that is emitted downward -from the canopy, reflected from the ground, and transmitted through the -canopy to the atmosphere. The last term is the longwave radiation -emitted by the ground and transmitted through the canopy to the -atmosphere. +where :math:`\varepsilon _{v}` is the vegetation emissivity and :math:`T_{v}^{n+1}` and :math:`T_{v}^{n}` are the vegetation temperatures at the current and previous time steps, respectively (:ref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`). The first term in the equation above is the atmospheric longwave radiation that is transmitted through the canopy, reflected by the ground, and transmitted through the canopy to the atmosphere. The second term is the longwave radiation emitted by the canopy directly to the atmosphere. The third term is the longwave radiation emitted downward from the canopy, reflected by the ground, and transmitted through the canopy to the atmosphere. The fourth term is the increase (decrease) in longwave radiation due to an increase (decrease) in canopy temperature that is emitted by the canopy directly to the atmosphere. The fifth term is the increase (decrease) in longwave radiation due to an increase (decrease) in canopy temperature that is emitted downward from the canopy, reflected from the ground, and transmitted through the canopy to the atmosphere. The last term is the longwave radiation emitted by the ground and transmitted through the canopy to the atmosphere. The upward longwave radiation from the ground is @@ -248,59 +152,42 @@ The upward longwave radiation from the ground is L_{g} \, \uparrow =\left(1-\varepsilon _{g} \right)L_{v} \, \downarrow +\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{4} -where :math:`L_{v} \, \downarrow` is the downward longwave radiation -below the vegetation +where :math:`L_{v} \, \downarrow` is the downward longwave radiation below the vegetation .. math:: :label: 4.16 L_{v} \, \downarrow =\left(1-\varepsilon _{v} \right)L_{atm} \, \downarrow +\varepsilon _{v} \sigma \left(T_{v}^{n} \right)^{4} +4\varepsilon _{v} \sigma \left(T_{v}^{n} \right)^{3} \left(T_{v}^{n+1} -T_{v}^{n} \right). -The net longwave radiation flux for the ground is (positive toward the -atmosphere) +The net longwave radiation flux for the ground is (positive toward the atmosphere) .. math:: :label: 4.17 \vec{L}_{g} =\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{4} -\delta _{veg} \varepsilon _{g} L_{v} \, \downarrow -\left(1-\delta _{veg} \right)\varepsilon _{g} L_{atm} \, \downarrow . -The above expression for :math:`\vec{L}_{g}` is the net longwave -radiation forcing that is used in the soil temperature calculation -(:ref:`rst_Soil and Snow Temperatures`). Once updated soil -temperatures have been obtained, the term -:math:`4\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{3} \left(T_{g}^{n+1} -T_{g}^{n} \right)` -is added to :math:`\vec{L}_{g}` to calculate the ground heat flux -(section :numref:`Update of Ground Sensible and Latent Heat Fluxes`) +The above expression for :math:`\vec{L}_{g}` is the net longwave radiation forcing that is used in the soil temperature calculation (:ref:`rst_Soil and Snow Temperatures`). Once updated soil temperatures have been obtained, the term :math:`4\varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{3} \left(T_{g}^{n+1} -T_{g}^{n} \right)` is added to :math:`\vec{L}_{g}` to calculate the ground heat flux (section :numref:`Update of Ground Sensible and Latent Heat Fluxes`) -The net longwave radiation flux for vegetation is (positive toward the -atmosphere) +The net longwave radiation flux for vegetation is (positive toward the atmosphere) .. math:: :label: 4.18 \vec{L}_{v} =\left[2-\varepsilon _{v} \left(1-\varepsilon _{g} \right)\right]\varepsilon _{v} \sigma \left(T_{v} \right)^{4} -\varepsilon _{v} \varepsilon _{g} \sigma \left(T_{g}^{n} \right)^{4} -\varepsilon _{v} \left[1+\left(1-\varepsilon _{g} \right)\left(1-\varepsilon _{v} \right)\right]L_{atm} \, \downarrow . -These equations assume that absorptivity equals emissivity. The -emissivity of the ground is +These equations assume that absorptivity equals emissivity. The emissivity of the ground is .. math:: :label: 4.19 \varepsilon _{g} =\varepsilon _{soi} \left(1-f_{sno} \right)+\varepsilon _{sno} f_{sno} -where :math:`\varepsilon _{soi} =0.96` for soil, 0.97 for glacier, -:math:`\varepsilon _{sno} =0.97`, and :math:`f_{sno}` -is the fraction of ground covered by snow -(section :numref:`Snow Covered Area Fraction`). The -vegetation emissivity is +where :math:`\varepsilon _{soi} =0.96` for soil, 0.97 for glacier, :math:`\varepsilon _{sno} =0.97`, and :math:`f_{sno}` is the fraction of ground covered by snow (section :numref:`Snow Covered Area Fraction`). The vegetation emissivity is .. math:: :label: 4.20 - \varepsilon _{v} =1-e^{-{\left(L+S\right)\mathord{\left/ {\vphantom {\left(L+S\right) \bar{\mu }}} \right. \kern-\nulldelimiterspace} \bar{\mu }} } + \varepsilon _{v} =1-e^{-{\left(L+S\right)\mathord{\left/ {\vphantom {\left(L+S\right) \bar{\mu }}} \right.} \bar{\mu }} } -where :math:`L` and :math:`S` are the leaf and stem area indices -(section :numref:`Phenology and vegetation burial by snow`) and -:math:`\bar{\mu }=1` is the average inverse optical -depth for longwave radiation. +where :math:`L` and :math:`S` are the leaf and stem area indices (section :numref:`Phenology and vegetation burial by snow`) and :math:`\bar{\mu }=1` is the average inverse optical depth for longwave radiation. diff --git a/doc/source/tech_note/References/CLM50_Tech_Note_References.rst b/doc/source/tech_note/References/CLM50_Tech_Note_References.rst index 4bd7a2cf9b..b824f705bd 100644 --- a/doc/source/tech_note/References/CLM50_Tech_Note_References.rst +++ b/doc/source/tech_note/References/CLM50_Tech_Note_References.rst @@ -5,88 +5,59 @@ References .. _Aberetal1990: -Aber, J.D., Melillo, J.M. and McClaugherty, C.A., 1990. Predicting -long-term patterns of mass loss, nitrogen dynamics, and soil organic -matter formation from initial fime litter chemistry in temperate forest -ecosystems. Canadian Journal of Botany, 68: 2201-2208. +Aber, J.D., Melillo, J.M. and McClaugherty, C.A., 1990. Predicting long-term patterns of mass loss, nitrogen dynamics, and soil organic matter formation from initial fime litter chemistry in temperate forest ecosystems. Canadian Journal of Botany, 68: 2201-2208. .. _Aberetal2003: -Aber, J.D., Goodale, C.L., Ollinger, S.V., Smith, M.-L., Magill, A.H., -Martin, M.E., Hallett, R.A., and Stoddard, J.L. 2003. Is nitrogen -deposition altering the nitrogen status of northeastern forests? -BioScience 53:375-389. +Aber, J.D., Goodale, C.L., Ollinger, S.V., Smith, M.-L., Magill, A.H., Martin, M.E., Hallett, R.A., and Stoddard, J.L. 2003. Is nitrogen deposition altering the nitrogen status of northeastern forests? BioScience 53:375-389. .. _Alietal2016: -Ali, A. A., C. Xu, A. Rogers, R. A. Fisher, S. D. Wullschleger, E. Massoud, J. A. Vrugt, J. D. Muss, N. McDowell, -and J. Fisher, 2016: A global scale mechanistic model of -photosynthetic capacity (LUNA V1. 0). Geosci. Mod. Dev., 9:587-606. +Ali, A. A., C. Xu, A. Rogers, R. A. Fisher, S. D. Wullschleger, E. Massoud, J. A. Vrugt, J. D. Muss, N. McDowell, and J. Fisher, 2016: A global scale mechanistic model of photosynthetic capacity (LUNA V1. 0). Geosci. Mod. Dev., 9:587-606. .. _Allenetal2005: -Allen, C.B., Will, R.E., and Jacobson, M.A. 2005. Production efficiency -and radiation use efficiency of four tree species receiving irrigation -and fertilization. Forest Science 51:556-569. +Allen, C.B., Will, R.E., and Jacobson, M.A. 2005. Production efficiency and radiation use efficiency of four tree species receiving irrigation and fertilization. Forest Science 51:556-569. .. _Anderson1976: -Anderson, E.A. 1976. A point energy and mass balance model of a snow -cover. NOAA Technical Report NWS 19, Office of Hydrology, National -Weather Service, Silver Spring, MD. +Anderson, E.A. 1976. A point energy and mass balance model of a snow cover. NOAA Technical Report NWS 19, Office of Hydrology, National Weather Service, Silver Spring, MD. .. _Andréetal1986: -André, J.-C., Goutorbe, J.-P., and Perrier, A. 1986. HAPEX-MOBILHY: A -hydrologic atmosphere experiment for the study of water budget and -evaporation flux at the climatic scale. Bull. Amer. Meteor. Soc. -67:138-144. +André, J.-C., Goutorbe, J.-P., and Perrier, A. 1986. HAPEX-MOBILHY: A hydrologic atmosphere experiment for the study of water budget and evaporation flux at the climatic scale. Bull. Amer. Meteor. Soc. 67:138-144. .. _AndrénPaustian1987: -Andrén, O. and Paustian, K., 1987. Barley straw decomposition in the -field: a comparison of models. Ecology 68:1190-1200. +Andrén, O. and Paustian, K., 1987. Barley straw decomposition in the field: a comparison of models. Ecology 68:1190-1200. .. _ArahStephen1998: -Arah, J.R.M. and Stephen, K.D., 1998. A model of the processes leading -to methane emission from peatland. Atmos. Environ. 32:3257-3264. +Arah, J.R.M. and Stephen, K.D., 1998. A model of the processes leading to methane emission from peatland. Atmos. Environ. 32:3257-3264. .. _ArahVinten1995: -Arah, J. and Vinten, A., 1995. Simplified models of anoxia and -denitrification in aggregated and simple-structured soils. European -Journal of Soil Science 46:507-517. +Arah, J. and Vinten, A., 1995. Simplified models of anoxia and denitrification in aggregated and simple-structured soils. European Journal of Soil Science 46:507-517. .. _Arendtetal2012: -Arendt, A., et al. 2012. Randolph Glacier Inventory: A Dataset of Global -Glacier Outlines Version: 1.0, Global Land Ice Measurements from Space, -Boulder Colorado, USA. Digital Media. +Arendt, A., et al. 2012. Randolph Glacier Inventory: A Dataset of Global Glacier Outlines Version: 1.0, Global Land Ice Measurements from Space, Boulder Colorado, USA. Digital Media. .. _AroraBoer2005: -Arora, V.K. and Boer, G.J. 2005. Fire as an interactive component of -dynamic vegetation models. J. Geophys. Res. 110:G02008. -DOI:10.1029/2005JG000042. +Arora, V.K. and Boer, G.J. 2005. Fire as an interactive component of dynamic vegetation models. J. Geophys. Res. 110:G02008. DOI:10.1029/2005JG000042. .. _Arya2001: -Arya, S.P. 2001. Introduction to Meteorology. Academic Press, San Diego, -CA. +Arya, S.P. 2001. Introduction to Meteorology. Academic Press, San Diego, CA. .. _Asneretal1998: -Asner, G.P., Wessman, C.A., Schimel, D.S., and Archer, S. 1998. -Variability in leaf and litter optical properties: implications for BRDF -model inversions using AVHRR, MODIS, and MISR. Remote Sens. Environ. -63:243-257. +Asner, G.P., Wessman, C.A., Schimel, D.S., and Archer, S. 1998. Variability in leaf and litter optical properties: implications for BRDF model inversions using AVHRR, MODIS, and MISR. Remote Sens. Environ. 63:243-257. .. _AxelssonAxelsson1986: -Axelsson, E., and Axelsson, B. 1986. Changes in carbon allocation -patterns in spruce and pine trees following irrigation and -fertilization. Tree Phys. 2:189-204. +Axelsson, E., and Axelsson, B. 1986. Changes in carbon allocation patterns in spruce and pine trees following irrigation and fertilization. Tree Phys. 2:189-204. .. _Atkin2016: @@ -94,139 +65,99 @@ Atkin OK, Bloomfield KJ, Reich PB, Tjoelker MG, Asner GP, Bonal D et al (2015) G .. _Atkin2017: -Leaf Respiration in Terrestrial Biosphere Models. In Plant Respiration: Metabolic Fluxes and Carbon Balance, Advances -in Photosynthesis and Respiration 43, G. Tcherkez, J. Ghashghaie (eds.) Springer International Publishing AG 2017 +Leaf Respiration in Terrestrial Biosphere Models. In Plant Respiration: Metabolic Fluxes and Carbon Balance, Advances in Photosynthesis and Respiration 43, G. Tcherkez, J. Ghashghaie (eds.) Springer International Publishing AG 2017 .. _BadgerandDirmeyer2015: -Badger, A.M., and Dirmeyer, P.A., 2015. Climate response to Amazon forest -replacement by heterogeneous crop cover. Hydrol. Earth. Syst. Sci. 19:4547- -4557. +Badger, A.M., and Dirmeyer, P.A., 2015. Climate response to Amazon forest replacement by heterogeneous crop cover. Hydrol. Earth. Syst. Sci. 19:4547- 4557. .. _Bairdetal2004: -Baird, A.J., Beckwith, C.W., Waldron, S. and Waddington, J.M., 2004. -Ebullition of methane-containing gas bubbles from near-surface Sphagnum -peat. Geophys. Res. Lett. 31. DOI:10.1029/2004GL021157. +Baird, A.J., Beckwith, C.W., Waldron, S. and Waddington, J.M., 2004. Ebullition of methane-containing gas bubbles from near-surface Sphagnum peat. Geophys. Res. Lett. 31. DOI:10.1029/2004GL021157. .. _Baldocchietal2001: -Baldocchi, D., et al. 2001. FLUXNET: A new tool to study the temporal -and spatial variability of ecosystem-scale carbon dioxide, water vapor, -and energy flux densities. Bull. Amer. Meteor. Soc. 82:2415-2433. +Baldocchi, D., et al. 2001. FLUXNET: A new tool to study the temporal and spatial variability of ecosystem-scale carbon dioxide, water vapor, and energy flux densities. Bull. Amer. Meteor. Soc. 82:2415-2433. .. _Barbottinetal2005: -Barbottin, A., Lecomte, C., Bouchard, C., and Jeuffroy, M.-H. 2005. -Nitrogen remobilization during grain filling in wheat: Genotypic and -environmental effects. Crop Sci. 45:1141-1150. +Barbottin, A., Lecomte, C., Bouchard, C., and Jeuffroy, M.-H. 2005. Nitrogen remobilization during grain filling in wheat: Genotypic and environmental effects. Crop Sci. 45:1141-1150. .. _Batjes2006: -Batjes, N.H., 2006. ISRIC-WISE derived soil properties on a 5 by 5 -arc-minutes global grid. Report 2006/02 (available through : -http://www.isric.org) +Batjes, N.H., 2006. ISRIC-WISE derived soil properties on a 5 by 5 arc-minutes global grid. Report 2006/02 (available through : http://www.isric.org) .. _Berger1978a: -Berger, A.L. 1978a. Long-term variations of daily insolation and -quaternary climatic changes. J. Atmos. Sci. 35:2362-2367. +Berger, A.L. 1978a. Long-term variations of daily insolation and quaternary climatic changes. J. Atmos. Sci. 35:2362-2367. .. _Berger1978b: -Berger, A.L. 1978b. A simple algorithm to compute long-term variations -of daily or monthly insolation. Contribution de l’Institut d’Astronomie -et de Géophysique, Université Catholique de Louvain, Louvain-la-Neuve, -No. 18. +Berger, A.L. 1978b. A simple algorithm to compute long-term variations of daily or monthly insolation. Contribution de l'Institut d'Astronomie et de Géophysique, Université Catholique de Louvain, Louvain-la-Neuve, No. 18. .. _Bergeretal1993: -Berger, A., Loutre, M.-F., and Tricot, C. 1993. Insolation and Earth’s -orbital periods. J. Geophys. Res. 98:10341-10362. +Berger, A., Loutre, M.-F., and Tricot, C. 1993. Insolation and Earth's orbital periods. J. Geophys. Res. 98:10341-10362. .. _BerkowitzBalberg1992: -Berkowitz, B., and Balberg, I. 1992. Percolation approach to the problem -of hydraulic conductivity in porous media. Transport in Porous Media -9:275–286. +Berkowitz, B., and Balberg, I. 1992. Percolation approach to the problem of hydraulic conductivity in porous media. Transport in Porous Media 9:275–286. .. _BevenKirkby1979: -Beven, K.J., and Kirkby, M.J. 1979. A physically based variable -contributing area model of basin hydrology. Hydrol. Sci. Bull. 24:43-69. +Beven, K.J., and Kirkby, M.J. 1979. A physically based variable contributing area model of basin hydrology. Hydrol. Sci. Bull. 24:43-69. .. _BohrenHuffman1983: -Bohren, C. F., and Huffman, D. R. 1983. Absorption and scattering of -light by small particles. John Wiley & Sons, New York, NY. +Bohren, C. F., and Huffman, D. R. 1983. Absorption and scattering of light by small particles. John Wiley & Sons, New York, NY. .. _Bonan1996: -Bonan, G.B. 1996. A land surface model (LSM version 1.0) for ecological, -hydrological, and atmospheric studies: Technical description and user’s -guide. NCAR Technical Note NCAR/TN-417+STR, National Center for -Atmospheric Research, Boulder, CO, 150 pp. +Bonan, G.B. 1996. A land surface model (LSM version 1.0) for ecological, hydrological, and atmospheric studies: Technical description and user's guide. NCAR Technical Note NCAR/TN-417+STR, National Center for Atmospheric Research, Boulder, CO, 150 pp. .. _Bonan1998: -Bonan, G.B. 1998. The land surface climatology of the NCAR Land Surface -Model coupled to the NCAR Community Climate Model. J. Climate -11:1307-1326. +Bonan, G.B. 1998. The land surface climatology of the NCAR Land Surface Model coupled to the NCAR Community Climate Model. J. Climate 11:1307-1326. .. _Bonan2002: -Bonan, G.B. 2002. Ecological Climatology: Concepts and Applications. -Cambridge University Press. +Bonan, G.B. 2002. Ecological Climatology: Concepts and Applications. Cambridge University Press. .. _Bonanetal2002a: -Bonan, G.B., Oleson, K.W., Vertenstein, M., Levis, S., Zeng, X., Dai, -Y., Dickinson, R.E., and Yang, Z.-L. 2002a. The land surface climatology -of the Community Land Model coupled to the NCAR Community Climate Model. -J. Climate 15: 3123-3149. +Bonan, G.B., Oleson, K.W., Vertenstein, M., Levis, S., Zeng, X., Dai, Y., Dickinson, R.E., and Yang, Z.-L. 2002a. The land surface climatology of the Community Land Model coupled to the NCAR Community Climate Model. J. Climate 15: 3123-3149. .. _Bonanetal2002b: -Bonan, G.B., Levis, S., Kergoat, L., and Oleson, K.W. 2002b. Landscapes -as patches of plant functional types: An integrating concept for climate -and ecosystem models. Global Biogeochem. Cycles 16: 5.1-5.23. +Bonan, G.B., Levis, S., Kergoat, L., and Oleson, K.W. 2002b. Landscapes as patches of plant functional types: An integrating concept for climate and ecosystem models. Global Biogeochem. Cycles 16: 5.1-5.23. .. _BonanLevis2006: -Bonan, G.B., and Levis, S. 2006. Evaluating aspects of the Community -Land and Atmosphere Models (CLM3 and CAM3) using a dynamic global -vegetation model. J. Climate 19:2290-2301. +Bonan, G.B., and Levis, S. 2006. Evaluating aspects of the Community Land and Atmosphere Models (CLM3 and CAM3) using a dynamic global vegetation model. J. Climate 19:2290-2301. .. _Bonanetal2011: -Bonan, G.B., Lawrence P.J., Oleson K.W., Levis S., Jung M., Reichstein -M., Lawrence, D.M., and Swenson, S.C. 2011. Improving canopy processes -in the Community Land Model (CLM4) using global flux fields empirically -inferred from FLUXNET data. J. Geophys. Res. 116, G02014. -DOI:10.1029/2010JG001593. +Bonan, G.B., Lawrence P.J., Oleson K.W., Levis S., Jung M., Reichstein M., Lawrence, D.M., and Swenson, S.C. 2011. Improving canopy processes in the Community Land Model (CLM4) using global flux fields empirically inferred from FLUXNET data. J. Geophys. Res. 116, G02014. DOI:10.1029/2010JG001593. .. _Bonanetal2012: -Bonan, G. B., Oleson, K.W., Fisher, R.A., Lasslop, G., and Reichstein, -M. 2012. Reconciling leaf physiological traits and canopy flux data: Use -of the TRY and FLUXNET databases in the Community Land Model version 4, -J. Geophys. Res., 117, G02026. DOI:10.1029/2011JG001913. +Bonan, G. B., Oleson, K.W., Fisher, R.A., Lasslop, G., and Reichstein, M. 2012. Reconciling leaf physiological traits and canopy flux data: Use of the TRY and FLUXNET databases in the Community Land Model version 4, J. Geophys. Res., 117, G02026. DOI:10.1029/2011JG001913. .. _Bonanetal2014: -Bonan, G.B., Williams, M., Fisher, R.A., and Oleson, K.W. 2014. Modeling -stomatal conductance in the earth system: linking leaf water-use -efficiency and water transport along the soil–plant–atmosphere continuum, -Geosci. Model Dev., 7, 2193-2222, doi:10.5194/gmd-7-2193-2014. +Bonan, G.B., Williams, M., Fisher, R.A., and Oleson, K.W. 2014. Modeling stomatal conductance in the earth system: linking leaf water-use efficiency and water transport along the soil–plant–atmosphere continuum, Geosci. Model Dev., 7, 2193-2222, doi:10.5194/gmd-7-2193-2014. .. _botta2000: Botta, A et al., 2000. A global prognostic scheme of leaf onset using satellite data. Global Change Biology 6.7, pp. 709-725. +.. _Brownetal1997: + +Brown J., Ferrians O. J. Jr, Heginbottom J. A. and Melnikov E. S. 1997. Circum-Arctic Map of Permafrost and Ground-Ice Conditions (Boulder, CO: National Snow and Ice Data Center) version 2, DOI: 10.3133/cp45 + .. _Brun1989: -Brun, E. 1989. Investigation of wet-snow metamorphism in respect of -liquid water content. Ann. Glaciol. 13:22-26. +Brun, E. 1989. Investigation of wet-snow metamorphism in respect of liquid water content. Ann. Glaciol. 13:22-26. .. _Brunkeetal2016: @@ -238,19 +169,15 @@ Brzostek, E. R., J. B. Fisher, and R. P. Phillips, 2014. Modeling the carbon cos .. _BugmannSolomon2000: -Bugmann, H., and Solomon, A.M. 2000. Explaining forest composition and -biomass across multiple biogeographical regions. Ecol. Appl. 10:95-114. +Bugmann, H., and Solomon, A.M. 2000. Explaining forest composition and biomass across multiple biogeographical regions. Ecol. Appl. 10:95-114. .. _Busing2005: -Busing, R.T. 2005. Tree mortality, canopy turnover, and woody detritus -in old cove forests of the southern Appalachians. Ecology 86:73-84. +Busing, R.T. 2005. Tree mortality, canopy turnover, and woody detritus in old cove forests of the southern Appalachians. Ecology 86:73-84. .. _Buzanetal2015: -Buzan, J.R., Oleson, K., and Huber, M. 2015: Implementation and -comparison of a suite of heat stress metrics within the Community Land -Model version 4.5, Geosci. Model Dev., 8, 151-170, doi:10.5194/gmd-8-151-2015. +Buzan, J.R., Oleson, K., and Huber, M. 2015: Implementation and comparison of a suite of heat stress metrics within the Community Land Model version 4.5, Geosci. Model Dev., 8, 151-170, doi:10.5194/gmd-8-151-2015. .. _byram1959: @@ -258,105 +185,67 @@ Byram, G.M., 1959. Combustion of forest fuels. In Forest fire: control and use.( .. _CampbellNorman1998: -Campbell, G.S., and Norman, J.M. 1998. An Introduction to Environmental -Biophysics (2:math:`{}^{nd}` edition). Springer-Verlag, New York. +Campbell, G.S., and Norman, J.M. 1998. An Introduction to Environmental Biophysics (2:math:`{}^{nd}` edition). Springer-Verlag, New York. .. _Castilloetal2012: -Castillo, G., Kendra, C., Levis, S., and Thornton, P. 2012. Evaluation -of the new CNDV option of the Community Land Model: effects of dynamic -vegetation and interactive nitrogen on CLM4 means and variability. J. -Climate 25:3702–3714. +Castillo, G., Kendra, C., Levis, S., and Thornton, P. 2012. Evaluation of the new CNDV option of the Community Land Model: effects of dynamic vegetation and interactive nitrogen on CLM4 means and variability. J. Climate 25:3702–3714. .. _Caoetal1996: -Cao, M., Marshall, S. and Gregson, K., 1996. Global carbon exchange and -methane emissions from natural wetlands: Application of a process-based -model. J. Geophys. Res. 101(D9):14,399-14,414. +Cao, M., Marshall, S. and Gregson, K., 1996. Global carbon exchange and methane emissions from natural wetlands: Application of a process-based model. J. Geophys. Res. 101(D9):14,399-14,414. .. _Chengetal2019: -Cheng, Y. et al., 2019. Parameterizing perennial bioenergy -crops in Version 5 of the Community Land Model Based on Site‐Level -Observations in the Central Midwestern United States. -Journal of Advances in Modeling Earth Systems, -2(2013), 1–24. https://doi.org/10.1029/2019MS001719 +Cheng, Y. et al., 2019. Parameterizing perennial bioenergy crops in Version 5 of the Community Land Model Based on Site‐Level Observations in the Central Midwestern United States. Journal of Advances in Modeling Earth Systems, 2(2013), 1–24. https://doi.org/10.1029/2019MS001719 .. _Chuangetal2006: -Chuang Y.L., Oren R., Bertozzi A.L, Phillips N., Katul G.G. 2006. The -porous media model for the hydraulic system of a conifer tree: Linking -sap flux data to transpiration rate, Ecological Modelling, 191, 447-468, -doi:10.1016/j.ecolmodel.2005.03.027. +Chuang Y.L., Oren R., Bertozzi A.L, Phillips N., Katul G.G. 2006. The porous media model for the hydraulic system of a conifer tree: Linking sap flux data to transpiration rate, Ecological Modelling, 191, 447-468, doi:10.1016/j.ecolmodel.2005.03.027. .. _Churkinaetal2003: -Churkina, G. et al., 2003. Analyzing the ecosystem carbon dynamics of -four European coniferous forests using a biogeochemistry model. -Ecosystems, 6: 168-184. +Churkina, G. et al., 2003. Analyzing the ecosystem carbon dynamics of four European coniferous forests using a biogeochemistry model. Ecosystems, 6: 168-184. .. _CIESIN2005: -CIESIN: Gridded population of the world version 3 (GPWv3), 2005. -Population density grids, Technical report, Socioeconomic Data and -Applications Center (SEDAC), Columbia University, Palisades, New York, -USA. +CIESIN: Gridded population of the world version 3 (GPWv3), 2005. Population density grids, Technical report, Socioeconomic Data and Applications Center (SEDAC), Columbia University, Palisades, New York, USA. .. _ClappHornberger1978: -Clapp, R.B., and Hornberger, G.M. 1978. Empirical equations for some -soil hydraulic properties. Water Resour. Res. 14:601-604. +Clapp, R.B., and Hornberger, G.M. 1978. Empirical equations for some soil hydraulic properties. Water Resour. Res. 14:601-604. .. _ClauserHuenges1995: -Clauser, C., and Huenges, E. 1995. Thermal conductivity of rocks and -minerals. pp. 105-126. In: T. J. Ahrens (editor) Rock Physics and Phase -Relations: A Handbook of Physical Constants. Washington, D.C. +Clauser, C., and Huenges, E. 1995. Thermal conductivity of rocks and minerals. pp. 105-126. In: T. J. Ahrens (editor) Rock Physics and Phase Relations: A Handbook of Physical Constants. Washington, D.C. .. _Clevelandetal1999: -Cleveland, C.C., Townsend, A.R., Schimel, D.S., Fisher, H., Howarth, -R.W., Hedin, L.O., Perakis, S.S., Latty, E.F., Von Fischer, J.C., -Elseroad, A., and Wasson, M.F. 1999. Global patterns of terrestrial -biological nitrogen (N2) fixation in natural ecosystems. Global -Biogeochem. Cycles 13:623-645. +Cleveland, C.C., Townsend, A.R., Schimel, D.S., Fisher, H., Howarth, R.W., Hedin, L.O., Perakis, S.S., Latty, E.F., Von Fischer, J.C., Elseroad, A., and Wasson, M.F. 1999. Global patterns of terrestrial biological nitrogen (N2) fixation in natural ecosystems. Global Biogeochem. Cycles 13:623-645. .. _Collatzetal1991: -Collatz, G.J., Ball, J.T., Grivet, C., and Berry, J.A. 1991. -Physiological and environmental regulation of stomatal conductance, -photosynthesis, and transpiration: A model that includes a laminar -boundary layer. Agric. For. Meteor. 54:107-136. +Collatz, G.J., Ball, J.T., Grivet, C., and Berry, J.A. 1991. Physiological and environmental regulation of stomatal conductance, photosynthesis, and transpiration: A model that includes a laminar boundary layer. Agric. For. Meteor. 54:107-136. .. _Collatzetal1992: -Collatz, G.J., Ribas-Carbo, M., and Berry, J.A. 1992. Coupled -photosynthesis-stomatal conductance model for leaves of -C\ :math:`{}_{4}` plants. Aust. J. Plant Physiol. 19:519-538. +Collatz, G.J., Ribas-Carbo, M., and Berry, J.A. 1992. Coupled photosynthesis-stomatal conductance model for leaves of C\ :math:`{}_{4}` plants. Aust. J. Plant Physiol. 19:519-538. .. _Colmer2003: -Colmer, T.D., 2003. Long-distance transport of gases in plants: a -perspective on internal aeration and radial oxygen loss from roots. -Plant Cell and Environment 26:17-36. +Colmer, T.D., 2003. Long-distance transport of gases in plants: a perspective on internal aeration and radial oxygen loss from roots. Plant Cell and Environment 26:17-36. .. _Conwayetal1996: -Conway, H., Gades, A., and Raymond, C.F. 1996. Albedo of dirty snow -during conditions of melt. Water Resour. Res. 32:1713-1718. +Conway, H., Gades, A., and Raymond, C.F. 1996. Albedo of dirty snow during conditions of melt. Water Resour. Res. 32:1713-1718. .. _Cosbyetal1984: -Cosby, B.J., Hornberger, G.M., Clapp, R.B., and Ginn, T.R. 1984. A -statistical exploration of the relationships of soil moisture -characteristics to the physical properties of soils. Water Resour. Res. -20:682-690. +Cosby, B.J., Hornberger, G.M., Clapp, R.B., and Ginn, T.R. 1984. A statistical exploration of the relationships of soil moisture characteristics to the physical properties of soils. Water Resour. Res. 20:682-690. .. _Crawfordetal1982: -Crawford, T. W., Rendig, V. V., and Broadent, F. E. 1982. Sources, -fluxes, and sinks of nitrogen during early reproductive growth of maize -(Zea mays L.). Plant Physiol. 70:1645-1660. +Crawford, T. W., Rendig, V. V., and Broadent, F. E. 1982. Sources, fluxes, and sinks of nitrogen during early reproductive growth of maize (Zea mays L.). Plant Physiol. 70:1645-1660. .. _Dahlinetal2015: @@ -364,168 +253,111 @@ Dahlin, K., R. Fisher, and P. Lawrence, 2015: Environmental drivers of drought d .. _DaiZeng1997: -Dai, Y., and Zeng, Q. 1997. A land surface model (IAP94) for climate -studies. Part I: formulation and validation in off-line experiments. -Adv. Atmos. Sci. 14:433-460. +Dai, Y., and Zeng, Q. 1997. A land surface model (IAP94) for climate studies. Part I: formulation and validation in off-line experiments. Adv. Atmos. Sci. 14:433-460. .. _Daietal2001: -Dai, Y., et al. 2001. Common Land Model: Technical documentation and -user’s guide [Available online at -http://climate.eas.gatech.edu/dai/clmdoc.pdf]. +Dai, Y., et al. 2001. Common Land Model: Technical documentation and user's guide [Available online at http://climate.eas.gatech.edu/dai/clmdoc.pdf]. .. _Daietal2003: -Dai, Y., Zeng, X., Dickinson, R.E., Baker, I., Bonan, G.B., Bosilovich, -M.G., Denning, A.S., Dirmeyer, P.A., Houser, P.R., Niu, G., Oleson, -K.W., Schlosser, C.A., and Yang, Z.-L. 2003. The Common Land Model. -Bull. Amer. Meteor. Soc. 84:1013-1023. +Dai, Y., Zeng, X., Dickinson, R.E., Baker, I., Bonan, G.B., Bosilovich, M.G., Denning, A.S., Dirmeyer, P.A., Houser, P.R., Niu, G., Oleson, K.W., Schlosser, C.A., and Yang, Z.-L. 2003. The Common Land Model. Bull. Amer. Meteor. Soc. 84:1013-1023. .. _Daietal2004: -Dai, Y., Dickinson, R.E., and Wang, Y.-P. 2004. A two-big-leaf model for -canopy temperature, photosynthesis, and stomatal conductance. J. Climate -17:2281-2299. +Dai, Y., Dickinson, R.E., and Wang, Y.-P. 2004. A two-big-leaf model for canopy temperature, photosynthesis, and stomatal conductance. J. Climate 17:2281-2299. .. _DaiTrenberth2002: -Dai, A., and Trenberth, K.E. 2002. Estimates of freshwater discharge -from continents: Latitudinal and seasonal variations. J. Hydrometeor. -3:660-687. +Dai, A., and Trenberth, K.E. 2002. Estimates of freshwater discharge from continents: Latitudinal and seasonal variations. J. Hydrometeor. 3:660-687. .. _DeFriesetal2000: -DeFries, R.S., Hansen, M.C., Townshend, J.R.G., Janetos, A.C., and -Loveland, T.R. 2000. A new global 1-km dataset of percentage tree cover -derived from remote sensing. Global Change Biol. 6:247-254. +DeFries, R.S., Hansen, M.C., Townshend, J.R.G., Janetos, A.C., and Loveland, T.R. 2000. A new global 1-km dataset of percentage tree cover derived from remote sensing. Global Change Biol. 6:247-254. .. _DegensSparling1996: -Degens, B. and Sparling, G., 1996. Changes in aggregation do not -correspond with changes in labile organic C fractions in soil amended -with :math:`{}^{14}`\ C-glucose. Soil Biology and Biochemistry, 28(4/5): -453-462. +Degens, B. and Sparling, G., 1996. Changes in aggregation do not correspond with changes in labile organic C fractions in soil amended with :math:`{}^{14}`\ C-glucose. Soil Biology and Biochemistry, 28(4/5): 453-462. .. _deKauwe2015: -de Kauwe, D.A., Kala, J., Lin, Y.-S., Pitman, A.J., Medlyn, B.E., Duursma, R.A., -Abramowitz, G., Wang, Y.-P., Miralles, D.G. 2015. A test of an optimal stomatal -conductance scheme within the CABLE land surface model. Geosci. Model Dev. -8(2):431-452. +de Kauwe, D.A., Kala, J., Lin, Y.-S., Pitman, A.J., Medlyn, B.E., Duursma, R.A., Abramowitz, G., Wang, Y.-P., Miralles, D.G. 2015. A test of an optimal stomatal conductance scheme within the CABLE land surface model. Geosci. Model Dev. 8(2):431-452. .. _deVries1963: -de Vries, D.A. 1963. Thermal Properties of Soils. In: W.R. van Wijk -(editor) Physics of the Plant Environment. North-Holland, Amsterdam. +de Vries, D.A. 1963. Thermal Properties of Soils. In: W.R. van Wijk (editor) Physics of the Plant Environment. North-Holland, Amsterdam. .. _Dickinson1983: -Dickinson, R.E. 1983. Land surface processes and climate-surface albedos -and energy balance. Adv. Geophys. 25:305-353. +Dickinson, R.E. 1983. Land surface processes and climate-surface albedos and energy balance. Adv. Geophys. 25:305-353. .. _Dickinsonetal1993: -Dickinson, R.E., Henderson-Sellers, A., and Kennedy, P.J. 1993. -Biosphere-Atmosphere Transfer Scheme (BATS) version 1e as coupled to the -NCAR Community Climate Model. NCAR Technical Note NCAR/TN-387+STR. -National Center for Atmospheric Research, Boulder, CO. +Dickinson, R.E., Henderson-Sellers, A., and Kennedy, P.J. 1993. Biosphere-Atmosphere Transfer Scheme (BATS) version 1e as coupled to the NCAR Community Climate Model. NCAR Technical Note NCAR/TN-387+STR. National Center for Atmospheric Research, Boulder, CO. .. _Dickinsonetal2006: -Dickinson, R.E., Oleson, K.W., Bonan, G., Hoffman, F., Thornton, P., -Vertenstein, M., Yang, Z.-L., and Zeng, X. 2006. The Community Land -Model and its climate statistics as a component of the Community Climate -System Model. J. Climate 19:2302-2324. +Dickinson, R.E., Oleson, K.W., Bonan, G., Hoffman, F., Thornton, P., Vertenstein, M., Yang, Z.-L., and Zeng, X. 2006. The Community Land Model and its climate statistics as a component of the Community Climate System Model. J. Climate 19:2302-2324. .. _Dingman2002: -Dingman, S.L. 2002. Physical Hydrology. Second Edition. Prentice Hall, -NJ. +Dingman, S.L. 2002. Physical Hydrology. Second Edition. Prentice Hall, NJ. .. _Dirmeyeretal1999: -Dirmeyer, P.A., Dolman, A.J., and Sato, N. 1999. The pilot phase of the -Global Soil Wetness Project. Bull. Amer. Meteor. Soc. 80:851-878. +Dirmeyer, P.A., Dolman, A.J., and Sato, N. 1999. The pilot phase of the Global Soil Wetness Project. Bull. Amer. Meteor. Soc. 80:851-878. .. _Dobsonetal2000: -Dobson, J.E., Bright, E.A., Coleman, P.R., Durfee, R.C., and Worley, -B.A. 2000. LandScan: A global population database for estimating -populations at risk. Photogramm. Eng. Rem. Sens. 66:849-857. +Dobson, J.E., Bright, E.A., Coleman, P.R., Durfee, R.C., and Worley, B.A. 2000. LandScan: A global population database for estimating populations at risk. Photogramm. Eng. Rem. Sens. 66:849-857. .. _DormanSellers1989: -Dorman, J.L., and Sellers, P.J. 1989. A global climatology of albedo, -roughness length and stomatal resistance for atmospheric general -circulation models as represented by the simple biosphere model (SiB). -J. Appl. Meteor. 28:833-855. +Dorman, J.L., and Sellers, P.J. 1989. A global climatology of albedo, roughness length and stomatal resistance for atmospheric general circulation models as represented by the simple biosphere model (SiB). J. Appl. Meteor. 28:833-855. .. _Doughertyetal1994: -Dougherty, R.L., Bradford, J.A., Coyne, P.I., and Sims, P.L. 1994. -Applying an empirical model of stomatal conductance to three C4 grasses. -Agric. For. Meteor. 67:269-290. +Dougherty, R.L., Bradford, J.A., Coyne, P.I., and Sims, P.L. 1994. Applying an empirical model of stomatal conductance to three C4 grasses. Agric. For. Meteor. 67:269-290. .. _Drewniaketal2013: -Drewniak, B., Song, J., Prell, J., Kotamarthi, V.R., and Jacob, R. 2013. -Modeling agriculture in the Community Land Model. Geosci. Model Dev. -6:495-515. DOI:10.5194/gmd-6-495-2013. +Drewniak, B., Song, J., Prell, J., Kotamarthi, V.R., and Jacob, R. 2013. Modeling agriculture in the Community Land Model. Geosci. Model Dev. 6:495-515. DOI:10.5194/gmd-6-495-2013. .. _Dunfieldetal1993: -Dunfield, P., Knowles, R., Dumont, R. and Moore, T.R., 1993. Methane -Production and Consumption in Temperate and Sub-Arctic Peat Soils - -Response to Temperature and Ph. Soil Biology & Biochemistry 25:321-326. +Dunfield, P., Knowles, R., Dumont, R. and Moore, T.R., 1993. Methane Production and Consumption in Temperate and Sub-Arctic Peat Soils - Response to Temperature and Ph. Soil Biology & Biochemistry 25:321-326. .. _EntekhabiEagleson1989: -Entekhabi, D., and Eagleson, P.S. 1989. Land surface hydrology -parameterization for atmospheric general circulation models including -subgrid scale spatial variability. J. Climate 2:816-831. +Entekhabi, D., and Eagleson, P.S. 1989. Land surface hydrology parameterization for atmospheric general circulation models including subgrid scale spatial variability. J. Climate 2:816-831. .. _FangStefan1996: -Fang, X. and Stefan, H.G., 1996. Long-term lake water temperature and -ice cover simulations/measurements. Cold Regions Science and Technology -24:289-304. +Fang, X. and Stefan, H.G., 1996. Long-term lake water temperature and ice cover simulations/measurements. Cold Regions Science and Technology 24:289-304. .. _Farouki1981: -Farouki, O.T. 1981. The thermal properties of soils in cold regions. -Cold Regions Sci. and Tech. 5:67-75. +Farouki, O.T. 1981. The thermal properties of soils in cold regions. Cold Regions Sci. and Tech. 5:67-75. .. _Farquharetal1980: -Farquhar, G.D., von Caemmerer, S., and Berry, J.A. 1980. A biochemical -model of photosynthetic CO\ :sub:`2` assimilation in leaves of -C\ :math:`{}_{3}` species. Planta 149:78-90. +Farquhar, G.D., von Caemmerer, S., and Berry, J.A. 1980. A biochemical model of photosynthetic CO\ :sub:`2` assimilation in leaves of C\ :math:`{}_{3}` species. Planta 149:78-90. .. _FarquharvonCaemmerer1982: -Farquhar, G.D., and von Caemmerer, S. 1982. Modeling of photosynthetic -response to environmental conditions. pp. 549-587. In: O.L. Lange, P.S. -Nobel, C.B. Osmond, and H. Zeigler (editors) Encyclopedia of Plant -Physiology. Vol. 12B. Physiological Plant Ecology. II. Water Relations -and Carbon Assimilation. Springer-Verlag, New York. +Farquhar, G.D., and von Caemmerer, S. 1982. Modeling of photosynthetic response to environmental conditions. pp. 549-587. In: O.L. Lange, P.S. Nobel, C.B. Osmond, and H. Zeigler (editors) Encyclopedia of Plant Physiology. Vol. 12B. Physiological Plant Ecology. II. Water Relations and Carbon Assimilation. Springer-Verlag, New York. .. _FeddemaKauffman2016: -Feddema, J., Kauffman, B. 2016. Urban Properties Tool (Version 1.2). -NCAR THESIS Tools Library. Retrieved from: https://svn-iam-thesis-release.cgd.ucar.edu/urban_properties/. -doi:10.5065/D6R78CMT. +Feddema, J., Kauffman, B. 2016. Urban Properties Tool (Version 1.2). NCAR THESIS Tools Library. Retrieved from: https://svn-iam-thesis-release.cgd.ucar.edu/urban_properties/. doi:10.5065/D6R78CMT. .. _Ferrari1999: -Ferrari, J.B., 1999. Fine-scale patterns of leaf litterfall and nitrogen -cycling in an old-growth forest. Canadian Journal of Forest Research, -29: 291-302. +Ferrari, J.B., 1999. Fine-scale patterns of leaf litterfall and nitrogen cycling in an old-growth forest. Canadian Journal of Forest Research, 29: 291-302. .. _FirestoneDavidson1989: -Firestone, M.K. and Davidson, E.A. 1989. Exchange of Trace Gases between -Terrestrial Ecosystems and the Atmosphere. In: M.O. Andreae and D.S. -Schimel (Editors). John Wiley and Sons, pp. 7-21. +Firestone, M.K. and Davidson, E.A. 1989. Exchange of Trace Gases between Terrestrial Ecosystems and the Atmosphere. In: M.O. Andreae and D.S. Schimel (Editors). John Wiley and Sons, pp. 7-21. .. _Fisheretal2010: @@ -541,25 +373,19 @@ Fisher, R.A., C.D. Koven, W.R.L. Anderegg, et al., 2018: Vegetation demographics .. _FlannerZender2005: -Flanner, M.G., and Zender. C.S. 2005. Snowpack radiative heating: -Influence on Tibetan Plateau climate. Geophys. Res. Lett. 32:L06501. -DOI:10.1029/2004GL022076. +Flanner, M.G., and Zender. C.S. 2005. Snowpack radiative heating: Influence on Tibetan Plateau climate. Geophys. Res. Lett. 32:L06501. DOI:10.1029/2004GL022076. .. _FlannerZender2006: -Flanner, M.G., and Zender, C.S. 2006. Linking snowpack microphysics and -albedo evolution. J. Geophys. Res. 111:D12208. DOI:10.1029/2005JD006834. +Flanner, M.G., and Zender, C.S. 2006. Linking snowpack microphysics and albedo evolution. J. Geophys. Res. 111:D12208. DOI:10.1029/2005JD006834. .. _Flanneretal2007: -Flanner, M.G., Zender, C.S., Randerson, J.T., and Rasch, P.J. 2007. -Present day climate forcing and response from black carbon in snow. J. -Geophys. Res. 112:D11202. DOI:10.1029/2006JD008003. +Flanner, M.G., Zender, C.S., Randerson, J.T., and Rasch, P.J. 2007. Present day climate forcing and response from black carbon in snow. J. Geophys. Res. 112:D11202. DOI:10.1029/2006JD008003. .. _Flatauetal1992: -Flatau, P.J., Walko, R.L., and Cotton, W.R. 1992. Polynomial fits to -saturation vapor pressure. J. Appl. Meteor. 31:1507-1513. +Flatau, P.J., Walko, R.L., and Cotton, W.R. 1992. Polynomial fits to saturation vapor pressure. J. Appl. Meteor. 31:1507-1513. .. _foley1996: @@ -567,16 +393,11 @@ Foley, J.A. et al., 1996. An integrated biosphere model of land surface processe .. _Friedl,etal2002: -Friedl, M.A., McIver, D.K., Hodges, J.C.F., Zhang, X.Y., Muchoney, D., -Strahler, A.H., Woodcock, C.E., Gopal, S., Schneider, A., Cooper, A., -Baccini, A., Gao, F., and Schaaf, C. 2002. Global land cover mapping -from MODIS: algorithms and early results. Remote Sens. Environ. -83:287-302. +Friedl, M.A., McIver, D.K., Hodges, J.C.F., Zhang, X.Y., Muchoney, D., Strahler, A.H., Woodcock, C.E., Gopal, S., Schneider, A., Cooper, A., Baccini, A., Gao, F., and Schaaf, C. 2002. Global land cover mapping from MODIS: algorithms and early results. Remote Sens. Environ. 83:287-302. .. _Frolkingetal2001: -Frolking, S., et al. 2001. Modeling Northern Peatland Decomposition and -Peat Accumulation. Ecosystems. 4:479-498. +Frolking, S., et al. 2001. Modeling Northern Peatland Decomposition and Peat Accumulation. Ecosystems. 4:479-498. .. _fyllas2014: @@ -584,46 +405,31 @@ Fyllas, N.M. et al., 2014. Analysing Amazonian forest productivity using a new i .. _Gallaisetal2006: -Gallais, A., Coque, M. Quillere, I., Prioul, J., and Hirel, B. 2006. -Modeling postsilking nitrogen fluxes in maize (Zea mays) using -15N-labeling field experiments. New Phytologist 172:696-707. +Gallais, A., Coque, M. Quillere, I., Prioul, J., and Hirel, B. 2006. Modeling postsilking nitrogen fluxes in maize (Zea mays) using 15N-labeling field experiments. New Phytologist 172:696-707. .. _Gallaisetal2007: -Gallais, A., Coque, M., Gouis, J. L., Prioul, J. L., Hirel, B., and -Quillere, I. 2007. Estimating the proportion of nitrogen remobilization -and of postsilking nitrogen uptake allocated to maize kernels by -Nitrogen-15 labeling. Crop Sci. 47:685-693. +Gallais, A., Coque, M., Gouis, J. L., Prioul, J. L., Hirel, B., and Quillere, I. 2007. Estimating the proportion of nitrogen remobilization and of postsilking nitrogen uptake allocated to maize kernels by Nitrogen-15 labeling. Crop Sci. 47:685-693. .. _Gallowayetal2004: -Galloway, J.N., et al. 2004. Nitrogen cycles: past, present, and future. -Biogeochem. 70:153-226. +Galloway, J.N., et al. 2004. Nitrogen cycles: past, present, and future. Biogeochem. 70:153-226. .. _Garciaetal1988: -Garcia, R.L., Kanemasu, E.T., Blad, B.L., Bauer, A., Hatfield, J.L., -Major, D.A., Reginato, R.J., and Hubbard, K.G. 1988. Interception and -use efficiency of light in winter wheat under different nitrogen -regimes. Agric. For. Meteor. 44:175-186. +Garcia, R.L., Kanemasu, E.T., Blad, B.L., Bauer, A., Hatfield, J.L., Major, D.A., Reginato, R.J., and Hubbard, K.G. 1988. Interception and use efficiency of light in winter wheat under different nitrogen regimes. Agric. For. Meteor. 44:175-186. .. _Gardner1960: -Gardner, W. R. 1960. Dynamic aspects of water availability to plants, -Soil Sci., 89, 63–73. +Gardner, W. R. 1960. Dynamic aspects of water availability to plants, Soil Sci., 89, 63–73. .. _Gashetal1996: -Gash, J.H.C., Nobre, C.A., Roberts, J.M., and Victoria, R.L. 1996. An -overview of ABRACOS. pp. 1-14. In: J.H.C. Gash, C.A. Nobre, J.M. -Roberts, and R.L. Victoria (editors) Amazonian Deforestation and -Climate. John Wiley and Sons, Chichester, England. +Gash, J.H.C., Nobre, C.A., Roberts, J.M., and Victoria, R.L. 1996. An overview of ABRACOS. pp. 1-14. In: J.H.C. Gash, C.A. Nobre, J.M. Roberts, and R.L. Victoria (editors) Amazonian Deforestation and Climate. John Wiley and Sons, Chichester, England. .. _Getiranaetal2012: -Getirana, A. C. V., A. Boone, D. Yamazaki, B. Decharme, F. Papa, and -N. Mognard. 2012. The hydrological modeling and analysis platform -(HyMAP): Evaluation in the Amazon basin, J. Hydrometeorol., 13, 1641-1665. +Getirana, A. C. V., A. Boone, D. Yamazaki, B. Decharme, F. Papa, and N. Mognard. 2012. The hydrological modeling and analysis platform (HyMAP): Evaluation in the Amazon basin, J. Hydrometeorol., 13, 1641-1665. .. _Ghimireetal2016: @@ -631,49 +437,35 @@ Ghimire, B., W. J. Riley, C. D. Koven, M. Mu, and J. T. Randerson, 2016: Represe .. _Gholzetal1985: -Gholz, H.L., Perry, C.S., Cropper, W.P., Jr. and Hendry, L.C., 1985. -Litterfall, decomposition, and nitrogen and phosphorous dynamics in a -chronosequence of slash pine (*Pinus elliottii*) plantations. Forest -Science, 31: 463-478. +Gholz, H.L., Perry, C.S., Cropper, W.P., Jr. and Hendry, L.C., 1985. Litterfall, decomposition, and nitrogen and phosphorous dynamics in a chronosequence of slash pine (*Pinus elliottii*) plantations. Forest Science, 31: 463-478. .. _Giglioetal2006: -Giglio, L., Csiszar, I., and Justice, C.O. 2006. Global distribution and -seasonality of active fires as observed with the Terra and Aqua Moderate -Resolution Imaging Spectroradiometer (MODIS) sensors. J. Geophys. Res. -111:G02016. DOI:10.1029/2005JG000142. +Giglio, L., Csiszar, I., and Justice, C.O. 2006. Global distribution and seasonality of active fires as observed with the Terra and Aqua Moderate Resolution Imaging Spectroradiometer (MODIS) sensors. J. Geophys. Res. 111:G02016. DOI:10.1029/2005JG000142. .. _GlobalSoilDataTask2000: -Global Soil Data Task 2000. Global soil data products CD-ROM (IGBP-DIS). -International Geosphere-Biosphere Programme-Data and Information -Available Services [Available online at http://www.daac.ornl.gov]. +Global Soil Data Task 2000. Global soil data products CD-ROM (IGBP-DIS). International Geosphere-Biosphere Programme-Data and Information Available Services [Available online at http://www.daac.ornl.gov]. .. _Gomesetal2003: -Gomes, E.P.C., Mantovani, W., and Kageyama, P.Y. 2003. Mortality and -recruitment of trees in a secondary montane rain forest in southeastern -Brazil. Brazilian Journal of Biology 63:47-60. +Gomes, E.P.C., Mantovani, W., and Kageyama, P.Y. 2003. Mortality and recruitment of trees in a secondary montane rain forest in southeastern Brazil. Brazilian Journal of Biology 63:47-60. .. _Goszetal1973: -Gosz, J.R., Likens, G.E., and Bormann, F.H. 1973. Nutrient release from -decomposing leaf and branch litter in the Hubbard Brook Forest, New -Hampshire. Ecological Monographs 43:173-191. +Gosz, J.R., Likens, G.E., and Bormann, F.H. 1973. Nutrient release from decomposing leaf and branch litter in the Hubbard Brook Forest, New Hampshire. Ecological Monographs 43:173-191. .. _GotangcoCastilloetal2012: -Gotangco Castillo C., Levis S., and Thornton P. 2012. Evaluation of the -new CNDV option of the Community Land Model: Effects of dynamic -vegetation and interactive nitrogen on CLM4 means and variability. J. -Climate 25:3702-3714. DOI:10.1175/JCLID-11-00372.1. +Gotangco Castillo C., Levis S., and Thornton P. 2012. Evaluation of the new CNDV option of the Community Land Model: Effects of dynamic vegetation and interactive nitrogen on CLM4 means and variability. J. Climate 25:3702-3714. DOI:10.1175/JCLID-11-00372.1. .. _Grahametal1999: -Graham, S.T., Famiglietti, J.S., and Maidment, D.R. 1999. Five-minute, -1/2º, and 1º data sets of continental watersheds and river networks for -use in regional and global hydrologic and climate system modeling -studies. Water Resour. Res. 35:583-587. +Graham, S.T., Famiglietti, J.S., and Maidment, D.R. 1999. Five-minute, 1/2°, and 1° data sets of continental watersheds and river networks for use in regional and global hydrologic and climate system modeling studies. Water Resour. Res. 35:583-587. + +.. _Grahametal2021: + +Graham, M. W., Thomas, R. Q., Lombardozzi, D. L., & O'Rourke, M. E. (2021). Modest capacity of no-till farming to offset emissions over 21st century. Environmental Research Letters, 16(5), 054055. doi: 10.1088/1748-9326/abe6c6 .. _Gravenetal2017: @@ -681,118 +473,71 @@ Graven, H., C. E. Allison, D. M. Etheridge, S. Hammer, R. F. Keeling, I. Levin, .. _GrenfellWarren1999: -Grenfell, T.C., and Warren, S.G. 1999. Representation of a nonspherical -ice particle by a collection of independent spheres for scattering and -absorption of radiation. J. Geophys. Res. 104(D24):37697-37709. +Grenfell, T.C., and Warren, S.G. 1999. Representation of a nonspherical ice particle by a collection of independent spheres for scattering and absorption of radiation. J. Geophys. Res. 104(D24):37697-37709. .. _delGrossoetal2000: -del Grosso, S.J., et al. 2000. General model for N2O and N2 gas -emissions from soils due to dentrification. Global Biogeochem. Cycles -14:1045-1060. +del Grosso, S.J., et al. 2000. General model for N2O and N2 gas emissions from soils due to dentrification. Global Biogeochem. Cycles 14:1045-1060. .. _Guentheretal1995: -Guenther, A., Hewitt, C.N., Erickson, D., Fall, R., Geron, C., Graedel, -T., Harley, P., Klinger, L., Lerdau, M., McKay, W.A., Pierce, T., -Scholes, B., Steinbrecher, R., Tallamraju, R., Taylor, J., and -Zimmerman, P. 1995. A global model of natural volatile organic compound -emissions. J. Geophys. Res. 100:8873-8892. +Guenther, A., Hewitt, C.N., Erickson, D., Fall, R., Geron, C., Graedel, T., Harley, P., Klinger, L., Lerdau, M., McKay, W.A., Pierce, T., Scholes, B., Steinbrecher, R., Tallamraju, R., Taylor, J., and Zimmerman, P. 1995. A global model of natural volatile organic compound emissions. J. Geophys. Res. 100:8873-8892. .. _Guentheretal2006: -Guenther, A., Karl, T., Harley, P., Wiedinmyer, C., Palmer. P.I., and -Geron, C. 2006. Estimates of global terrestrial isoprene emissions using -MEGAN (Model of Emissions of Gases and Aerosols from Nature). Atmos. -Chem. Phys. 6:3181–3210. +Guenther, A., Karl, T., Harley, P., Wiedinmyer, C., Palmer. P.I., and Geron, C. 2006. Estimates of global terrestrial isoprene emissions using MEGAN (Model of Emissions of Gases and Aerosols from Nature). Atmos. Chem. Phys. 6:3181–3210. .. _Guentheretal2012: -Guenther, A. B., Jiang, X., Heald, C. L., Sakulyanontvittaya, T., Duhl, -T., Emmons, L. K., & Wang, X., 2012. The Model of Emissions of Gases and -Aerosols from Nature version 2.1 (MEGAN2.1): an extended and updated -framework for modeling biogenic emissions, Geosci. Model Dev., 5, -1471–1492. DOI:10.5194. +Guenther, A. B., Jiang, X., Heald, C. L., Sakulyanontvittaya, T., Duhl, T., Emmons, L. K., & Wang, X., 2012. The Model of Emissions of Gases and Aerosols from Nature version 2.1 (MEGAN2.1): an extended and updated framework for modeling biogenic emissions, Geosci. Model Dev., 5, 1471–1492. DOI:10.5194. .. _Hacketal2006: -Hack, J.J., Caron, J.M., Yeager, S.G., Oleson, K.W., Holland, M.M., -Truesdale, J.E., and Rasch, P.J. 2006. Simulation of the global -hydrological cycle in the CCSM Community Atmosphere Model version 3 -(CAM3): mean features. J. Climate 19:2199-2221. +Hack, J.J., Caron, J.M., Yeager, S.G., Oleson, K.W., Holland, M.M., Truesdale, J.E., and Rasch, P.J. 2006. Simulation of the global hydrological cycle in the CCSM Community Atmosphere Model version 3 (CAM3): mean features. J. Climate 19:2199-2221. .. _Hansenetal2003: -Hansen, M., DeFries, R.S., Townshend, J.R.G., Carroll, M., Dimiceli, C., -and Sohlberg, R.A. 2003. Global percent tree cover at a spatial -resolution of 500 meters: first results of the MODIS vegetation -continuous fields algorithm. Earth Interactions 7:1-15. +Hansen, M., DeFries, R.S., Townshend, J.R.G., Carroll, M., Dimiceli, C., and Sohlberg, R.A. 2003. Global percent tree cover at a spatial resolution of 500 meters: first results of the MODIS vegetation continuous fields algorithm. Earth Interactions 7:1-15. .. _Hastingsetal1999: -Hastings, D.A., Dunbar, P.K., Elphingstone, G.M., Bootz, M., Murakami, -H., Maruyama, H., Masaharu, H., Holland, P., Payne, J., Bryant, N.A., -Logan, T.L., Muller, J.-P., Schreier, G., and MacDonald, J.S., eds., -1999. The Global Land One-kilometer Base Elevation (GLOBE) Digital -Elevation Model, Version 1.0. National Oceanic and Atmospheric -Administration, National Geophysical Data Center, 325 Broadway, Boulder, -Colorado 80305-3328, U.S.A. +Hastings, D.A., Dunbar, P.K., Elphingstone, G.M., Bootz, M., Murakami, H., Maruyama, H., Masaharu, H., Holland, P., Payne, J., Bryant, N.A., Logan, T.L., Muller, J.-P., Schreier, G., and MacDonald, J.S., eds., 1999. The Global Land One-kilometer Base Elevation (GLOBE) Digital Elevation Model, Version 1.0. National Oceanic and Atmospheric Administration, National Geophysical Data Center, 325 Broadway, Boulder, Colorado 80305-3328, U.S.A. .. _Healdetal2008: -Heald, C.L., Henze, D.K., Horowitz, L.W., Feddema, J., Lamarque, J.-F., -Guenther, A., Hess, P.G., Vitt, F., Seinfeld, J.H., Goldstein, A.H., and -Fung, I. 2008. Predicted change in global secondary organic aerosol -concentrations in response to future climate, emissions, and land use -change. J. Geophys. Res. 113:D05211. DOI:10.1029/2007JD009092. +Heald, C.L., Henze, D.K., Horowitz, L.W., Feddema, J., Lamarque, J.-F., Guenther, A., Hess, P.G., Vitt, F., Seinfeld, J.H., Goldstein, A.H., and Fung, I. 2008. Predicted change in global secondary organic aerosol concentrations in response to future climate, emissions, and land use change. J. Geophys. Res. 113:D05211. DOI:10.1029/2007JD009092. .. _Healdetal2009: -Heald, C.L., Wilkinson, M.J., Monson, R.K., Alo, C.A., Wang, G.L., and -Guenther, A. 2009. Response of isoprene emission to ambient -CO\ :sub:`2` changes and implications for global budgets. Global -Change Biol. 15:1127-1140. DOI:10.1111/j.1365-2486.2008.01802.x +Heald, C.L., Wilkinson, M.J., Monson, R.K., Alo, C.A., Wang, G.L., and Guenther, A. 2009. Response of isoprene emission to ambient CO\ :sub:`2` changes and implications for global budgets. Global Change Biol. 15:1127-1140. DOI:10.1111/j.1365-2486.2008.01802.x .. _Henderson-Sellers1985: -Henderson-Sellers, B. 1985. New formulation of eddy diffusion -thermocline models. Appl. Math. Modelling 9:441-446. +Henderson-Sellers, B. 1985. New formulation of eddy diffusion thermocline models. Appl. Math. Modelling 9:441-446. .. _Henderson-Sellers1986: -Henderson-Sellers, B. 1986. Calculating the surface energy balance for -lake and reservoir modeling: A review. Rev. Geophys. 24:625-649. +Henderson-Sellers, B. 1986. Calculating the surface energy balance for lake and reservoir modeling: A review. Rev. Geophys. 24:625-649. .. _Henderson-Sellersetal1993: -Henderson-Sellers, A., Yang, Z.-L., and Dickinson, R.E. 1993. The -project for intercomparison of land-surface parameterization schemes. -Bull. Amer. Meteor. Soc. 74: 1335-1349. +Henderson-Sellers, A., Yang, Z.-L., and Dickinson, R.E. 1993. The project for intercomparison of land-surface parameterization schemes. Bull. Amer. Meteor. Soc. 74: 1335-1349. .. _HostetlerBartlein1990: -Hostetler, S.W., and Bartlein, P.J. 1990. Simulation of lake evaporation -with application to modeling lake level variations of Harney-Malheur -Lake, Oregon. Water Resour. Res. 26:2603-2612. +Hostetler, S.W., and Bartlein, P.J. 1990. Simulation of lake evaporation with application to modeling lake level variations of Harney-Malheur Lake, Oregon. Water Resour. Res. 26:2603-2612. .. _Hostetleretal1993: -Hostetler, S.W., Bates, G.T., and Giorgi, F. 1993. Interactive coupling -of a lake thermal model with a regional climate model. J. Geophys. Res. -98:5045-5057. +Hostetler, S.W., Bates, G.T., and Giorgi, F. 1993. Interactive coupling of a lake thermal model with a regional climate model. J. Geophys. Res. 98:5045-5057. .. _Hostetleretal1994: -Hostetler, S.W., Giorgi, F., Bates, G.T., and Bartlein, P.J. 1994. -Lake-atmosphere feedbacks associated with paleolakes Bonneville and -Lahontan. Science 263:665-668. +Hostetler, S.W., Giorgi, F., Bates, G.T., and Bartlein, P.J. 1994. Lake-atmosphere feedbacks associated with paleolakes Bonneville and Lahontan. Science 263:665-668. .. _Houetal2012: -Hou, Z., Huang, M., Leung, L.R., Lin, G., and Ricciuto, D.M. 2012. -Sensitivity of surface flux simulations to hydrologic parameters based -on an uncertainty quantification framework applied to the Community Land -Model. J. Geophys. Res. 117:D15108. +Hou, Z., Huang, M., Leung, L.R., Lin, G., and Ricciuto, D.M. 2012. Sensitivity of surface flux simulations to hydrologic parameters based on an uncertainty quantification framework applied to the Community Land Model. J. Geophys. Res. 117:D15108. .. _Houltonetal2008: @@ -800,176 +545,111 @@ Houlton, B.Z., Wang, Y.P., Vitousek, P.M. and Field, C.B., 2008. A unifying fram .. _HuangLiang2006: -Huang, M., and Liang, X. 2006. On the assessment of the impact of -reducing parameters and identification of parameter uncertainties for a -hydrologic model with applications to ungauged basins. J. Hydrol. -320:37-61. +Huang, M., and Liang, X. 2006. On the assessment of the impact of reducing parameters and identification of parameter uncertainties for a hydrologic model with applications to ungauged basins. J. Hydrol. 320:37-61. .. _Hugeliusetal2012: -Hugelius, G., C. Tarnocai, G. Broll, J.G. Canadell, P. Kuhry, adn D.K. -Swanson, 2012. The Northern Circumpolar Soil Carbon Database: spatially -distributed datasets of soil coverage and soil carbon storage in the -northern permafrost regions. Earth Syst. Sci. Data Discuss., 5, 707-733 -(available online at (http://dev1.geo.su.se/bbcc/dev/ncscd/). +Hugelius, G., C. Tarnocai, G. Broll, J.G. Canadell, P. Kuhry, adn D.K. Swanson, 2012. The Northern Circumpolar Soil Carbon Database: spatially distributed datasets of soil coverage and soil carbon storage in the northern permafrost regions. Earth Syst. Sci. Data Discuss., 5, 707-733 (available online at (http://dev1.geo.su.se/bbcc/dev/ncscd/). .. _Huntetal1988: -Hunt, H.W., Ingham, E.R., Coleman, D.C., Elliott, E.T., and Reid, C.P.P. -1988. Nitrogen limitation of production and decomposition in prairie, -mountain meadow, and pine forest. Ecology 69:1009-1016. +Hunt, H.W., Ingham, E.R., Coleman, D.C., Elliott, E.T., and Reid, C.P.P. 1988. Nitrogen limitation of production and decomposition in prairie, mountain meadow, and pine forest. Ecology 69:1009-1016. .. _HuntRunning1992: -Hunt, E.R., Jr. and Running, S.W., 1992. Simulated dry matter yields for -aspen and spruce stands in the north american boreal forest. Canadian -Journal of Remote Sensing, 18: 126-133. +Hunt, E.R., Jr. and Running, S.W., 1992. Simulated dry matter yields for aspen and spruce stands in the north american boreal forest. Canadian Journal of Remote Sensing, 18: 126-133. .. _Huntetal1996: -Hunt, E.R., Jr. et al., 1996. Global net carbon exchange and -intra-annual atmospheric CO\ :sub:`2` concentrations predicted by -an ecosystem process model and three-dimensional atmospheric transport -model. Global Biogeochemical Cycles, 10: 431-456. +Hunt, E.R., Jr. et al., 1996. Global net carbon exchange and intra-annual atmospheric CO\ :sub:`2` concentrations predicted by an ecosystem process model and three-dimensional atmospheric transport model. Global Biogeochemical Cycles, 10: 431-456. .. _Hurttetal2006: -Hurtt, G.C., Frolking, S., Fearon, M.G., Moore, B., Shevliakova, E., -Malyshev, S., Pacala, S.W., and Houghton, R.A. 2006. The underpinnings -of land-use history: three centuries of global gridded land-use -transitions, wood-harvest activity, and resulting secondary lands. -Global Change Biol. 12:1208-1229. +Hurtt, G.C., Frolking, S., Fearon, M.G., Moore, B., Shevliakova, E., Malyshev, S., Pacala, S.W., and Houghton, R.A. 2006. The underpinnings of land-use history: three centuries of global gridded land-use transitions, wood-harvest activity, and resulting secondary lands. Global Change Biol. 12:1208-1229. .. _Hurttetal2011: -Hurtt, G.C., et al. 2011. Harmonization of land-use scenarios for the -period 1500-2100: 600 years of global gridded annual land-use -transitions, wood harvest, and resulting secondary lands. Climatic -Change 109:117-161. DOI:10.1007/s10584-011-0153-2. +Hurtt, G.C., et al. 2011. Harmonization of land-use scenarios for the period 1500-2100: 600 years of global gridded annual land-use transitions, wood harvest, and resulting secondary lands. Climatic Change 109:117-161. DOI:10.1007/s10584-011-0153-2. .. _Idso1981: -Idso, S.B. 1981. A set of equations for full spectrum and 8- to -14-\ :math:`\mu` \ m and 10.5- to 12.5-\ :math:`\mu` \ m thermal -radiation from cloudless skies. Water Resour. Res. 17:295-304. +Idso, S.B. 1981. A set of equations for full spectrum and 8- to 14-\ :math:`\mu` \ m and 10.5- to 12.5-\ :math:`\mu` \ m thermal radiation from cloudless skies. Water Resour. Res. 17:295-304. .. _IiyamaHasegawa2005: -Iiyama, I. and Hasegawa, S., 2005. Gas diffusion coefficient of -undisturbed peat soils. Soil Science and Plant Nutrition 51:431-435. +Iiyama, I. and Hasegawa, S., 2005. Gas diffusion coefficient of undisturbed peat soils. Soil Science and Plant Nutrition 51:431-435. .. _Jacksonetal1996: -Jacksonetal1996: -E., and Schulze, E. D. 1996. A global analysis of root distributions for -terrestrial biomes Oecologia 108:389–411. DOI:10.1007/BF00333714. +Jacksonetal1996: E., and Schulze, E. D. 1996. A global analysis of root distributions for terrestrial biomes Oecologia 108:389–411. DOI:10.1007/BF00333714. .. _Jacksonetal2010: -Jackson, T.L., Feddema, J.J., Oleson, K.W., Bonan, G.B., and Bauer, J.T. -2010. Parameterization of urban characteristics for global climate -modeling. Annals of the Association of American Geographers. -100:848-865. +Jackson, T.L., Feddema, J.J., Oleson, K.W., Bonan, G.B., and Bauer, J.T. 2010. Parameterization of urban characteristics for global climate modeling. Annals of the Association of American Geographers. 100:848-865. .. _JenkinsonColeman2008: -Jenkinson, D. and Coleman, K. 2008. The turnover of organic carbon in -subsoils. Part 2. Modelling carbon turnover. European Journal of Soil -Science 59:400-413. +Jenkinson, D. and Coleman, K. 2008. The turnover of organic carbon in subsoils. Part 2. Modelling carbon turnover. European Journal of Soil Science 59:400-413. .. _Jordan1991: -Jordan, R. 1991. A One-dimensional Temperature Model for a Snow Cover: -Technical Documentation for SNTHERM.89. U.S. Army Cold Regions Research -and Engineering Laboratory, Special Report 91-16. +Jordan, R. 1991. A One-dimensional Temperature Model for a Snow Cover: Technical Documentation for SNTHERM.89. U.S. Army Cold Regions Research and Engineering Laboratory, Special Report 91-16. .. _KattgeKnorr2007: -Kattge, J., and Knorr, W. 2007. Temperature acclimation in a biochemical -model of photosynthesis: a reanalysis of data from 36 species. Plant -Cell Environ. 30:1176-1190. DOI:10.1111/j.1365-3040.2007.01690.x. +Kattge, J., and Knorr, W. 2007. Temperature acclimation in a biochemical model of photosynthesis: a reanalysis of data from 36 species. Plant Cell Environ. 30:1176-1190. DOI:10.1111/j.1365-3040.2007.01690.x. .. _Kattgeetal2009: -Kattge, J., Knorr, W., Raddatz, T., and Wirth C. 2009: Quantifying -photosynthetic capacity and its relationship to leaf nitrogen content -for global–scale terrestrial biosphere models. Global Change Biol. -15:976–991. +Kattge, J., Knorr, W., Raddatz, T., and Wirth C. 2009: Quantifying photosynthetic capacity and its relationship to leaf nitrogen content for global–scale terrestrial biosphere models. Global Change Biol. 15:976–991. .. _Kavetskietal2002: -Kavetski, D., Binning, P. and Sloan, S.W., 2002. Noniterative time -stepping schemes with adaptive truncation error control for the -solution of Richards equation. Water Resources Research, 38(10). +Kavetski, D., Binning, P. and Sloan, S.W., 2002. Noniterative time stepping schemes with adaptive truncation error control for the solution of Richards equation. Water Resources Research, 38(10). .. _Kelleretal2004: -Keller, M., Palace, M., Asner, G.P., Pereira, R., Jr. and Silva, J.N.M., -2004. Coarse woody debris in undisturbed and logged forests in the -eastern Brazilian Amazon. Global Change Biology, 10: 784-795. +Keller, M., Palace, M., Asner, G.P., Pereira, R., Jr. and Silva, J.N.M., 2004. Coarse woody debris in undisturbed and logged forests in the eastern Brazilian Amazon. Global Change Biology, 10: 784-795. .. _Kellneretal2006: -Kellner, E., Baird, A.J., Oosterwoud, M., Harrison, K. and Waddington, -J.M., 2006. Effect of temperature and atmospheric pressure on methane -(CH4) ebullition from near-surface peats. Geophys. Res. Lett. 33. -DOI:10.1029/2006GL027509. +Kellner, E., Baird, A.J., Oosterwoud, M., Harrison, K. and Waddington, J.M., 2006. Effect of temperature and atmospheric pressure on methane (CH4) ebullition from near-surface peats. Geophys. Res. Lett. 33. DOI:10.1029/2006GL027509. .. _Kimballetal1997: -Kimball, J.S., Thornton, P.E., White, M.A. and Running, S.W. 1997. -Simulating forest productivity and surface-atmosphere exchange in the -BOREAS study region. Tree Physiology 17:589-599. +Kimball, J.S., Thornton, P.E., White, M.A. and Running, S.W. 1997. Simulating forest productivity and surface-atmosphere exchange in the BOREAS study region. Tree Physiology 17:589-599. .. _Kohyamaetal2001: -Kohyama, T., Suzuki, E., Partomihardjo, T., and Yamada, T. 2001. Dynamic -steady state of patch-mosaic tree size structure of a mixed diptocarp -forest regulated by local crowding. Ecological Research 16:85-98. +Kohyama, T., Suzuki, E., Partomihardjo, T., and Yamada, T. 2001. Dynamic steady state of patch-mosaic tree size structure of a mixed diptocarp forest regulated by local crowding. Ecological Research 16:85-98. .. _Kourzeneva2009: -Kourzeneva, E., 2009. Global dataset for the parameterization of lakes -in Numerical Weather Prediction and Climate modeling. ALADIN Newsletter, -No 37, July-December, 2009, F. Bouttier and C. Fischer, Eds., -Meteo-France, Toulouse, France, 46-53. +Kourzeneva, E., 2009. Global dataset for the parameterization of lakes in Numerical Weather Prediction and Climate modeling. ALADIN Newsletter, No 37, July-December, 2009, F. Bouttier and C. Fischer, Eds., Meteo-France, Toulouse, France, 46-53. .. _Kourzeneva2010: -Kourzeneva, E., 2010: External data for lake parameterization in -Numerical Weather Prediction and climate modeling. Boreal Environment -Research, 15, 165-177. +Kourzeneva, E., 2010: External data for lake parameterization in Numerical Weather Prediction and climate modeling. Boreal Environment Research, 15, 165-177. .. _Kourzenevaetal2012: -Kourzeneva, E., Asensio, H., Martin, E. and Faroux, S., 2012. Global -gridded dataset of lake coverage and lake depth for use in numerical -weather prediction and climate modelling. Tellus A 64. +Kourzeneva, E., Asensio, H., Martin, E. and Faroux, S., 2012. Global gridded dataset of lake coverage and lake depth for use in numerical weather prediction and climate modelling. Tellus A 64. .. _Kovenetal2009: -Koven, C., et al. 2009. On the formation of high-latitude soil carbon -stocks: The effects of cryoturbation and insulation by organic matter in -a land surface model. Geophys. Res. Lett. 36: L21501. +Koven, C., et al. 2009. On the formation of high-latitude soil carbon stocks: The effects of cryoturbation and insulation by organic matter in a land surface model. Geophys. Res. Lett. 36: L21501. .. _Kovenetal2011: -Koven, C.D., et al. 2011. Permafrost carbon-climate feedbacks accelerate -global warming. Proceedings of the National Academy of Sciences -108:14769-14774. +Koven, C.D., et al. 2011. Permafrost carbon-climate feedbacks accelerate global warming. Proceedings of the National Academy of Sciences 108:14769-14774. .. _Kovenetal2013: -Koven, C.D. et al. 2013. The effect of vertically-resolved soil -biogeochemistry and alternate soil C and N models on C dynamics of CLM4. -Biogeosciences Discussions 10:7201-7256. +Koven, C.D. et al. 2013. The effect of vertically-resolved soil biogeochemistry and alternate soil C and N models on C dynamics of CLM4. Biogeosciences Discussions 10:7201-7256. .. _Kovenetal2015: -Koven, C.D. et al. 2015. Permafrost carbon-climate feedback is -sensitive to deep soil carbon decomposability but not deep soil -nitrogen dynamics. Proceedings of the National Academies of Science, -112, 12, 3752-3757, doi:10.1073/pnas.1415123112 +Koven, C.D. et al. 2015. Permafrost carbon-climate feedback is sensitive to deep soil carbon decomposability but not deep soil nitrogen dynamics. Proceedings of the National Academies of Science, 112, 12, 3752-3757, doi:10.1073/pnas.1415123112 .. _Kovenetal2017: @@ -981,232 +661,147 @@ Kucharik, C.J., J.M. Norman, and S.T. Gower, 1998. Measurements of branch area a .. _Kuchariketal2000: -Kucharik, C.J., Foley, J.A., Delire, C., Fisher, V.A., Coe, M.T., -Lenters, J.D., Young-Molling, C., and Ramankutty, N. 2000. Testing the -performance of a dynamic global ecosystem model: water balance, carbon -balance, and vegetation structure. Global Biogeochem. Cycles 14: -795–825. +Kucharik, C.J., Foley, J.A., Delire, C., Fisher, V.A., Coe, M.T., Lenters, J.D., Young-Molling, C., and Ramankutty, N. 2000. Testing the performance of a dynamic global ecosystem model: water balance, carbon balance, and vegetation structure. Global Biogeochem. Cycles 14: 795–825. .. _KucharikBrye2003: -Kucharik, C.J., and Brye, K.R. 2003. Integrated BIosphere Simulator -(IBIS) yield and nitrate loss predictions for Wisconsin maize receiving -varied amounts of nitrogen fertilizer. Journal of Environmental Quality -32: 247–268. +Kucharik, C.J., and Brye, K.R. 2003. Integrated BIosphere Simulator (IBIS) yield and nitrate loss predictions for Wisconsin maize receiving varied amounts of nitrogen fertilizer. Journal of Environmental Quality 32: 247–268. .. _Laddetal2992: -Ladd, J.N., Jocteur-Monrozier, L. and Amato, M., 1992. Carbon turnover -and nitrogen transformations in an alfisol and vertisol amended with -[U-:math:`{}^{14}`\ C] glucose and [:math:`{}^{15}`\ N] ammonium -sulfate. Soil Biology and Biochemistry, 24: 359-371. +Ladd, J.N., Jocteur-Monrozier, L. and Amato, M., 1992. Carbon turnover and nitrogen transformations in an alfisol and vertisol amended with [U-:math:`{}^{14}`\ C] glucose and [:math:`{}^{15}`\ N] ammonium sulfate. Soil Biology and Biochemistry, 24: 359-371. .. _Lamarqueetal2010: -Lamarque, J.-F., et al. 2010. Historical (1850-2000) gridded -anthropogenic and biomass burning emissions of reactive gases and -aerosols: methodology and application. Atmos. Chem. Phys. Discuss. -10:4963-5019. DOI:10.5194/acpd-10-4963-2010. +Lamarque, J.-F., et al. 2010. Historical (1850-2000) gridded anthropogenic and biomass burning emissions of reactive gases and aerosols: methodology and application. Atmos. Chem. Phys. Discuss. 10:4963-5019. DOI:10.5194/acpd-10-4963-2010. .. _Larcher1995: -Larcher, W. 1995. Physiological Plant Ecology, Springer-Verlag, Berlin -Heidelberg. +Larcher, W. 1995. Physiological Plant Ecology, Springer-Verlag, Berlin Heidelberg. .. _LavigneRyan1997: -Lavigne, M.B., and Ryan, M.G. 1997. Growth and maintenance respiration -rates of aspen, black spruce, and jack pine stems at northern and -southern BOREAS sites. Tree Phys. 17:543-551. +Lavigne, M.B., and Ryan, M.G. 1997. Growth and maintenance respiration rates of aspen, black spruce, and jack pine stems at northern and southern BOREAS sites. Tree Phys. 17:543-551. .. _Lawetal2003: -Law, B.E., Sun, O.J., Campbell, J., Van Tuyl, S. and Thornton, P.E. -2003. Changes in carbon storage and fluxes in a chronosequence of -ponderosa pine. Global Change Biology, 9: 510-514. +Law, B.E., Sun, O.J., Campbell, J., Van Tuyl, S. and Thornton, P.E. 2003. Changes in carbon storage and fluxes in a chronosequence of ponderosa pine. Global Change Biology, 9: 510-514. .. _Lawrenceetal2007: -Lawrence, D.M., Thornton, P.E., Oleson, K.W., and Bonan, G.B. 2007. The -partitioning of evapotranspiration into transpiration, soil evaporation, -and canopy evaporation in a GCM: Impacts on land-atmosphere interaction. -J. Hydrometeor. 8:862-880. +Lawrence, D.M., Thornton, P.E., Oleson, K.W., and Bonan, G.B. 2007. The partitioning of evapotranspiration into transpiration, soil evaporation, and canopy evaporation in a GCM: Impacts on land-atmosphere interaction. J. Hydrometeor. 8:862-880. .. _LawrenceSlater2008: -Lawrence, D.M., and Slater, A.G. 2008. Incorporating organic soil into a -global climate model. Clim. Dyn. 30. DOI:10.1007/s00382-007-0278-1. +Lawrence, D.M., and Slater, A.G. 2008. Incorporating organic soil into a global climate model. Clim. Dyn. 30. DOI:10.1007/s00382-007-0278-1. .. _Lawrenceetal2008: -Lawrence, D.M., Slater, A.G., Romanovsky, V.E., and Nicolsky, D.J. 2008. -The sensitivity of a model projection of near-surface permafrost -degradation to soil column depth and inclusion of soil organic matter. -J. Geophys. Res. 113:F02011. DOI:10.1029/2007JF000883. +Lawrence, D.M., Slater, A.G., Romanovsky, V.E., and Nicolsky, D.J. 2008. The sensitivity of a model projection of near-surface permafrost degradation to soil column depth and inclusion of soil organic matter. J. Geophys. Res. 113:F02011. DOI:10.1029/2007JF000883. .. _Lawrenceetal2011: -Lawrence, D.M., K.W. Oleson, M.G. Flanner, P.E. Thornton, S.C. Swenson, -P.J. Lawrence, X. Zeng, Z.-L. Yang, S. Levis, K. Sakaguchi, G.B. Bonan, -and A.G. Slater, 2011. Parameterization improvements and functional and -structural advances in version 4 of the Community Land Model. J. Adv. -Model. Earth Sys. 3. DOI:10.1029/2011MS000045. +Lawrence, D.M., K.W. Oleson, M.G. Flanner, P.E. Thornton, S.C. Swenson, P.J. Lawrence, X. Zeng, Z.-L. Yang, S. Levis, K. Sakaguchi, G.B. Bonan, and A.G. Slater, 2011. Parameterization improvements and functional and structural advances in version 4 of the Community Land Model. J. Adv. Model. Earth Sys. 3. DOI:10.1029/2011MS000045. .. _Lawrenceetal2016: -Lawrence, D.M., Hurtt, G.C., Arneth, A., Brovkin, V., Calvin, K.V., -Jones, A.D., Jones, C.D., Lawrence, P.J., de Noblet-Ducoudré, N., Pongratz, -J., Seneviratne, S.I., and Shevliakova, E. 2016. The Land Use Model -Intercomparison Project (LUMIP) contribution to CMIP6: rationale -and experimental design. Geosci. Model Dev. 9:2973-2998. -DOI:10.5194/gmd-9-2973-2016. +Lawrence, D.M., Hurtt, G.C., Arneth, A., Brovkin, V., Calvin, K.V., Jones, A.D., Jones, C.D., Lawrence, P.J., de Noblet-Ducoudré, N., Pongratz, J., Seneviratne, S.I., and Shevliakova, E. 2016. The Land Use Model Intercomparison Project (LUMIP) contribution to CMIP6: rationale and experimental design. Geosci. Model Dev. 9:2973-2998. DOI:10.5194/gmd-9-2973-2016. .. _LawrenceChase2007: -Lawrence, P.J., and Chase, T.N. 2007. Representing a MODIS consistent -land surface in the Community Land Model (CLM 3.0). J. Geophys. Res. -112:G01023. DOI:10.1029/2006JG000168. +Lawrence, P.J., and Chase, T.N. 2007. Representing a MODIS consistent land surface in the Community Land Model (CLM 3.0). J. Geophys. Res. 112:G01023. DOI:10.1029/2006JG000168. .. _LawrenceChase2010: -Lawrence, P.J., and Chase, T.N. 2010. Investigating the climate impacts -of global land cover change in the Community Climate System Model. Int. -J. Climatol. 30:2066-2087. DOI:10.1002/joc.2061. +Lawrence, P.J., and Chase, T.N. 2010. Investigating the climate impacts of global land cover change in the Community Climate System Model. Int. J. Climatol. 30:2066-2087. DOI:10.1002/joc.2061. .. _Lawrenceetal2012: -Lawrence, P.J., et al. 2012. Simulating the biogeochemical and -biogeophysical impacts of transient land cover change and wood harvest -in the Community Climate System Model (CCSM4) from 1850 to 2100. J. -Climate 25:3071-3095. DOI:10.1175/JCLI-D-11-00256.1. +Lawrence, P.J., et al. 2012. Simulating the biogeochemical and biogeophysical impacts of transient land cover change and wood harvest in the Community Climate System Model (CCSM4) from 1850 to 2100. J. Climate 25:3071-3095. DOI:10.1175/JCLI-D-11-00256.1. .. _LehnerDoll2004: -Lehner, B. and Döll, P., 2004. Development and validation of a global -database of lakes, reservoirs and wetlands, J. Hydrol., 296, 1–22. +Lehner, B. and Döll, P., 2004. Development and validation of a global database of lakes, reservoirs and wetlands, J. Hydrol., 296, 1–22. + +.. _Leeetal2014: + +Lee, H., Swenson, S.C., Slater A.G. and Lawrence D.M., 2014. Effects of excess ground ice on projections of permafrost in a warming climate. Environmental Research Letters 9:12 124006. DOI: 10.1088/1748-9326/9/12/124006 .. _Lehneretal2008: -Lehner, B., Verdin, K. and Jarvis, A., 2008. New global hydrograhy -derived from spaceborne elevation data. Eos Trans., AGU, 89, 93 – 94. +Lehner, B., Verdin, K. and Jarvis, A., 2008. New global hydrograhy derived from spaceborne elevation data. Eos Trans., AGU, 89, 93 – 94. .. _LePageetal2010: -Le Page, Y., van der Werf, G.R., Morton, D.C., and Pereira, J.M.C. 2010. -Modeling fire-driven deforestation potential in Amazonia under current -and projected climate conditions. J. Geophys. Res. 115:G03012. -DOI:10.1029/2009JG001190. +Le Page, Y., van der Werf, G.R., Morton, D.C., and Pereira, J.M.C. 2010. Modeling fire-driven deforestation potential in Amazonia under current and projected climate conditions. J. Geophys. Res. 115:G03012. DOI:10.1029/2009JG001190. .. _Lerman1979: -Lerman, A., 1979. Geochemical processes: Water and sediment -environments. John Wiley and Sons, New York, N.Y. +Lerman, A., 1979. Geochemical processes: Water and sediment environments. John Wiley and Sons, New York, N.Y. .. _Lettsetal2000: -Letts, M.G., Roulet, N.T., Comer, N.T., Skarupa, M.R., and Verseghy, -D.L. 2000. Parametrization of peatland hydraulic properties for the -Canadian Land Surface Scheme. Atmos.-Ocean 38:141-160. +Letts, M.G., Roulet, N.T., Comer, N.T., Skarupa, M.R., and Verseghy, D.L. 2000. Parametrization of peatland hydraulic properties for the Canadian Land Surface Scheme. Atmos.-Ocean 38:141-160. .. _Levisetal2003: -Levis, S., Wiedinmyer, C., Bonan, G.B., and Guenther, A. 2003. -Simulating biogenic volatile organic compound emissions in the Community -Climate System Model. J. Geophys. Res. 108:4659. -DOI:10.1029/2002JD003203. +Levis, S., Wiedinmyer, C., Bonan, G.B., and Guenther, A. 2003. Simulating biogenic volatile organic compound emissions in the Community Climate System Model. J. Geophys. Res. 108:4659. DOI:10.1029/2002JD003203. .. _Levisetal2004: -Levis, S., Bonan, G.B., Vertenstein, M., and Oleson, K.W. 2004. The -community land model’s dynamic global vegetation model (CLM-DGVM): -technical description and user’s guide. NCAR Technical Note -NCAR/TN-459+STR. National Center for Atmospheric Research, Boulder, -Colorado. 50 pp. +Levis, S., Bonan, G.B., Vertenstein, M., and Oleson, K.W. 2004. The community land model's dynamic global vegetation model (CLM-DGVM): technical description and user's guide. NCAR Technical Note NCAR/TN-459+STR. National Center for Atmospheric Research, Boulder, Colorado. 50 pp. .. _Levisetal2009: -Levis, S., Thornton, P., Bonan, G., and Kucharik, C. 2009. Modeling land -use and land management with the Community Land Model. iLeaps -newsletter, No. 7. +Levis, S., Thornton, P., Bonan, G., and Kucharik, C. 2009. Modeling land use and land management with the Community Land Model. iLeaps newsletter, No. 7. .. _Levisetal2012: -Levis, S., Bonan, G., Kluzek, E., Thornton, P., Jones, A., Sacks, W., -and Kucharik, C 2012. Interactive crop management in the Community Earth -System Model (CESM1): Seasonal influences on land-atmosphere fluxes. J. -Climate 25: 4839-4859. DOI:10.1175/JCLI-D-11-00446.1. +Levis, S., Bonan, G., Kluzek, E., Thornton, P., Jones, A., Sacks, W., and Kucharik, C 2012. Interactive crop management in the Community Earth System Model (CESM1): Seasonal influences on land-atmosphere fluxes. J. Climate 25: 4839-4859. DOI:10.1175/JCLI-D-11-00446.1. .. _Levisetal2016: -Levis, S., Badger, A., Drewniak, B., Nevison, C., Ren, X. 2016. CLMcrop -yields and water requirements: avoided impacts by choosing RCP 4.5 over 8.5. -Climatic Change. DOI:10.1007/s10584-016-1654-9. +Levis, S., Badger, A., Drewniak, B., Nevison, C., Ren, X. 2016. CLMcrop yields and water requirements: avoided impacts by choosing RCP 4.5 over 8.5. Climatic Change. DOI:10.1007/s10584-016-1654-9. .. _Lietal2000: -Li, C., Aber, J., Stange, F., Butterbach-Bahl, K. and Papen, H. 2000. A -process-oriented model of N2O and NO emissions from forest soils: 1. -Model development. J. Geophys. Res. 105(D4):4369-4384. +Li, C., Aber, J., Stange, F., Butterbach-Bahl, K. and Papen, H. 2000. A process-oriented model of N2O and NO emissions from forest soils: 1. Model development. J. Geophys. Res. 105(D4):4369-4384. .. _Lietal2012a: -Li, F., Zeng, X.-D., and Levis, S. 2012a. A process-based fire -parameterization of intermediate complexity in a Dynamic Global -Vegetation Model. Biogeosciences 9:2761-2780. +Li, F., Zeng, X.-D., and Levis, S. 2012a. A process-based fire parameterization of intermediate complexity in a Dynamic Global Vegetation Model. Biogeosciences 9:2761-2780. .. _Lietal2012b: -Li, F., Zeng, X. D., and Levis, S. 2012b. Corrigendum to “A -process-based fire parameterization of intermediate complexity in a -Dynamic Global Vegetation Model” published in Biogeosciences, 9, -2761–2780, 2012”. Biogeosciences 9: 4771-4772. +Li, F., Zeng, X. D., and Levis, S. 2012b. Corrigendum to "A process-based fire parameterization of intermediate complexity in a Dynamic Global Vegetation Model" published in Biogeosciences, 9, 2761–2780, 2012". Biogeosciences 9: 4771-4772. .. _Lietal2013a: -Li, F., Levis, S., and Ward, D. S. 2013a. Quantifying the role of fire -in the Earth system – Part 1: Improved global fire modeling in the -Community Earth System Model (CESM1). Biogeosciences 10:2293-2314. +Li, F., Levis, S., and Ward, D. S. 2013a. Quantifying the role of fire in the Earth system – Part 1: Improved global fire modeling in the Community Earth System Model (CESM1). Biogeosciences 10:2293-2314. .. _LiLawrence2017: -Li, F., and Lawrence, D. 2017. Role of fire in the global land water -budget during the 20th century through changing ecosystems. -J. Clim. 30: 1894-1908. +Li, F., and Lawrence, D. 2017. Role of fire in the global land water budget during the 20th century through changing ecosystems. J. Clim. 30: 1894-1908. .. _Lietal2013b: -Li, H.-Y., Huang, M., Tesfa, T., Ke, Y., Sun, Y., Liu, Y., and Leung, L. -R. 2013b. A subbasin-based framework to represent land surface processes -in an Earth System Model, Geosci. Model Dev. Discuss. 6:2699-2730. -DOI:10.5194/gmdd-6-2699-2013. +Li, H.-Y., Huang, M., Tesfa, T., Ke, Y., Sun, Y., Liu, Y., and Leung, L. R. 2013b. A subbasin-based framework to represent land surface processes in an Earth System Model, Geosci. Model Dev. Discuss. 6:2699-2730. DOI:10.5194/gmdd-6-2699-2013. .. _Lietal2011: -Li, H., Huang, M., Wigmosta, M.S., Ke, Y., Coleman, A.M., Leung, L.R., -Wang, A., and Ricciuto, D.M. 2011. Evaluating runoff simulations from -the Community Land Model 4.0 using observations from flux towers and a -mountainous watershed. J. Geophys. Res. 116:D24120. -DOI:10.1029/2011JD016276. +Li, H., Huang, M., Wigmosta, M.S., Ke, Y., Coleman, A.M., Leung, L.R., Wang, A., and Ricciuto, D.M. 2011. Evaluating runoff simulations from the Community Land Model 4.0 using observations from flux towers and a mountainous watershed. J. Geophys. Res. 116:D24120. DOI:10.1029/2011JD016276. .. _Lietal2015a: -Li, H., L. Leung, A. Getirana, M. Huang, H. Wu, Y. Xu, J. Guo and -N. Voisin. 2015a. Evaluating global streamflow simulations by a -physically-based routing model coupled with the Community Land Model, -J. of Hydromet., 16(2):948-971, doi: 10.1175/JHM-D-14-0079.1 +Li, H., L. Leung, A. Getirana, M. Huang, H. Wu, Y. Xu, J. Guo and N. Voisin. 2015a. Evaluating global streamflow simulations by a physically-based routing model coupled with the Community Land Model, J. of Hydromet., 16(2):948-971, doi: 10.1175/JHM-D-14-0079.1 .. _Lietal2015b: -Li, H., L. Leung, T. Tesfa, N. Voisin, M. Hejazi, L. Liu, Y. Liu, -J. Rice, H. Wu, and X. Yang. 2015. Modeling stream temperature in the -Anthropocene: An earth system modeling approach, J. Adv. Model. -Earth Syst., 7, doi:10.1002/2015MS000471. +Li, H., L. Leung, T. Tesfa, N. Voisin, M. Hejazi, L. Liu, Y. Liu, J. Rice, H. Wu, and X. Yang. 2015. Modeling stream temperature in the Anthropocene: An earth system modeling approach, J. Adv. Model. Earth Syst., 7, doi:10.1002/2015MS000471. .. _Liangetal1994: -Liang, X., Lettenmaier, D.P., Wood, E.F., and Burges, S.J. 1994. A -simple hydrologically based model of land surface water and energy -fluxes for GSMs. J. Geophys. Res. 99(D7):14,415–14,428. +Liang, X., Lettenmaier, D.P., Wood, E.F., and Burges, S.J. 1994. A simple hydrologically based model of land surface water and energy fluxes for GSMs. J. Geophys. Res. 99(D7):14,415–14,428. .. _lichstein2011: @@ -1214,99 +809,67 @@ Lichstein, J.W. and S.W. Pacala, 2011. Local diversity in heterogeneous landscap .. _LipscombSacks2012: -Lipscomb, W., and Sacks, W. 2012. The CESM land ice model documentation -and user’s guide. 46 pp. [Available online at -http://www.cesm.ucar.edu/models/cesm1.1/cism/]. +Lipscomb, W., and Sacks, W. 2012. The CESM land ice model documentation and user's guide. 46 pp. [Available online at http://www.cesm.ucar.edu/models/cesm1.1/cism/]. .. _lischke2006: Lischke, H. et al., 2006. TreeMig: a forest-landscape model for simulating spatio-temporal patterns from stand to landscape scale. Ecological Modelling 199.4, pp. 409-420. 41 - .. _LloydTaylor1994: -Lloyd, J. and Taylor, J.A., 1994. On the temperature dependence of soil -respiration. Functional Ecology, 8: 315-323. +Lloyd, J. and Taylor, J.A., 1994. On the temperature dependence of soil respiration. Functional Ecology, 8: 315-323. .. _Lloydetal2010: -Lloyd, J., et al. 2010. Optimisation of photosynthetic carbon gain and -within-canopy gradients of associated foliar traits for Amazon forest -trees. Biogeosci. 7:1833-1859. DOI:10.5194/bg-7-1833-2010. +Lloyd, J., et al. 2010. Optimisation of photosynthetic carbon gain and within-canopy gradients of associated foliar traits for Amazon forest trees. Biogeosci. 7:1833-1859. DOI:10.5194/bg-7-1833-2010. .. _Lobelletal2006: -Lobell, D.B., Bala, G., and Duffy, P.B. 2006. Biogeophysical impacts of -cropland management changes on climate. Geophys. Res. Lett. 33:L06708. -DOI:10.1029/2005GL025492. +Lobell, D.B., Bala, G., and Duffy, P.B. 2006. Biogeophysical impacts of cropland management changes on climate. Geophys. Res. Lett. 33:L06708. DOI:10.1029/2005GL025492. .. _Lombardozzietal2015: -Lombardozzi, D.L., Bonan, G.B., Smith, N.G., Dukes, J.S. 2015. Temperature -acclimation of photosynthesis and respiration: A key uncertainty in the -carbon cycle-climate feedback. Geophys. Res. Lett. 42:8624-8631. +Lombardozzi, D.L., Bonan, G.B., Smith, N.G., Dukes, J.S. 2015. Temperature acclimation of photosynthesis and respiration: A key uncertainty in the carbon cycle-climate feedback. Geophys. Res. Lett. 42:8624-8631. .. _Lovelandetal2000: -Loveland, T.R., Reed, B.C., Brown, J.F., Ohlen, D.O., Zhu, Z., Yang, L., -and Merchant, J.W. 2000. Development of a global land cover -characteristics database and IGBP DISCover from 1 km AVHRR data. Int. J. -Remote Sens. 21:1303-1330. +Loveland, T.R., Reed, B.C., Brown, J.F., Ohlen, D.O., Zhu, Z., Yang, L., and Merchant, J.W. 2000. Development of a global land cover characteristics database and IGBP DISCover from 1 km AVHRR data. Int. J. Remote Sens. 21:1303-1330. .. _Lowe1977: -Lowe, P.R. 1977. An approximating polynomial for the computation of -saturation vapor pressure. J. Appl. Meteor. 16:100-103. +Lowe, P.R. 1977. An approximating polynomial for the computation of saturation vapor pressure. J. Appl. Meteor. 16:100-103. .. _Luoetal2006: -Luo, Y., Hui, D., and Zhang, D. 2006. Elevated CO2 stimulates net -accumulations of carbon and nitrogen in land ecosystems: a -meta-analysis. Ecology 87:53-63. +Luo, Y., Hui, D., and Zhang, D. 2006. Elevated CO2 stimulates net accumulations of carbon and nitrogen in land ecosystems: a meta-analysis. Ecology 87:53-63. .. _Magilletal1997: -Magill, A.H. et al., 1997. Biogeochemical response of forest ecosystems -to simulated chronic nitrogen deposition. Ecological Applications, 7: -402-415. +Magill, A.H. et al., 1997. Biogeochemical response of forest ecosystems to simulated chronic nitrogen deposition. Ecological Applications, 7: 402-415. .. _Mahowaldetal2006: -Mahowald, N.M., Muhs, D.R., Levis, S., Rasch, P.J., Yoshioka, M., -Zender, C.S., and Luo, C. 2006. Change in atmospheric mineral aerosols -in response to climate: last glacial period, pre-industrial, modern and -doubled CO\ :sub:`2` climates. J. Geophys. Res\ *.* 111:D10202. -DOI:10.1029/2005JD006653. +Mahowald, N.M., Muhs, D.R., Levis, S., Rasch, P.J., Yoshioka, M., Zender, C.S., and Luo, C. 2006. Change in atmospheric mineral aerosols in response to climate: last glacial period, pre-industrial, modern and doubled CO\ :sub:`2` climates. J. Geophys. Res\ *.* 111:D10202. DOI:10.1029/2005JD006653. .. _Makela2002: -Makela, A. 2002. Derivation of stem taper from the pipe model theory in -a carbon balance framework. Tree Phys. 22:891-905. +Makela, A. 2002. Derivation of stem taper from the pipe model theory in a carbon balance framework. Tree Phys. 22:891-905. .. _Maoetal2012: -Mao, J., Thornton, P.E., Shi, X., Zhao, M., and Post, W.M. 2012. Remote -sensing evaluation of CLM4 GPP for the period 2000 to 2009. J. Climate -25:5327-5342. +Mao, J., Thornton, P.E., Shi, X., Zhao, M., and Post, W.M. 2012. Remote sensing evaluation of CLM4 GPP for the period 2000 to 2009. J. Climate 25:5327-5342. .. _Maoetal2013: -Mao, J., Shi, X., Thornton, P.E., Hoffman, F.M., Zhu, Z., and Ranga B. -Myneni, R.B. 2013. Global latitudinal-asymmetric vegetation growth -trends and their driving mechanisms: 1982-2009. Remote Sensing -5:1484-1497. +Mao, J., Shi, X., Thornton, P.E., Hoffman, F.M., Zhu, Z., and Ranga B. Myneni, R.B. 2013. Global latitudinal-asymmetric vegetation growth trends and their driving mechanisms: 1982-2009. Remote Sensing 5:1484-1497. .. _Martinetal1980: -Martin, J.P., Haider, K. and Kassim, G., 1980. Biodegradation and -stabilization after 2 years of specific crop, lignin, and polysaccharide -carbons in soils. Soil Science Society of America Journal 44:1250-1255. +Martin, J.P., Haider, K. and Kassim, G., 1980. Biodegradation and stabilization after 2 years of specific crop, lignin, and polysaccharide carbons in soils. Soil Science Society of America Journal 44:1250-1255. .. _Maryetal1993: -Mary, B., Fresneau, C., Morel, J.L. and Mariotti, A., 1993. C and N -cycling during decomposition of root mucilage, roots and glucose in -soil. Soil Biology and Biochemistry 25:1005-1014. +Mary, B., Fresneau, C., Morel, J.L. and Mariotti, A., 1993. C and N cycling during decomposition of root mucilage, roots and glucose in soil. Soil Biology and Biochemistry 25:1005-1014. .. _Mcdowelletal2013: @@ -1314,52 +877,44 @@ McDowell, N.G. et al., 2013. Evaluating theories of drought-induced vegetation m .. _McGuireetal1992: -McGuire, A.D., Melillo, J.M., Joyce, L.A., Kicklighter, D.W., Grace, -A.L., Moore III, B., and Vorosmarty, C.J. 1992. Interactions between -carbon and nitrogen dynamics in estimating net primary productivity for -potential vegetation in North America. Global Biogeochem. Cycles -6:101-124. +McGuire, A.D., Melillo, J.M., Joyce, L.A., Kicklighter, D.W., Grace, A.L., Moore III, B., and Vorosmarty, C.J. 1992. Interactions between carbon and nitrogen dynamics in estimating net primary productivity for potential vegetation in North America. Global Biogeochem. Cycles 6:101-124. .. _Medlynetal2011: -Medlyn, B.E., Duursma, R.A., Eamus, D., Ellsworth, D.S., Prentice, I.C., -Barton, C.V.M., Crous, K.Y., De Angelis, P., Freeman, M., and -Wingate, L., 2011. Reconciling the optimal and empirical approaches to -modelling stomatal conductance. Global Change Biology, 17: 2134–2144. -doi:10.1111/j.1365-2486.2010.02375.x +Medlyn, B.E., Duursma, R.A., Eamus, D., Ellsworth, D.S., Prentice, I.C., Barton, C.V.M., Crous, K.Y., De Angelis, P., Freeman, M., and Wingate, L., 2011. Reconciling the optimal and empirical approaches to modelling stomatal conductance. Global Change Biology, 17: 2134–2144. doi:10.1111/j.1365-2486.2010.02375.x + +.. _Meieretal2022: + +Meier, R., Davin, E. L., Bonan, G. B., Lawrence, D. M., Hu, X., +Duveiller, G., Prigent, C., and Seneviratne, S. I., 2022. Impacts of a +revised surface roughness parameterization in the Community Land Model +5.1, Geosci. Model Dev., 15, 2365–2393, +https://doi.org/10.5194/gmd-15-2365-2022 + .. _MelzerOLeary1987: -Melzer, E., and O’Leary, M.H. 1987. Anapleurotic CO2 Fixation by -Phosphoenolpyruvate Carboxylase in C3 Plants. Plant. Physiol. 84:58. +Melzer, E., and O'Leary, M.H. 1987. Anapleurotic CO2 Fixation by Phosphoenolpyruvate Carboxylase in C3 Plants. Plant. Physiol. 84:58. .. _Milleretal1994: -Miller, J.R., Russell, G.L., and Caliri, G. 1994. Continental-scale -river flow in climate models. J. Climate 7:914-928. +Miller, J.R., Russell, G.L., and Caliri, G. 1994. Continental-scale river flow in climate models. J. Climate 7:914-928. .. _MillingtonQuirk1961: -Millington, R. and Quirk, J.P., 1961. Permeability of Porous Solids. -Transactions of the Faraday Society 57:1200-1207. +Millington, R. and Quirk, J.P., 1961. Permeability of Porous Solids. Transactions of the Faraday Society 57:1200-1207. .. _Mironovetal2010: -Mironov, D. et al., 2010. Implementation of the lake parameterisation -scheme FLake into the numerical weather prediction model COSMO. Boreal -Environment Research 15:218-230. +Mironov, D. et al., 2010. Implementation of the lake parameterisation scheme FLake into the numerical weather prediction model COSMO. Boreal Environment Research 15:218-230. .. _MitchellJones2005: -Mitchell, T.D., and Jones, P.D. 2005. An improved method of constructing -a database of monthly climate observations and associated -high-resolution grids. Int. J. Climatol. 25:693-712. +Mitchell, T.D., and Jones, P.D. 2005. An improved method of constructing a database of monthly climate observations and associated high-resolution grids. Int. J. Climatol. 25:693-712. .. _Moldrupetal2003: -Moldrup, P. et al. 2003. Modeling diffusion and reaction in soils: X. A -unifying model for solute and gas diffusivity in unsaturated soil. Soil -Science 168:321-337. +Moldrup, P. et al. 2003. Modeling diffusion and reaction in soils: X. A unifying model for solute and gas diffusivity in unsaturated soil. Soil Science 168:321-337. .. _mc_2001: @@ -1367,62 +922,39 @@ Moorcroft, P.R., G.C. Hurtt, and S.W. Pacala, 2001. A method for scaling vegetat .. _Mynenietal2002: -Myneni, R.B., et al. 2002. Global products of vegetation leaf area and -fraction absorbed PAR from year one of MODIS data. Remote Sens. Environ. -83:214-231. +Myneni, R.B., et al. 2002. Global products of vegetation leaf area and fraction absorbed PAR from year one of MODIS data. Remote Sens. Environ. 83:214-231. .. _Neffetal2005: -Neff, J.C., Harden, J.W. and Gleixner, G. 2005. Fire effects on soil -organic matter content, composition, and nutrients in boreal interior -Alaska. Canadian Journal of Forest Research-Revue Canadienne De -Recherche Forestiere 35:2178-2187. +Neff, J.C., Harden, J.W. and Gleixner, G. 2005. Fire effects on soil organic matter content, composition, and nutrients in boreal interior Alaska. Canadian Journal of Forest Research-Revue Canadienne De Recherche Forestiere 35:2178-2187. .. _Neitschetal2005: -Neitsch, S.L., Arnold, J.G., Kiniry, J.R., and Williams J.R. 2005. Soil -and Water Assessment Tool, Theoretical Documentation: Version 2005. -Temple, TX. USDA Agricultural Research Service and Texas A&M Blackland -Research Center. +Neitsch, S.L., Arnold, J.G., Kiniry, J.R., and Williams J.R. 2005. Soil and Water Assessment Tool, Theoretical Documentation: Version 2005. Temple, TX. USDA Agricultural Research Service and Texas A&M Blackland Research Center. .. _NegronJuarezetal2015: -Negron-Juarez, R. Koven, C.D., Riley, W.J., Knox, R.G., Chambers, J.Q. -2015. Environmental Research Letters 10:064017. DOI:10.1088/1748-9326/10/6/064017. +Negron-Juarez, R. Koven, C.D., Riley, W.J., Knox, R.G., Chambers, J.Q. 2015. Environmental Research Letters 10:064017. DOI:10.1088/1748-9326/10/6/064017. .. _NemaniRunning1996: -Nemani, R.R., and Running, S.W. 1996. Implementation of a hierarchical -global vegetation classification in ecosystem function models. J. Veg. -Sci. 7:337-346. +Nemani, R.R., and Running, S.W. 1996. Implementation of a hierarchical global vegetation classification in ecosystem function models. J. Veg. Sci. 7:337-346. .. _Niinemetstal1998: -Niinemets, U., Kull, O., and Tenhunen, J.D. 1998. An analysis of light -effects on foliar morphology, physiology, and light interception in -temperate deciduous woody species of contrasting shade tolerance. Tree -Phys. 18:681-696. +Niinemets, U., Kull, O., and Tenhunen, J.D. 1998. An analysis of light effects on foliar morphology, physiology, and light interception in temperate deciduous woody species of contrasting shade tolerance. Tree Phys. 18:681-696. .. _Niuetal2005: -Niu, G.-Y., Yang, Z.-L., Dickinson, R.E., and Gulden, L.E. 2005. A -simple TOPMODEL-based runoff parameterization (SIMTOP) for use in global -climate models. J. Geophys. Res. 110:D21106. DOI:10.1029/2005JD006111. +Niu, G.-Y., Yang, Z.-L., Dickinson, R.E., and Gulden, L.E. 2005. A simple TOPMODEL-based runoff parameterization (SIMTOP) for use in global climate models. J. Geophys. Res. 110:D21106. DOI:10.1029/2005JD006111. .. _NiuYang2006: -Niu, G.-Y., and Yang, Z.-L. 2006. Effects of frozen soil on snowmelt -runoff and soil water storage at a continental scale. J. Hydrometeor. -7:937-952. +Niu, G.-Y., and Yang, Z.-L. 2006. Effects of frozen soil on snowmelt runoff and soil water storage at a continental scale. J. Hydrometeor. 7:937-952. -Niu, G.-Y., Yang, Z.-L., Dickinson, R.E., Gulden, L.E., and Su, H. 2007. -Development of a simple groundwater model for use in climate models and -evaluation with Gravity Recovery and Climate Experiment data. J. -Geophys. Res. 112:D07103. DOI:10.1029/2006JD007522. +Niu, G.-Y., Yang, Z.-L., Dickinson, R.E., Gulden, L.E., and Su, H. 2007. Development of a simple groundwater model for use in climate models and evaluation with Gravity Recovery and Climate Experiment data. J. Geophys. Res. 112:D07103. DOI:10.1029/2006JD007522. -Niu, G.-Y., and Yang, Z.-L. 2007. An observation-based formulation of -snow cover fraction and its evaluation over large North American river -basins. J. Geophys. Res. 112:D21101. DOI:10.1029/2007JD008674. +Niu, G.-Y., and Yang, Z.-L. 2007. An observation-based formulation of snow cover fraction and its evaluation over large North American river basins. J. Geophys. Res. 112:D21101. DOI:10.1029/2007JD008674. .. _norman1979: @@ -1430,147 +962,95 @@ Norman, J.M., 1979. Modeling the complete crop canopy. Modification of the Aeria .. _Oikawaetal2005: -Oikawa, S., Hikosaka, K. and Hirose, T., 2005. Dynamics of leaf area and -nitrogen in the canopy of an annual herb, Xanthium canadense. Oecologia, -143: 517-526. +Oikawa, S., Hikosaka, K. and Hirose, T., 2005. Dynamics of leaf area and nitrogen in the canopy of an annual herb, Xanthium canadense. Oecologia, 143: 517-526. .. _Oke1987: -Oke, T. 1987. Boundary Layer Climates (2:math:`{}^{nd}` edition). -Routledge, London and New York. +Oke, T. 1987. Boundary Layer Climates (2:math:`{}^{nd}` edition). Routledge, London and New York. .. _OlesonBonan2000: -Oleson, K.W., and Bonan, G.B. 2000. The effects of remotely-sensed plant -functional type and leaf area index on simulations of boreal forest -surface fluxes by the NCAR land surface model. J. Hydrometeor. -1:431-446. +Oleson, K.W., and Bonan, G.B. 2000. The effects of remotely-sensed plant functional type and leaf area index on simulations of boreal forest surface fluxes by the NCAR land surface model. J. Hydrometeor. 1:431-446. .. _Olesonetal2004: -Oleson, K.W., Dai, Y., Bonan, G., Bosilovich, M., Dickinson, R., -Dirmeyer, P., Hoffman, F., Houser, P., Levis, S., Niu, G.-Y., Thornton, -P., Vertenstein, M., Yang, Z.-L., and Zeng. X. 2004. Technical -description of the Community Land Model (CLM). NCAR Technical Note -NCAR/TN-461+STR. National Center for Atmospheric Research, Boulder, -Colorado. 173 pp. +Oleson, K.W., Dai, Y., Bonan, G., Bosilovich, M., Dickinson, R., Dirmeyer, P., Hoffman, F., Houser, P., Levis, S., Niu, G.-Y., Thornton, P., Vertenstein, M., Yang, Z.-L., and Zeng. X. 2004. Technical description of the Community Land Model (CLM). NCAR Technical Note NCAR/TN-461+STR. National Center for Atmospheric Research, Boulder, Colorado. 173 pp. .. _Olesonetal2008a: -Oleson, K.W., Niu, G.-Y., Yang, Z.-L., Lawrence, D.M., Thornton, P.E., -Lawrence, P.J., Stöckli, R., Dickinson, R.E., Bonan, G.B., Levis, S., -Dai, A., and Qian, T. 2008a. Improvements to the Community Land Model -and their impact on the hydrological cycle. J. Geophys. Res. 113:G01021. -DOI:10.1029/2007JG000563. +Oleson, K.W., Niu, G.-Y., Yang, Z.-L., Lawrence, D.M., Thornton, P.E., Lawrence, P.J., Stöckli, R., Dickinson, R.E., Bonan, G.B., Levis, S., Dai, A., and Qian, T. 2008a. Improvements to the Community Land Model and their impact on the hydrological cycle. J. Geophys. Res. 113:G01021. DOI:10.1029/2007JG000563. .. _Olesonetal2008b: -Oleson, K.W., Bonan, G.B., Feddema, J., Vertenstein, M., and Grimmond, -C.S.B. 2008b. An urban parameterization for a global climate model. 1. -Formulation and evaluation for two cities. J. Appl. Meteor. Clim. -47:1038-1060. +Oleson, K.W., Bonan, G.B., Feddema, J., Vertenstein, M., and Grimmond, C.S.B. 2008b. An urban parameterization for a global climate model. 1. Formulation and evaluation for two cities. J. Appl. Meteor. Clim. 47:1038-1060. .. _Olesonetal2008c: -Oleson, K.W., Bonan, G.B., Feddema, J., and Vertenstein, M. 2008c. An -urban parameterization for a global climate model. 2. Sensitivity to -input parameters and the simulated urban heat island in offline -simulations. J. Appl. Meteor. Clim. 47:1061-1076. +Oleson, K.W., Bonan, G.B., Feddema, J., and Vertenstein, M. 2008c. An urban parameterization for a global climate model. 2. Sensitivity to input parameters and the simulated urban heat island in offline simulations. J. Appl. Meteor. Clim. 47:1061-1076. .. _Olesonetal2010a: -Oleson, K.W., et al. 2010a. Technical description of version 4.0 of the -Community Land model (CLM). NCAR Technical Note NCAR/TN-478+STR, -National Center for Atmospheric Research, Boulder, CO, 257 pp. +Oleson, K.W., et al. 2010a. Technical description of version 4.0 of the Community Land model (CLM). NCAR Technical Note NCAR/TN-478+STR, National Center for Atmospheric Research, Boulder, CO, 257 pp. .. _Olesonetal2010b: -Oleson, K.W., Bonan, G.B., Feddema, J., Vertenstein, M., and Kluzek, E. -2010b. Technical description of an urban parameterization for the -Community Land Model (CLMU). NCAR Technical Note NCAR/TN-480+STR, -National Center for Atmospheric Research, Boulder, CO, 169 pp. +Oleson, K.W., Bonan, G.B., Feddema, J., Vertenstein, M., and Kluzek, E. 2010b. Technical description of an urban parameterization for the Community Land Model (CLMU). NCAR Technical Note NCAR/TN-480+STR, National Center for Atmospheric Research, Boulder, CO, 169 pp. .. _Olesonetal2013: -Oleson, K.W., et al. 2013. Technical description of version 4.5 of the -Community Land Model (CLM). NCAR Technical Note NCAR/TN-503+STR, -National Center for Atmospheric Research, Boulder, CO, 420 pp. +Oleson, K.W., et al. 2013. Technical description of version 4.5 of the Community Land Model (CLM). NCAR Technical Note NCAR/TN-503+STR, National Center for Atmospheric Research, Boulder, CO, 420 pp. .. _OlesonFeddema2018: -Oleson, K.W., and Feddema, J. 2018. Parameterization and surface data -improvements and new capabilities for the Community Land Model -Urban (CLMU). JAMES, submitted. +Oleson, K.W., and Feddema, J. 2018. Parameterization and surface data improvements and new capabilities for the Community Land Model Urban (CLMU). JAMES, submitted. .. _Olson1963: -Olson, J.S., 1963. Energy storage and the balance of producers and -decomposers in ecological systems. Ecology 44:322-331. +Olson, J.S., 1963. Energy storage and the balance of producers and decomposers in ecological systems. Ecology 44:322-331. .. _Olsonetal2001: -Olson, D.M., Dinerstein, E., Wikramanayake, E.D., Burgess, N.D., Powell, -G.V.N., Underwood, E.C., D’Amico, J.A., Itoua, I., Strand, H. E., -Morrison, J. C., Loucks, C. J., Allnutt, T. F., Ricketts, T. H., Kura, -Y., Lamoreux, J. F., Wettengel, W. W., Heda, P., and Kassem, K. R., -2001. Terrestrial ecoregions of the world a new map of life on earth, -Bioscience, 51, 933–938. +Olson, D.M., Dinerstein, E., Wikramanayake, E.D., Burgess, N.D., Powell, G.V.N., Underwood, E.C., D'Amico, J.A., Itoua, I., Strand, H. E., Morrison, J. C., Loucks, C. J., Allnutt, T. F., Ricketts, T. H., Kura, Y., Lamoreux, J. F., Wettengel, W. W., Heda, P., and Kassem, K. R., 2001. Terrestrial ecoregions of the world a new map of life on earth, Bioscience, 51, 933–938. .. _OrchardCook1983: -Orchard, V.A. and Cook, F.J., 1983. Relationship between soil -respiration and soil moisture. Soil Biology and Biochemistry, 15: -447-453. +Orchard, V.A. and Cook, F.J., 1983. Relationship between soil respiration and soil moisture. Soil Biology and Biochemistry, 15: 447-453. .. _Owen1964: -Owen, P.R. 1964. Saltation of uniform grains in air. J. Fluid Mech\ *.* -20:225-242. +Owen, P.R. 1964. Saltation of uniform grains in air. J. Fluid Mech\ *.* 20:225-242. .. _Ozdoganetal2010: -Ozdogan, M., Rodell, M., Beaudoing, H.K., and Toll, D.L. 2010. -Simulating the effects of irrigation over the United States in a land -surface model based on satellite-derived agricultural data. Journal of -Hydrometeorology 11:171-184. +Ozdogan, M., Rodell, M., Beaudoing, H.K., and Toll, D.L. 2010. Simulating the effects of irrigation over the United States in a land surface model based on satellite-derived agricultural data. Journal of Hydrometeorology 11:171-184. .. _Pageetal2002: -Page, S.E., Siegert, F., Rieley, J.O., Boehm, H-D.V., Jaya, A., and -Limin, S. 2002. The amount of carbon released from peat and forest fires -in Indonesia in 1997. Nature 420:61-65. +Page, S.E., Siegert, F., Rieley, J.O., Boehm, H-D.V., Jaya, A., and Limin, S. 2002. The amount of carbon released from peat and forest fires in Indonesia in 1997. Nature 420:61-65. .. _PanofskyDutton1984: -Panofsky, H.A., and Dutton, J.A. 1984. Atmospheric Turbulence: Models -and Methods for Engineering Applications. John Wiley and Sons, New York. +Panofsky, H.A., and Dutton, J.A. 1984. Atmospheric Turbulence: Models and Methods for Engineering Applications. John Wiley and Sons, New York. .. _Partonetal1988: -Parton, W., Stewart, J. and Cole, C., 1988. Dynamics of C, N, P And S in -Grassland Soils - A Model. Biogeochemistry 5:109-131. +Parton, W., Stewart, J. and Cole, C., 1988. Dynamics of C, N, P And S in Grassland Soils - A Model. Biogeochemistry 5:109-131. .. _Partonetal1993: -Parton, W.J., et al. 1993. Observations and modeling of biomass and soil -organic matter dynamics for the grassland biome worlwide. Global -Biogeochemical Cycles 7:785-809. +Parton, W.J., et al. 1993. Observations and modeling of biomass and soil organic matter dynamics for the grassland biome worlwide. Global Biogeochemical Cycles 7:785-809. .. _Partonetal1996: -Parton, W. et al. 1996. Generalized model for N2 and N2O production from -nitrification and denitrification. Global Biogeochemical Cycles -10:401-412. +Parton, W. et al. 1996. Generalized model for N2 and N2O production from nitrification and denitrification. Global Biogeochemical Cycles 10:401-412. .. _Partonetal2001: -Parton, W.J. et al. 2001. Generalized model for NOx and N2O emissions -from soils. J. Geophys. Res. 106(D15):17403-17419. +Parton, W.J. et al. 2001. Generalized model for NOx and N2O emissions from soils. J. Geophys. Res. 106(D15):17403-17419. .. _Paterson1994: -Paterson, W.S.B., 1994. The Physics of Glaciers. Elsevier Science Inc., -New York, 480 pp. +Paterson, W.S.B., 1994. The Physics of Glaciers. Elsevier Science Inc., New York, 480 pp. .. _Pelletieretal2016: @@ -1582,8 +1062,7 @@ Peterson, D.L. and K.C. Ryan, 1986. Modeling postfire conifer mortality for long .. _Petrescuetal2010: -Petrescu, A.M.R. et al. 2010. Modeling regional to global CH4 emissions -of boreal and arctic wetlands. Global Biogeochemical Cycles, 24(GB4009). +Petrescu, A.M.R. et al. 2010. Modeling regional to global CH4 emissions of boreal and arctic wetlands. Global Biogeochemical Cycles, 24(GB4009). .. _pfeiffer2013: @@ -1591,56 +1070,39 @@ Pfeiffer, M., A. Spessa, and J.O. Kaplan, 2013. A model for global biomass burni .. _Philip1957: -Philip, J.R. 1957. Evaporation, and moisture and heat fields in the -soil. J. Meteor. 14:354-366. +Philip, J.R. 1957. Evaporation, and moisture and heat fields in the soil. J. Meteor. 14:354-366. .. _Piaoetal2012: -Piao, S.L., et al. 2012. The carbon budget of terrestrial ecosystems in -East Asia over the last two decades. Biogeosciences 9:3571-3586. +Piao, S.L., et al. 2012. The carbon budget of terrestrial ecosystems in East Asia over the last two decades. Biogeosciences 9:3571-3586. .. _Pivovarov1972: -Pivovarov, A.A., 1972. Thermal Conditions in Freezing Lakes and -Reservoirs. John Wiley, New York. +Pivovarov, A.A., 1972. Thermal Conditions in Freezing Lakes and Reservoirs. John Wiley, New York. .. _Pollmeretal1979: -Pollmer, W.G., Eberhard, D., Klein, D., and Dhillon, B.S. 1979. Genetic -control of nitrogen uptake and translocation in maize. Crop Sci. -19:82-86. +Pollmer, W.G., Eberhard, D., Klein, D., and Dhillon, B.S. 1979. Genetic control of nitrogen uptake and translocation in maize. Crop Sci. 19:82-86. .. _Pomeroyetal1998: -Pomeroy, J. W., D. M. Gray, K. R. Shook, B. Toth, R. L. H. Essery, -A. Pietroniro, and N. Hedstrom. 1998. An evaluation of snow accumulation -and ablation processes for land surface modelling. Hydrol. Process. 12:2339–2367. +Pomeroy, J. W., D. M. Gray, K. R. Shook, B. Toth, R. L. H. Essery, A. Pietroniro, and N. Hedstrom. 1998. An evaluation of snow accumulation and ablation processes for land surface modelling. Hydrol. Process. 12:2339–2367. .. _Portmannetal2010: -Portmann, F.T., Siebert, S., and Döll, P. 2010. MIRCA2000 - Global -monthly irrigated and rainfed crop areas around the year 2000: A new -high-resolution data set for agricultural and hydrological modeling. -Global Biogeochem. Cycles. 24, GB1011. DOI:10.1029/2008GB003435. +Portmann, F.T., Siebert, S., and Döll, P. 2010. MIRCA2000 - Global monthly irrigated and rainfed crop areas around the year 2000: A new high-resolution data set for agricultural and hydrological modeling. Global Biogeochem. Cycles. 24, GB1011. DOI:10.1029/2008GB003435. .. _Pressetal1992: -Press, W.H., Teukolsky, S.A., Vetterling, W.T., and Flannery, B.P. 1992. -Numerical Recipes in FORTRAN: The Art of Scientific Computing. Cambridge -University Press, New York. +Press, W.H., Teukolsky, S.A., Vetterling, W.T., and Flannery, B.P. 1992. Numerical Recipes in FORTRAN: The Art of Scientific Computing. Cambridge University Press, New York. .. _Prigentetal2007: -Prigent, C., Papa, F., Aires, F., Rossow, W.B. and Matthews, E. 2007. -Global inundation dynamics inferred from multiple satellite -observations, 1993-2000. J. Geophys. Res. 112(D12). +Prigent, C., Papa, F., Aires, F., Rossow, W.B. and Matthews, E. 2007. Global inundation dynamics inferred from multiple satellite observations, 1993-2000. J. Geophys. Res. 112(D12). .. _Pritchardetal2008: -Pritchard, M.S., Bush, A.B.G., and Marshall, S.J. 2008. Neglecting -ice-atmosphere interactions underestimates ice sheet melt in -millennial-scale deglaciation simulations. Geophys. Res. Lett. ** -35:L01503. DOI:10.1029/2007GL031738. +Pritchard, M.S., Bush, A.B.G., and Marshall, S.J. 2008. Neglecting ice-atmosphere interactions underestimates ice sheet melt in millennial-scale deglaciation simulations. Geophys. Res. Lett. ** 35:L01503. DOI:10.1029/2007GL031738. .. _purves2008: @@ -1652,55 +1114,35 @@ Qian, T et al., 2006. Simulation of global land surface conditions from 1948 to .. _RamankuttyFoley1998: -Ramankutty, N., and Foley, J. A., 1998. Characterizing patterns of -global land use: An analysis of global croplands data. Global -Biogeochemical Cycles, 12, 667-685. +Ramankutty, N., and Foley, J. A., 1998. Characterizing patterns of global land use: An analysis of global croplands data. Global Biogeochemical Cycles, 12, 667-685. .. _Ramankuttyetal2008: -Ramankutty, N., Evan, A., Monfreda, C., and Foley, J.A. 2008. Farming -the Planet. Part 1: The Geographic Distribution of Global Agricultural -Lands in the Year 2000. Global Biogeochem. Cycles. 22:GB1003. -DOI:10.1029/2007GB002952. +Ramankutty, N., Evan, A., Monfreda, C., and Foley, J.A. 2008. Farming the Planet. Part 1: The Geographic Distribution of Global Agricultural Lands in the Year 2000. Global Biogeochem. Cycles. 22:GB1003. DOI:10.1029/2007GB002952. .. _Randlettetal1996: -Randlett, D.L., Zak, D.R., Pregitzer, K.S., and Curtis, P.S. 1996. -Elevated atmospheric carbon dioxide and leaf litter chemistry: -Influences on microbial respiration and net nitrogen mineralization. -Soil Sci. Soc. Am. J. 60:1571-1577. +Randlett, D.L., Zak, D.R., Pregitzer, K.S., and Curtis, P.S. 1996. Elevated atmospheric carbon dioxide and leaf litter chemistry: Influences on microbial respiration and net nitrogen mineralization. Soil Sci. Soc. Am. J. 60:1571-1577. .. _Rastetteretal1991: -Rastetter, E.B., Ryan, M.G., Shaver, G.R., Melillo, J.M., Nadelhoffer, -K.J., Hobbie, J.E., and Aber, J.D. 1991. A general biogeochemical model -describing the responses of the C and N cycles in terrestrial ecosystems -to changes in CO2, climate and N deposition. Tree Phys. 9:101-126. +Rastetter, E.B., Ryan, M.G., Shaver, G.R., Melillo, J.M., Nadelhoffer, K.J., Hobbie, J.E., and Aber, J.D. 1991. A general biogeochemical model describing the responses of the C and N cycles in terrestrial ecosystems to changes in CO2, climate and N deposition. Tree Phys. 9:101-126. .. _Rastneretal2012: -Rastner, P., Bolch, T., Mölg, N., Machguth, H., and Paul, F., 2012. The -first complete glacier inventory for the whole of Greenland, The -Cryosphere Discuss., 6, 2399-2436, 10.5194/tcd-6-2399-2012. +Rastner, P., Bolch, T., Mölg, N., Machguth, H., and Paul, F., 2012. The first complete glacier inventory for the whole of Greenland, The Cryosphere Discuss., 6, 2399-2436, 10.5194/tcd-6-2399-2012. .. _Rileyetal2011a: -Riley, W. J., Z. M. Subin, D. M. Lawrence, S. C. Swenson, M. S. Torn, L. -Meng, N. Mahowald, and P. Hess, 2011a. Barriers to predicting global -terrestrial methane fluxes: Analyses using a methane biogeochemistry -model integrated in CESM. Biogeosciences, 8, 1925–1953. -DOI:10.5194/bg-8-1925-2011. +Riley, W. J., Z. M. Subin, D. M. Lawrence, S. C. Swenson, M. S. Torn, L. Meng, N. Mahowald, and P. Hess, 2011a. Barriers to predicting global terrestrial methane fluxes: Analyses using a methane biogeochemistry model integrated in CESM. Biogeosciences, 8, 1925–1953. DOI:10.5194/bg-8-1925-2011. .. _Rileyetal2011b: -Riley, W.J. et al. 2011b. CLM4Me, a Methane Biogeochemistry Model -Integrated in CESM, Land and Biogeochemistry Model Working Group -Meeting, Boulder, CO. +Riley, W.J. et al. 2011b. CLM4Me, a Methane Biogeochemistry Model Integrated in CESM, Land and Biogeochemistry Model Working Group Meeting, Boulder, CO. .. _Roeschetal2001: -Roesch, A., M. Wild, H. Gilgen, and A. Ohmura. 2001. A new snow cover -fraction parametrization for the ECHAM4 GCM, Clim. Dyn., 17:933–946. +Roesch, A., M. Wild, H. Gilgen, and A. Ohmura. 2001. A new snow cover fraction parametrization for the ECHAM4 GCM, Clim. Dyn., 17:933–946. .. _Rogers2014: @@ -1712,55 +1154,35 @@ Rogers, A., B. E. Medlyn, J. S. Dukes, G. Bonan, S. Caemmerer, M. C. Dietze, J. .. _Ryan1991: -Ryan, M. G. 1991. A simple method for estimating gross carbon budgets -for vegetation in forest ecosystems. Tree Phys. 9:255-266. +Ryan, M. G. 1991. A simple method for estimating gross carbon budgets for vegetation in forest ecosystems. Tree Phys. 9:255-266. .. _RunningCoughlan1988: -Running, S.W. and Coughlan, J.C., 1988. A general model of forest -ecosystem processes for regional applications. I. Hydrological balance, -canopy gas exchange and primary production processes. Ecological -Modelling, 42: 125-154. +Running, S.W. and Coughlan, J.C., 1988. A general model of forest ecosystem processes for regional applications. I. Hydrological balance, canopy gas exchange and primary production processes. Ecological Modelling, 42: 125-154. .. _Runningetal1989: -Running, S.W. et al., 1989. Mapping regional forest evapotranspiration -and photosynthesis by coupling satellite data with ecosystem simlation. -Ecology, 70: 1090-1101. +Running, S.W. et al., 1989. Mapping regional forest evapotranspiration and photosynthesis by coupling satellite data with ecosystem simlation. Ecology, 70: 1090-1101. .. _RunningGower1991: -Running, S.W. and Gower, S.T., 1991. FOREST BGC, A general model of -forest ecosystem processes for regional applications. II. Dynamic carbon -allocation and nitrogen budgets. Tree Physiology, 9: 147-160. +Running, S.W. and Gower, S.T., 1991. FOREST BGC, A general model of forest ecosystem processes for regional applications. II. Dynamic carbon allocation and nitrogen budgets. Tree Physiology, 9: 147-160. .. _RunningHunt1993: -Running, S.W. and Hunt, E.R., Jr., 1993. Generalization of a forest -ecosystem process model for other biomes, BIOME-BGC, and an -applicationfor global-scale models. In: J.R. Ehleringer and C. Field -(Editors), Scaling Physiological Processes: Leaf to Globe. Academic -Press, San Diego, CA, pp. 141-158. +Running, S.W. and Hunt, E.R., Jr., 1993. Generalization of a forest ecosystem process model for other biomes, BIOME-BGC, and an applicationfor global-scale models. In: J.R. Ehleringer and C. Field (Editors), Scaling Physiological Processes: Leaf to Globe. Academic Press, San Diego, CA, pp. 141-158. .. _Sacksetal2009: -Sacks, W. J., Cook, B. I., Buenning, N., Levis, S., and Helkowski, J. H. -2009. Effects of global irrigation on the near-surface climate. Climate -Dyn., 33, 159–175. DOI:10.1007/s00382-008-0445-z. +Sacks, W. J., Cook, B. I., Buenning, N., Levis, S., and Helkowski, J. H. 2009. Effects of global irrigation on the near-surface climate. Climate Dyn., 33, 159–175. DOI:10.1007/s00382-008-0445-z. .. _Saggaretal1994: -Saggar, S., Tate, K.R., Feltham, C.W., Childs, C.W. and Parshotam, A., -1994. Carbon turnover in a range of allophanic soils amended with -:math:`{}^{14}`\ C-labelled glucose. Soil Biology and Biochemistry, 26: -1263-1271. +Saggar, S., Tate, K.R., Feltham, C.W., Childs, C.W. and Parshotam, A., 1994. Carbon turnover in a range of allophanic soils amended with :math:`{}^{14}`\ C-labelled glucose. Soil Biology and Biochemistry, 26: 1263-1271. .. _Sakaguchietal2009: -Sakaguchi, K., and Zeng, X. 2009. Effects of soil wetness, plant litter, -and under-canopy atmospheric stability on ground evaporation in the -Community Land Model (CLM3.5). J. Geophys. Res. 114:D01107. -DOI:10.1029/2008JD010834. +Sakaguchi, K., and Zeng, X. 2009. Effects of soil wetness, plant litter, and under-canopy atmospheric stability on ground evaporation in the Community Land Model (CLM3.5). J. Geophys. Res. 114:D01107. DOI:10.1029/2008JD010834. .. _sato2007: @@ -1768,64 +1190,43 @@ Sato, H., A. Itoh, and T. Kohyama, 2007. SEIB-DGVM: A new Dynamic Global Vegetat .. _Schaafetal2002: -Schaaf, C.B., Gao, F., Strahler, A.H., Lucht, W., Li, X., Tsang, T., -Strugnell, N.C., Zhang, X., Jin, Y., and Muller, J.-P. 2002. First -operational BRDF, albedo nadir reflectance products from MODIS. Remote -Sens. Environ. 83:135-148. +Schaaf, C.B., Gao, F., Strahler, A.H., Lucht, W., Li, X., Tsang, T., Strugnell, N.C., Zhang, X., Jin, Y., and Muller, J.-P. 2002. First operational BRDF, albedo nadir reflectance products from MODIS. Remote Sens. Environ. 83:135-148. .. _Schlesinger1997: -Schlesinger, W.H., 1997. Biogeochemistry: an analysis of global change. -Academic Press, London, 588 pp. +Schlesinger, W.H., 1997. Biogeochemistry: an analysis of global change. Academic Press, London, 588 pp. .. _SchnellKing1996: -Schnell, S. and King, G.M., 1996. Responses of methanotrophic activity -in soils and cultures to water stress. Applied and Environmental -Microbiology 62:3203-3209. +Schnell, S. and King, G.M., 1996. Responses of methanotrophic activity in soils and cultures to water stress. Applied and Environmental Microbiology 62:3203-3209. .. _Segers1998: -Segers, R., 1998. Methane production and methane consumption: a review -of processes underlying wetland methane fluxes. Biogeochemistry -41:23-51. +Segers, R., 1998. Methane production and methane consumption: a review of processes underlying wetland methane fluxes. Biogeochemistry 41:23-51. .. _Sellers1985: -Sellers, P.J. 1985. Canopy reflectance, photosynthesis and -transpiration. Int. J. Remote Sens. 6:1335-1372. +Sellers, P.J. 1985. Canopy reflectance, photosynthesis and transpiration. Int. J. Remote Sens. 6:1335-1372. .. _Sellersetal1986: -Sellers, P.J., Mintz, Y., Sud, Y.C., and Dalcher, A. 1986. A simple -biosphere model (SiB) for use within general circulation models. J. -Atmos. Sci. 43:505-531. +Sellers, P.J., Mintz, Y., Sud, Y.C., and Dalcher, A. 1986. A simple biosphere model (SiB) for use within general circulation models. J. Atmos. Sci. 43:505-531. .. _Sellersetal1988: -Sellers, P.J., Hall, F.G., Asrar, G., Strebel, D.E., and Murphy, R.E. -1988. The First ISLSCP Field Experiment (FIFE). Bull. Amer. Meteor. Soc. -69:22-27. +Sellers, P.J., Hall, F.G., Asrar, G., Strebel, D.E., and Murphy, R.E. 1988. The First ISLSCP Field Experiment (FIFE). Bull. Amer. Meteor. Soc. 69:22-27. .. _Sellersetal1992: -Sellers, P.J., Berry, J.A., Collatz, G.J., Field, C.B., and Hall, F.G. -1992. Canopy reflectance, photosynthesis, and transpiration. III. A -reanalysis using improved leaf models and a new canopy integration -scheme. Remote Sens. Environ. 42:187-216. +Sellers, P.J., Berry, J.A., Collatz, G.J., Field, C.B., and Hall, F.G. 1992. Canopy reflectance, photosynthesis, and transpiration. III. A reanalysis using improved leaf models and a new canopy integration scheme. Remote Sens. Environ. 42:187-216. .. _Sellersetal1995: -Sellers, P.J., et al. 1995. The Boreal Ecosystem-Atmosphere Study -(BOREAS): An overview and early results from the 1994 field year. Bull. -Amer. Meteor. Soc. 76:1549-1577. +Sellers, P.J., et al. 1995. The Boreal Ecosystem-Atmosphere Study (BOREAS): An overview and early results from the 1994 field year. Bull. Amer. Meteor. Soc. 76:1549-1577. .. _Sellersetal1996: -Sellers, P.J., Randall, D.A., Collatz, G.J., Berry, J.A., Field, C.B., -Dazlich, D.A., Zhang, C., Collelo, G.D., and Bounoua, L. 1996. A revised -land surface parameterization (SiB2) for atmospheric GCMs. Part I: Model -formulation. J. Climate 9:676-705. +Sellers, P.J., Randall, D.A., Collatz, G.J., Berry, J.A., Field, C.B., Dazlich, D.A., Zhang, C., Collelo, G.D., and Bounoua, L. 1996. A revised land surface parameterization (SiB2) for atmospheric GCMs. Part I: Model formulation. J. Climate 9:676-705. .. _sellers1996: @@ -1833,10 +1234,7 @@ Sellers, Piers J et al. (1996). A revised land surface parameterization (SiB2) f .. _Shietal2013: -Shi, X., Mao, J., Thornton, P.E., and Huang, M. 2013. Spatiotemporal -patterns of evapotranspiration in response to multiple environmental -factors simulated by the Community Land Model. Environ. Res. Lett. -8:024012. +Shi, X., Mao, J., Thornton, P.E., and Huang, M. 2013. Spatiotemporal patterns of evapotranspiration in response to multiple environmental factors simulated by the Community Land Model. Environ. Res. Lett. 8:024012. .. _Shietal2016: @@ -1844,26 +1242,19 @@ Shi, M., J. B. Fisher, E. R. Brzostek, and R. P. Phillips, 2016: Carbon cost of .. _Shiklomanov2000: -Shiklomanov, I.A. 2000. Appraisal and assessment of world water -resources. Water International 25:11-32. +Shiklomanov, I.A. 2000. Appraisal and assessment of world water resources. Water International 25:11-32. .. _Siebertetal2005: -Siebert, S., Döll, P., Hoogeveen, J., Faures, J.M., Frenken, K., Feick, -S., 2005. Development and validation of the global map of irrigation -areas. Hydrol Earth Syst Sc 9:535–547 +Siebert, S., Döll, P., Hoogeveen, J., Faures, J.M., Frenken, K., Feick, S., 2005. Development and validation of the global map of irrigation areas. Hydrol Earth Syst Sc 9:535–547 .. _Simardetal2011: -Simard, M., Pinto, N., Fisher, J.B., and Baccini, A. (2011), Mapping -forest canopy height globally with spaceborne lidar. -J. Geophys. Res., 116, G04021, doi:10.1029/2011JG001708. +Simard, M., Pinto, N., Fisher, J.B., and Baccini, A. (2011), Mapping forest canopy height globally with spaceborne lidar. J. Geophys. Res., 116, G04021, doi:10.1029/2011JG001708. .. _Simpsonetal1983: -Simpson, R.J., Lambers, H., and Dalling, M.J. 1983. Nitrogen -redistribution during grain growth in wheat (Triticum avestivum L.). -Plant Physiol. 71:7-14. +Simpson, R.J., Lambers, H., and Dalling, M.J. 1983. Nitrogen redistribution during grain growth in wheat (Triticum avestivum L.). Plant Physiol. 71:7-14. .. _sitch2003: @@ -1871,9 +1262,7 @@ Sitch, S et al. (2003). Evaluation of ecosystem dynamics, plant geography and te .. _Sivak2013: -Sivak, M. 2013. Air conditioning versus heating: climate control is more -energy demanding in Minneapolis than in Miami. Environ. Res. Lett., 8, -doi:10.1088/1748-9326/8/1/014050. +Sivak, M. 2013. Air conditioning versus heating: climate control is more energy demanding in Minneapolis than in Miami. Environ. Res. Lett., 8, doi:10.1088/1748-9326/8/1/014050. .. _smith2001: @@ -1881,10 +1270,7 @@ Smith, B., I.C. Prentice, and M.T. Sykes, 2001. Representation of vegetation dyn .. _Smithetal2005: -Smith, A.M.S., Wooster, M.J., Drake, N.A., Dipotso, F.M. and Perry, -G.L.W., 2005. Fire in African savanna: Testing the impact of incomplete -combustion on pyrogenic emissions estimates. Ecological Applications, -15: 1074-1082. +Smith, A.M.S., Wooster, M.J., Drake, N.A., Dipotso, F.M. and Perry, G.L.W., 2005. Fire in African savanna: Testing the impact of incomplete combustion on pyrogenic emissions estimates. Ecological Applications, 15: 1074-1082. .. _smith2007: @@ -1892,144 +1278,95 @@ Smith, A.M. and M. Stitt, 2007. Coordination of carbon supply and plant growth. .. _Sollins1982: -Sollins, P., 1982. Input and decay of coarse woody debris in coniferous -stands in western Oregon and Washington. Canadian Journal of Forest -Research, 12: 18-28. +Sollins, P., 1982. Input and decay of coarse woody debris in coniferous stands in western Oregon and Washington. Canadian Journal of Forest Research, 12: 18-28. .. _SonGower1991: -Son, Y. and Gower, S.T., 1991. Aboveground nitrogen and phosphorus use -by five plantation-grown trees with different leaf longevities. -Biogeochemistry, 14: 167-191. +Son, Y. and Gower, S.T., 1991. Aboveground nitrogen and phosphorus use by five plantation-grown trees with different leaf longevities. Biogeochemistry, 14: 167-191. .. _Sorensen1981: -Sørensen, L.H., 1981. Carbon-nitrogen relationships during the -humification of cellulose in soils containing different amounts of clay. -Soil Biology and Biochemistry, 13: 313-321. +Sørensen, L.H., 1981. Carbon-nitrogen relationships during the humification of cellulose in soils containing different amounts of clay. Soil Biology and Biochemistry, 13: 313-321. .. _Sperryetal1998: -Sperry, J.S., Adler, F.R., Campbell, G.S. and Comstock, J.P. 1998. -Limitation of plant water use by rhizosphere and xylem conductance: -results from a model. Plant, Cell & Environment, 21: 347–359. -doi:10.1046/j.1365-3040.1998.00287.x +Sperry, J.S., Adler, F.R., Campbell, G.S. and Comstock, J.P. 1998. Limitation of plant water use by rhizosphere and xylem conductance: results from a model. Plant, Cell & Environment, 21: 347–359. doi:10.1046/j.1365-3040.1998.00287.x .. _SperryandLove2015: -Sperry, J.S. and Love, D.M. 2015. What plant hydraulics can tell us -about responses to climate-change droughts. New Phytol, 207: 14–27. -doi:10.1111/nph.13354 +Sperry, J.S. and Love, D.M. 2015. What plant hydraulics can tell us about responses to climate-change droughts. New Phytol, 207: 14–27. doi:10.1111/nph.13354 .. _Sprugeletal1995: -Sprugel, D.G., Ryan, M.G., Brooks, J.R., Vogt, K.A., and Martin, T.A. -1995. Respiration from the organ level to stand level. pp. 255-299. In: -W. K. Smith and T. M. Hinkley (editors) Resource Physiology of Conifers. -Academic Press, San Diego,CA. +Sprugel, D.G., Ryan, M.G., Brooks, J.R., Vogt, K.A., and Martin, T.A. 1995. Respiration from the organ level to stand level. pp. 255-299. In: W. K. Smith and T. M. Hinkley (editors) Resource Physiology of Conifers. Academic Press, San Diego,CA. .. _StaufferAharony1994: -Stauffer, D., and Aharony, A. 1994. Introduction to Percolation Theory. -Taylor and Francis, London. +Stauffer, D., and Aharony, A. 1994. Introduction to Percolation Theory. Taylor and Francis, London. .. _Stilletal2003: -Still, C.J., Berry, J.A., Collatz, G.J., and DeFries, R.S. 2003. Global -distribution of C3 and C4 vegetation: carbon cycle implications. Global -Biogeochem. Cycles 17:1006. DOI:10.1029/2001GB001807. +Still, C.J., Berry, J.A., Collatz, G.J., and DeFries, R.S. 2003. Global distribution of C3 and C4 vegetation: carbon cycle implications. Global Biogeochem. Cycles 17:1006. DOI:10.1029/2001GB001807. .. _Stocklietal2008: -Stöckli, R., Lawrence, D.M., Niu, G.-Y., Oleson, K.W., Thornton, P.E., -Yang, Z.-L., Bonan, G.B., Denning, A.S., and Running, S.W. 2008. Use of -FLUXNET in the Community Land Model development. J. Geophys. Res. -113:G01025. DOI:10.1029/2007JG000562. +Stöckli, R., Lawrence, D.M., Niu, G.-Y., Oleson, K.W., Thornton, P.E., Yang, Z.-L., Bonan, G.B., Denning, A.S., and Running, S.W. 2008. Use of FLUXNET in the Community Land Model development. J. Geophys. Res. 113:G01025. DOI:10.1029/2007JG000562. .. _Stracketal2006: -Strack, M., Kellner, E. and Waddington, J.M., 2006. Effect of entrapped -gas on peatland surface level fluctuations. Hydrological Processes -20:3611-3622. +Strack, M., Kellner, E. and Waddington, J.M., 2006. Effect of entrapped gas on peatland surface level fluctuations. Hydrological Processes 20:3611-3622. .. _Strahleretal1999: -Strahler, A.H., Muchoney, D., Borak, J., Friedl, M., Gopal, S., Lambin, -E., and Moody. A. 1999. MODIS Land Cover Product: Algorithm Theoretical -Basis Document (Version 5.0). Boston University, Boston. +Strahler, A.H., Muchoney, D., Borak, J., Friedl, M., Gopal, S., Lambin, E., and Moody. A. 1999. MODIS Land Cover Product: Algorithm Theoretical Basis Document (Version 5.0). Boston University, Boston. .. _Stull1988: -Stull, R.B. 1988. An Introduction to Boundary Layer Meteorology. Kluwer -Academic Publishers, Dordrecht. +Stull, R.B. 1988. An Introduction to Boundary Layer Meteorology. Kluwer Academic Publishers, Dordrecht. .. _Subinetal2012a: -Subin, Z.M., Riley, W.J. and Mironov, D. 2012a. Improved lake model for -climate simulations, J. Adv. Model. Earth Syst., 4, M02001. -DOI:10.1029/2011MS000072. +Subin, Z.M., Riley, W.J. and Mironov, D. 2012a. Improved lake model for climate simulations, J. Adv. Model. Earth Syst., 4, M02001. DOI:10.1029/2011MS000072. .. _Subinetal2012b: -Subin, Z.M., Murphy, L.N., Li, F., Bonfils, C. and Riley, W.J., 2012b. -Boreal lakes moderate seasonal and diurnal temperature variation and -perturb atmospheric circulation: analyses in the Community Earth System -Model 1 (CESM1). Tellus A, North America, 64. +Subin, Z.M., Murphy, L.N., Li, F., Bonfils, C. and Riley, W.J., 2012b. Boreal lakes moderate seasonal and diurnal temperature variation and perturb atmospheric circulation: analyses in the Community Earth System Model 1 (CESM1). Tellus A, North America, 64. .. _Sunetal2012: -Sun, Y., Gu, L., and Dickinson, R. E. 2012. A numerical issue in -calculating the coupled carbon and water fluxes in a climate model, J. -Geophys. Res., 117, D22103. DOI:10.1029/2012JD018059. +Sun, Y., Gu, L., and Dickinson, R. E. 2012. A numerical issue in calculating the coupled carbon and water fluxes in a climate model, J. Geophys. Res., 117, D22103. DOI:10.1029/2012JD018059. .. _Swensonetal2012: -Swenson, S.C., Lawrence, D.M., and Lee, H. 2012. Improved Simulation of -the Terrestrial Hydrological Cycle in Permafrost Regions by the -Community Land Model. JAMES, 4, M08002. DOI:10.1029/2012MS000165. +Swenson, S.C., Lawrence, D.M., and Lee, H. 2012. Improved Simulation of the Terrestrial Hydrological Cycle in Permafrost Regions by the Community Land Model. JAMES, 4, M08002. DOI:10.1029/2012MS000165. .. _SwensonLawrence2012: -Swenson, S.C. and Lawrence, D.M. 2012. A New Fractional Snow Covered -Area Parameterization for the Community Land Model and its Effect on the -Surface Energy Balance. JGR, 117, D21107. DOI:10.1029/2012JD018178. +Swenson, S.C. and Lawrence, D.M. 2012. A New Fractional Snow Covered Area Parameterization for the Community Land Model and its Effect on the Surface Energy Balance. JGR, 117, D21107. DOI:10.1029/2012JD018178. .. _SwensonLawrence2014: -Swenson, S.C., and D. M. Lawrence. 2014. Assessing a dry surface -layer-based soil resistance parameterization for the Community Land Model -using GRACE and FLUXNET-MTE data. JGR, 119, 10, 299–10,312, -DOI:10.1002/2014JD022314. +Swenson, S.C., and D. M. Lawrence. 2014. Assessing a dry surface layer-based soil resistance parameterization for the Community Land Model using GRACE and FLUXNET-MTE data. JGR, 119, 10, 299–10,312, DOI:10.1002/2014JD022314. .. _SwensonLawrence2015: -Swenson, S.C., and D. M. Lawrence. 2015. A GRACE-based assessment of -interannual groundwater dynamics in the Community Land Model. WRR, 51, -doi:10.1002/2015WR017582. +Swenson, S.C., and D. M. Lawrence. 2015. A GRACE-based assessment of interannual groundwater dynamics in the Community Land Model. WRR, 51, doi:10.1002/2015WR017582. .. _TaWeiland1992: -Ta, C.T. and Weiland, R.T. 1992. Nitrogen partitioning in maize during -ear development. Crop Sci. 32:443-451. +Ta, C.T. and Weiland, R.T. 1992. Nitrogen partitioning in maize during ear development. Crop Sci. 32:443-451. .. _TangRiley2013: -Tang, J.Y. and Riley, W.J. 2013. A new top boundary condition for -modeling surface diffusive exchange of a generic volatile tracer: -Theoretical analysis and application to soil evaporation. Hydrol. Earth -Syst. Sci. 17:873-893. +Tang, J.Y. and Riley, W.J. 2013. A new top boundary condition for modeling surface diffusive exchange of a generic volatile tracer: Theoretical analysis and application to soil evaporation. Hydrol. Earth Syst. Sci. 17:873-893. .. _Tarnocaietal2011: -Tarnocai, C., Kettles, I. M., and Lacelle, B., 2011. Peatlands of -Canada, Geological Survey of Canada, Open File 6561, CD-ROM. -DOI:10.495/288786. +Tarnocai, C., Kettles, I. M., and Lacelle, B., 2011. Peatlands of Canada, Geological Survey of Canada, Open File 6561, CD-ROM. DOI:10.495/288786. .. _Tayloretal1989: -Taylor, B.R., Parkinson, D. and Parsons, W.F.J., 1989. Nitrogen and -lignin content as predictors of litter decay rates: A microcosm test. -Ecology, 70: 97-104. +Taylor, B.R., Parkinson, D. and Parsons, W.F.J., 1989. Nitrogen and lignin content as predictors of litter decay rates: A microcosm test. Ecology, 70: 97-104. .. _Thomasetal2015: @@ -2037,101 +1374,63 @@ Thomas R.Q., Brookshire E.N., Gerber S. 2015. Nitrogen limitation on land: how c .. _Thonickeetal2001: -Thonicke, K., Venevsky, S., Sitch, S., and Cramer, W. 2001. The role of -fire disturbance for global vegetation dynamics: coupling fire into a -Dynamic Global Vegetation Model. Global Ecology and Biogeography -10:661-667. +Thonicke, K., Venevsky, S., Sitch, S., and Cramer, W. 2001. The role of fire disturbance for global vegetation dynamics: coupling fire into a Dynamic Global Vegetation Model. Global Ecology and Biogeography 10:661-667. .. _thonickeetal2010: Thonicke, K. et al., 2010. The influence of vegetation, fire spread and fire behaviour on biomass burning and trace gas emissions: results from a process-based model. Biogeosciences 7.6, pp. 1991-2011. - .. _Thornton1998: -Thornton, P.E., 1998. Regional ecosystem simulation: combining surface- -and satellite-based observations to study linkages between terrestrial -energy and mass budgets. Ph.D. Thesis, The University of Montana, -Missoula, 280 pp. +Thornton, P.E., 1998. Regional ecosystem simulation: combining surface- and satellite-based observations to study linkages between terrestrial energy and mass budgets. Ph.D. Thesis, The University of Montana, Missoula, 280 pp. .. _Thorntonetal2002: -Thornton, P.E., Law, B.E., Gholz, H.L., Clark, K.L., Falge, E., -Ellsworth, D.S., Goldstein, A.H., Monson, R.K., Hollinger, D., Falk, M., -Chen, J., and Sparks, J.P. 2002. Modeling and measuring the effects of -disturbance history and climate on carbon and water budgets in evergreen -needleleaf forests. Agric. For. Meteor. 113:185-222. +Thornton, P.E., Law, B.E., Gholz, H.L., Clark, K.L., Falge, E., Ellsworth, D.S., Goldstein, A.H., Monson, R.K., Hollinger, D., Falk, M., Chen, J., and Sparks, J.P. 2002. Modeling and measuring the effects of disturbance history and climate on carbon and water budgets in evergreen needleleaf forests. Agric. For. Meteor. 113:185-222. .. _ThorntonRosenbloom2005: -Thornton, P.E., and Rosenbloom, N.A. 2005. Ecosystem model spin-up: -estimating steady state conditions in a coupled terrestrial carbon and -nitrogen cycle model. Ecological Modelling 189:25-48. +Thornton, P.E., and Rosenbloom, N.A. 2005. Ecosystem model spin-up: estimating steady state conditions in a coupled terrestrial carbon and nitrogen cycle model. Ecological Modelling 189:25-48. .. _ThorntonZimmermann2007: -Thornton, P.E., and Zimmermann, N.E. 2007. An improved canopy -integration scheme for a land surface model with prognostic canopy -structure. J. Climate 20:3902-3923. +Thornton, P.E., and Zimmermann, N.E. 2007. An improved canopy integration scheme for a land surface model with prognostic canopy structure. J. Climate 20:3902-3923. .. _Thorntonetal2007: -Thornton, P.E., Lamarque, J.-F., Rosenbloom, N.A., and Mahowald, N.M. -2007. Influence of carbon-nitrogen cycle coupling on land model response -to CO\ :sub:`2` fertilization and climate variability. Global -Biogeochem. Cycles 21:GB4018. +Thornton, P.E., Lamarque, J.-F., Rosenbloom, N.A., and Mahowald, N.M. 2007. Influence of carbon-nitrogen cycle coupling on land model response to CO\ :sub:`2` fertilization and climate variability. Global Biogeochem. Cycles 21:GB4018. .. _Thorntonetal2009: -Thornton, P.E., Doney, S.C., Lindsay, K., Moore, J.K., Mahowald, N., -Randerson, J.T., Fung, I., Lamarque, J.F., Feddema, J.J., and Lee, Y.H. -2009. Carbon-nitrogen interactions regulate climate-carbon cycle -feedbacks: results from an atmosphere-ocean general circulation model. -Biogeosci. 6:2099-2120. +Thornton, P.E., Doney, S.C., Lindsay, K., Moore, J.K., Mahowald, N., Randerson, J.T., Fung, I., Lamarque, J.F., Feddema, J.J., and Lee, Y.H. 2009. Carbon-nitrogen interactions regulate climate-carbon cycle feedbacks: results from an atmosphere-ocean general circulation model. Biogeosci. 6:2099-2120. .. _Tianetal2010: -Tian, H. et al. 2010. Spatial and temporal patterns of CH4 and N2O -fluxes in terrestrial ecosystems of North America during 1979-2008: -application of a global biogeochemistry model. Biogeosciences -7:2673-2694. +Tian, H. et al. 2010. Spatial and temporal patterns of CH4 and N2O fluxes in terrestrial ecosystems of North America during 1979-2008: application of a global biogeochemistry model. Biogeosciences 7:2673-2694. .. _Toonetal1989: -Toon, O.B., McKay, C.P., Ackerman, T.P., and Santhanam, K. 1989. Rapid -calculation of radiative heating rates and photodissociation rates in -inhomogeneous multiple scattering atmospheres. J. Geophys. Res. -94(D13):16,287-16,301. +Toon, O.B., McKay, C.P., Ackerman, T.P., and Santhanam, K. 1989. Rapid calculation of radiative heating rates and photodissociation rates in inhomogeneous multiple scattering atmospheres. J. Geophys. Res. 94(D13):16,287-16,301. .. _Turetskyetal2002: -Turetsky, M.R., Wieder, R.K., Halsey, L.A., and Vitt, D.H. 2002. Current -disturbance and the diminishing peatland carbon sink. Geophys. Res. -Lett. 29:1526. DOI:10.1029/2001GL014000. +Turetsky, M.R., Wieder, R.K., Halsey, L.A., and Vitt, D.H. 2002. Current disturbance and the diminishing peatland carbon sink. Geophys. Res. Lett. 29:1526. DOI:10.1029/2001GL014000. .. _Turetskyetal2004: -Turetsky, M.R., Amiro, B.D., Bosch, E., and Bhatti, J.S. 2004. Historical -burn area in western Canadian peatlands and its relationship to fire -weather indices. Global Biogeochem. Cycles 18:GB4014. -DOI:10.1029/2004GB002222. +Turetsky, M.R., Amiro, B.D., Bosch, E., and Bhatti, J.S. 2004. Historical burn area in western Canadian peatlands and its relationship to fire weather indices. Global Biogeochem. Cycles 18:GB4014. DOI:10.1029/2004GB002222. .. _Tyeetal2005: -Tye, A.M., et al. 2005. The fate of N-15 added to high Arctic tundra to -mimic increased inputs of atmospheric nitrogen released from a melting -snowpack. Global Change Biology 11:1640-1654. +Tye, A.M., et al. 2005. The fate of N-15 added to high Arctic tundra to mimic increased inputs of atmospheric nitrogen released from a melting snowpack. Global Change Biology 11:1640-1654. .. _Unlandetal1996: -Unland, H.E., Houser, P.R., Shuttleworth, W.J., and Yang, Z.-L. 1996. -Surface flux measurement and modeling at a semi-arid Sonoran Desert -site. Agric. For. Meteor. 82:119-153. +Unland, H.E., Houser, P.R., Shuttleworth, W.J., and Yang, Z.-L. 1996. Surface flux measurement and modeling at a semi-arid Sonoran Desert site. Agric. For. Meteor. 82:119-153. .. _UNSTAT2005: -UNSTAT, 2005. National Accounts Main Aggregates Database, United Nations -Statistics Division. +UNSTAT, 2005. National Accounts Main Aggregates Database, United Nations Statistics Division. .. _uriarte2009: @@ -2139,51 +1438,31 @@ Uriarte, M. et al., 2009. Natural disturbance and human land use as determinants .. _VallanoSparks2007: -Vallano, D.M. and Sparks, J.P. 2007. Quantifying foliar uptake of -gaseous itrogen dioxide using enriched foliar -:math:`\delta^{15}`\ N values. New Phytologist -177:946-955. +Vallano, D.M. and Sparks, J.P. 2007. Quantifying foliar uptake of gaseous itrogen dioxide using enriched foliar :math:`\delta^{15}`\ N values. New Phytologist 177:946-955. .. _vanderWerfetal2010: -van der Werf, G.R., Randerson, J.T., Giglio, L., Collatz, G.J., Mu, M., -Kasibhatla, S.P., Morton, D.C., DeFries, R.S., Jin, Y., van Leeuwen, -T.T. 2010. Global fire emissions and the contribution of deforestation, -savanna, forest, agricultural, and peat fires (1997-2009) Atmos. Chem. -Phys. 10:11707-11735. +van der Werf, G.R., Randerson, J.T., Giglio, L., Collatz, G.J., Mu, M., Kasibhatla, S.P., Morton, D.C., DeFries, R.S., Jin, Y., van Leeuwen, T.T. 2010. Global fire emissions and the contribution of deforestation, savanna, forest, agricultural, and peat fires (1997-2009) Atmos. Chem. Phys. 10:11707-11735. .. _van Veenetal1984: -van Veen, J.A., Ladd, J.N. and Frissel, M.J., 1984. Modelling C and N -turnover through the microbial biomass in soil. Plant and Soil, 76: -257-274. +van Veen, J.A., Ladd, J.N. and Frissel, M.J., 1984. Modelling C and N turnover through the microbial biomass in soil. Plant and Soil, 76: 257-274. .. _vanKampenhoutetal2017: -van Kampenhout, L., J.T.M. Lenaerts, W.H. Lipscomb, W.J. Sacks, D.M. -Lawrence, A.G. Slater, and M.R. van den Broeke, 2017. -Improving the Representation of Polar Snow and Firn in the -Community Earth System Model. Journal of Advances in Modeling Earth Systems 9, no. 7: 2583–2600. https://doi.org/10.1002/2017MS000988. +van Kampenhout, L., J.T.M. Lenaerts, W.H. Lipscomb, W.J. Sacks, D.M. Lawrence, A.G. Slater, and M.R. van den Broeke, 2017. Improving the Representation of Polar Snow and Firn in the Community Earth System Model. Journal of Advances in Modeling Earth Systems 9, no. 7: 2583–2600. https://doi.org/10.1002/2017MS000988. .. _VanTrichtetal2016: -Van Tricht, K., Lhermitte, S., Gorodetskaya, I.V. and van Lipzig, -N.P.M., 2016. Improving satellite-retrieved surface radiative fluxes in -polar regions using a smart sampling approach. The Cryosphere -10:2379-2397. doi:10.5194/tc-10-2379-2016 +Van Tricht, K., Lhermitte, S., Gorodetskaya, I.V. and van Lipzig, N.P.M., 2016. Improving satellite-retrieved surface radiative fluxes in polar regions using a smart sampling approach. The Cryosphere 10:2379-2397. doi:10.5194/tc-10-2379-2016 .. _VanVuurenetal2006: -Van Vuuren, D.P., Lucas, P.S., and Hilderink, H.B.M., 2006. Downscaling -drivers of global environmental change: enabling use of global SRES -scenarios at the national and grid levels, Report 550025001, Netherlands -Environmental Assessment Agency, 45 pp. +Van Vuuren, D.P., Lucas, P.S., and Hilderink, H.B.M., 2006. Downscaling drivers of global environmental change: enabling use of global SRES scenarios at the national and grid levels, Report 550025001, Netherlands Environmental Assessment Agency, 45 pp. .. _VanninenMakela2005: -Vanninen, P., and Makela, A. 2005. Carbon budget for Scots pine trees: -effects of size, competition and site fertility on growth allocation and -production. Tree Phys. 25:17-30. +Vanninen, P., and Makela, A. 2005. Carbon budget for Scots pine trees: effects of size, competition and site fertility on growth allocation and production. Tree Phys. 25:17-30. .. _venevsky2002: @@ -2191,55 +1470,35 @@ Venevsky, S. et al., 2002. Simulating fire regimes in human-dominated ecosystems .. _VerdinGreenlee1996: -Verdin, K. L., and S. K. Greenlee, 1996. Development of continental -scale digital elevation models and extraction of hydrographic features, -paper presented at the Third International Conference/Workshop on -Integrating GIS and Environmental Modeling, Santa Fe, New Mexico, 21–26 -January, Natl. Cent. for Geogr. Inf. and Anal., Santa Barbara, Calif. +Verdin, K. L., and S. K. Greenlee, 1996. Development of continental scale digital elevation models and extraction of hydrographic features, paper presented at the Third International Conference/Workshop on Integrating GIS and Environmental Modeling, Santa Fe, New Mexico, 21–26 January, Natl. Cent. for Geogr. Inf. and Anal., Santa Barbara, Calif. .. _Vionnetetal2012: -Vionnet, V., E. Brun, S. Morin, A. Boone, S. Faroux, P. Le Moigne, E. Martin, and J.-M. Willemet. -The Detailed Snowpack Scheme Crocus and Its Implementation in SURFEX v7.2. -GMD 5, no. 3 (May 24, 2012): 773-91. https://doi.org/10.5194/gmd-5-773-2012. +Vionnet, V., E. Brun, S. Morin, A. Boone, S. Faroux, P. Le Moigne, E. Martin, and J.-M. Willemet. The Detailed Snowpack Scheme Crocus and Its Implementation in SURFEX v7.2. GMD 5, no. 3 (May 24, 2012): 773-91. https://doi.org/10.5194/gmd-5-773-2012. .. _Viovy2011: -Viovy, N. 2011. CRUNCEP dataset. [Description available at -http://dods.extra.cea.fr/data/p529viov/cruncep/readme.htm. Data -available at -http://dods.extra.cea.fr/store/p529viov/cruncep/V4\_1901\_2011/]. +Viovy, N. 2011. CRUNCEP dataset. [Description available at http://dods.extra.cea.fr/data/p529viov/cruncep/readme.htm. Data available at http://dods.extra.cea.fr/store/p529viov/cruncep/V4\_1901\_2011/]. .. _VitousekHowarth1991: -Vitousek, P.M., and Howarth, R.W. 1991. Nitrogen limitation on land and -in the sea: How can it occur? Biogeochem. 13:87-115. +Vitousek, P.M., and Howarth, R.W. 1991. Nitrogen limitation on land and in the sea: How can it occur? Biogeochem. 13:87-115. .. _Walteretal2001: -Walter, B.P., Heimann, M. and Matthews, E., 2001. Modeling modern -methane emissions from natural wetlands 1. Model description and -results. J. Geophys. Res. 106(D24):34189-34206. +Walter, B.P., Heimann, M. and Matthews, E., 2001. Modeling modern methane emissions from natural wetlands 1. Model description and results. J. Geophys. Res. 106(D24):34189-34206. .. _Waniaetal2009: -Wania, R., Ross, I. and Prentice, I.C. 2009. Integrating peatlands and -permafrost into a dynamic global vegetation model: 2. Evaluation and -sensitivity of vegetation and carbon cycle processes. Global Biogeochem. -Cycles 23. +Wania, R., Ross, I. and Prentice, I.C. 2009. Integrating peatlands and permafrost into a dynamic global vegetation model: 2. Evaluation and sensitivity of vegetation and carbon cycle processes. Global Biogeochem. Cycles 23. .. _Waniaetal2010: -Wania, R., Ross, I. and Prentice, I.C. 2010. Implementation and -evaluation of a new methane model within a dynamic global vegetation -model LPJ-WHyMe v1.3. Geoscientific Model Development Discussions -3:1-59. +Wania, R., Ross, I. and Prentice, I.C. 2010. Implementation and evaluation of a new methane model within a dynamic global vegetation model LPJ-WHyMe v1.3. Geoscientific Model Development Discussions 3:1-59. .. _WangZeng2009: -Wang, A., and Zeng, X. 2009. Improving the treatment of vertical snow -burial fraction over short vegetation in the NCAR CLM3. Adv. Atmos. Sci. -26:877-886. DOI:10.1007/s00376-009-8098-3. +Wang, A., and Zeng, X. 2009. Improving the treatment of vertical snow burial fraction over short vegetation in the NCAR CLM3. Adv. Atmos. Sci. 26:877-886. DOI:10.1007/s00376-009-8098-3. .. _weng2014: @@ -2247,62 +1506,39 @@ Weng, E.S. et al., 2014. Scaling from individuals to ecosystems in an Earth Syst .. _Whiteetal1997: -White, M.A., Thornton, P.E., and Running, S.W. 1997. A continental -phenology model for monitoring vegetation responses to interannual -climatic variability. Global Biogeochem. Cycles 11:217-234. +White, M.A., Thornton, P.E., and Running, S.W. 1997. A continental phenology model for monitoring vegetation responses to interannual climatic variability. Global Biogeochem. Cycles 11:217-234. .. _Whiteetal2000: -White, M.A., Thornton, P.E., Running, S.W., and Nemani, R.R. 2000. -Parameterization and sensitivity analysis of the Biome-BGC terrestrial -ecosystem model: net primary production controls. Earth Interactions -4:1-85. +White, M.A., Thornton, P.E., Running, S.W., and Nemani, R.R. 2000. Parameterization and sensitivity analysis of the Biome-BGC terrestrial ecosystem model: net primary production controls. Earth Interactions 4:1-85. .. _Wiederetal2015: -Wieder, W. R., Cleveland, C. C., Lawrence, D. M., and Bonan, G. B. 2015. -Effects of model structural uncertainty on carbon cycle projections: -biological nitrogen fixation as a case study. Environmental Research -Letters, 10(4), 044016. +Wieder, W. R., Cleveland, C. C., Lawrence, D. M., and Bonan, G. B. 2015. Effects of model structural uncertainty on carbon cycle projections: biological nitrogen fixation as a case study. Environmental Research Letters, 10(4), 044016. .. _Williamsetal1996: -Williams, M., Rastetter, E.B., Fernandes, D.N., Goulden, M.L., -Wofsy, S.C., Shaver, G.R., Melillo, J.M., Munger, J.W., Fan, S.M. -and Nadelhoffer, K.J. 1996. Modelling the soil-plant-atmosphere -continuum in a Quercus–Acer stand at Harvard Forest: the regulation -of stomatal conductance by light, nitrogen and soil/plant hydraulic -properties. Plant, Cell & Environment, 19: 911–927. -doi:10.1111/j.1365-3040.1996.tb00456.x +Williams, M., Rastetter, E.B., Fernandes, D.N., Goulden, M.L., Wofsy, S.C., Shaver, G.R., Melillo, J.M., Munger, J.W., Fan, S.M. and Nadelhoffer, K.J. 1996. Modelling the soil-plant-atmosphere continuum in a Quercus–Acer stand at Harvard Forest: the regulation of stomatal conductance by light, nitrogen and soil/plant hydraulic properties. Plant, Cell & Environment, 19: 911–927. doi:10.1111/j.1365-3040.1996.tb00456.x .. _WiscombeWarren1980: -Wiscombe, W.J., and Warren, S.G. 1980. A model for the spectral albedo -of snow. I. Pure snow. J. Atmos. Sci. 37:2712-2733. +Wiscombe, W.J., and Warren, S.G. 1980. A model for the spectral albedo of snow. I. Pure snow. J. Atmos. Sci. 37:2712-2733. .. _Woodetal1992: -Wood, E.F., Lettenmaier, D.P., and Zartarian, V.G. 1992. A land-surface -hydrology parameterization with subgrid variability for general -circulation models. J. Geophys. Res. 97(D3):2717–2728. -DOI:10.1029/91JD01786. +Wood, E.F., Lettenmaier, D.P., and Zartarian, V.G. 1992. A land-surface hydrology parameterization with subgrid variability for general circulation models. J. Geophys. Res. 97(D3):2717–2728. DOI:10.1029/91JD01786. .. _WorldBank2004: -World Bank, 2004. World development indicators 2004, Oxford University -Press, New York, 416 pp. +World Bank, 2004. World development indicators 2004, Oxford University Press, New York, 416 pp. .. _Wuetal2011: -Wu, H., J. S. Kimball, N. Mantua, and J. Stanford, 2011: Automated -upscaling of river networks for macroscale hydrological modeling. -Water Resour. Res., 47, W03517, doi:10.1029/2009WR008871. +Wu, H., J. S. Kimball, N. Mantua, and J. Stanford, 2011: Automated upscaling of river networks for macroscale hydrological modeling. Water Resour. Res., 47, W03517, doi:10.1029/2009WR008871. .. _Wuetal2012: -Wu, H., J. S. Kimball, H. Li, M. Huang, L. R. Leung, and R. F. Adler, -2012. A New Global River Network Database for Macroscale Hydrologic -modeling, Water Resour. Res., 48, W09701, doi:10.1029/2012WR012313. +Wu, H., J. S. Kimball, H. Li, M. Huang, L. R. Leung, and R. F. Adler, 2012. A New Global River Network Database for Macroscale Hydrologic modeling, Water Resour. Res., 48, W09701, doi:10.1029/2012WR012313. .. _xiaodong2005: @@ -2314,76 +1550,51 @@ Xu, C., R. Fisher, S. D. Wullschleger, C. J. Wilson, M. Cai, and N. G. McDowell, .. _Yang1998: -Yang, Z.-L. 1998. Technical note of a 10-layer soil moisture and -temperature model. Unpublished manuscript. +Yang, Z.-L. 1998. Technical note of a 10-layer soil moisture and temperature model. Unpublished manuscript. .. _Zenderetal2003: -Zender, C.S., Bian, H., and Newman, D. 2003. Mineral dust entrainment -and deposition (DEAD) model: Description and 1990s dust climatology. ** -J. Geophys. Res\ *.* 108(D14):4416. DOI:10.1029/2002JD002775. +Zender, C.S., Bian, H., and Newman, D. 2003. Mineral dust entrainment and deposition (DEAD) model: Description and 1990s dust climatology. ** J. Geophys. Res\ *.* 108(D14):4416. DOI:10.1029/2002JD002775. .. _ZengDickinson1998: -Zeng, X., and Dickinson, R.E. 1998. Effect of surface sublayer on -surface skin temperature and fluxes. J.Climate 11:537-550. +Zeng, X., and Dickinson, R.E. 1998. Effect of surface sublayer on surface skin temperature and fluxes. J.Climate 11:537-550. .. _Zengetal1998: -Zeng, X., Zhao, M., and Dickinson, R.E. 1998. Intercomparison of bulk -aerodynamic algorithms for the computation of sea surface fluxes using -the TOGA COARE and TAO data. J. Climate 11:2628-2644. +Zeng, X., Zhao, M., and Dickinson, R.E. 1998. Intercomparison of bulk aerodynamic algorithms for the computation of sea surface fluxes using the TOGA COARE and TAO data. J. Climate 11:2628-2644. .. _Zeng2001: -Zeng, X. 2001. Global vegetation root distribution for land modeling. J. -Hydrometeor. 2:525-530. +Zeng, X. 2001. Global vegetation root distribution for land modeling. J. Hydrometeor. 2:525-530. .. _Zengetal2002: -Zeng, X., Shaikh, M., Dai, Y., Dickinson, R.E., and Myneni, R. 2002. -Coupling of the Common Land Model to the NCAR Community Climate Model. -J. Climate 15:1832-1854. +Zeng, X., Shaikh, M., Dai, Y., Dickinson, R.E., and Myneni, R. 2002. Coupling of the Common Land Model to the NCAR Community Climate Model. J. Climate 15:1832-1854. .. _Zengetal2005: -Zeng, X., Dickinson, R.E., Barlage, M., Dai, Y., Wang, G., and Oleson, -K. 2005. Treatment of under-canopy turbulence in land models. J. Climate -18:5086-5094. +Zeng, X., Dickinson, R.E., Barlage, M., Dai, Y., Wang, G., and Oleson, K. 2005. Treatment of under-canopy turbulence in land models. J. Climate 18:5086-5094. .. _ZengWang2007: -Zeng, X., and Wang, A. 2007. Consistent parameterization of roughness -length and displacement height for sparse and dense canopies in land -models. J. Hydrometeor. 8:730-737. +Zeng, X., and Wang, A. 2007. Consistent parameterization of roughness length and displacement height for sparse and dense canopies in land models. J. Hydrometeor. 8:730-737. -Zeng, X., and Decker, M. 2009. Improving the numerical solution of soil -moisture-based Richards equation for land models with a deep or shallow -water table. J. Hydrometeor. 10:308-319. +Zeng, X., and Decker, M. 2009. Improving the numerical solution of soil moisture-based Richards equation for land models with a deep or shallow water table. J. Hydrometeor. 10:308-319. .. _Zengetal2008: -Zeng, X., Zeng, X., and Barlage, M. 2008. Growing temperate shrubs over -arid and semiarid regions in the Community Land Model - Dynamic Global -Vegetation Model. Global Biogeochem. Cycles 22:GB3003. -DOI:10.1029/2007GB003014. +Zeng, X., Zeng, X., and Barlage, M. 2008. Growing temperate shrubs over arid and semiarid regions in the Community Land Model - Dynamic Global Vegetation Model. Global Biogeochem. Cycles 22:GB3003. DOI:10.1029/2007GB003014. .. _Zhangetal2002: -Zhang, Y., Li, C.S., Trettin, C.C., Li, H. and Sun, G., 2002. An -integrated model of soil, hydrology, and vegetation for carbon dynamics -in wetland ecosystems. Global Biogeochemical Cycles 16. -DOI:10.1029/2001GB001838. +Zhang, Y., Li, C.S., Trettin, C.C., Li, H. and Sun, G., 2002. An integrated model of soil, hydrology, and vegetation for carbon dynamics in wetland ecosystems. Global Biogeochemical Cycles 16. DOI:10.1029/2001GB001838. .. _Zhuangetal2004: -Zhuang, Q., et al. 2004. Methane fluxes between terrestrial ecosystems -and the atmosphere at northern high latitudes during the past century: A -retrospective analysis with a process-based biogeochemistry model. -Global Biogeochemical Cycles 18. DOI:10.1029/2004GB002239. +Zhuang, Q., et al. 2004. Methane fluxes between terrestrial ecosystems and the atmosphere at northern high latitudes during the past century: A retrospective analysis with a process-based biogeochemistry model. Global Biogeochemical Cycles 18. DOI:10.1029/2004GB002239. .. _Zilitinkevich1970: -Zilitinkevich, S.S. 1970. Dynamics of the Atmospheric Boundary Layer. -Leningrad Gidrometeor. +Zilitinkevich, S.S. 1970. Dynamics of the Atmospheric Boundary Layer. Leningrad Gidrometeor. diff --git a/doc/source/tech_note/Snow_Hydrology/CLM50_Tech_Note_Snow_Hydrology.rst b/doc/source/tech_note/Snow_Hydrology/CLM50_Tech_Note_Snow_Hydrology.rst index 8d22a19bc9..fdc559e1c2 100644 --- a/doc/source/tech_note/Snow_Hydrology/CLM50_Tech_Note_Snow_Hydrology.rst +++ b/doc/source/tech_note/Snow_Hydrology/CLM50_Tech_Note_Snow_Hydrology.rst @@ -3,18 +3,7 @@ Snow Hydrology =============== -The parameterizations for snow are based primarily on -:ref:`Anderson (1976) `, :ref:`Jordan (1991) `, -and :ref:`Dai and Zeng (1997) `. The snowpack -can have up to twelve layers. These layers are indexed in the Fortran code -as :math:`i=-11,-10,...,-1,0` where layer :math:`i=0` is the snow layer -next to the top soil layer and layer :math:`i=-11` is the top layer of a -twelve-layer snow pack. Since the number of snow layers varies according -to the snow depth, we use the notation :math:`snl+1` to describe the top -layer of snow for the variable layer snow pack, where :math:`snl` is the -negative of the number of snow layers. Refer to :numref:`Figure three layer -snow pack` for an example of the snow layer structure for a three layer -snow pack. +The parameterizations for snow are based primarily on :ref:`Anderson (1976) `, :ref:`Jordan (1991) `, and :ref:`Dai and Zeng (1997) `. The snowpack can have up to twelve layers. These layers are indexed in the Fortran code as :math:`i=-11,-10,...,-1,0` where layer :math:`i=0` is the snow layer next to the top soil layer and layer :math:`i=-11` is the top layer of a twelve-layer snow pack. Since the number of snow layers varies according to the snow depth, we use the notation :math:`snl+1` to describe the top layer of snow for the variable layer snow pack, where :math:`snl` is the negative of the number of snow layers. Refer to :numref:`Figure three layer snow pack` for an example of the snow layer structure for a three layer snow pack. .. _Figure three layer snow pack: @@ -22,80 +11,41 @@ snow pack. Example of three layer snow pack (:math:`snl=-3`). -Shown are three snow layers, :math:`i=-2`, :math:`i=-1`, and -:math:`i=0`. The layer node depth is :math:`z`, the layer interface is -:math:`z_{h}` , and the layer thickness is :math:`\Delta z`. - -The state variables for snow are the mass of water :math:`w_{liq,i}` -(kg m\ :sup:`-2`), mass of ice :math:`w_{ice,i}` (kg m\ :sup:`-2`), layer -thickness :math:`\Delta z_{i}` (m), and temperature :math:`T_{i}` -(Chapter :numref:`rst_Soil and Snow Temperatures`). The water vapor phase is -neglected. Snow can also exist in the model without being represented by -explicit snow layers. This occurs when the snowpack is less than a -specified minimum snow depth (:math:`z_{sno} < 0.01` m). In this case, -the state variable is the mass of snow :math:`W_{sno}` (kg m\ :sup:`-2`). - -Section :numref:`Snow Covered Area Fraction` describes the calculation -of fractional snow covered area, which is used in the surface albedo -calculation (Chapter :numref:`rst_Surface Albedos`) and the surface flux -calculations (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent -Heat Fluxes`). The following two sections (:numref:`Ice Content` and -:numref:`Water Content`) describe the ice and water content of the snow -pack assuming that at least one snow layer exists. Section :numref:`Black -and organic carbon and mineral dust within snow` describes how black and -organic carbon and mineral dust particles are represented within snow, -including meltwater flushing. See Section :numref:`Initialization of snow -layer` for a description of how a snow layer is initialized. +Shown are three snow layers, :math:`i=-2`, :math:`i=-1`, and :math:`i=0`. The layer node depth is :math:`z`, the layer interface is :math:`z_{h}`, and the layer thickness is :math:`\Delta z`. + +The state variables for snow are the mass of water :math:`w_{liq,i}` (kg m\ :sup:`-2`), mass of ice :math:`w_{ice,i}` (kg m\ :sup:`-2`), layer thickness :math:`\Delta z_{i}` (m), and temperature :math:`T_{i}` (Chapter :numref:`rst_Soil and Snow Temperatures`). The water vapor phase is neglected. Snow can also exist in the model without being represented by explicit snow layers. This occurs when the snowpack is less than a specified minimum snow depth (:math:`z_{sno} < 0.01` m). In this case, the state variable is the mass of snow :math:`W_{sno}` (kg m\ :sup:`-2`). + +Section :numref:`Snow Covered Area Fraction` describes the calculation of fractional snow covered area, which is used in the surface albedo calculation (Chapter :numref:`rst_Surface Albedos`) and the surface flux calculations (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`). The following two sections (:numref:`Ice Content` and :numref:`Water Content`) describe the ice and water content of the snow pack assuming that at least one snow layer exists. Section :numref:`Black and organic carbon and mineral dust within snow` describes how black and organic carbon and mineral dust particles are represented within snow, including meltwater flushing. See Section :numref:`Initialization of snow layer` for a description of how a snow layer is initialized. .. _Snow Covered Area Fraction: Snow Covered Area Fraction ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The fraction of the ground covered by snow, :math:`f_{sno}` , is based -on the method of :ref:`Swenson and Lawrence (2012) `. -Because the processes -governing snowfall and snowmelt differ, changes in :math:`f_{sno}` are -calculated separately for accumulation and depletion. When snowfall -occurs, :math:`f_{sno}` is updated as +The fraction of the ground covered by snow, :math:`f_{sno}`, is based on the method of :ref:`Swenson and Lawrence (2012) `. Because the processes governing snowfall and snowmelt differ, changes in :math:`f_{sno}` are calculated separately for accumulation and depletion. When snowfall occurs, :math:`f_{sno}` is updated as .. math:: :label: 8.14 f^{n+1} _{sno} =1-\left(\left(1-\tanh (k_{accum} q_{sno} \Delta t)\right)\left(1-f^{n} _{sno} \right)\right) -where :math:`k_{accum}` is a constant whose default value is 0.1, -:math:`q_{sno} \Delta t` is the amount of new snow, -:math:`f^{n+1} _{sno}` is the updated snow covered fraction (SCF), and -:math:`f^{n} _{sno}` is the SCF from the previous time step. +where :math:`k_{accum}` is a constant whose default value is 0.1, :math:`q_{sno} \Delta t` is the amount of new snow, :math:`f^{n+1} _{sno}` is the updated snow covered fraction (SCF), and :math:`f^{n} _{sno}` is the SCF from the previous time step. -When snow melt occurs, :math:`f_{sno}` is calculated from the depletion -curve +When snow melt occurs, :math:`f_{sno}` is calculated from the depletion curve .. math:: :label: 8.15 f_{sno} =1-\left(\frac{\cos ^{-1} \left(2R_{sno} -1\right)}{\pi } \right)^{N_{melt} } -where :math:`R_{sno}` is the ratio of :math:`W_{sno}` to the maximum -accumulated snow :math:`W_{\max }` , and :math:`N_{melt}` is a -parameter that depends on the topographic variability within the grid -cell. Whenever :math:`W_{sno}` reaches zero, :math:`W_{\max }` is -reset to zero. The depletion curve shape parameter is defined as +where :math:`R_{sno}` is the ratio of :math:`W_{sno}` to the maximum accumulated snow :math:`W_{\max }`, and :math:`N_{melt}` is a parameter that depends on the topographic variability within the grid cell. Whenever :math:`W_{sno}` reaches zero, :math:`W_{\max }` is reset to zero. The depletion curve shape parameter is defined as .. math:: :label: 8.16 N_{melt} =\frac{200}{\min \left(10,\sigma _{topo} \right)} -The standard deviation of the elevation within a grid cell, -:math:`\sigma _{topo}` , is calculated from a high resolution DEM (a -1km DEM is used for CLM). -Note that *glacier\_mec* columns (section :numref:`Multiple elevation class scheme`) -are treated differently in this respect, as they already account for the -subgrid topography in a grid cell in their own way. -Therefore, in each *glacier\_mec* column very flat terrain is assumed, -implemented as :math:`N_{melt}=10`. +The standard deviation of the elevation within a grid cell, :math:`\sigma _{topo}`, is calculated from a high resolution DEM (a 1km DEM is used for CLM). Note that *glacier\_mec* columns (section :numref:`Multiple elevation class scheme`) are treated differently in this respect, as they already account for the subgrid topography in a grid cell in their own way. Therefore, in each *glacier\_mec* column very flat terrain is assumed, implemented as :math:`N_{melt}=10`. .. _Ice Content: @@ -108,30 +58,19 @@ The conservation equation for mass of ice in snow layers is :label: 8.17 \frac{\partial w_{ice,\, i} }{\partial t} = - \left\{\begin{array}{lr} - f_{sno} \ q_{ice,\, i-1} -\frac{\left(\Delta w_{ice,\, i} \right)_{p} }{\Delta t} & \qquad i=snl+1 \\ - -\frac{\left(\Delta w_{ice,\, i} \right)_{p} }{\Delta t} & \qquad i=snl+2,\ldots ,0 + \left\{\begin{array}{lr} + f_{sno} \ q_{ice,\, i-1} -\frac{\left(\Delta w_{ice,\, i} \right)_{p} }{\Delta t} & \qquad i=snl+1 \\ + -\frac{\left(\Delta w_{ice,\, i} \right)_{p} }{\Delta t} & \qquad i=snl+2,\ldots ,0 \end{array}\right\} -where :math:`q_{ice,\, i-1}` is the rate of ice accumulation from -precipitation or frost or the rate of ice loss from sublimation (kg -m\ :sup:`-2` s\ :sup:`-1`) in the top layer and -:math:`{\left(\Delta w_{ice,\, i} \right)_{p} \mathord{\left/ {\vphantom {\left(\Delta w_{ice,\, i} \right)_{p} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t}` -is the change in ice due to phase change (melting rate) (section :numref:`Phase Change`). -The term :math:`q_{ice,\, i-1}` is computed in two steps as +where :math:`q_{ice,\, i-1}` is the rate of ice accumulation from precipitation or frost or the rate of ice loss from sublimation (kg m\ :sup:`-2` s\ :sup:`-1`) in the top layer and :math:`{\left(\Delta w_{ice,\, i} \right)_{p} \mathord{\left/ {\vphantom {\left(\Delta w_{ice,\, i} \right)_{p} \Delta t}} \right.} \Delta t}` is the change in ice due to phase change (melting rate) (section :numref:`Phase Change`). The term :math:`q_{ice,\, i-1}` is computed in two steps as .. math:: :label: 8.18 q_{ice,\, i-1} =q_{grnd,\, ice} +\left(q_{frost} -q_{subl} \right) -where :math:`q_{grnd,\, ice}` is the rate of solid precipitation -reaching the ground (section :numref:`Canopy Water`) and :math:`q_{frost}` and -:math:`q_{subl}` are gains due to frost and losses due to sublimation, -respectively (sectio :numref:`Update of Ground Sensible and Latent Heat Fluxes`). In the first step, immediately after -:math:`q_{grnd,\, ice}` has been determined after accounting for -interception (section :numref:`Canopy Water`), a new snow depth :math:`z_{sno}` (m) is -calculated from +where :math:`q_{grnd,\, ice}` is the rate of solid precipitation reaching the ground (section :numref:`Canopy Water`) and :math:`q_{frost}` and :math:`q_{subl}` are gains due to frost and losses due to sublimation, respectively (sectio :numref:`Update of Ground Sensible and Latent Heat Fluxes`). In the first step, immediately after :math:`q_{grnd,\, ice}` has been determined after accounting for interception (section :numref:`Canopy Water`), a new snow depth :math:`z_{sno}` (m) is calculated from .. math:: :label: 8.19 @@ -145,9 +84,7 @@ where \Delta z_{sno} =\frac{q_{grnd,\, ice} \Delta t}{f_{sno} \rho _{sno} } -and :math:`\rho _{sno}` is the bulk density of newly fallen snow (kg -m\ :sup:`-3`), which parameterized by a temperature-dependent and a -wind-dependent term: +and :math:`\rho _{sno}` is the bulk density of newly fallen snow (kg m\ :sup:`-3`), which parameterized by a temperature-dependent and a wind-dependent term: .. math:: :label: 8.21a @@ -159,21 +96,17 @@ The temperature dependent term is given by (:ref:`van Kampenhout et al. (2017) < .. math:: :label: 8.21b - \rho_{T} = - \left\{\begin{array}{lr} - 50 + 1.7 \left(17\right)^{1.5} & \qquad T_{atm} >T_{f} +2 \ \\ - 50+1.7 \left(T_{atm} -T_{f} + 15\right)^{1.5} & \qquad T_{f} - 15 < T_{atm} \le T_{f} + 2 \ \\ - -3.833 \ \left( T_{atm} -T_{f} \right) - 0.0333 \ \left( T_{atm} -T_{f} \right)^{2} - &\qquad T_{atm} \le T_{f} - 15 + \rho_{T} = + \left\{\begin{array}{lr} + 50 + 1.7 \left(17\right)^{1.5} & \qquad T_{atm} >T_{f} +2 \ \\ + 50+1.7 \left(T_{atm} -T_{f} + 15\right)^{1.5} & \qquad T_{f} - 15 < T_{atm} \le T_{f} + 2 \ \\ + -3.833 \ \left( T_{atm} -T_{f} \right) - 0.0333 \ \left( T_{atm} -T_{f} \right)^{2} + &\qquad T_{atm} \le T_{f} - 15 \end{array}\right\} .. bifall(c) = -(50._r8/15._r8 + 0.0333_r8*15_r8)*(forc_t(c)-tfrz) - 0.0333_r8*(forc_t(c)-tfrz)**2 -where :math:`T_{atm}` is the atmospheric temperature (K), and :math:`T_{f}` is -the freezing temperature of water (K) (:numref:`Table Physical Constants`). -When 10 m wind speed :math:`W_{atm}` is greater than 0.1 m\ :sup:`-1`, snow density -increases due to wind-driven compaction according to -:ref:`van Kampenhout et al. 2017 ` +where :math:`T_{atm}` is the atmospheric temperature (K), and :math:`T_{f}` is the freezing temperature of water (K) (:numref:`Table Physical Constants`). When 10 m wind speed :math:`W_{atm}` is greater than 0.1 m\ :sup:`-1`, snow density increases due to wind-driven compaction according to :ref:`van Kampenhout et al. 2017 ` .. math:: :label: 8.21c @@ -184,7 +117,6 @@ increases due to wind-driven compaction according to which is added to the temperature-dependent term (cf. equation :eq:`8.21a`). - The mass of snow :math:`W_{sno}` is .. math:: @@ -204,26 +136,16 @@ The ice content of the top layer and the layer thickness are updated as \Delta z_{snl+1}^{n+1} =\Delta z_{snl+1}^{n} +\Delta z_{sno} . -In the second step, after surface fluxes and snow/soil temperatures have -been determined (Chapters :numref:`rst_Momentum, Sensible Heat, and Latent Heat -Fluxes` and :numref:`rst_Soil and Snow Temperatures`), -:math:`w_{ice,\, snl+1}` is updated for frost or sublimation as +In the second step, after surface fluxes and snow/soil temperatures have been determined (Chapters :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes` and :numref:`rst_Soil and Snow Temperatures`), :math:`w_{ice,\, snl+1}` is updated for frost or sublimation as .. math:: :label: 8.25 w_{ice,\, snl+1}^{n+1} =w_{ice,\, snl+1}^{n} +f_{sno} \left(q_{frost} -q_{subl} \right)\Delta t. -If :math:`w_{ice,\, snl+1}^{n+1} <0` upon solution of equation , the ice -content is reset to zero and the liquid water content -:math:`w_{liq,\, snl+1}` is reduced by the amount required to bring -:math:`w_{ice,\, snl+1}^{n+1}` up to zero. +If :math:`w_{ice,\, snl+1}^{n+1} <0` upon solution of equation :eq:`8.25`, the ice content is reset to zero and the liquid water content :math:`w_{liq,\, snl+1}` is reduced by the amount required to bring :math:`w_{ice,\, snl+1}^{n+1}` up to zero. -The snow water equivalent :math:`W_{sno}` is capped to not exceed 10,000 -kg m\ :sup:`-2`. If the addition of :math:`q_{frost}` were to -result in :math:`W_{sno} > 10,000` kg m\ :sup:`-2`, the frost term -:math:`q_{frost}` is instead added to the ice runoff term -:math:`q_{snwcp,\, ice}` (section :numref:`Runoff from glaciers and snow-capped surfaces`). +The snow water equivalent :math:`W_{sno}` is capped to not exceed 10,000 kg m\ :sup:`-2`. If the addition of :math:`q_{frost}` were to result in :math:`W_{sno} > 10,000` kg m\ :sup:`-2`, the frost term :math:`q_{frost}` is instead added to the ice runoff term :math:`q_{snwcp,\, ice}` (section :numref:`Runoff from glaciers and snow-capped surfaces`). .. _Water Content: @@ -237,50 +159,28 @@ The conservation equation for mass of water in snow layers is \frac{\partial w_{liq,\, i} }{\partial t} =\left(q_{liq,\, i-1} -q_{liq,\, i} \right)+\frac{\left(\Delta w_{liq,\, i} \right)_{p} }{\Delta t} -where :math:`q_{liq,\, i-1}` is the flow of liquid water into layer -:math:`i` from the layer above, :math:`q_{liq,\, i}` is the flow of -water out of layer :math:`i` to the layer below, -:math:`{\left(\Delta w_{liq,\, i} \right)_{p} \mathord{\left/ {\vphantom {\left(\Delta w_{liq,\, i} \right)_{p} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t}` -is the change in liquid water due to phase change (melting rate) -(section :numref:`Phase Change`). For the top snow layer only, +where :math:`q_{liq,\, i-1}` is the flow of liquid water into layer :math:`i` from the layer above, :math:`q_{liq,\, i}` is the flow of water out of layer :math:`i` to the layer below, :math:`{\left(\Delta w_{liq,\, i} \right)_{p} \mathord{\left/ {\vphantom {\left(\Delta w_{liq,\, i} \right)_{p} \Delta t}} \right.} \Delta t}` is the change in liquid water due to phase change (melting rate) (section :numref:`Phase Change`). For the top snow layer only, .. math:: :label: 8.27 q_{liq,\, i-1} =f_{sno} \left(q_{grnd,\, liq} +\left(q_{sdew} -q_{seva} \right)\right) -where :math:`q_{grnd,\, liq}` is the rate of liquid precipitation -reaching the snow (section :numref:`Canopy Water`), :math:`q_{seva}` is the -evaporation of liquid water and :math:`q_{sdew}` is the liquid dew (section -:numref:`Update of Ground Sensible and Latent Heat Fluxes`). After surface -fluxes and snow/soil temperatures have been determined -(Chapters :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes` and -:numref:`rst_Soil and Snow Temperatures`), :math:`w_{liq,\, snl+1}` is -updated for the liquid precipitation reaching the ground and dew or -evaporation as +where :math:`q_{grnd,\, liq}` is the rate of liquid precipitation reaching the snow (section :numref:`Canopy Water`), :math:`q_{seva}` is the evaporation of liquid water and :math:`q_{sdew}` is the liquid dew (section :numref:`Update of Ground Sensible and Latent Heat Fluxes`). After surface fluxes and snow/soil temperatures have been determined (Chapters :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes` and :numref:`rst_Soil and Snow Temperatures`), :math:`w_{liq,\, snl+1}` is updated for the liquid precipitation reaching the ground and dew or evaporation as .. math:: :label: 8.28 w_{liq,\, snl+1}^{n+1} =w_{liq,\, snl+1}^{n} +f_{sno} \left(q_{grnd,\, liq} +q_{sdew} -q_{seva} \right)\Delta t. -When the liquid water within a snow layer exceeds the layer’s holding -capacity, the excess water is added to the underlying layer, limited by -the effective porosity (:math:`1-\theta _{ice}` ) of the layer. The flow -of water is assumed to be zero (:math:`q_{liq,\, i} =0`) if the -effective porosity of either of the two layers -(:math:`1-\theta _{ice,\, i} {\rm \; and\; }1-\theta _{ice,\, i+1}` ) is -less than :math:`\theta _{imp} =0.05`, the water impermeable volumetric -water content. Thus, water flow between layers, :math:`q_{liq,\, i}` , -for layers :math:`i=snl+1,\ldots ,0`, is initially calculated as +When the liquid water within a snow layer exceeds the layer's holding capacity, the excess water is added to the underlying layer, limited by the effective porosity (:math:`1-\theta _{ice}` ) of the layer. The flow of water is assumed to be zero (:math:`q_{liq,\, i} =0`) if the effective porosity of either of the two layers (:math:`1-\theta _{ice,\, i} {\rm \; and\; }1-\theta _{ice,\, i+1}` ) is less than :math:`\theta _{imp} =0.05`, the water impermeable volumetric water content. Thus, water flow between layers, :math:`q_{liq,\, i}`, for layers :math:`i=snl+1,\ldots,0`, is initially calculated as .. math:: :label: 8.29 q_{liq,\, i} =\frac{\rho _{liq} \left[\theta _{liq,\, i} -S_{r} \left(1-\theta _{ice,\, i} \right)\right]f_{sno} \Delta z_{i} }{\Delta t} \ge 0 -where the volumetric liquid water :math:`\theta _{liq,\, i}` and ice -:math:`\theta _{ice,\, i}` contents are +where the volumetric liquid water :math:`\theta _{liq,\, i}` and ice :math:`\theta _{ice,\, i}` contents are .. math:: :label: 8.30 @@ -292,12 +192,7 @@ where the volumetric liquid water :math:`\theta _{liq,\, i}` and ice \theta _{liq,\, i} =\frac{w_{liq,\, i} }{f_{sno} \Delta z_{i} \rho _{liq} } \le 1-\theta _{ice,\, i} , -and :math:`S_{r} =0.033` is the irreducible water saturation (snow -holds a certain amount of liquid water due to capillary retention after -drainage has ceased (:ref:`Anderson (1976) `)). The water holding capacity of the -underlying layer limits the flow of water :math:`q_{liq,\, i}` -calculated in equation , unless the underlying layer is the surface soil -layer, as +and :math:`S_{r} =0.033` is the irreducible water saturation (snow holds a certain amount of liquid water due to capillary retention after drainage has ceased (:ref:`Anderson (1976) `)). The water holding capacity of the underlying layer limits the flow of water :math:`q_{liq,\, i}` calculated in equation :eq:`8.29`, unless the underlying layer is the surface soil layer, as .. math:: :label: 8.32 @@ -311,33 +206,16 @@ The liquid water content :math:`w_{liq,\, i}` is updated as w_{liq,\, i}^{n+1} =w_{liq,\, i}^{n} +\left(q_{i-1} -q_{i} \right)\Delta t. -Equations - are solved sequentially from top (:math:`i=snl+1`) to -bottom (:math:`i=0`) snow layer in each time step. The total flow of -liquid water reaching the soil surface is then :math:`q_{liq,\, 0}` -which is used in the calculation of surface runoff and infiltration -(sections :numref:`Surface Runoff` and :numref:`Infiltration`). +Equations :eq:`8.29` - :eq:`8.33` are solved sequentially from top (:math:`i=snl+1`) to bottom (:math:`i=0`) snow layer in each time step. The total flow of liquid water reaching the soil surface is then :math:`q_{liq,\, 0}` which is used in the calculation of surface runoff and infiltration (sections :numref:`Surface Runoff` and :numref:`Infiltration`). .. _Black and organic carbon and mineral dust within snow: Black and organic carbon and mineral dust within snow ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Particles within snow originate from atmospheric aerosol deposition -(:math:`D_{sp}` in Table 2.3 (kg m\ :sup:`-2` s\ :sup:`-1`) -and influence snow radiative transfer (sections :numref:`Snow Albedo`, -:numref:`Snowpack Optical Properties`, and :numref:`Snow Aging`). -Particle masses and mixing ratios are represented with a simple -mass-conserving scheme. The model maintains masses of the following -eight particle species within each snow layer: hydrophilic black carbon, -hydrophobic black carbon, hydrophilic organic carbon, hydrophobic -organic carbon, and four species of mineral dust with the following -particle sizes: 0.1-1.0, 1.0-2.5, 2.5-5.0, and 5.0-10.0 :math:`\mu m`. -Each of these species has unique optical properties -(:numref:`Table Single-scatter albedo values used for snowpack impurities and ice`) -and meltwater removal efficiencies (:numref:`Table Meltwater scavenging`). +Particles within snow originate from atmospheric aerosol deposition (:math:`D_{sp}` in Table 2.3 (kg m\ :sup:`-2` s\ :sup:`-1`) and influence snow radiative transfer (sections :numref:`Snow Albedo`, :numref:`Snowpack Optical Properties`, and :numref:`Snow Aging`). Particle masses and mixing ratios are represented with a simple mass-conserving scheme. The model maintains masses of the following eight particle species within each snow layer: hydrophilic black carbon, hydrophobic black carbon, hydrophilic organic carbon, hydrophobic organic carbon, and four species of mineral dust with the following particle sizes: 0.1-1.0, 1.0-2.5, 2.5-5.0, and 5.0-10.0 :math:`\mu m`. Each of these species has unique optical properties (:numref:`Table Single-scatter albedo values used for snowpack impurities and ice`) and meltwater removal efficiencies (:numref:`Table Meltwater scavenging`). -The black carbon and organic carbon deposition rates described in Table -2.3 are combined into four categories as follows +The black carbon and organic carbon deposition rates described in Table 2.3 are combined into four categories as follows .. math:: :label: 8.34 @@ -359,50 +237,26 @@ The black carbon and organic carbon deposition rates described in Table D_{oc,\, hphob} =D_{oc,\, dryhphob} -Deposited particles are assumed to be instantly mixed (homogeneously) -within the surface snow layer and are added after the inter-layer water -fluxes are computed (section :numref:`Water Content`) so that some aerosol is in the top -layer after deposition and is not immediately washed out before radiative -calculations are done. Particle masses are then redistributed each time -step based on meltwater drainage through the snow column (section -:numref:`Water Content`) and snow layer combination and subdivision -(section :numref:`Snow Layer Combination and Subdivision`). The change in -mass of each of the particle species :math:`\Delta m_{sp,\, i}` -(kg m\ :sup:`-2`) is +Deposited particles are assumed to be instantly mixed (homogeneously) within the surface snow layer and are added after the inter-layer water fluxes are computed (section :numref:`Water Content`) so that some aerosol is in the top layer after deposition and is not immediately washed out before radiative calculations are done. Particle masses are then redistributed each time step based on meltwater drainage through the snow column (section :numref:`Water Content`) and snow layer combination and subdivision (section :numref:`Snow Layer Combination and Subdivision`). The change in mass of each of the particle species :math:`\Delta m_{sp,\, i}` (kg m\ :sup:`-2`) is .. math:: :label: 8.38 \Delta m_{sp,\, i} =\left[k_{sp} \left(q_{liq,\, i-1} c_{sp,\, i-1} -q_{liq,\, i} c_{i} \right)+D_{sp} \right]\Delta t -where :math:`k_{sp}` is the meltwater scavenging efficiency that is -unique for each species (:numref:`Table Meltwater scavenging`), :math:`q_{liq,\, i-1}` is the flow -of liquid water into layer :math:`i` from the layer above, -:math:`q_{liq,\, i}` is the flow of water out of layer :math:`i` into -the layer below (kg m\ :sup:`-2` s\ :sup:`-1`) (section -:numref:`Water Content`), :math:`c_{sp,\, i-1}` and :math:`c_{sp,\, i}` are the particle -mass mixing ratios in layers :math:`i-1` and :math:`i` (kg -kg\ :sup:`-1`), :math:`D_{sp}` is the atmospheric deposition rate -(zero for all layers except layer :math:`snl+1`), and :math:`\Delta t` -is the model time step (s). The particle mass mixing ratio is +where :math:`k_{sp}` is the meltwater scavenging efficiency that is unique for each species (:numref:`Table Meltwater scavenging`), :math:`q_{liq,\, i-1}` is the flow of liquid water into layer :math:`i` from the layer above, :math:`q_{liq,\, i}` is the flow of water out of layer :math:`i` into the layer below (kg m\ :sup:`-2` s\ :sup:`-1`) (section :numref:`Water Content`), :math:`c_{sp,\, i-1}` and :math:`c_{sp,\, i}` are the particle mass mixing ratios in layers :math:`i-1` and :math:`i` (kg kg\ :sup:`-1`), :math:`D_{sp}` is the atmospheric deposition rate (zero for all layers except layer :math:`snl+1`), and :math:`\Delta t` is the model time step (s). The particle mass mixing ratio is .. math:: :label: 8.39 c_{i} =\frac{m_{sp,\, i} }{w_{liq,\, i} +w_{ice,\, i} } . -Values of :math:`k_{sp}` are partially derived from experiments -published by :ref:`Conway et al. (1996) `. Particles masses are re-distributed -proportionately with snow mass when layers are combined or divided, thus -conserving particle mass within the snow column. The mass of particles -carried out with meltwater through the bottom snow layer is assumed to -be permanently lost from the snowpack, and is not maintained within the -model. +Values of :math:`k_{sp}` are partially derived from experiments published by :ref:`Conway et al. (1996) `. Particles masses are re-distributed proportionately with snow mass when layers are combined or divided, thus conserving particle mass within the snow column. The mass of particles carried out with meltwater through the bottom snow layer is assumed to be permanently lost from the snowpack, and is not maintained within the model. .. _Table Meltwater scavenging: .. table:: Meltwater scavenging efficiency for particles within snow - + +------------------------------------------+-------------------+ | Species | :math:`k_{sp}` | +==========================================+===================+ @@ -428,21 +282,18 @@ model. Initialization of snow layer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If there are no existing snow layers (:math:`snl+1=1`) but -:math:`z_{sno} \ge 0.01` m after accounting for solid precipitation -:math:`q_{sno}` , then a snow layer is initialized (:math:`snl=-1`) as -follows +If there are no existing snow layers (:math:`snl+1=1`) but :math:`z_{sno} \ge 0.01` m after accounting for solid precipitation :math:`q_{sno}`, then a snow layer is initialized (:math:`snl=-1`) as follows .. math:: :label: 8.40 - \begin{array}{lcr} - \Delta z_{0} & = & z_{sno} \\ - z_{o} & = & -0.5\Delta z_{0} \\ - z_{h,\, -1} & = & -\Delta z_{0} \\ - T_{0} & = & \min \left(T_{f} ,T_{atm} \right) \\ - w_{ice,\, 0} & = & W_{sno} \\ - w_{liq,\, 0} & = & 0 + \begin{array}{lcr} + \Delta z_{0} & = & z_{sno} \\ + z_{o} & = & -0.5\Delta z_{0} \\ + z_{h,\, -1} & = & -\Delta z_{0} \\ + T_{0} & = & \min \left(T_{f} ,T_{atm} \right) \\ + w_{ice,\, 0} & = & W_{sno} \\ + w_{liq,\, 0} & = & 0 \end{array}. .. _Snow Compaction: @@ -450,19 +301,14 @@ follows Snow Compaction ^^^^^^^^^^^^^^^^^^^^^ -Snow compaction is initiated after the soil hydrology calculations -[surface runoff (section :numref:`Surface Runoff`), infiltration (section -:numref:`Infiltration`), soil water (section :numref:`Soil Water`)] are -complete. Currently, there are four processes included that lead to snow -compaction: +Snow compaction is initiated after the soil hydrology calculations [surface runoff (section :numref:`Surface Runoff`), infiltration (section :numref:`Infiltration`), soil water (section :numref:`Soil Water`)] are complete. Currently, there are four processes included that lead to snow compaction: #. destructive metamorphism of new snow (crystal breakdown due to wind or thermodynamic stress) #. snow load or compaction by overburden pressure #. melting (changes in snow structure due to melt-freeze cycles plus changes in crystals due to liquid water) - #. drifting snow compaction. + #. drifting snow compaction. -The total fractional compaction rate for each snow layer :math:`C_{R,\, i}` -(s\ :sup:`-1`) is the sum of multiple compaction processes +The total fractional compaction rate for each snow layer :math:`C_{R,\, i}` (s\ :sup:`-1`) is the sum of multiple compaction processes .. math:: :label: 8.41 @@ -476,8 +322,7 @@ Compaction is not allowed if the layer is saturated 1-\left(\frac{w_{ice,\, i} }{f_{sno} \Delta z_{i} \rho _{ice} } +\frac{w_{liq,\, i} }{f_{sno} \Delta z_{i} \rho _{liq} } \right)\le 0.001 -or if the ice content is below a minimum value -(:math:`w_{ice,\, i} \le 0.1`). +or if the ice content is below a minimum value (:math:`w_{ice,\, i} \le 0.1`). The snow layer thickness after compaction is @@ -486,8 +331,6 @@ The snow layer thickness after compaction is \Delta z_{i}^{n+1} =\Delta z_{i}^{n} \left(1+C_{R,\, i} \Delta t\right). - - .. _Destructive metamorphism: Destructive metamorphism @@ -505,21 +348,17 @@ where :math:`c_{3} =2.777\times 10^{-6}` (s\ :sup:`-1`) is the fractional compa .. math:: :label: 8.44 - \begin{array}{lr} + \begin{array}{lr} c_{1} = 1 & \qquad \frac{w_{ice,\, i} }{f_{sno} \Delta z_{i} } \le 175{\rm \; kg\; m}^{{\rm -3}} \\ c_{1} = \exp \left[-0.046\left(\frac{w_{ice,\, i} }{f_{sno} \Delta z_{i} } -175\right)\right] & \qquad \frac{w_{ice,\, i} }{f_{sno} \Delta z_{i} } >175{\rm \; kg\; m}^{{\rm -3}} \\ - c_{2} = 2 & \qquad \frac{w_{liq,\, i} }{f_{sno} \Delta z_{i} } >0.01 \\ - c_{2} = 1 & \qquad \frac{w_{liq,\, i} }{f_{sno} \Delta z_{i} } \le 0.01 + c_{2} = 2 & \qquad \frac{w_{liq,\, i} }{f_{sno} \Delta z_{i} } >0.01 \\ + c_{2} = 1 & \qquad \frac{w_{liq,\, i} }{f_{sno} \Delta z_{i} } \le 0.01 \end{array} .. upper limit (upplim_destruct_metamorph) used to be 100 but was changed to 175 for CLM5 (Van Kampenhout et al., 2017) -where -:math:`{w_{ice,\, i} \mathord{\left/ {\vphantom {w_{ice,\, i} \left(f_{sno} \Delta z_{i} \right)}} \right. \kern-\nulldelimiterspace} \left(f_{sno} \Delta z_{i} \right)}` -and -:math:`{w_{liq,\, i} \mathord{\left/ {\vphantom {w_{liq,\, i} \left(f_{sno} \Delta z_{i} \right)}} \right. \kern-\nulldelimiterspace} \left(f_{sno} \Delta z_{i} \right)}` -are the bulk densities of liquid water and ice (kg m\ :sup:`-3`). - +where :math:`{w_{ice,\, i} \mathord{\left/ {\vphantom {w_{ice,\, i} \left(f_{sno} \Delta z_{i} \right)}} \right.} \left(f_{sno} \Delta z_{i} \right)}` and +:math:`{w_{liq,\, i} \mathord{\left/ {\vphantom {w_{liq,\, i} \left(f_{sno} \Delta z_{i} \right)}} \right.} \left(f_{sno} \Delta z_{i} \right)}` are the bulk densities of liquid water and ice (kg m\ :sup:`-3`). .. _Overburden pressure compaction: @@ -533,44 +372,34 @@ The compaction rate as a result of overburden :math:`C_{R2,\; i}` (s\ :sup:`-1`) C_{R2,\, i} =\left[\frac{1}{\Delta z_{i} } \frac{\partial \Delta z_{i} }{\partial t} \right]_{overburden} =-\frac{P_{s,\, i} }{\eta } -The snow load pressure :math:`P_{s,\, i}` is calculated for each layer as the sum of -the ice :math:`w_{ice,\, i}` and liquid water contents -:math:`w_{liq,\, i}` of the layers above plus half the ice and liquid -water contents of the layer being compacted +The snow load pressure :math:`P_{s,\, i}` is calculated for each layer as the sum of the ice :math:`w_{ice,\, i}` and liquid water contents :math:`w_{liq,\, i}` of the layers above plus half the ice and liquid water contents of the layer being compacted .. math:: :label: 8.47 P_{s,\, i} =\frac{w_{ice,\, i} +w_{liq,\, i} }{2} +\sum _{j=snl+1}^{j=i-1}\left(w_{ice,\, j} +w_{liq,\, j} \right) . - -Variable :math:`\eta` in :eq:`8.45` is a viscosity coefficient (kg s m\ :sup:`-2`) that varies with density and -temperature as +Variable :math:`\eta` in :eq:`8.45` is a viscosity coefficient (kg s m\ :sup:`-2`) that varies with density and temperature as .. math:: :label: 8.46 \eta = f_{1} f_{2} \eta_{0} \frac{\rho_{i}}{c_{\eta}} \exp \left[ a_{\eta} \left(T_{f} -T_{i} \right) + b_{\eta} \rho_{i} \right] -with constant factors :math:`\eta _{0} = 7.62237 \times 10^{6}` kg s\ :sup:`-1` m\ :sup:`-2`, -:math:`a_{\eta} = 0.1` K\ :sup:`-1`, :math:`b_{\eta} = 0.023` m\ :sup:`-3` kg\ :sup:`-1`, -and :math:`c_{\eta} = 450` kg m\ :sup:`-3` (:ref:`van Kampenhout et al. (2017) `). -Further, factor :math:`f_1` accounts for the presence of liquid water (:ref:`Vionnet et al. (2012) `): +with constant factors :math:`\eta _{0} = 7.62237 \times 10^{6}` kg s\ :sup:`-1` m\ :sup:`-2`, :math:`a_{\eta} = 0.1` K\ :sup:`-1`, :math:`b_{\eta} = 0.023` m\ :sup:`-3` kg\ :sup:`-1`, and :math:`c_{\eta} = 450` kg m\ :sup:`-3` (:ref:`van Kampenhout et al. (2017) `). Further, factor :math:`f_1` accounts for the presence of liquid water (:ref:`Vionnet et al. (2012) `): -.. math:: +.. math:: :label: 8.46b f_{1} = \frac{1}{1+ 60 \frac{w_{\mathrm{liq},\, i}}{\rho_{\mathrm{liq}} \Delta z_{i} }}. -Factor :math:`f_2` originally accounts for the presence of angular grains, but since grain shape is not modelled -:math:`f_2` is fixed to the value 4. +Factor :math:`f_2` originally accounts for the presence of angular grains, but since grain shape is not modelled :math:`f_2` is fixed to the value 4. .. _Compaction by melt: Compaction by melt '''''''''''''''''' -The compaction rate due to melting :math:`C_{R3,\; i}` (s\ :sup:`-1`) is taken to be the ratio of the change in snow ice -mass after the melting to the mass before melting +The compaction rate due to melting :math:`C_{R3,\; i}` (s\ :sup:`-1`) is taken to be the ratio of the change in snow ice mass after the melting to the mass before melting .. math:: :label: 8.48 @@ -578,39 +407,33 @@ mass after the melting to the mass before melting C_{R3,\, i} = \left[\frac{1}{\Delta z_{i} } \frac{\partial \Delta z_{i} }{\partial t} \right]_{melt} = -\frac{1}{\Delta t} \max \left(0,\frac{W_{sno,\, i}^{n} -W_{sno,\, i}^{n+1} }{W_{sno,\, i}^{n} } \right) -and melting is identified during the phase change calculations (section -:numref:`Phase Change`). Because snow depth is defined as the average -depth of the snow covered area, the snow depth must also be updated for -changes in :math:`f_{sno}` when :math:`W_{sno}` has changed. - +and melting is identified during the phase change calculations (section :numref:`Phase Change`). Because snow depth is defined as the average depth of the snow covered area, the snow depth must also be updated for changes in :math:`f_{sno}` when :math:`W_{sno}` has changed. + .. math:: :label: 8.49 - + C_{R4,\, i} =\left[\frac{1}{\Delta z_{i} } \frac{\partial \Delta z_{i} }{\partial t} \right]_{fsno} =-\frac{1}{\Delta t} \max \left(0,\frac{f_{sno,\, i}^{n} -f_{sno,\, i}^{n+1} }{f_{sno,\, i}^{n} } \right) .. _Compaction by drifting snow: Compaction by drifting snow ''''''''''''''''''''''''''' -Crystal breaking by drifting snow leads to higher snow densities at the surface. -This process is particularly important on ice sheets, where destructive metamorphism is slow due to low temperatures -but high wind speeds (katabatic winds) are prevailing. -Therefore a drifting snow compaction parametrization was introduced, based on (:ref:`Vionnet et al. (2012) `). +Crystal breaking by drifting snow leads to higher snow densities at the surface. This process is particularly important on ice sheets, where destructive metamorphism is slow due to low temperatures but high wind speeds (katabatic winds) are prevailing. Therefore a drifting snow compaction parametrization was introduced, based on (:ref:`Vionnet et al. (2012) `). .. math:: :label: 8.50 C_{R5,\, i} = \left[\frac{1}{\Delta z_{i} } \frac{\partial \Delta z_{i} }{\partial t} \right]_{drift} = - \frac{\rho_{\max} - \rho_i}{\tau_{i}}. -Here, :math:`\rho_{\max} = 350` kg m\ :sup:`-3` is the upper limit to which this process is active, and -:math:`\tau_{i}` is a timescale which is depth dependent: +Here, :math:`\rho_{\max} = 350` kg m\ :sup:`-3` is the upper limit to which this process is active, and +:math:`\tau_{i}` is a timescale which is depth dependent: .. math:: :label: 8.50b \tau_i = \frac{\tau}{\Gamma_{\mathrm{drift}}^i} \quad \mathrm{,} \:\; \Gamma^i_\mathrm{drift} = \max\left[ 0, S_\mathrm{I}^i \exp(-z_i / 0.1) \right]. -Here, :math:`\tau` is a characteristic time scale for drifting snow compaction and is empirically set to 48 h, and +Here, :math:`\tau` is a characteristic time scale for drifting snow compaction and is empirically set to 48 h, and :math:`z_i` is a pseudo-depth which takes into account previous hardening of snow layers above the current layer: :math:`z_i = \sum_j \Delta z_j \cdot (3.25 - S_\mathrm{I}^j)`. The driftability index :math:`S_\mathrm{I}` reflects how well snow can be drifted and depends on the mobility of the snow @@ -624,49 +447,31 @@ as well as the 10 m wind speed: M_\mathrm{O} & = & -0.069 + 0.66 F(\rho) \end{array} -The latter equation (for the mobility index :math:`M_\mathrm{O}`) is a simplification from the original paper -by removing the dependency on grain size and assuming spherical grains -(see :ref:`van Kampenhout et al. (2017) `). +The latter equation (for the mobility index :math:`M_\mathrm{O}`) is a simplification from the original paper by removing the dependency on grain size and assuming spherical grains (see :ref:`van Kampenhout et al. (2017) `). .. _Snow Layer Combination and Subdivision: Snow Layer Combination and Subdivision ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -After the determination of snow temperature including phase change(Chapter -:numref:`rst_Soil and Snow Temperatures`), snow hydrology (Chapter -:numref:`rst_Snow Hydrology`), and the compaction calculations (section -:numref:`Snow Compaction`) , the number of snow layers is adjusted by -either combining or subdividing layers. The combination and subdivision -of snow layers is based on :ref:`Jordan (1991) `. +After the determination of snow temperature including phase change(Chapter :numref:`rst_Soil and Snow Temperatures`), snow hydrology (Chapter :numref:`rst_Snow Hydrology`), and the compaction calculations (section :numref:`Snow Compaction`), the number of snow layers is adjusted by either combining or subdividing layers. The combination and subdivision of snow layers is based on :ref:`Jordan (1991) `. .. _Combination: Combination ''''''''''''''''''' -If a snow layer has nearly melted or if its thickness -:math:`\Delta z_{i}` is less than the prescribed minimum thickness -:math:`\Delta z_{\min }` (:numref:`Table snow layer thickness`), the layer is combined with a -neighboring layer. The overlying or underlying layer is selected as the -neighboring layer according to the following rules +If a snow layer has nearly melted or if its thickness :math:`\Delta z_{i}` is less than the prescribed minimum thickness :math:`\Delta z_{\min }` (:numref:`Table snow layer thickness`), the layer is combined with a neighboring layer. The overlying or underlying layer is selected as the neighboring layer according to the following rules -#. If the top layer is being removed, it is combined with the underlying - layer +#. If the top layer is being removed, it is combined with the underlying layer -#. If the underlying layer is not snow (i.e., it is the top soil layer), - the layer is combined with the overlying layer +#. If the underlying layer is not snow (i.e., it is the top soil layer), the layer is combined with the overlying layer -#. If the layer is nearly completely melted, the layer is combined with - the underlying layer +#. If the layer is nearly completely melted, the layer is combined with the underlying layer -#. If none of the above rules apply, the layer is combined with the - thinnest neighboring layer. +#. If none of the above rules apply, the layer is combined with the thinnest neighboring layer. -A first pass is made through all snow layers to determine if any layer -is nearly melted (:math:`w_{ice,\, i} \le 0.1`). If so, the remaining -liquid water and ice content of layer :math:`i` is combined with the -underlying neighbor :math:`i+1` as +A first pass is made through all snow layers to determine if any layer is nearly melted (:math:`w_{ice,\, i} \le 0.1`). If so, the remaining liquid water and ice content of layer :math:`i` is combined with the underlying neighbor :math:`i+1` as .. math:: :label: 8.51 @@ -678,18 +483,9 @@ underlying neighbor :math:`i+1` as w_{ice,\, i+1} =w_{ice,\, i+1} +w_{ice,\, i} . -This includes the snow layer directly above the top soil layer. In this -case, the liquid water and ice content of the melted snow layer is added -to the contents of the top soil layer. The layer properties, -:math:`T_{i}` , :math:`w_{ice,\, i}` , :math:`w_{liq,\, i}` , -:math:`\Delta z_{i}` , are then re-indexed so that the layers above the -eliminated layer are shifted down by one and the number of snow layers -is decremented accordingly. +This includes the snow layer directly above the top soil layer. In this case, the liquid water and ice content of the melted snow layer is added to the contents of the top soil layer. The layer properties, :math:`T_{i}`, :math:`w_{ice,\, i}`, :math:`w_{liq,\, i}`, :math:`\Delta z_{i}`, are then re-indexed so that the layers above the eliminated layer are shifted down by one and the number of snow layers is decremented accordingly. -At this point, if there are no explicit snow layers remaining -(:math:`snl=0`), the snow water equivalent :math:`W_{sno}` and snow -depth :math:`z_{sno}` are set to zero, otherwise, :math:`W_{sno}` and -:math:`z_{sno}` are re-calculated as +At this point, if there are no explicit snow layers remaining (:math:`snl=0`), the snow water equivalent :math:`W_{sno}` and snow depth :math:`z_{sno}` are set to zero, otherwise, :math:`W_{sno}` and :math:`z_{sno}` are re-calculated as .. math:: :label: 8.53 @@ -701,16 +497,9 @@ depth :math:`z_{sno}` are set to zero, otherwise, :math:`W_{sno}` and z_{sno} =\sum _{i=snl+1}^{i=0}\Delta z_{i} . -If the snow depth :math:`00} \\ {\lambda _{vap} \qquad {\rm otherwise}} \end{array}\right\} -where :math:`\lambda _{sub}` and :math:`\lambda _{vap}` are the -latent heat of sublimation and vaporization, respectively (J -kg\ :sup:`-1`) (:numref:`Table Physical Constants`), and :math:`w_{liq,\, snl+1}` -and :math:`w_{ice,\, snl+1}` are the liquid water and ice contents of the -top snow/soil layer, respectively (kg m\ :sup:`-2`) -(Chapter :numref:`rst_Hydrology`). +where :math:`\lambda _{sub}` and :math:`\lambda _{vap}` are the latent heat of sublimation and vaporization, respectively (J kg\ :sup:`-1`) (:numref:`Table Physical Constants`), and :math:`w_{liq,\, snl+1}` and :math:`w_{ice,\, snl+1}` are the liquid water and ice contents of the top snow/soil layer, respectively (kg m\ :sup:`-2`) (Chapter :numref:`rst_Hydrology`). For the top soil layer, :math:`i=1`, the coefficients are @@ -402,34 +311,18 @@ The heat flux into the soil surface from the overlying atmosphere h=\overrightarrow{S}_{soil} -\overrightarrow{L}_{soil} -H_{soil} -\lambda E_{soil} -It can be seen that when no snow is present (:math:`f_{sno} =0`), the -expressions for the coefficients of the top soil layer have the same -form as those for the top snow layer. +It can be seen that when no snow is present (:math:`f_{sno} =0`), the expressions for the coefficients of the top soil layer have the same form as those for the top snow layer. -The surface snow/soil layer temperature computed in this way is the -layer-averaged temperature and hence has somewhat reduced diurnal -amplitude compared with surface temperature. An accurate surface -temperature is provided that compensates for this effect and numerical -error by tuning the heat capacity of the top layer (through adjustment -of the layer thickness) to give an exact match to the analytic solution -for diurnal heating. The top layer thickness for :math:`i=snl+1` is -given by +The surface snow/soil layer temperature computed in this way is the layer-averaged temperature and hence has somewhat reduced diurnal amplitude compared with surface temperature. An accurate surface temperature is provided that compensates for this effect and numerical error by tuning the heat capacity of the top layer (through adjustment of the layer thickness) to give an exact match to the analytic solution for diurnal heating. The top layer thickness for :math:`i=snl+1` is given by .. math:: :label: 6.34 \Delta z_{i*} =0.5\left[z_{i} -z_{h,\, i-1} +c_{a} \left(z_{i+1} -z_{h,\, i-1} \right)\right] -where :math:`c_{a}` is a tunable parameter, varying from 0 to 1, and is -taken as 0.34 by comparing the numerical solution with the analytic -solution (:ref:`Z.-L. Yang 1998, unpublished manuscript`). -:math:`\Delta z_{i*}` is used in place of :math:`\Delta z_{i}` for -:math:`i=snl+1` in equations -. The top snow/soil layer temperature -computed in this way is the ground surface temperature -:math:`T_{g}^{n+1}` . +where :math:`c_{a}` is a tunable parameter, varying from 0 to 1, and is taken as 0.34 by comparing the numerical solution with the analytic solution (:ref:`Z.-L. Yang 1998, unpublished manuscript`). :math:`\Delta z_{i*}` is used in place of :math:`\Delta z_{i}` for :math:`i=snl+1` in equations -. The top snow/soil layer temperature computed in this way is the ground surface temperature :math:`T_{g}^{n+1}`. -The boundary condition at the bottom of the snow/soil column is zero -heat flux, :math:`F_{i} =0`, resulting in, for :math:`i=N_{levgrnd}` , +The boundary condition at the bottom of the snow/soil column is zero heat flux, :math:`F_{i} =0`, resulting in, for :math:`i=N_{levgrnd}`, .. math:: :label: 6.35 @@ -463,8 +356,7 @@ where F_{i-1} =-\frac{\lambda \left[z_{h,\, i-1} \right]}{z_{i} -z_{i-1} } \left(T_{i-1}^{n} -T_{i}^{n} \right). -For the interior snow/soil layers, :math:`snl+1T_{f} {\rm \; and\; }w_{ice,\, i} >0 & \qquad i=snl+1,\ldots ,N_{levgrnd} \qquad {\rm melting} \end{array} .. math:: :label: 6.53b - \begin{array}{lr} - \begin{array}{lr} - T_{i}^{n+1} 0 & \qquad i=snl+1,\ldots ,0 \\ + \begin{array}{lr} + \begin{array}{lr} + T_{i}^{n+1} 0 & \qquad i=snl+1,\ldots ,0 \\ T_{i}^{n+1} w_{liq,\, \max ,\, i} & \quad i=1,\ldots ,N_{levgrnd} - \end{array} & \quad {\rm freezing} + \end{array} & \quad {\rm freezing} \end{array} -where :math:`T_{i}^{n+1}` is the soil layer temperature after solution -of the tridiagonal equation set, :math:`w_{ice,\, i}` and -:math:`w_{liq,\, i}` are the mass of ice and liquid water (kg -m\ :sup:`-2`) in each snow/soil layer, respectively, and :math:`T_{f}` -is the freezing temperature of water (K) (:numref:`Table Physical Constants`). -For the freezing process in soil layers, the concept of supercooled soil -water from :ref:`Niu and Yang (2006)` is adopted. The supercooled -soil water is the liquid water that coexists with ice over a wide range of -temperatures below freezing and is implemented through a freezing point -depression equation +where :math:`T_{i}^{n+1}` is the soil layer temperature after solution of the tridiagonal equation set, :math:`w_{ice,\, i}` and :math:`w_{liq,\, i}` are the mass of ice and liquid water (kg m\ :sup:`-2`) in each snow/soil layer, respectively, and :math:`T_{f}` is the freezing temperature of water (K) (:numref:`Table Physical Constants`). For the freezing process in soil layers, the concept of supercooled soil water from :ref:`Niu and Yang (2006)` is adopted. The supercooled soil water is the liquid water that coexists with ice over a wide range of temperatures below freezing and is implemented through a freezing point depression equation .. math:: :label: 6.54 - w_{liq,\, \max ,\, i} =\Delta z_{i} \theta _{sat,\, i} \left[\frac{10^{3} L_{f} \left(T_{f} -T_{i} \right)}{gT_{i} \psi _{sat,\, i} } \right]^{{-1\mathord{\left/ {\vphantom {-1 B_{i} }} \right. \kern-\nulldelimiterspace} B_{i} } } \qquad T_{i} ` exponent -(section :numref:`Soil Water`). +where :math:`w_{liq,\, \max,\, i}` is the maximum liquid water in layer :math:`i` (kg m\ :sup:`-2`) when the soil temperature :math:`T_{i}` is below the freezing temperature :math:`T_{f}`, :math:`L_{f}` is the latent heat of fusion (J kg\ :sup:`-1`) (:numref:`Table Physical Constants`), :math:`g` is the gravitational acceleration (m s\ :sup:`-2`) (:numref:`Table Physical Constants`), and :math:`\psi _{sat,\, i}` and :math:`B_{i}` are the soil texture-dependent saturated matric potential (mm) and :ref:`Clapp and Hornberger (1978)` exponent (section :numref:`Soil Water`). -For the special case when snow is present (snow mass :math:`W_{sno} >0`) -but there are no explicit snow layers (:math:`snl=0`) (i.e., there is -not enough snow present to meet the minimum snow depth requirement of -0.01 m), snow melt will take place for soil layer :math:`i=1` if the -soil layer temperature is greater than the freezing temperature -(:math:`T_{1}^{n+1} >T_{f}` ). +For the special case when snow is present (snow mass :math:`W_{sno} >0`) but there are no explicit snow layers (:math:`snl=0`) (i.e., there is not enough snow present to meet the minimum snow depth requirement of 0.01 m), snow melt will take place for soil layer :math:`i=1` if the soil layer temperature is greater than the freezing temperature (:math:`T_{1}^{n+1} >T_{f}` ). -The rate of phase change is assessed from the energy excess (or deficit) -needed to change :math:`T_{i}` to freezing temperature, :math:`T_{f}` . -The excess or deficit of energy :math:`H_{i}` (W m\ :sup:`-2`) is -determined as follows +The rate of phase change is assessed from the energy excess (or deficit) needed to change :math:`T_{i}` to freezing temperature, :math:`T_{f}`. The excess or deficit of energy :math:`H_{i}` (W m\ :sup:`-2`) is determined as follows .. math:: :label: 6.55 - H_{i} =\left\{\begin{array}{lr} - \frac{\partial h}{\partial T} \left(T_{f} -T_{i}^{n} \right)-\frac{c_{i} \Delta z_{i} }{\Delta t} \left(T_{f} -T_{i}^{n} \right) & \quad \quad i=snl+1 \\ - \left(1-f_{sno} -f_{h2osfc} \right)\frac{\partial h}{\partial T} \left(T_{f} -T_{i}^{n} \right)-\frac{c_{i} \Delta z_{i} }{\Delta t} \left(T_{f} -T_{i}^{n} \right)\quad {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} & i=1 \\ - -\frac{c_{i} \Delta z_{i} }{\Delta t} \left(T_{f} -T_{i}^{n} \right) & \quad \quad i\ne \left\{1,snl+1\right\} + H_{i} =\left\{\begin{array}{lr} + \frac{\partial h}{\partial T} \left(T_{f} -T_{i}^{n} \right)-\frac{c_{i} \Delta z_{i} }{\Delta t} \left(T_{f} -T_{i}^{n} \right) & \quad \quad i=snl+1 \\ + \left(1-f_{sno} -f_{h2osfc} \right)\frac{\partial h}{\partial T} \left(T_{f} -T_{i}^{n} \right)-\frac{c_{i} \Delta z_{i} }{\Delta t} \left(T_{f} -T_{i}^{n} \right)\quad {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} & i=1 \\ + -\frac{c_{i} \Delta z_{i} }{\Delta t} \left(T_{f} -T_{i}^{n} \right) & \quad \quad i\ne \left\{1,snl+1\right\} \end{array}\right\}. -If the melting criteria is met :eq:`6.53a` and -:math:`H_{m} =\frac{H_{i} \Delta t}{L_{f} } >0`, then the ice mass is -readjusted as +If the melting criteria is met :eq:`6.53a` and :math:`H_{m} =\frac{H_{i} \Delta t}{L_{f} } >0`, then the ice mass is readjusted as .. math:: :label: 6.56 w_{ice,\, i}^{n+1} =w_{ice,\, i}^{n} -H_{m} \ge 0\qquad i=snl+1,\ldots ,N_{levgrnd} . -If the freezing criteria is met :eq:`6.53b` and :math:`H_{m} <0`, then -the ice mass is readjusted for :math:`i=snl+1,\ldots ,0` as +If the freezing criteria is met :eq:`6.53b` and :math:`H_{m} <0`, then the ice mass is readjusted for :math:`i=snl+1,\ldots,0` as .. math:: :label: 6.57 w_{ice,\, i}^{n+1} =\min \left(w_{liq,\, i}^{n} +w_{ice,\, i}^{n} ,w_{ice,\, i}^{n} -H_{m} \right) -and for :math:`i=1,\ldots ,N_{levgrnd}` as +and for :math:`i=1,\ldots,N_{levgrnd}` as .. math:: :label: 6.58 - w_{ice,\, i}^{n+1} = - \left\{\begin{array}{lr} - \min \left(w_{liq,\, i}^{n} +w_{ice,\, i}^{n} -w_{liq,\, \max ,\, i}^{n} ,\, w_{ice,\, i}^{n} -H_{m} \right) & \qquad w_{liq,\, i}^{n} +w_{ice,\, i}^{n} \ge w_{liq,\, \max ,\, i}^{n} {\rm \; } \\ - {\rm 0} & \qquad w_{liq,\, i}^{n} +w_{ice,\, i}^{n} 0`) as +and this energy is used to cool or warm the snow/soil layer (if :math:`\left|H_{i*} \right|>0`) as .. math:: :label: 6.61 - T_{i}^{n+1} = - \left\{\begin{array}{lr} - T_{f} +{\frac{\Delta t}{c_{i} \Delta z_{i} } H_{i*} \mathord{\left/ {\vphantom {\frac{\Delta t}{c_{i} \Delta z_{i} } H_{i*} \left(1-\frac{\Delta t}{c_{i} \Delta z_{i} } \frac{\partial h}{\partial T} \right)}} \right. \kern-\nulldelimiterspace} \left(1-\frac{\Delta t}{c_{i} \Delta z_{i} } \frac{\partial h}{\partial T} \right)} & \quad \quad \quad \quad \, i=snl+1 \\ - T_{f} +{\frac{\Delta t}{c_{i} \Delta z_{i} } H_{i*} \mathord{\left/ {\vphantom {\frac{\Delta t}{c_{i} \Delta z_{i} } H_{i*} \left(1-\left(1-f_{sno} -f_{h2osfc} \right)\frac{\Delta t}{c_{i} \Delta z_{i} } \frac{\partial h}{\partial T} \right)}} \right. \kern-\nulldelimiterspace} \left(1-\left(1-f_{sno} -f_{h2osfc} \right)\frac{\Delta t}{c_{i} \Delta z_{i} } \frac{\partial h}{\partial T} \right)} & \qquad i=1 \\ + T_{i}^{n+1} = + \left\{\begin{array}{lr} + T_{f} +{\frac{\Delta t}{c_{i} \Delta z_{i} } H_{i*} \mathord{\left/ {\vphantom {\frac{\Delta t}{c_{i} \Delta z_{i} } H_{i*} \left(1-\frac{\Delta t}{c_{i} \Delta z_{i} } \frac{\partial h}{\partial T} \right)}} \right.} \left(1-\frac{\Delta t}{c_{i} \Delta z_{i} } \frac{\partial h}{\partial T} \right)} & \quad \quad \quad \quad \, i=snl+1 \\ + T_{f} +{\frac{\Delta t}{c_{i} \Delta z_{i} } H_{i*} \mathord{\left/ {\vphantom {\frac{\Delta t}{c_{i} \Delta z_{i} } H_{i*} \left(1-\left(1-f_{sno} -f_{h2osfc} \right)\frac{\Delta t}{c_{i} \Delta z_{i} } \frac{\partial h}{\partial T} \right)}} \right.} \left(1-\left(1-f_{sno} -f_{h2osfc} \right)\frac{\Delta t}{c_{i} \Delta z_{i} } \frac{\partial h}{\partial T} \right)} & \qquad i=1 \\ T_{f} +\frac{\Delta t}{c_{i} \Delta z_{i} } H_{i*} & \quad \quad \quad \quad \, i\ne \left\{1,snl+1\right\} \end{array}\right\}. -For the special case when snow is present (:math:`W_{sno} >0`), there -are no explicit snow layers (:math:`snl=0`), and -:math:`\frac{H_{1} \Delta t}{L_{f} } >0` (melting), the snow mass -:math:`W_{sno}` (kg m\ :sup:`-2`) is reduced according to +For the special case when snow is present (:math:`W_{sno} >0`), there are no explicit snow layers (:math:`snl=0`), and :math:`\frac{H_{1} \Delta t}{L_{f} } >0` (melting), the snow mass :math:`W_{sno}` (kg m\ :sup:`-2`) is reduced according to .. math:: :label: 6.62 @@ -685,27 +539,21 @@ The snow depth is reduced proportionally z_{sno}^{n+1} =\frac{W_{sno}^{n+1} }{W_{sno}^{n} } z_{sno}^{n} . -Again, because part of the energy may not be consumed in melting, the -energy for the surface soil layer :math:`i=1` is recalculated as +Again, because part of the energy may not be consumed in melting, the energy for the surface soil layer :math:`i=1` is recalculated as .. math:: :label: 6.64 H_{1*} =H_{1} -\frac{L_{f} \left(W_{sno}^{n} -W_{sno}^{n+1} \right)}{\Delta t} . -If there is excess energy (:math:`H_{1*} >0`), this energy becomes -available to the top soil layer as +If there is excess energy (:math:`H_{1*} >0`), this energy becomes available to the top soil layer as .. math:: :label: 6.65 H_{1} =H_{1*} . -The ice mass, liquid water content, and temperature of the top soil -layer are then determined from :eq:`6.56`, :eq:`6.59`, and :eq:`6.61` -using the recalculated energy from :eq:`6.65`. Snow melt :math:`M_{1S}` -(kg m\ :sup:`-2` s\ :sup:`-1`) and phase change energy :math:`E_{p,\, 1S}` -(W m\ :sup:`-2`) for this special case are +The ice mass, liquid water content, and temperature of the top soil layer are then determined from :eq:`6.56`, :eq:`6.59`, and :eq:`6.61` using the recalculated energy from :eq:`6.65`. Snow melt :math:`M_{1S}` (kg m\ :sup:`-2` s\ :sup:`-1`) and phase change energy :math:`E_{p,\, 1S}` (W m\ :sup:`-2`) for this special case are .. math:: :label: 6.66 @@ -717,8 +565,7 @@ using the recalculated energy from :eq:`6.65`. Snow melt :math:`M_{1S}` E_{p,\, 1S} =L_{f} M_{1S} . -The total energy of phase change :math:`E_{p}` (W m\ :sup:`-2`) -for the snow/soil column is +The total energy of phase change :math:`E_{p}` (W m\ :sup:`-2`) for the snow/soil column is .. math:: :label: 6.68 @@ -732,8 +579,7 @@ where E_{p,\, i} =L_{f} \frac{\left(w_{ice,\, i}^{n} -w_{ice,\, i}^{n+1} \right)}{\Delta t} . -The total snow melt :math:`M` (kg m\ :sup:`-2` -s\ :sup:`-1`) is +The total snow melt :math:`M` (kg m\ :sup:`-2` s\ :sup:`-1`) is .. math:: :label: 6.70 @@ -754,27 +600,21 @@ The solution for snow/soil temperatures conserves energy as G-E_{p} -\sum _{i=snl+1}^{i=N_{levgrnd} }\frac{c_{i} \Delta z_{i} }{\Delta t} \left(T_{i}^{n+1} -T_{i}^{n} \right)=0 -where :math:`G` is the ground heat flux (section -:numref:`Update of Ground Sensible and Latent Heat Fluxes`). +where :math:`G` is the ground heat flux (section :numref:`Update of Ground Sensible and Latent Heat Fluxes`). .. _Surface Water: Surface Water ^^^^^^^^^^^^^^^^^^^ -Phase change of surface water takes place when the surface water -temperature, :math:`T_{h2osfc}` , becomes less than :math:`T_{f}` . The -energy available for freezing is +Phase change of surface water takes place when the surface water temperature, :math:`T_{h2osfc}`, becomes less than :math:`T_{f}`. The energy available for freezing is .. math:: :label: 6.73 H_{h2osfc} =\frac{\partial h}{\partial T} \left(T_{f} -T_{h2osfc}^{n} \right)-\frac{c_{h2osfc} \Delta z_{h2osfc} }{\Delta t} \left(T_{f} -T_{h2osfc}^{n} \right) -where :math:`c_{h2osfc}` is the volumetric heat capacity of water, and -:math:`\Delta z_{h2osfc}` is the depth of the surface water layer. If -:math:`H_{m} =\frac{H_{h2osfc} \Delta t}{L_{f} } >0` then :math:`H_{m}` -is removed from surface water and added to the snow column as ice +where :math:`c_{h2osfc}` is the volumetric heat capacity of water, and :math:`\Delta z_{h2osfc}` is the depth of the surface water layer. If :math:`H_{m} =\frac{H_{h2osfc} \Delta t}{L_{f} } >0` then :math:`H_{m}` is removed from surface water and added to the snow column as ice .. math:: :label: 6.74 @@ -793,85 +633,63 @@ The snow depth is adjusted to account for the additional ice mass \Delta z_{sno} =\frac{H_{m} }{\rho _{ice} } -If :math:`H_{m}` \ is greater than :math:`W_{sfc}` , the excess heat -:math:`\frac{L_{f} \left(H_{m} -W_{sfc} \right)}{\Delta t}` is used to -cool the snow layer. +If :math:`H_{m}` \ is greater than :math:`W_{sfc}`, the excess heat :math:`\frac{L_{f} \left(H_{m} -W_{sfc} \right)}{\Delta t}` is used to cool the snow layer. .. _Soil and Snow Thermal Properties: Soil and Snow Thermal Properties ------------------------------------ -The thermal properties of the soil are assumed to be a weighted combination of -the mineral and organic properties of the soil -(:ref:`Lawrence and Slater 2008 `). -The soil layer organic matter fraction :math:`f_{om,i}` is +The thermal properties of the soil are assumed to be a weighted combination of the mineral and organic properties of the soil (:ref:`Lawrence and Slater 2008 `). The soil layer organic matter fraction :math:`f_{om,i}` is .. math:: :label: 6.77 f_{om,i} =\rho _{om,i} /\rho _{om,\max } . -Soil thermal conductivity :math:`\lambda _{i}` (W m\ :sup:`-1` K\ :sup:`-1`) -is from :ref:`Farouki (1981) ` +Soil thermal conductivity :math:`\lambda _{i}` (W m\ :sup:`-1` K\ :sup:`-1`) is from :ref:`Farouki (1981) ` .. math:: :label: 6.78 - \begin{array}{lr} + \begin{array}{lr} \lambda _{i} = \left\{ - \begin{array}{lr} - K_{e,\, i} \lambda _{sat,\, i} +\left(1-K_{e,\, i} \right)\lambda _{dry,\, i} &\qquad S_{r,\, i} > 1\times 10^{-7} \\ - \lambda _{dry,\, i} &\qquad S_{r,\, i} \le 1\times 10^{-7} - \end{array}\right\} &\qquad i=1,\ldots ,N_{levsoi} \\ - - \lambda _{i} =\lambda _{bedrock} &\qquad i=N_{levsoi} +1,\ldots N_{levgrnd} + \begin{array}{lr} + K_{e,\, i} \lambda _{sat,\, i} +\left(1-K_{e,\, i} \right)\lambda _{dry,\, i} &\qquad S_{r,\, i} > 1\times 10^{-7} \\ + \lambda _{dry,\, i} &\qquad S_{r,\, i} \le 1\times 10^{-7} + \end{array}\right\} &\qquad i=1,\ldots ,N_{levsoi} \\ + \lambda _{i} =\lambda _{bedrock} &\qquad i=N_{levsoi} +1,\ldots N_{levgrnd} \end{array} -where :math:`\lambda _{sat,\, i}` is the saturated thermal -conductivity, :math:`\lambda _{dry,\, i}` is the dry thermal -conductivity, :math:`K_{e,\, i}` is the Kersten number, -:math:`S_{r,\, i}` is the wetness of the soil with respect to -saturation, and :math:`\lambda _{bedrock} =3` W m\ :sup:`-1` -K\ :sup:`-1` is the thermal conductivity assumed for the deep -ground layers (typical of saturated granitic rock; -:ref:`Clauser and Huenges 1995 `). For glaciers, +where :math:`\lambda _{sat,\, i}` is the saturated thermal conductivity, :math:`\lambda _{dry,\, i}` is the dry thermal conductivity, :math:`K_{e,\, i}` is the Kersten number, :math:`S_{r,\, i}` is the wetness of the soil with respect to saturation, and :math:`\lambda _{bedrock} =3` W m\ :sup:`-1` K\ :sup:`-1` is the thermal conductivity assumed for the deep ground layers (typical of saturated granitic rock; :ref:`Clauser and Huenges 1995 `). For glaciers, .. math:: :label: 6.79 \lambda _{i} =\left\{\begin{array}{l} {\lambda _{liq,\, i} \qquad T_{i} \ge T_{f} } \\ {\lambda _{ice,\, i} \qquad T_{i} `). :math:`\theta _{sat,\, i}` is the -volumetric water content at saturation (porosity) (section :numref:`Hydraulic Properties`). +and :math:`\lambda _{s,om} =0.25`\ W m\ :sup:`-1` K\ :sup:`-1` (:ref:`Farouki 1981 `). :math:`\theta _{sat,\, i}` is the volumetric water content at saturation (porosity) (section :numref:`Hydraulic Properties`). The thermal conductivity of dry soil is @@ -880,29 +698,22 @@ The thermal conductivity of dry soil is \lambda _{dry,i} =(1-f_{om,i} )\lambda _{dry,\min ,i} +f_{om,i} \lambda _{dry,om} -where the thermal conductivity of dry mineral soil -:math:`\lambda _{dry,\min ,i}` \ (W m\ :sup:`-1` -K\ :sup:`-1`) depends on the bulk density -:math:`\rho _{d,\, i} =2700\left(1-\theta _{sat,\, i} \right)` (kg -m\ :sup:`-3`) as +where the thermal conductivity of dry mineral soil :math:`\lambda _{dry,\min,i}` \ (W m\ :sup:`-1` K\ :sup:`-1`) depends on the bulk density :math:`\rho _{d,\, i} =2700\left(1-\theta _{sat,\, i} \right)` (kg m\ :sup:`-3`) as .. math:: :label: 6.84 \lambda _{dry,\, \min ,i} =\frac{0.135\rho _{d,\, i} +64.7}{2700-0.947\rho _{d,\, i} } -and :math:`\lambda _{dry,om} =0.05` W m\ :sup:`-1` -K\ :sup:`-1` (:ref:`Farouki 1981 `) is the dry thermal conductivity of -organic matter. The Kersten number :math:`K_{e,\, i}` is a function of -the degree of saturation :math:`S_{r}` and phase of water +and :math:`\lambda _{dry,om} =0.05` W m\ :sup:`-1` K\ :sup:`-1` (:ref:`Farouki 1981 `) is the dry thermal conductivity of organic matter. The Kersten number :math:`K_{e,\, i}` is a function of the degree of saturation :math:`S_{r}` and phase of water .. math:: :label: 6.85 K_{e,\, i} = \left\{ - \begin{array}{lr} - \log \left(S_{r,\, i} \right)+1\ge 0 &\qquad T_{i} \ge T_{f} \\ - S_{r,\, i} &\qquad T_{i} ` +Thermal conductivity :math:`\lambda _{i}` (W m\ :sup:`-1` K\ :sup:`-1`) for snow is from :ref:`Jordan (1991) ` .. math:: :label: 6.87 \lambda _{i} =\lambda _{air} +\left(7.75\times 10^{-5} \rho _{sno,\, i} +1.105\times 10^{-6} \rho _{sno,\, i}^{2} \right)\left(\lambda _{ice} -\lambda _{air} \right) -where :math:`\lambda _{air}` is the thermal conductivity of air (:numref:`Table Physical Constants`) -and :math:`\rho _{sno,\, i}` is the bulk density of snow (kg m\ :sup:`-3`) +where :math:`\lambda _{air}` is the thermal conductivity of air (:numref:`Table Physical Constants`) and :math:`\rho _{sno,\, i}` is the bulk density of snow (kg m\ :sup:`-3`) .. math:: :label: 6.88 \rho _{sno,\, i} =\frac{w_{ice,\, i} +w_{liq,\, i} }{\Delta z_{i} } . -The volumetric heat capacity :math:`c_{i}` (J m\ :sup:`-3` K\ :sup:`-1`) for -soil is from :ref:`de Vries (1963) ` and depends on the -heat capacities of the soil solid, liquid water, and ice constituents +The volumetric heat capacity :math:`c_{i}` (J m\ :sup:`-3` K\ :sup:`-1`) for soil is from :ref:`de Vries (1963) ` and depends on the heat capacities of the soil solid, liquid water, and ice constituents .. math:: :label: 6.89 c_{i} =c_{s,\, i} \left(1-\theta _{sat,\, i} \right)+\frac{w_{ice,\, i} }{\Delta z_{i} } C_{ice} +\frac{w_{liq,\, i} }{\Delta z_{i} } C_{liq} -where :math:`C_{liq}` and :math:`C_{ice}` are the specific heat -capacities (J kg\ :sup:`-1` K\ :sup:`-1`) of liquid water -and ice, respectively (:numref:`Table Physical Constants`). The heat capacity of soil solids -:math:`c_{s,i}` \ (J m\ :sup:`-3` K\ :sup:`-1`) is +where :math:`C_{liq}` and :math:`C_{ice}` are the specific heat capacities (J kg\ :sup:`-1` K\ :sup:`-1`) of liquid water and ice, respectively (:numref:`Table Physical Constants`). The heat capacity of soil solids :math:`c_{s,i}` \ (J m\ :sup:`-3` K\ :sup:`-1`) is .. math:: :label: 6.90 c_{s,i} =(1-f_{om,i} )c_{s,\min ,i} +f_{om,i} c_{s,om} -where the heat capacity of mineral soil solids -:math:`c_{s,\min ,\, i}` (J m\ :sup:`-3` K\ :sup:`-1`) is +where the heat capacity of mineral soil solids :math:`c_{s,\min,\, i}` (J m\ :sup:`-3` K\ :sup:`-1`) is .. math:: :label: 6.91 - \begin{array}{lr} - c_{s,\min ,\, i} =\left(\frac{2.128{\rm \; }\left(\% sand\right)_{i} +{\rm 2.385\; }\left(\% clay\right)_{i} }{\left(\% sand\right)_{i} +\left(\% clay\right)_{i} } \right)\times 10^{6} &\qquad i=1,\ldots ,N_{levsoi} \\ - c_{s,\, \min ,i} =c_{s,\, bedrock} &\qquad i=N_{levsoi} +1,\ldots ,N_{levgrnd} + \begin{array}{lr} + c_{s,\min ,\, i} =\left(\frac{2.128{\rm \; }\left(\% sand\right)_{i} +{\rm 2.385\; }\left(\% clay\right)_{i} }{\left(\% sand\right)_{i} +\left(\% clay\right)_{i} } \right)\times 10^{6} &\qquad i=1,\ldots ,N_{levsoi} \\ + c_{s,\, \min ,i} =c_{s,\, bedrock} &\qquad i=N_{levsoi} +1,\ldots ,N_{levgrnd} \end{array} -where :math:`c_{s,bedrock} =2\times 10^{6}` J m\ :sup:`-3` -K\ :sup:`-1` is the heat capacity of bedrock and -:math:`c_{s,om} =2.5\times 10^{6}` \ J m\ :sup:`-3` -K\ :sup:`-1` (:ref:`Farouki 1981 `) is the heat capacity of organic -matter. For glaciers and snow +where :math:`c_{s,bedrock} =2\times 10^{6}` J m\ :sup:`-3` K\ :sup:`-1` is the heat capacity of bedrock and :math:`c_{s,om} =2.5\times 10^{6}` \ J m\ :sup:`-3` K\ :sup:`-1` (:ref:`Farouki 1981 `) is the heat capacity of organic matter. For glaciers and snow .. math:: :label: 6.92 c_{i} =\frac{w_{ice,\, i} }{\Delta z_{i} } C_{ice} +\frac{w_{liq,\, i} }{\Delta z_{i} } C_{liq} . -For the special case when snow is present (:math:`W_{sno} >0`) but -there are no explicit snow layers (:math:`snl=0`), the heat capacity of -the top layer is a blend of ice and soil heat capacity +For the special case when snow is present (:math:`W_{sno} >0`) but there are no explicit snow layers (:math:`snl=0`), the heat capacity of the top layer is a blend of ice and soil heat capacity .. math:: :label: 6.93 @@ -979,3 +776,36 @@ the top layer is a blend of ice and soil heat capacity c_{1} =c_{1}^{*} +\frac{C_{ice} W_{sno} }{\Delta z_{1} } where :math:`c_{1}^{*}` is calculated from :eq:`6.89` or :eq:`6.92`. + +.. _Excess Ground Ice: + +Excess Ground Ice +------------------------------------ + +An optional parameterization of excess ground ice melt and respective subsidence based on (:ref:`Lee et al., (2014) `). Initial excess ground ice concentrations for soil columns are derived from (:ref:`Brown et al., (1997) `). When the excess ground ice is present in the soil column, soil depth for a given layer (:math:`z_{i}`) is adjusted by the amount of excess ice in the column: + +.. math:: + :label: 6.94 + + z_{i}^{'}=\Sigma_{j=1}^{i} \ z_{j}^{'}+\frac{w_{exice,\, j}}{\rho_{ice} } + +where :math:`w_{exice,\,j}` is excess ground ice amount (kg m :sup:`-2`) in layer :math:`j` and :math:`\rho_{ice}` is the density of ice (kg m :sup:`-3`). After adjustment of layer depths have been made, all of the soil temperature equations (from :eq:`6.80` to :eq:`6.89`) are calculted based on the adjusted depths. Thermal properties are additionally adjusted (:eq:`6.8` and :eq:`6.8`) in the following way: + +.. math:: + :label: 6.95 + + \begin{array}{lr} + \theta_{sat}^{'} =\frac{\theta _{liq} }{\theta _{liq} +\theta _{ice} +\theta_{exice}}{\theta_{sat}} \\ + \lambda _{sat}^{'} =\lambda _{s}^{1-\theta _{sat}^{'} } \lambda _{liq}^{\frac{\theta _{liq} }{\theta _{liq} +\theta _{ice} +\theta_{exice}} \theta _{sat}^{'} } \lambda _{ice}^{\theta _{sat}^{'} \left(1-\frac{\theta _{liq} }{\theta _{liq} +\theta _{ice} +\theta_{exice}} \right)} \\ + c_{i}^{'} =c_{s,\, i} \left(1-\theta _{sat,\, i}^{'} \right)+\frac{w_{ice,\, i} +w_{exice,\,j}}{\Delta z_{i}^{'} } C_{ice} +\frac{w_{liq,\, i} }{\Delta z_{i}^{'} } C_{liq} + \end{array} + +Soil subsidence at the timestep :math:`n+1` (:math:`z_{exice}^{n+1}`, m) is then calculated as: + +.. math:: + :label: 6.96 + + z_{exice}^{n+1}=\Sigma_{i=1}^{N_{levgrnd}} \ z_{j}^{',\ ,n+1}-z_{j}^{',\ ,n } + +With regards to hydraulic counductivity, excess ground ice is treated the same way normal soil ice is treated in :numref:`Frozen Soils and Perched Water Table`. When a soil layer thaws, excess ground ice is only allowed to melt when no normals soil ice is present in the layer. When a soil layer refreezes, liquid soil water can only turn into normal soil ice, thus, no new of excess ice can be created but only melted. The excess liquid soil moisture from excess ice melt is distributed within the soil column according to :numref:`Lateral Sub-surface Runoff`. + diff --git a/doc/source/tech_note/Surface_Albedos/CLM50_Tech_Note_Surface_Albedos.rst b/doc/source/tech_note/Surface_Albedos/CLM50_Tech_Note_Surface_Albedos.rst index 4aee9dbcd5..f28d5583f1 100644 --- a/doc/source/tech_note/Surface_Albedos/CLM50_Tech_Note_Surface_Albedos.rst +++ b/doc/source/tech_note/Surface_Albedos/CLM50_Tech_Note_Surface_Albedos.rst @@ -8,9 +8,7 @@ Surface Albedos Canopy Radiative Transfer ----------------------------- -Radiative transfer within vegetative canopies is calculated from the -two-stream approximation of :ref:`Dickinson (1983) ` and -:ref:`Sellers (1985) ` as described by :ref:`Bonan (1996) ` +Radiative transfer within vegetative canopies is calculated from the two-stream approximation of :ref:`Dickinson (1983) ` and :ref:`Sellers (1985) ` as described by :ref:`Bonan (1996) ` .. math:: :label: 3.1 @@ -22,44 +20,16 @@ two-stream approximation of :ref:`Dickinson (1983) ` and \bar{\mu }\frac{dI\, \downarrow }{d\left(L+S\right)} +\left[1-\left(1-\beta \right)\omega \right]I\, \downarrow -\omega \beta I\, \uparrow =\omega \bar{\mu }K\left(1-\beta _{0} \right)e^{-K\left(L+S\right)} -where :math:`I\, \uparrow` and :math:`I\, \downarrow` are the upward -and downward diffuse radiative fluxes per unit incident flux, -:math:`K={G\left(\mu \right)\mathord{\left/ {\vphantom {G\left(\mu \right) \mu }} \right. \kern-\nulldelimiterspace} \mu }` -is the optical depth of direct beam per unit leaf and stem area, -:math:`\mu` is the cosine of the zenith angle of the incident beam, -:math:`G\left(\mu \right)` is the relative projected area of leaf and -stem elements in the direction :math:`\cos ^{-1} \mu` , -:math:`\bar{\mu }` is the average inverse diffuse optical depth per unit -leaf and stem area, :math:`\omega` is a scattering coefficient, -:math:`\beta` and :math:`\beta _{0}` are upscatter parameters for -diffuse and direct beam radiation, respectively, :math:`L` is the -exposed leaf area index , and :math:`S` is the exposed stem area index -(section :numref:`Phenology and vegetation burial by snow`). Given the -direct beam albedo :math:`\alpha _{g,\, \Lambda }^{\mu }` and diffuse albedo -:math:`\alpha _{g,\, \Lambda }` of the ground (section :numref:`Ground Albedos`), these -equations are solved to calculate the fluxes, per unit incident flux, -absorbed by the vegetation, reflected by the vegetation, and transmitted -through the vegetation for direct and diffuse radiation and for visible -(:math:`<` 0.7\ :math:`\mu {\rm m}`) and near-infrared -(:math:`\geq` 0.7\ :math:`\mu {\rm m}`) wavebands. The absorbed -radiation is partitioned to sunlit and shaded fractions of the canopy. -The optical parameters :math:`G\left(\mu \right)`, :math:`\bar{\mu }`, -:math:`\omega`, :math:`\beta`, and :math:`\beta _{0}` are calculated -based on work in :ref:`Sellers (1985) ` as follows. - -The relative projected area of leaves and stems in the direction -:math:`\cos ^{-1} \mu` is +where :math:`I\, \uparrow` and :math:`I\, \downarrow` are the upward and downward diffuse radiative fluxes per unit incident flux, :math:`K={G\left(\mu \right)\mathord{\left/ {\vphantom {G\left(\mu \right) \mu }} \right.} \mu }` is the optical depth of direct beam per unit leaf and stem area, :math:`\mu` is the cosine of the zenith angle of the incident beam, :math:`G\left(\mu \right)` is the relative projected area of leaf and stem elements in the direction :math:`\cos ^{-1} \mu`, :math:`\bar{\mu }` is the average inverse diffuse optical depth per unit leaf and stem area, :math:`\omega` is a scattering coefficient, :math:`\beta` and :math:`\beta _{0}` are upscatter parameters for diffuse and direct beam radiation, respectively, :math:`L` is the exposed leaf area index, and :math:`S` is the exposed stem area index (section :numref:`Phenology and vegetation burial by snow`). Given the direct beam albedo :math:`\alpha _{g,\, \Lambda }^{\mu }` and diffuse albedo :math:`\alpha _{g,\, \Lambda }` of the ground (section :numref:`Ground Albedos`), these equations are solved to calculate the fluxes, per unit incident flux, absorbed by the vegetation, reflected by the vegetation, and transmitted through the vegetation for direct and diffuse radiation and for visible (:math:`<` 0.7\ :math:`\mu {\rm m}`) and near-infrared (:math:`\geq` 0.7\ :math:`\mu {\rm m}`) wavebands. The absorbed radiation is partitioned to sunlit and shaded fractions of the canopy. The optical parameters :math:`G\left(\mu \right)`, :math:`\bar{\mu }`, :math:`\omega`, :math:`\beta`, and :math:`\beta _{0}` are calculated based on work in :ref:`Sellers (1985) ` as follows. + +The relative projected area of leaves and stems in the direction :math:`\cos ^{-1} \mu` is .. math:: :label: 3.3 G\left(\mu \right)=\phi _{1} +\phi _{2} \mu -where :math:`\phi _{1} ={\rm 0.5}-0.633\chi _{L} -0.33\chi _{L}^{2}` -and :math:`\phi _{2} =0.877\left(1-2\phi _{1} \right)` for -:math:`-0.4\le \chi _{L} \le 0.6`. :math:`\chi _{L}` is the departure -of leaf angles from a random distribution and equals +1 for horizontal -leaves, 0 for random leaves, and –1 for vertical leaves. +where :math:`\phi _{1} ={\rm 0.5}-0.633\chi _{L} -0.33\chi _{L}^{2}` and :math:`\phi _{2} =0.877\left(1-2\phi _{1} \right)` for :math:`-0.4\le \chi _{L} \le 0.6`. :math:`\chi _{L}` is the departure of leaf angles from a random distribution and equals +1 for horizontal leaves, 0 for random leaves, and –1 for vertical leaves. The average inverse diffuse optical depth per unit leaf and stem area is @@ -70,17 +40,10 @@ The average inverse diffuse optical depth per unit leaf and stem area is where :math:`\mu '` is the direction of the scattered flux. -The optical parameters :math:`\omega`, :math:`\beta`, and :math:`\beta _{0}`, -which vary with wavelength (:math:`\Lambda` ), are weighted combinations of values -for vegetation and snow, using the canopy snow-covered fraction :math:`f_{can,\, sno}` -(Chapter :numref:`rst_Hydrology`). The optical parameters are - +The optical parameters :math:`\omega`, :math:`\beta`, and :math:`\beta _{0}`, which vary with wavelength (:math:`\Lambda` ), are weighted combinations of values for vegetation and snow, using the canopy snow-covered fraction :math:`f_{can,\, sno}` (Chapter :numref:`rst_Hydrology`). The optical parameters are .. - The model determines that snow is on the canopy if - :math:`T_{v} \le T_{f}` , where :math:`T_{v}` is the vegetation temperature (K) (Chapter - :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) and :math:`T_{f}` is the - freezing temperature of water (K) (:numref:`Table Physical Constants`). In this case, the optical parameters are + The model determines that snow is on the canopy if :math:`T_{v} \le T_{f}`, where :math:`T_{v}` is the vegetation temperature (K) (Chapter :numref:`rst_Momentum, Sensible Heat, and Latent Heat Fluxes`) and :math:`T_{f}` is the freezing temperature of water (K) (:numref:`Table Physical Constants`). In this case, the optical parameters are .. math:: :label: 3.5 @@ -97,9 +60,7 @@ for vegetation and snow, using the canopy snow-covered fraction :math:`f_{can,\, \omega _{\Lambda } \beta _{0,\, \Lambda } =\omega _{\Lambda }^{veg} \beta _{0,\, \Lambda }^{veg} \left(1-f_{can,\, sno} \right)+\omega _{\Lambda }^{sno} \beta _{0,\, \Lambda }^{sno} f_{can,\, sno} -The snow and vegetation weights are applied to the products -:math:`\omega _{\Lambda } \beta _{\Lambda }` and :math:`\omega _{\Lambda } \beta _{0,\, \Lambda }` because these -products are used in the two-stream equations. If there is no snow on the canopy, this reduces to +The snow and vegetation weights are applied to the products :math:`\omega _{\Lambda } \beta _{\Lambda }` and :math:`\omega _{\Lambda } \beta _{0,\, \Lambda }` because these products are used in the two-stream equations. If there is no snow on the canopy, this reduces to .. math:: :label: 3.8 @@ -116,22 +77,14 @@ products are used in the two-stream equations. If there is no snow on the canopy \omega _{\Lambda } \beta _{0,\, \Lambda } =\omega _{\Lambda }^{veg} \beta _{0,\, \Lambda }^{veg} . -For vegetation, -:math:`\omega _{\Lambda }^{veg} =\alpha _{\Lambda } +\tau _{\Lambda }` . -:math:`\alpha _{\Lambda }` is a weighted combination of the leaf and -stem reflectances -(:math:`\alpha _{\Lambda }^{leaf} ,\alpha _{\Lambda }^{stem}` ) +For vegetation, :math:`\omega _{\Lambda }^{veg} =\alpha _{\Lambda } +\tau _{\Lambda }`. :math:`\alpha _{\Lambda }` is a weighted combination of the leaf and stem reflectances (:math:`\alpha _{\Lambda }^{leaf},\alpha _{\Lambda }^{stem}` ) .. math:: :label: 3.11 \alpha _{\Lambda } =\alpha _{\Lambda }^{leaf} w_{leaf} +\alpha _{\Lambda }^{stem} w_{stem} -where -:math:`w_{leaf} ={L\mathord{\left/ {\vphantom {L \left(L+S\right)}} \right. \kern-\nulldelimiterspace} \left(L+S\right)}` -and -:math:`w_{stem} ={S\mathord{\left/ {\vphantom {S \left(L+S\right)}} \right. \kern-\nulldelimiterspace} \left(L+S\right)}` . -:math:`\tau _{\Lambda }` is a weighted combination of the leaf and stem transmittances (:math:`\tau _{\Lambda }^{leaf}, \tau _{\Lambda }^{stem}`) +where :math:`w_{leaf} ={L\mathord{\left/ {\vphantom {L \left(L+S\right)}} \right.} \left(L+S\right)}` and :math:`w_{stem} ={S\mathord{\left/ {\vphantom {S \left(L+S\right)}} \right.} \left(L+S\right)}`. :math:`\tau _{\Lambda }` is a weighted combination of the leaf and stem transmittances (:math:`\tau _{\Lambda }^{leaf}, \tau _{\Lambda }^{stem}`) .. math:: :label: 3.12 @@ -145,33 +98,14 @@ The upscatter for diffuse radiation is \omega _{\Lambda }^{veg} \beta _{\Lambda }^{veg} =\frac{1}{2} \left[\alpha _{\Lambda } +\tau _{\Lambda } +\left(\alpha _{\Lambda } -\tau _{\Lambda } \right)\cos ^{2} \bar{\theta }\right] -where :math:`\bar{\theta }` is the mean leaf inclination angle relative -to the horizontal plane (i.e., the angle between leaf normal and local -vertical) (:ref:`Sellers (1985) `). Here, :math:`\cos \bar{\theta }` is -approximated by +where :math:`\bar{\theta }` is the mean leaf inclination angle relative to the horizontal plane (i.e., the angle between leaf normal and local vertical) (:ref:`Sellers (1985) `). Here, :math:`\cos \bar{\theta }` is approximated by .. math:: :label: 3.14 \cos \bar{\theta }=\frac{1+\chi _{L} }{2} -Using this approximation, for vertical leaves (:math:`\chi _{L} =-1`, -:math:`\bar{\theta }=90^{{\rm o}}` ), -:math:`\omega _{\Lambda }^{veg} \beta _{\Lambda }^{veg} =0.5\left(\alpha _{\Lambda } +\tau _{\Lambda } \right)`, -and for horizontal leaves (:math:`\chi _{L} =1`, -:math:`\bar{\theta }=0^{{\rm o}}` ) , -:math:`\omega _{\Lambda }^{veg} \beta _{\Lambda }^{veg} =\alpha _{\Lambda }` , -which agree with both :ref:`Dickinson (1983) ` and :ref:`Sellers (1985) `. For random -(spherically distributed) leaves (:math:`\chi _{L} =0`, -:math:`\bar{\theta }=60^{{\rm o}}` ), the approximation yields -:math:`\omega _{\Lambda }^{veg} \beta _{\Lambda }^{veg} ={5\mathord{\left/ {\vphantom {5 8}} \right. \kern-\nulldelimiterspace} 8} \alpha _{\Lambda } +{3\mathord{\left/ {\vphantom {3 8}} \right. \kern-\nulldelimiterspace} 8} \tau _{\Lambda }` -whereas the approximate solution of :ref:`Dickinson (1983) ` is -:math:`\omega _{\Lambda }^{veg} \beta _{\Lambda }^{veg} ={2\mathord{\left/ {\vphantom {2 3}} \right. \kern-\nulldelimiterspace} 3} \alpha _{\Lambda } +{1\mathord{\left/ {\vphantom {1 3}} \right. \kern-\nulldelimiterspace} 3} \tau _{\Lambda }` . -This discrepancy arises from the fact that a spherical leaf angle -distribution has a true mean leaf inclination -:math:`\bar{\theta }\approx 57` :ref:`(Campbell and Norman 1998) ` in equation , -while :math:`\bar{\theta }=60` in equation . The upscatter for direct -beam radiation is +Using this approximation, for vertical leaves (:math:`\chi _{L} =-1`, :math:`\bar{\theta }=90^{{\rm o}}` ), :math:`\omega _{\Lambda }^{veg} \beta _{\Lambda }^{veg} =0.5\left(\alpha _{\Lambda } +\tau _{\Lambda } \right)`, and for horizontal leaves (:math:`\chi _{L} =1`, :math:`\bar{\theta }=0^{{\rm o}}` ), :math:`\omega _{\Lambda }^{veg} \beta _{\Lambda }^{veg} =\alpha _{\Lambda }`, which agree with both :ref:`Dickinson (1983) ` and :ref:`Sellers (1985) `. For random (spherically distributed) leaves (:math:`\chi _{L} =0`, :math:`\bar{\theta }=60^{{\rm o}}` ), the approximation yields :math:`\omega _{\Lambda }^{veg} \beta _{\Lambda }^{veg} ={5\mathord{\left/ {\vphantom {5 8}} \right.} 8} \alpha _{\Lambda } +{3\mathord{\left/ {\vphantom {3 8}} \right.} 8} \tau _{\Lambda }` whereas the approximate solution of :ref:`Dickinson (1983) ` is :math:`\omega _{\Lambda }^{veg} \beta _{\Lambda }^{veg} ={2\mathord{\left/ {\vphantom {2 3}} \right.} 3} \alpha _{\Lambda } +{1\mathord{\left/ {\vphantom {1 3}} \right.} 3} \tau _{\Lambda }`. This discrepancy arises from the fact that a spherical leaf angle distribution has a true mean leaf inclination :math:`\bar{\theta }\approx 57` :ref:`(Campbell and Norman 1998) ` in equation :eq:`3.13`, while :math:`\bar{\theta }=60` in equation :eq:`3.14`. The upscatter for direct beam radiation is .. math:: :label: 3.15 @@ -185,12 +119,9 @@ where the single scattering albedo is \begin{array}{rcl} {a_{s} \left(\mu \right)_{\Lambda } } & {=} & {\frac{\omega _{\Lambda }^{veg} }{2} \int _{0}^{1}\frac{\mu 'G\left(\mu \right)}{\mu G\left(\mu '\right)+\mu 'G\left(\mu \right)} d\mu '} \\ {} & {=} & {\frac{\omega _{\Lambda }^{veg} }{2} \frac{G\left(\mu \right)}{\max (\mu \phi _{2} +G\left(\mu \right),1e-6)} \left[1-\frac{\mu \phi _{1} }{\max (\mu \phi _{2} +G\left(\mu \right),1e-6)} \ln \left(\frac{\mu \phi _{1} +\max (\mu \phi _{2} +G\left(\mu \right),1e-6)}{\mu \phi _{1} } \right)\right].} \end{array} -Note here the restriction on :math:`\mu \phi _{2} +G\left(\mu \right)`. We have seen cases where small values -can cause unrealistic single scattering albedo associated with the log calculation, -thereby eventually causing a negative soil albedo. +Note here the restriction on :math:`\mu \phi _{2} +G\left(\mu \right)`. We have seen cases where small values can cause unrealistic single scattering albedo associated with the log calculation, thereby eventually causing a negative soil albedo. -The upward diffuse fluxes per unit incident direct beam and diffuse flux -(i.e., the surface albedos) are +The upward diffuse fluxes per unit incident direct beam and diffuse flux (i.e., the surface albedos) are .. math:: :label: 3.17 @@ -202,8 +133,7 @@ The upward diffuse fluxes per unit incident direct beam and diffuse flux I\, \uparrow _{\Lambda } =h_{7} +h_{8} . -The downward diffuse fluxes per unit incident direct beam and diffuse -radiation, respectively, are +The downward diffuse fluxes per unit incident direct beam and diffuse radiation, respectively, are .. math:: :label: 3.19 @@ -215,10 +145,7 @@ radiation, respectively, are I\, \downarrow _{\Lambda } =h_{9} s_{1} +\frac{h_{10} }{s_{1} } . -With reference to :numref:`Figure Radiation Schematic`, the direct beam flux transmitted through -the canopy, per unit incident flux, is :math:`e^{-K\left(L+S\right)}` , -and the direct beam and diffuse fluxes absorbed by the vegetation, per -unit incident flux, are +With reference to :numref:`Figure Radiation Schematic`, the direct beam flux transmitted through the canopy, per unit incident flux, is :math:`e^{-K\left(L+S\right)}`, and the direct beam and diffuse fluxes absorbed by the vegetation, per unit incident flux, are .. math:: :label: 3.21 @@ -230,10 +157,7 @@ unit incident flux, are \vec{I}_{\Lambda } =1-I\, \uparrow _{\Lambda } -\left(1-\alpha _{g,\, \Lambda } \right)I\, \downarrow _{\Lambda } . -These fluxes are partitioned to the sunlit and shaded canopy using an -analytical solution to the two-stream approximation for sunlit and -shaded leaves :ref:`(Dai et al. 2004) `, as described by :ref:`Bonan et al. (2011) `. -The absorption of direct beam radiation by sunlit leaves is +These fluxes are partitioned to the sunlit and shaded canopy using an analytical solution to the two-stream approximation for sunlit and shaded leaves :ref:`(Dai et al. 2004) `, as described by :ref:`Bonan et al. (2011) `. The absorption of direct beam radiation by sunlit leaves is .. math:: :label: 3.23 @@ -252,12 +176,12 @@ with .. math:: :label: 3.25 - a_{1} =\frac{h_{1} }{\sigma } \left[\frac{1-s_{2}^{2} }{2K} \right]+h_{2} \left[\frac{1-s_{2} s_{1} }{K+h} \right]+h_{3} \left[\frac{1-{s_{2} \mathord{\left/ {\vphantom {s_{2} s_{1} }} \right. \kern-\nulldelimiterspace} s_{1} } }{K-h} \right] + a_{1} =\frac{h_{1} }{\sigma } \left[\frac{1-s_{2}^{2} }{2K} \right]+h_{2} \left[\frac{1-s_{2} s_{1} }{K+h} \right]+h_{3} \left[\frac{1-{s_{2} \mathord{\left/ {\vphantom {s_{2} s_{1} }} \right.} s_{1} } }{K-h} \right] .. math:: :label: 3.26 - a_{2} =\frac{h_{4} }{\sigma } \left[\frac{1-s_{2}^{2} }{2K} \right]+h_{5} \left[\frac{1-s_{2} s_{1} }{K+h} \right]+h_{6} \left[\frac{1-{s_{2} \mathord{\left/ {\vphantom {s_{2} s_{1} }} \right. \kern-\nulldelimiterspace} s_{1} } }{K-h} \right]. + a_{2} =\frac{h_{4} }{\sigma } \left[\frac{1-s_{2}^{2} }{2K} \right]+h_{5} \left[\frac{1-s_{2} s_{1} }{K+h} \right]+h_{6} \left[\frac{1-{s_{2} \mathord{\left/ {\vphantom {s_{2} s_{1} }} \right.} s_{1} } }{K-h} \right]. For diffuse radiation, the absorbed radiation for sunlit leaves is @@ -278,16 +202,14 @@ with .. math:: :label: 3.29 - a_{1} =h_{7} \left[\frac{1-s_{2} s_{1} }{K+h} \right]+h_{8} \left[\frac{1-{s_{2} \mathord{\left/ {\vphantom {s_{2} s_{1} }} \right. \kern-\nulldelimiterspace} s_{1} } }{K-h} \right] + a_{1} =h_{7} \left[\frac{1-s_{2} s_{1} }{K+h} \right]+h_{8} \left[\frac{1-{s_{2} \mathord{\left/ {\vphantom {s_{2} s_{1} }} \right.} s_{1} } }{K-h} \right] .. math:: :label: 3.30 - a_{2} =h_{9} \left[\frac{1-s_{2} s_{1} }{K+h} \right]+h_{10} \left[\frac{1-{s_{2} \mathord{\left/ {\vphantom {s_{2} s_{1} }} \right. \kern-\nulldelimiterspace} s_{1} } }{K-h} \right]. + a_{2} =h_{9} \left[\frac{1-s_{2} s_{1} }{K+h} \right]+h_{10} \left[\frac{1-{s_{2} \mathord{\left/ {\vphantom {s_{2} s_{1} }} \right.} s_{1} } }{K-h} \right]. -The parameters :math:`h_{1}` –:math:`h_{10}` , :math:`\sigma` , -:math:`h`, :math:`s_{1}` , and :math:`s_{2}` are from :ref:`Sellers (1985) ` -[note the error in :math:`h_{4}` in :ref:`Sellers (1985) `]: +The parameters :math:`h_{1}` –:math:`h_{10}`, :math:`\sigma`, :math:`h`, :math:`s_{1}`, and :math:`s_{2}` are from :ref:`Sellers (1985) ` [note the error in :math:`h_{4}` in :ref:`Sellers (1985) `]: .. math:: :label: 3.31 @@ -322,7 +244,7 @@ The parameters :math:`h_{1}` –:math:`h_{10}` , :math:`\sigma` , .. math:: :label: 3.37 - u_{1} =b-{c\mathord{\left/ {\vphantom {c \alpha _{g,\, \Lambda }^{\mu } }} \right. \kern-\nulldelimiterspace} \alpha _{g,\, \Lambda }^{\mu } } {\rm \; or\; }u_{1} =b-{c\mathord{\left/ {\vphantom {c \alpha _{g,\, \Lambda } }} \right. \kern-\nulldelimiterspace} \alpha _{g,\, \Lambda } } + u_{1} =b-{c\mathord{\left/ {\vphantom {c \alpha _{g,\, \Lambda }^{\mu } }} \right.} \alpha _{g,\, \Lambda }^{\mu } } {\rm \; or\; }u_{1} =b-{c\mathord{\left/ {\vphantom {c \alpha _{g,\, \Lambda } }} \right.} \alpha _{g,\, \Lambda } } .. math:: :label: 3.38 @@ -424,13 +346,7 @@ The parameters :math:`h_{1}` –:math:`h_{10}` , :math:`\sigma` , h_{10} =\frac{-s_{1} \left(u_{2} -\bar{\mu }h\right)}{d_{2} } . -Plant functional type optical properties (:numref:`Table Plant functional type optical properties`) -for trees and shrubs are from :ref:`Dorman and Sellers (1989) `. Leaf and stem optical -properties (VIS and NIR reflectance and transmittance) were derived -for grasslands and crops from full optical range spectra of measured -optical properties (:ref:`Asner et al. 1998 `). Optical properties for -intercepted snow (:numref:`Table Intercepted snow optical properties`) are from -:ref:`Sellers et al. (1986) `. +Plant functional type optical properties (:numref:`Table Plant functional type optical properties`) for trees and shrubs are from :ref:`Dorman and Sellers (1989) `. Leaf and stem optical properties (VIS and NIR reflectance and transmittance) were derived for grasslands and crops from full optical range spectra of measured optical properties (:ref:`Asner et al. 1998 `). Optical properties for intercepted snow (:numref:`Table Intercepted snow optical properties`) are from :ref:`Sellers et al. (1986) `. .. _Table Plant functional type optical properties: @@ -509,9 +425,7 @@ intercepted snow (:numref:`Table Intercepted snow optical properties`) are from Ground Albedos ------------------ -The overall direct beam :math:`\alpha _{g,\, \Lambda }^{\mu }` and diffuse -:math:`\alpha _{g,\, \Lambda }` ground albedos are weighted -combinations of “soil” and snow albedos +The overall direct beam :math:`\alpha _{g,\, \Lambda }^{\mu }` and diffuse :math:`\alpha _{g,\, \Lambda }` ground albedos are weighted combinations of "soil" and snow albedos .. math:: :label: 3.58 @@ -523,19 +437,15 @@ combinations of “soil” and snow albedos \alpha _{g,\, \Lambda } =\alpha _{soi,\, \Lambda } \left(1-f_{sno} \right)+\alpha _{sno,\, \Lambda } f_{sno} -where :math:`f_{sno}` is the fraction of the ground covered with snow -(section :numref:`Snow Covered Area Fraction`). +where :math:`f_{sno}` is the fraction of the ground covered with snow (section :numref:`Snow Covered Area Fraction`). -:math:`\alpha _{soi,\, \Lambda }^{\mu }` and -:math:`\alpha _{soi,\, \Lambda }` vary with glacier, lake, and -soil surfaces. Glacier albedos are from :ref:`Paterson (1994) ` +:math:`\alpha _{soi,\, \Lambda }^{\mu }` and :math:`\alpha _{soi,\, \Lambda }` vary with glacier, lake, and soil surfaces. Glacier albedos are from :ref:`Paterson (1994) ` .. math:: \alpha _{soi,\, vis}^{\mu } =\alpha _{soi,\, vis} =0.6 .. math:: \alpha _{soi,\, nir}^{\mu } =\alpha _{soi,\, nir} =0.4. -Unfrozen lake albedos depend on the cosine of the solar -zenith angle :math:`\mu` +Unfrozen lake albedos depend on the cosine of the solar zenith angle :math:`\mu` .. math:: :label: 3.60 @@ -555,31 +465,9 @@ As in NCAR LSM (:ref:`Bonan 1996 `), soil albedos vary with color cla \alpha _{soi,\, \Lambda }^{\mu } =\alpha _{soi,\, \Lambda } =\left(\alpha _{sat,\, \Lambda } +\Delta \right)\le \alpha _{dry,\, \Lambda } -where :math:`\Delta` depends on the volumetric water content of the -first soil layer :math:`\theta _{1}` (section :numref:`Soil Water`) as -:math:`\Delta =0.11-0.40\theta _{1} >0`, and -:math:`\alpha _{sat,\, \Lambda }` and -:math:`\alpha _{dry,\, \Lambda }` are albedos for saturated and dry -soil color classes (:numref:`Table Dry and saturated soil albedos`). - -CLM soil colors are prescribed so that they best reproduce observed -MODIS local solar noon surface albedo values at the CLM grid cell -following the methods of :ref:`Lawrence and Chase (2007) `. -The soil colors are fitted over the range of 20 soil classes shown in -:numref:`Table Dry and saturated soil albedos` and compared -to the MODIS monthly local solar noon all-sky surface albedo as -described in :ref:`Strahler et al. (1999) ` and -:ref:`Schaaf et al. (2002) `. The CLM -two-stream radiation model was used to calculate the model equivalent -surface albedo using climatological monthly soil moisture along with the -vegetation parameters of PFT fraction, LAI, and SAI. The soil color that -produced the closest all-sky albedo in the two-stream radiation model -was selected as the best fit for the month. The fitted monthly soil -colors were averaged over all snow-free months to specify a -representative soil color for the grid cell. In cases where there was no -snow-free surface albedo for the year, the soil color derived from -snow-affected albedo was used to give a representative soil color that -included the effects of the minimum permanent snow cover. +where :math:`\Delta` depends on the volumetric water content of the first soil layer :math:`\theta _{1}` (section :numref:`Soil Water`) as :math:`\Delta =0.11-0.40\theta _{1} >0`, and :math:`\alpha _{sat,\, \Lambda }` and :math:`\alpha _{dry,\, \Lambda }` are albedos for saturated and dry soil color classes (:numref:`Table Dry and saturated soil albedos`). + +CLM soil colors are prescribed so that they best reproduce observed MODIS local solar noon surface albedo values at the CLM grid cell following the methods of :ref:`Lawrence and Chase (2007) `. The soil colors are fitted over the range of 20 soil classes shown in :numref:`Table Dry and saturated soil albedos` and compared to the MODIS monthly local solar noon all-sky surface albedo as described in :ref:`Strahler et al. (1999) ` and :ref:`Schaaf et al. (2002) `. The CLM two-stream radiation model was used to calculate the model equivalent surface albedo using climatological monthly soil moisture along with the vegetation parameters of PFT fraction, LAI, and SAI. The soil color that produced the closest all-sky albedo in the two-stream radiation model was selected as the best fit for the month. The fitted monthly soil colors were averaged over all snow-free months to specify a representative soil color for the grid cell. In cases where there was no snow-free surface albedo for the year, the soil color derived from snow-affected albedo was used to give a representative soil color that included the effects of the minimum permanent snow cover. .. _Table Dry and saturated soil albedos: @@ -616,32 +504,9 @@ included the effects of the minimum permanent snow cover. Snow Albedo ^^^^^^^^^^^^^^^^^ -Snow albedo and solar absorption within each snow layer are simulated -with the Snow, Ice, and Aerosol Radiative Model (SNICAR), which -incorporates a two-stream radiative transfer solution from -:ref:`Toon et al. (1989) `. Albedo and the vertical absorption -profile depend on solar zenith angle, albedo of the substrate underlying snow, mass -concentrations of atmospheric-deposited aerosols (black carbon, mineral -dust, and organic carbon), and ice effective grain size -(:math:`r_{e}`), which is simulated with a snow aging routine -described in section :numref:`Snow Aging`. Representation of impurity mass -concentrations within the snowpack is described in section -:numref:`Black and organic carbon and mineral dust within snow`. -Implementation of SNICAR in CLM is also described somewhat by -:ref:`Flanner and Zender (2005) ` and -:ref:`Flanner et al. (2007) `. - -The two-stream solution requires the following bulk optical properties -for each snow layer and spectral band: extinction optical depth -(:math:`\tau`), single-scatter albedo (:math:`\omega`), and -scattering asymmetry parameter (*g*). The snow layers used for radiative -calculations are identical to snow layers applied elsewhere in CLM, -except for the case when snow mass is greater than zero but no snow -layers exist. When this occurs, a single radiative layer is specified to -have the column snow mass and an effective grain size of freshly-fallen -snow (section :numref:`Snow Aging`). The bulk optical properties are weighted functions -of each constituent *k*, computed for each snow layer and spectral band -as +Snow albedo and solar absorption within each snow layer are simulated with the Snow, Ice, and Aerosol Radiative Model (SNICAR), which incorporates a two-stream radiative transfer solution from :ref:`Toon et al. (1989) `. Albedo and the vertical absorption profile depend on solar zenith angle, albedo of the substrate underlying snow, mass concentrations of atmospheric-deposited aerosols (black carbon, mineral dust, and organic carbon), and ice effective grain size (:math:`r_{e}`), which is simulated with a snow aging routine described in section :numref:`Snow Aging`. Representation of impurity mass concentrations within the snowpack is described in section :numref:`Black and organic carbon and mineral dust within snow`. Implementation of SNICAR in CLM is also described somewhat by :ref:`Flanner and Zender (2005) ` and :ref:`Flanner et al. (2007) `. + +The two-stream solution requires the following bulk optical properties for each snow layer and spectral band: extinction optical depth (:math:`\tau`), single-scatter albedo (:math:`\omega`), and scattering asymmetry parameter (*g*). The snow layers used for radiative calculations are identical to snow layers applied elsewhere in CLM, except for the case when snow mass is greater than zero but no snow layers exist. When this occurs, a single radiative layer is specified to have the column snow mass and an effective grain size of freshly-fallen snow (section :numref:`Snow Aging`). The bulk optical properties are weighted functions of each constituent *k*, computed for each snow layer and spectral band as .. math:: :label: 3.62 @@ -658,44 +523,14 @@ as g=\frac{\sum _{1}^{k}g_{k} \omega _{k} \tau _{k} }{\sum _{1}^{k}\omega _{k} \tau _{k} } -For each constituent (ice, two black carbon species, two organic carbon species, and -four dust species), :math:`\omega`, *g*, and the mass extinction cross-section -:math:`\psi` (m\ :sup:`2` kg\ :sub:`-1`) are computed offline with Mie Theory, e.g., -applying the computational technique from :ref:`Bohren and Huffman (1983) `. -The extinction optical depth for each constituent depends on its mass extinction -cross-section and layer mass, :math:`w _{k}` (kg\ m\ :sup:`-1`) as +For each constituent (ice, two black carbon species, two organic carbon species, and four dust species), :math:`\omega`, *g*, and the mass extinction cross-section :math:`\psi` (m\ :sup:`2` kg\ :sub:`-1`) are computed offline with Mie Theory, e.g., applying the computational technique from :ref:`Bohren and Huffman (1983) `. The extinction optical depth for each constituent depends on its mass extinction cross-section and layer mass, :math:`w _{k}` (kg\ m\ :sup:`-1`) as .. math:: :label: 3.65 \tau _{k} =\psi _{k} w_{k} -The two-stream solution (:ref:`Toon et al. (1989) `) applies a tri-diagonal matrix -solution to produce upward and downward radiative fluxes at each layer -interface, from which net radiation, layer absorption, and surface -albedo are easily derived. Solar fluxes are computed in five spectral -bands, listed in :numref:`Table Spectral bands and weights used for snow radiative transfer`. -Because snow albedo varies strongly across -the solar spectrum, it was determined that four bands were needed to -accurately represent the near-infrared (NIR) characteristics of snow, -whereas only one band was needed for the visible spectrum. Boundaries of -the NIR bands were selected to capture broad radiative features and -maximize accuracy and computational efficiency. We partition NIR (0.7-5.0 -:math:`\mu` m) surface downwelling flux from CLM according to the weights listed -in :numref:`Table Spectral bands and weights used for snow radiative transfer`, -which are unique for diffuse and direct incident flux. These fixed weights were -determined with offline hyperspectral radiative transfer calculations for an -atmosphere typical of mid-latitude winter (:ref:`Flanner et al. (2007) `). -The tri-diagonal solution includes intermediate terms that allow for easy -interchange of two-stream techniques. We apply the Eddington solution -for the visible band (following :ref:`Wiscombe and Warren 1980 `) and the -hemispheric mean solution ((:ref:`Toon et al. (1989) `) for NIR bands. These -choices were made because the Eddington scheme works well for highly -scattering media, but can produce negative albedo for absorptive NIR -bands with diffuse incident flux. Delta scalings are applied to -:math:`\tau`, :math:`\omega`, and :math:`g` (:ref:`Wiscombe and Warren 1980 `) in -all spectral bands, producing effective values (denoted with \*) that -are applied in the two-stream solution +The two-stream solution (:ref:`Toon et al. (1989) `) applies a tri-diagonal matrix solution to produce upward and downward radiative fluxes at each layer interface, from which net radiation, layer absorption, and surface albedo are easily derived. Solar fluxes are computed in five spectral bands, listed in :numref:`Table Spectral bands and weights used for snow radiative transfer`. Because snow albedo varies strongly across the solar spectrum, it was determined that four bands were needed to accurately represent the near-infrared (NIR) characteristics of snow, whereas only one band was needed for the visible spectrum. Boundaries of the NIR bands were selected to capture broad radiative features and maximize accuracy and computational efficiency. We partition NIR (0.7-5.0 :math:`\mu` m) surface downwelling flux from CLM according to the weights listed in :numref:`Table Spectral bands and weights used for snow radiative transfer`, which are unique for diffuse and direct incident flux. These fixed weights were determined with offline hyperspectral radiative transfer calculations for an atmosphere typical of mid-latitude winter (:ref:`Flanner et al. (2007) `). The tri-diagonal solution includes intermediate terms that allow for easy interchange of two-stream techniques. We apply the Eddington solution for the visible band (following :ref:`Wiscombe and Warren 1980 `) and the hemispheric mean solution ((:ref:`Toon et al. (1989) `) for NIR bands. These choices were made because the Eddington scheme works well for highly scattering media, but can produce negative albedo for absorptive NIR bands with diffuse incident flux. Delta scalings are applied to :math:`\tau`, :math:`\omega`, and :math:`g` (:ref:`Wiscombe and Warren 1980 `) in all spectral bands, producing effective values (denoted with :math:`*`) that are applied in the two-stream solution .. math:: :label: 3.66 @@ -730,105 +565,41 @@ are applied in the two-stream solution | Band 5: 1.5-5.0\ :math:`\mu`\ m (near-IR) | 0.204 | 0.103 | +---------------------------------------------------------+----------------------+------------------+ -Under direct-beam conditions, singularities in the radiative -approximation are occasionally approached in spectral bands 4 and 5 that -produce unrealistic conditions (negative energy absorption in a layer, -negative albedo, or total absorbed flux greater than incident flux). -When any of these three conditions occur, the Eddington approximation is -attempted instead, and if both approximations fail, the cosine of the -solar zenith angle is adjusted by 0.02 (conserving incident flux) and a -warning message is produced. This situation occurs in only about 1 in -10 :sup:`6` computations of snow albedo. After looping over the -five spectral bands, absorption fluxes and albedo are averaged back into -the bulk NIR band used by the rest of CLM. - -Soil albedo (or underlying substrate albedo), which is defined for -visible and NIR bands, is a required boundary condition for the snow -radiative transfer calculation. Currently, the bulk NIR soil albedo is -applied to all four NIR snow bands. With ground albedo as a lower -boundary condition, SNICAR simulates solar absorption in all snow layers -as well as the underlying soil or ground. With a thin snowpack, -penetrating solar radiation to the underlying soil can be quite large -and heat cannot be released from the soil to the atmosphere in this -situation. Thus, if the snowpack has total snow depth less than 0.1 m -(:math:`z_{sno} < 0.1`) and there are no explicit snow layers, the solar -radiation is absorbed by the top soil layer. If there is a single snow layer, -the solar radiation is absorbed in that layer. If there is more than a single -snow layer, 75% of the solar radiation is absorbed in the top snow layer, -and 25% is absorbed in the next lowest snow layer. This prevents unrealistic -soil warming within a single timestep. - -The radiative transfer calculation is performed twice for each column -containing a mass of snow greater than -:math:`1 \times 10^{-30}` kg\ m\ :sup:`-2` (excluding lake and urban columns); once each for -direct-beam and diffuse incident flux. Absorption in each layer -:math:`i` of pure snow is initially recorded as absorbed flux per unit -incident flux on the ground (:math:`S_{sno,\, i}` ), as albedos must be -calculated for the next timestep with unknown incident flux. The snow -absorption fluxes that are used for column temperature calculations are - -.. math:: - :label: ZEqnNum275338 +Under direct-beam conditions, singularities in the radiative approximation are occasionally approached in spectral bands 4 and 5 that produce unrealistic conditions (negative energy absorption in a layer, negative albedo, or total absorbed flux greater than incident flux). When any of these three conditions occur, the Eddington approximation is attempted instead, and if both approximations fail, the cosine of the solar zenith angle is adjusted by 0.02 (conserving incident flux) and a warning message is produced. This situation occurs in only about 1 in 10 :sup:`6` computations of snow albedo. After looping over the five spectral bands, absorption fluxes and albedo are averaged back into the bulk NIR band used by the rest of CLM. - S_{g,\, i} =S_{sno,\, i} \left(1-\alpha _{sno} \right) +Soil albedo (or underlying substrate albedo), which is defined for visible and NIR bands, is a required boundary condition for the snow radiative transfer calculation. Currently, the bulk NIR soil albedo is applied to all four NIR snow bands. With ground albedo as a lower boundary condition, SNICAR simulates solar absorption in all snow layers as well as the underlying soil or ground. With a thin snowpack, penetrating solar radiation to the underlying soil can be quite large and heat cannot be released from the soil to the atmosphere in this situation. Thus, if the snowpack has total snow depth less than 0.1 m (:math:`z_{sno} < 0.1`) and there are no explicit snow layers, the solar radiation is absorbed by the top soil layer. If there is a single snow layer, the solar radiation is absorbed in that layer. If there is more than a single snow layer, 75% of the solar radiation is absorbed in the top snow layer, and 25% is absorbed in the next lowest snow layer. This prevents unrealistic soil warming within a single timestep. -This weighting is performed for direct-beam and diffuse, visible and NIR -fluxes. After the ground-incident fluxes (transmitted through the -vegetation canopy) have been calculated for the current time step -(sections :numref:`Canopy Radiative Transfer` and :numref:`Solar Fluxes`), -the layer absorption factors +The radiative transfer calculation is performed twice for each column containing a mass of snow greater than :math:`1 \times 10^{-30}` kg\ m\ :sup:`-2` (excluding lake and urban columns); once each for direct-beam and diffuse incident flux. Absorption in each layer :math:`i` of pure snow is initially recorded as absorbed flux per unit incident flux on the ground (:math:`S_{sno,\, i}` ), as albedos must be calculated for the next timestep with unknown incident flux. The snow absorption fluxes that are used for column temperature calculations are -(:math:`S_{g,\, i}`) are multiplied by the ground-incident fluxes to -produce solar absorption (W m\ :sup:`-2`) in each snow layer and -the underlying ground. +.. math:: + :label: 3.69 + + S_{g,\, i} =S_{sno,\, i} \left(1-\alpha _{sno} \right) + +This weighting is performed for direct-beam and diffuse, visible and NIR fluxes. After the ground-incident fluxes (transmitted through the vegetation canopy) have been calculated for the current time step (sections :numref:`Canopy Radiative Transfer` and :numref:`Solar Fluxes`), the layer absorption factors (:math:`S_{g,\, i}`) are multiplied by the ground-incident fluxes to produce solar absorption (W m\ :sup:`-2`) in each snow layer and the underlying ground. .. _Snowpack Optical Properties: Snowpack Optical Properties ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Ice optical properties for the five spectral bands are derived offline -and stored in a namelist-defined lookup table for online retrieval (see -CLM5.0 User’s Guide). Mie properties are first computed at fine spectral -resolution (470 bands), and are then weighted into the five bands -applied by CLM according to incident solar flux, -:math:`I^{\downarrow } (\lambda )`. For example, the broadband -mass-extinction cross section (:math:`\bar{\psi }`) over wavelength -interval :math:`\lambda _{1}` to :math:`\lambda _{2}` is +Ice optical properties for the five spectral bands are derived offline and stored in a namelist-defined lookup table for online retrieval (see CLM5.0 User's Guide). Mie properties are first computed at fine spectral resolution (470 bands), and are then weighted into the five bands applied by CLM according to incident solar flux, :math:`I^{\downarrow } (\lambda )`. For example, the broadband mass-extinction cross section (:math:`\bar{\psi }`) over wavelength interval :math:`\lambda _{1}` to :math:`\lambda _{2}` is .. math:: :label: 3.70 \bar{\psi }=\frac{\int _{\lambda _{1} }^{\lambda _{2} }\psi \left(\lambda \right) I^{\downarrow } \left(\lambda \right){\rm d}\lambda }{\int _{\lambda _{1} }^{\lambda _{2} }I^{\downarrow } \left(\lambda \right){\rm d}\lambda } -Broadband single-scatter albedo (:math:`\bar{\omega }`) is additionally -weighted by the diffuse albedo for a semi-infinite snowpack (:math:`\alpha _{sno}`) +Broadband single-scatter albedo (:math:`\bar{\omega }`) is additionally weighted by the diffuse albedo for a semi-infinite snowpack (:math:`\alpha _{sno}`) .. math:: :label: 3.71 \bar{\omega }=\frac{\int _{\lambda _{1} }^{\lambda _{2} }\omega (\lambda )I^{\downarrow } ( \lambda )\alpha _{sno} (\lambda ){\rm d}\lambda }{\int _{\lambda _{1} }^{\lambda _{2} }I^{\downarrow } ( \lambda )\alpha _{sno} (\lambda ){\rm d}\lambda } -Inclusion of this additional albedo weight was found to improve accuracy -of the five-band albedo solutions (relative to 470-band solutions) -because of the strong dependence of optically-thick snowpack albedo on -ice grain single-scatter albedo (:ref:`Flanner et al. (2007) `). -The lookup tables contain optical properties for lognormal distributions of ice -particles over the range of effective radii: 30\ :math:`\mu`\ m -:math:`< r _{e} < \text{1500} \mu \text{m}`, at 1 :math:`\mu` m resolution. -Single-scatter albedos for the end-members of this size range are listed in -:numref:`Table Single-scatter albedo values used for snowpack impurities and ice`. - -Optical properties for black carbon are described in :ref:`Flanner et al. (2007) `. -Single-scatter albedo, mass extinction cross-section, and -asymmetry parameter values for all snowpack species, in the five -spectral bands used, are listed in :numref:`Table Single-scatter albedo values used for snowpack impurities and ice`, -:numref:`Table Mass extinction values`, and -:numref:`Table Asymmetry scattering parameters used for snowpack impurities and ice`. -These properties were also derived with Mie Theory, using various published -sources of indices of refraction and assumptions about particle size -distribution. Weighting into the five CLM spectral bands was determined -only with incident solar flux, as in equation . +Inclusion of this additional albedo weight was found to improve accuracy of the five-band albedo solutions (relative to 470-band solutions) because of the strong dependence of optically-thick snowpack albedo on ice grain single-scatter albedo (:ref:`Flanner et al. (2007) `). The lookup tables contain optical properties for lognormal distributions of ice particles over the range of effective radii: 30\ :math:`\mu`\ m :math:`< r _{e} < \text{1500} \mu \text{m}`, at 1 :math:`\mu` m resolution. Single-scatter albedos for the end-members of this size range are listed in :numref:`Table Single-scatter albedo values used for snowpack impurities and ice`. + +Optical properties for black carbon are described in :ref:`Flanner et al. (2007) `. Single-scatter albedo, mass extinction cross-section, and asymmetry parameter values for all snowpack species, in the five spectral bands used, are listed in :numref:`Table Single-scatter albedo values used for snowpack impurities and ice`, :numref:`Table Mass extinction values`, and :numref:`Table Asymmetry scattering parameters used for snowpack impurities and ice`. These properties were also derived with Mie Theory, using various published sources of indices of refraction and assumptions about particle size distribution. Weighting into the five CLM spectral bands was determined only with incident solar flux, as in equation :eq:`3.69`. .. _Table Single-scatter albedo values used for snowpack impurities and ice: @@ -919,209 +690,109 @@ only with incident solar flux, as in equation . Snow Aging ^^^^^^^^^^^^^^^^ -Snow aging is represented as evolution of the ice effective grain size -(:math:`r_{e}`). Previous studies have shown that use of spheres -which conserve the surface area-to-volume ratio (or specific surface -area) of ice media composed of more complex shapes produces relatively -small errors in simulated hemispheric fluxes -(e.g., :ref:`Grenfell and Warren 1999 `). -Effective radius is the surface area-weighted mean -radius of an ensemble of spherical particles and is directly related to specific -surface area (*SSA*) as -:math:`r_{e} ={3\mathord{\left/ {\vphantom {3 \left(\rho _{ice} SSA\right)}} \right. \kern-\nulldelimiterspace} \left(\rho _{ice} SSA\right)}` , -where :math:`\rho_{ice}` is the density of ice. Hence, -:math:`r_{e}` is a simple and practical metric for relating the -snowpack microphysical state to dry snow radiative characteristics. - -Wet snow processes can also drive rapid changes in albedo. The presence -of liquid water induces rapid coarsening of the surrounding ice grains -(e.g., :ref:`Brun 1989 `), and liquid water tends to refreeze into large ice -clumps that darken the bulk snowpack. The presence of small liquid -drops, by itself, does not significantly darken snowpack, as ice and -water have very similar indices of refraction throughout the solar -spectrum. Pooled or ponded water, however, can significantly darken -snowpack by greatly reducing the number of refraction events per unit -mass. This influence is not currently accounted for. - -The net change in effective grain size occurring each time step is -represented in each snow layer as a summation of changes caused by dry -snow metamorphism (:math:`dr_{e,dry}`), liquid water-induced -metamorphism (:math:`dr_{e,wet}`), refreezing of liquid water, and -addition of freshly-fallen snow. The mass of each snow layer is -partitioned into fractions of snow carrying over from the previous time -step (:math:`f_{old}`), freshly-fallen snow -(:math:`f_{new}`), and refrozen liquid water -(:math:`f_{rfz}`), such that snow :math:`r_{e}` is updated -each time step *t* as +Snow aging is represented as evolution of the ice effective grain size (:math:`r_{e}`). Previous studies have shown that use of spheres which conserve the surface area-to-volume ratio (or specific surface area) of ice media composed of more complex shapes produces relatively small errors in simulated hemispheric fluxes (e.g., :ref:`Grenfell and Warren 1999 `). Effective radius is the surface area-weighted mean radius of an ensemble of spherical particles and is directly related to specific surface area (*SSA*) as :math:`r_{e} ={3\mathord{\left/ {\vphantom {3 \left(\rho _{ice} SSA\right)}} \right.} \left(\rho _{ice} SSA\right)}`, where :math:`\rho_{ice}` is the density of ice. Hence, :math:`r_{e}` is a simple and practical metric for relating the snowpack microphysical state to dry snow radiative characteristics. + +Wet snow processes can also drive rapid changes in albedo. The presence of liquid water induces rapid coarsening of the surrounding ice grains (e.g., :ref:`Brun 1989 `), and liquid water tends to refreeze into large ice clumps that darken the bulk snowpack. The presence of small liquid drops, by itself, does not significantly darken snowpack, as ice and water have very similar indices of refraction throughout the solar spectrum. Pooled or ponded water, however, can significantly darken snowpack by greatly reducing the number of refraction events per unit mass. This influence is not currently accounted for. + +The net change in effective grain size occurring each time step is represented in each snow layer as a summation of changes caused by dry snow metamorphism (:math:`dr_{e,dry}`), liquid water-induced metamorphism (:math:`dr_{e,wet}`), refreezing of liquid water, and addition of freshly-fallen snow. The mass of each snow layer is partitioned into fractions of snow carrying over from the previous time step (:math:`f_{old}`), freshly-fallen snow (:math:`f_{new}`), and refrozen liquid water (:math:`f_{rfz}`), such that snow :math:`r_{e}` is updated each time step *t* as .. math:: :label: 3.72 r_{e} \left(t\right)=\left[r_{e} \left(t-1\right)+dr_{e,\, dry} +dr_{e,\, wet} \right]f_{old} +r_{e,\, 0} f_{new} +r_{e,\, rfz} f_{rfrz} -Here, the effective radius of freshly-fallen snow -(:math:`r_{e,0}`) is based on a simple linear temperature-relationship. -Below -30 degrees Celsius, a minimum value is enforced of 54.5 :math:`\mu` m -(corresponding to a specific surface area of 60 m\ :sup:`2` kg\ :sup:`-1`). -Above 0 degrees Celsius, a maximum value is enforced of 204.5 :math:`\mu` m. -Between -30 and 0 a linear ramp is used. +Here, the effective radius of freshly-fallen snow (:math:`r_{e,0}`) is based on a simple linear temperature-relationship. Below -30 degrees Celsius, a minimum value is enforced of 54.5 :math:`\mu` m (corresponding to a specific surface area of 60 m\ :sup:`2` kg\ :sup:`-1`). Above 0 degrees Celsius, a maximum value is enforced of 204.5 :math:`\mu` m. Between -30 and 0 a linear ramp is used. The effective radius of refrozen liquid water (:math:`r_{e,rfz}`) is set to 1000\ :math:`\mu` m. -Dry snow aging is based on a microphysical model described by :ref:`Flanner -and Zender (2006) `. This model simulates diffusive vapor flux -amongst collections of ice crystals with various size and inter-particle -spacing. Specific surface area and effective radius are prognosed for -any combination of snow temperature, temperature gradient, density, and -initial size distribution. The combination of warm snow, large -temperature gradient, and low density produces the most rapid snow -aging, whereas aging proceeds slowly in cold snow, regardless of -temperature gradient and density. Because this model is currently too -computationally expensive for inclusion in climate models, we fit -parametric curves to model output over a wide range of snow conditions -and apply these parameters in CLM. The functional form of the parametric -equation is +Dry snow aging is based on a microphysical model described by :ref:`Flanner and Zender (2006) `. This model simulates diffusive vapor flux amongst collections of ice crystals with various size and inter-particle spacing. Specific surface area and effective radius are prognosed for any combination of snow temperature, temperature gradient, density, and initial size distribution. The combination of warm snow, large temperature gradient, and low density produces the most rapid snow aging, whereas aging proceeds slowly in cold snow, regardless of temperature gradient and density. Because this model is currently too computationally expensive for inclusion in climate models, we fit parametric curves to model output over a wide range of snow conditions and apply these parameters in CLM. The functional form of the parametric equation is .. math:: :label: 3.73 - \frac{dr_{e,\, dry} }{dt} =\left(\frac{dr_{e} }{dt} \right)_{0} \left(\frac{\eta }{\left(r_{e} -r_{e,\, 0} \right)+\eta } \right)^{{1\mathord{\left/ {\vphantom {1 \kappa }} \right. \kern-\nulldelimiterspace} \kappa } } + \frac{dr_{e,\, dry} }{dt} =\left(\frac{dr_{e} }{dt} \right)_{0} \left(\frac{\eta }{\left(r_{e} -r_{e,\, 0} \right)+\eta } \right)^{{1\mathord{\left/ {\vphantom {1 \kappa }} \right.} \kappa } } -The parameters :math:`{(\frac{dr_{e}}{dt}})_{0}`, -:math:`\eta`, and :math:`\kappa` are retrieved interactively from a -lookup table with dimensions corresponding to snow temperature, -temperature gradient, and density. The domain covered by this lookup -table includes temperature ranging from 223 to 273 K, temperature -gradient ranging from 0 to 300 K m\ :sup:`-1`, and density ranging -from 50 to 400 kg m\ :sup:`-3`. Temperature gradient is calculated -at the midpoint of each snow layer *n*, using mid-layer temperatures -(:math:`T_{n}`) and snow layer thicknesses (:math:`dz_{n}`), as +The parameters :math:`{(\frac{dr_{e}}{dt}})_{0}`, :math:`\eta`, and :math:`\kappa` are retrieved interactively from a lookup table with dimensions corresponding to snow temperature, temperature gradient, and density. The domain covered by this lookup table includes temperature ranging from 223 to 273 K, temperature gradient ranging from 0 to 300 K m\ :sup:`-1`, and density ranging from 50 to 400 kg m\ :sup:`-3`. Temperature gradient is calculated at the midpoint of each snow layer *n*, using mid-layer temperatures (:math:`T_{n}`) and snow layer thicknesses (:math:`dz_{n}`), as .. math:: :label: 3.74 \left(\frac{dT}{dz} \right)_{n} =\frac{1}{dz_{n} } abs\left[\frac{T_{n-1} dz_{n} +T_{n} dz_{n-1} }{dz_{n} +dz_{n-1} } +\frac{T_{n+1} dz_{n} +T_{n} dz_{n+1} }{dz_{n} +dz_{n+1} } \right] -For the bottom snow layer (:math:`n=0`), -:math:`T_{n+1}` is taken as the temperature of the -top soil layer, and for the top snow layer it is assumed that -:math:`T_{n-1}` = :math:`T_{n}`. +For the bottom snow layer (:math:`n=0`), :math:`T_{n+1}` is taken as the temperature of the top soil layer, and for the top snow layer it is assumed that :math:`T_{n-1}` = :math:`T_{n}`. -The contribution of liquid water to enhanced metamorphism is based on -parametric equations published by :ref:`Brun (1989) `, who measured grain -growth rates under different liquid water contents. This relationship, -expressed in terms of :math:`r_{e} (\mu \text{m})` and -subtracting an offset due to dry aging, depends on the mass liquid water -fraction :math:`f_{liq}` as +The contribution of liquid water to enhanced metamorphism is based on parametric equations published by :ref:`Brun (1989) `, who measured grain growth rates under different liquid water contents. This relationship, expressed in terms of :math:`r_{e} (\mu \text{m})` and subtracting an offset due to dry aging, depends on the mass liquid water fraction :math:`f_{liq}` as .. math:: :label: 3.75 \frac{dr_{e} }{dt} =\frac{10^{18} C_{1} f_{liq} ^{3} }{4\pi r_{e} ^{2} } -The constant *C*\ :sub:`1` is 4.22\ :math:`\times`\ 10\ :sup:`-13`, and: -:math:`f_{liq} =w_{liq} /(w_{liq} +w_{ice} )`\ (Chapter :numref:`rst_Snow Hydrology`). +The constant *C*\ :sub:`1` is 4.22\ :math:`\times`\ 10\ :sup:`-13`, and: :math:`f_{liq} =w_{liq} /(w_{liq} +w_{ice} )`\ (Chapter :numref:`rst_Snow Hydrology`). -In cases where snow mass is greater than zero, but a snow layer has not -yet been defined, :math:`r_{e}` is set to :math:`r_{e,0}`. When snow layers are combined or -divided, :math:`r_{e}` is calculated as a mass-weighted mean of -the two layers, following computations of other state variables (section -:numref:`Snow Layer Combination and Subdivision`). Finally, the allowable range of :math:`r_{e}`, -corresponding to the range over which Mie optical properties have been -defined, is 30-1500\ :math:`\mu` m. +In cases where snow mass is greater than zero, but a snow layer has not yet been defined, :math:`r_{e}` is set to :math:`r_{e,0}`. When snow layers are combined or divided, :math:`r_{e}` is calculated as a mass-weighted mean of the two layers, following computations of other state variables (section :numref:`Snow Layer Combination and Subdivision`). Finally, the allowable range of :math:`r_{e}`, corresponding to the range over which Mie optical properties have been defined, is 30-1500\ :math:`\mu` m. .. _Solar Zenith Angle: Solar Zenith Angle ---------------------- -The CLM uses the same formulation for solar zenith angle as the -Community Atmosphere Model. The cosine of the solar zenith angle -:math:`\mu` is +The CLM uses the same formulation for solar zenith angle as the Community Atmosphere Model. The cosine of the solar zenith angle :math:`\mu` is .. math:: :label: 3.76 \mu =\sin \phi \sin \delta -\cos \phi \cos \delta \cos h -where :math:`h` is the solar hour angle (radians) (24 hour periodicity), -:math:`\delta` is the solar declination angle (radians), and -:math:`\phi` is latitude (radians) (positive in Northern Hemisphere). -The solar hour angle :math:`h` (radians) is +where :math:`h` is the solar hour angle (radians) (24 hour periodicity), :math:`\delta` is the solar declination angle (radians), and :math:`\phi` is latitude (radians) (positive in Northern Hemisphere). The solar hour angle :math:`h` (radians) is .. math:: :label: 3.77 h=2\pi d+\theta -where :math:`d` is calendar day (:math:`d=0.0` at 0Z on January 1), and -:math:`\theta` is longitude (radians) (positive east of the -Greenwich meridian). +where :math:`d` is calendar day (:math:`d=0.0` at 0Z on January 1), and :math:`\theta` is longitude (radians) (positive east of the Greenwich meridian). -The solar declination angle :math:`\delta` is calculated as in :ref:`Berger -(1978a,b) ` and is valid for one million years past or hence, relative to -1950 A.D. The orbital parameters may be specified directly or the -orbital parameters are calculated for the desired year. The required -orbital parameters to be input by the user are the obliquity of the -Earth :math:`\varepsilon` (degrees, -:math:`-90^{\circ } <\varepsilon <90^{\circ }` ), Earth’s eccentricity -:math:`e` (:math:`0.0`)). The -solar declination :math:`\delta` (radians) is +The solar declination angle :math:`\delta` is calculated as in :ref:`Berger (1978a,b) ` and is valid for one million years past or hence, relative to 1950 A.D. The orbital parameters may be specified directly or the orbital parameters are calculated for the desired year. The required orbital parameters to be input by the user are the obliquity of the Earth :math:`\varepsilon` (degrees, :math:`-90^{\circ } <\varepsilon <90^{\circ }` ), Earth's eccentricity :math:`e` (:math:`0.0`)). The solar declination :math:`\delta` (radians) is .. math:: :label: 3.78 \delta =\sin ^{-1} \left[\sin \left(\varepsilon \right)\sin \left(\lambda \right)\right] -where :math:`\varepsilon` is Earth’s obliquity and :math:`\lambda` is -the true longitude of the Earth. +where :math:`\varepsilon` is Earth's obliquity and :math:`\lambda` is the true longitude of the Earth. -The obliquity of the Earth :math:`\varepsilon` (degrees) is +The obliquity of the Earth :math:`\varepsilon` (degrees) is .. math:: :label: 3.79 \varepsilon =\varepsilon *+\sum _{i=1}^{i=47}A_{i} \cos \left(f_{i} t+\delta _{i} \right) -where :math:`\varepsilon *` is a constant of integration (:numref:`Table Orbital parameters`), -:math:`A_{i}` , :math:`f_{i}` , and :math:`\delta _{i}` are amplitude, -mean rate, and phase terms in the cosine series expansion (:ref:`Berger -(1978a,b) `, and :math:`t=t_{0} -1950` where :math:`t_{0}` is the year. -The series expansion terms are not shown here but can be found in the -source code file shr\_orb\_mod.F90. +where :math:`\varepsilon *` is a constant of integration (:numref:`Table Orbital parameters`), :math:`A_{i}`, :math:`f_{i}`, and :math:`\delta _{i}` are amplitude, mean rate, and phase terms in the cosine series expansion (:ref:`Berger (1978a,b) `, and :math:`t=t_{0} -1950` where :math:`t_{0}` is the year. The series expansion terms are not shown here but can be found in the source code file shr\_orb\_mod.F90. -The true longitude of the Earth :math:`\lambda` (radians) is counted -counterclockwise from the vernal equinox (:math:`\lambda =0` at the -vernal equinox) +The true longitude of the Earth :math:`\lambda` (radians) is counted counterclockwise from the vernal equinox (:math:`\lambda =0` at the vernal equinox) .. math:: :label: 3.80 \lambda =\lambda _{m} +\left(2e-\frac{1}{4} e^{3} \right)\sin \left(\lambda _{m} -\tilde{\omega }\right)+\frac{5}{4} e^{2} \sin 2\left(\lambda _{m} -\tilde{\omega }\right)+\frac{13}{12} e^{3} \sin 3\left(\lambda _{m} -\tilde{\omega }\right) -where :math:`\lambda _{m}` is the mean longitude of the Earth at the -vernal equinox, :math:`e` is Earth’s eccentricity, and -:math:`\tilde{\omega }` is the longitude of the perihelion relative to -the moving vernal equinox. The mean longitude :math:`\lambda _{m}` is +where :math:`\lambda _{m}` is the mean longitude of the Earth at the vernal equinox, :math:`e` is Earth's eccentricity, and :math:`\tilde{\omega }` is the longitude of the perihelion relative to the moving vernal equinox. The mean longitude :math:`\lambda _{m}` is .. math:: :label: 3.81 \lambda _{m} =\lambda _{m0} +\frac{2\pi \left(d-d_{ve} \right)}{365} -where :math:`d_{ve} =80.5` is the calendar day at vernal equinox (March -21 at noon), and +where :math:`d_{ve} =80.5` is the calendar day at vernal equinox (March 21 at noon), and .. math:: :label: 3.82 \lambda _{m0} =2\left[\left(\frac{1}{2} e+\frac{1}{8} e^{3} \right)\left(1+\beta \right)\sin \tilde{\omega }-\frac{1}{4} e^{2} \left(\frac{1}{2} +\beta \right)\sin 2\tilde{\omega }+\frac{1}{8} e^{3} \left(\frac{1}{3} +\beta \right)\sin 3\tilde{\omega }\right] -where :math:`\beta =\sqrt{1-e^{2} }` . Earth’s eccentricity :math:`e` -is +where :math:`\beta =\sqrt{1-e^{2} }`. Earth's eccentricity :math:`e` is .. math:: :label: 3.83 @@ -1135,54 +806,35 @@ where \begin{array}{l} {e^{\cos } =\sum _{j=1}^{19}M_{j} \cos \left(g_{j} t+B_{j} \right) ,} \\ {e^{\sin } =\sum _{j=1}^{19}M_{j} \sin \left(g_{j} t+B_{j} \right) } \end{array} -are the cosine and sine series expansions for :math:`e`, and -:math:`M_{j}` , :math:`g_{j}` , and :math:`B_{j}` are amplitude, mean -rate, and phase terms in the series expansions (:ref:`Berger (1978a,b) `). The -longitude of the perihelion relative to the moving vernal equinox -:math:`\tilde{\omega }` (degrees) is +are the cosine and sine series expansions for :math:`e`, and :math:`M_{j}`, :math:`g_{j}`, and :math:`B_{j}` are amplitude, mean rate, and phase terms in the series expansions (:ref:`Berger (1978a,b) `). The longitude of the perihelion relative to the moving vernal equinox :math:`\tilde{\omega }` (degrees) is .. math:: :label: 3.85 \tilde{\omega }=\Pi \frac{180}{\pi } +\psi -where :math:`\Pi` is the longitude of the perihelion measured from the -reference vernal equinox (i.e., the vernal equinox at 1950 A.D.) and -describes the absolute motion of the perihelion relative to the fixed -stars, and :math:`\psi` is the annual general precession in longitude -and describes the absolute motion of the vernal equinox along Earth’s -orbit relative to the fixed stars. The general precession :math:`\psi` -(degrees) is +where :math:`\Pi` is the longitude of the perihelion measured from the reference vernal equinox (i.e., the vernal equinox at 1950 A.D.) and describes the absolute motion of the perihelion relative to the fixed stars, and :math:`\psi` is the annual general precession in longitude and describes the absolute motion of the vernal equinox along Earth's orbit relative to the fixed stars. The general precession :math:`\psi` (degrees) is .. math:: :label: 3.86 \psi =\frac{\tilde{\psi }t}{3600} +\zeta +\sum _{i=1}^{78}F_{i} \sin \left(f_{i} ^{{'} } t+\delta _{i} ^{{'} } \right) -where :math:`\tilde{\psi }` (arcseconds) and :math:`\zeta` (degrees) -are constants (:numref:`Table Orbital parameters`), and :math:`F_{i}` , :math:`f_{i} ^{{'} }` , -and :math:`\delta _{i} ^{{'} }` are amplitude, mean rate, and phase -terms in the sine series expansion (:ref:`Berger (1978a,b) `)). The longitude of -the perihelion :math:`\Pi` (radians) depends on the sine and cosine -series expansions for the eccentricity :math:`e`\ as follows: +where :math:`\tilde{\psi }` (arcseconds) and :math:`\zeta` (degrees) are constants (:numref:`Table Orbital parameters`), and :math:`F_{i}`, :math:`f_{i} ^{{'} }`, and :math:`\delta _{i} ^{{'} }` are amplitude, mean rate, and phase terms in the sine series expansion (:ref:`Berger (1978a,b) `)). The longitude of the perihelion :math:`\Pi` (radians) depends on the sine and cosine series expansions for the eccentricity :math:`e`\ as follows: .. math:: :label: 3.87 - \Pi =\left\{\begin{array}{lr} - 0 & \qquad {\rm for\; -1}\times {\rm 10}^{{\rm -8}} \le e^{\cos } \le 1\times 10^{-8} {\rm \; and\; }e^{\sin } = 0 \\ - 1.5\pi & \qquad {\rm for\; -1}\times {\rm 10}^{{\rm -8}} \le e^{\cos } \le 1\times 10^{-8} {\rm \; and\; }e^{\sin } < 0 \\ - 0.5\pi & \qquad {\rm for\; -1}\times {\rm 10}^{{\rm -8}} \le e^{\cos } \le 1\times 10^{-8} {\rm \; and\; }e^{\sin } > 0 \\ - \tan ^{-1} \left[\frac{e^{\sin } }{e^{\cos } } \right]+\pi & \qquad {\rm for\; }e^{\cos } <{\rm -1}\times {\rm 10}^{{\rm -8}} \\ - \tan ^{-1} \left[\frac{e^{\sin } }{e^{\cos } } \right]+2\pi & \qquad {\rm for\; }e^{\cos } >{\rm 1}\times {\rm 10}^{{\rm -8}} {\rm \; and\; }e^{\sin } <0 \\ - \tan ^{-1} \left[\frac{e^{\sin } }{e^{\cos } } \right] & \qquad {\rm for\; }e^{\cos } >{\rm 1}\times {\rm 10}^{{\rm -8}} {\rm \; and\; }e^{\sin } \ge 0 + \Pi =\left\{\begin{array}{lr} + 0 & \qquad {\rm for\; -1}\times {\rm 10}^{{\rm -8}} \le e^{\cos } \le 1\times 10^{-8} {\rm \; and\; }e^{\sin } = 0 \\ + 1.5\pi & \qquad {\rm for\; -1}\times {\rm 10}^{{\rm -8}} \le e^{\cos } \le 1\times 10^{-8} {\rm \; and\; }e^{\sin } < 0 \\ + 0.5\pi & \qquad {\rm for\; -1}\times {\rm 10}^{{\rm -8}} \le e^{\cos } \le 1\times 10^{-8} {\rm \; and\; }e^{\sin } > 0 \\ + \tan ^{-1} \left[\frac{e^{\sin } }{e^{\cos } } \right]+\pi & \qquad {\rm for\; }e^{\cos } <{\rm -1}\times {\rm 10}^{{\rm -8}} \\ + \tan ^{-1} \left[\frac{e^{\sin } }{e^{\cos } } \right]+2\pi & \qquad {\rm for\; }e^{\cos } >{\rm 1}\times {\rm 10}^{{\rm -8}} {\rm \; and\; }e^{\sin } <0 \\ + \tan ^{-1} \left[\frac{e^{\sin } }{e^{\cos } } \right] & \qquad {\rm for\; }e^{\cos } >{\rm 1}\times {\rm 10}^{{\rm -8}} {\rm \; and\; }e^{\sin } \ge 0 \end{array}\right\}. -The numerical solution for the longitude of the perihelion -:math:`\tilde{\omega }` is constrained to be between 0 and 360 degrees -(measured from the autumn equinox). A constant 180 degrees is then added -to :math:`\tilde{\omega }` because the Sun is considered as revolving -around the Earth (geocentric coordinate system) (:ref:`Berger et al. 1993 `)). +The numerical solution for the longitude of the perihelion :math:`\tilde{\omega }` is constrained to be between 0 and 360 degrees (measured from the autumn equinox). A constant 180 degrees is then added to :math:`\tilde{\omega }` because the Sun is considered as revolving around the Earth (geocentric coordinate system) (:ref:`Berger et al. 1993 `)). .. _Table Orbital parameters: diff --git a/doc/source/tech_note/Transient_Landcover/CLM50_Tech_Note_Transient_Landcover.rst b/doc/source/tech_note/Transient_Landcover/CLM50_Tech_Note_Transient_Landcover.rst index de7ef86173..c221a14d28 100644 --- a/doc/source/tech_note/Transient_Landcover/CLM50_Tech_Note_Transient_Landcover.rst +++ b/doc/source/tech_note/Transient_Landcover/CLM50_Tech_Note_Transient_Landcover.rst @@ -3,116 +3,35 @@ Transient Land Use and Land Cover Change ======================================== -CLM includes a treatment of mass and energy fluxes associated with -prescribed temporal land use and land cover change (LULCC). The model -uses an annual time series of the spatial distribution of the natural -and crop land units of each grid cell, in combination with the -distribution of PFTs and CFTs that exist in those land units. Additional -land use is prescribed through annual crop-specific management of -nitrogen fertilizer and irrigation (described further in -:numref:`rst_Crops and Irrigation`), and through wood harvest on tree -PFTs. For changes in the distributions of natural and crop vegetation, -CLM diagnoses the change in area of the PFTs and CFTs on January 1 of -each model year and then performs mass and energy balance accounting -necessary to represent the expansion and contraction of the PFT and CFT -areas. The biogeophysical impacts of LULCC are simulated through changes -in surface properties which in turn impact the surface albedo, -hydrology, and roughness which then impact fluxes of energy, moisture -and momentum to the atmosphere under the altered -properties. Additionally, changes in energy and moisture associated with -changes in the natural and crop vegetation distribution are accounted -for through small fluxes to the river and atmosphere. The biogeochemical -impacts of LULCC are simulated through changes in CLM carbon pools and -fluxes (see also Chapter :numref:`rst_CN Pools`). - -CLM can also respond to changes in ice sheet areas and elevations when -it is coupled to an evolving ice sheet model (in the CESM context, this -is the Community Ice Sheet Model, CISM; see also Chapter -:numref:`rst_Glaciers`). Conservation of water, energy, carbon and -nitrogen is handled similarly for glacier-vegetation transitions as for -natural vegetation-crop transitions. +CLM includes a treatment of mass and energy fluxes associated with prescribed temporal land use and land cover change (LULCC). The model uses an annual time series of the spatial distribution of the natural and crop land units of each grid cell, in combination with the distribution of PFTs and CFTs that exist in those land units. Additional land use is prescribed through annual crop-specific management of nitrogen fertilizer and irrigation (described further in :numref:`rst_Crops and Irrigation`), and through wood harvest on tree PFTs. For changes in the distributions of natural and crop vegetation, CLM diagnoses the change in area of the PFTs and CFTs on January 1 of each model year and then performs mass and energy balance accounting necessary to represent the expansion and contraction of the PFT and CFT areas. The biogeophysical impacts of LULCC are simulated through changes in surface properties which in turn impact the surface albedo, hydrology, and roughness which then impact fluxes of energy, moisture and momentum to the atmosphere under the altered properties. Additionally, changes in energy and moisture associated with changes in the natural and crop vegetation distribution are accounted for through small fluxes to the river and atmosphere. The biogeochemical impacts of LULCC are simulated through changes in CLM carbon pools and fluxes (see also Chapter :numref:`rst_CN Pools`). + +CLM can also respond to changes in ice sheet areas and elevations when it is coupled to an evolving ice sheet model (in the CESM context, this is the Community Ice Sheet Model, CISM; see also Chapter :numref:`rst_Glaciers`). Conservation of water, energy, carbon and nitrogen is handled similarly for glacier-vegetation transitions as for natural vegetation-crop transitions. .. _Transient land use and land cover data: Annual Transient Land Use and Land Cover Data --------------------------------------------- -The changes in area over time associated with changes in natural and crop vegetation and -the land use on that vegetation are prescribed through a forcing dataset, referred to here -as the *landuse.timeseries* dataset. The *landuse.timeseries* dataset consists of an -annual time series of global grids, where each annual time slice describes the fractional -area occupied by all PFTs and CFTs along with the nitrogen fertilizer and irrigation -fraction of each crop CFT, and the annual wood harvest applied to tree PFTs. Changes in -area of PFTs and CFTs are performed annually on the first time step of January 1 of the -year. Wood harvest for each PFT is also performed on the first time step of the -year. Fertilizer application and irrigation for each CFT are performed at each model time -step depending on rules from the crop model. Fertilizer application rates are set -annually. The irrigation fraction is also set annually; irrigated crops are placed on -separate columns from their unirrigated counterparts, so changes in irrigated fraction -triggers the changes in subgrid areas discussed below (sections :numref:`Transient -landcover reconciling changes in area` and :numref:`Transient landcover mass and energy -conservation`). - -As a special case, when the time dimension of the *landuse.timeseries* dataset starts at a -later year than the current model time step, the first time slice from the -*landuse.timeseries* dataset is used to represent the current time step PFT and CFT -fractional area distributions. Similarly, when the time dimension of the -*landuse.timeseries* dataset stops at an earlier year than the current model time step, -the last time slice of the *landuse.timeseries* dataset is used. Thus, the simulation will -have invariant representations of PFT and CFT distributions through time for the periods -prior to and following the time duration of the *landuse.timeseries* dataset, with -transient PFT and CFT distributions during the period covered by the *landuse.timeseries* -dataset. +The changes in area over time associated with changes in natural and crop vegetation and the land use on that vegetation are prescribed through a forcing dataset, referred to here as the *landuse.timeseries* dataset. The *landuse.timeseries* dataset consists of an annual time series of global grids, where each annual time slice describes the fractional area occupied by all PFTs and CFTs along with the nitrogen fertilizer and irrigation fraction of each crop CFT, and the annual wood harvest applied to tree PFTs. Changes in area of PFTs and CFTs are performed annually on the first time step of January 1 of the year. Wood harvest for each PFT is also performed on the first time step of the year. Fertilizer application and irrigation for each CFT are performed at each model time step depending on rules from the crop model. Fertilizer application rates are set annually. The irrigation fraction is also set annually; irrigated crops are placed on separate columns from their unirrigated counterparts, so changes in irrigated fraction triggers the changes in subgrid areas discussed below (sections :numref:`Transient landcover reconciling changes in area` and :numref:`Transient landcover mass and energy conservation`). + +As a special case, when the time dimension of the *landuse.timeseries* dataset starts at a later year than the current model time step, the first time slice from the *landuse.timeseries* dataset is used to represent the current time step PFT and CFT fractional area distributions. Similarly, when the time dimension of the *landuse.timeseries* dataset stops at an earlier year than the current model time step, the last time slice of the *landuse.timeseries* dataset is used. Thus, the simulation will have invariant representations of PFT and CFT distributions through time for the periods prior to and following the time duration of the *landuse.timeseries* dataset, with transient PFT and CFT distributions during the period covered by the *landuse.timeseries* dataset. .. _Transient landcover reconciling changes in area: Reconciling Changes in Area --------------------------- -In the first time step of January 1, changes in land unit weights can -potentially come from two sources: Changes in the area of the crop land -unit come from the *landuse.timeseries* dataset (section -:numref:`Transient land use and land cover data`), and changes in the -area of the glacier land unit come from the ice sheet model. The areas -of other land units are then adjusted so that the total land unit area -remains 100%. - -If the total land unit area of glaciers and crops has decreased, then -the natural vegetated landunit is increased to fill in the abandoned -land. If the total land unit area of glaciers and crops has increased, -then other land unit areas are decreased in a specified order until the -total is once again 100%. The order of decrease is: natural vegetation, -crop, urban medium density, urban high density, urban tall building -district, wetland, lake. +In the first time step of January 1, changes in land unit weights can potentially come from two sources: Changes in the area of the crop land unit come from the *landuse.timeseries* dataset (section :numref:`Transient land use and land cover data`), and changes in the area of the glacier land unit come from the ice sheet model. The areas of other land units are then adjusted so that the total land unit area remains 100%. + +If the total land unit area of glaciers and crops has decreased, then the natural vegetated landunit is increased to fill in the abandoned land. If the total land unit area of glaciers and crops has increased, then other land unit areas are decreased in a specified order until the total is once again 100%. The order of decrease is: natural vegetation, crop, urban medium density, urban high density, urban tall building district, wetland, lake. These rules have two important implications: -1. We always match CISM's glacier areas exactly, even if that means a - disagreement with prescribed crop areas. This is needed for - conservation when CISM is evolving in two-way-coupled mode. - -2. For land units other than crop, glacier and natural vegetation, their - areas can decrease (due to encroaching crops or glaciers), but can - never increase. So, for example, if a grid cell starts as 5% lake, - crops expand to fill the entire grid cell, then later crop area - decreases, the lake area will not return: instead, the abandoned - cropland will become entirely natural vegetation. - -For all levels of the subgrid hierarchy (land unit, column and patch), -we only track net changes in area, not gross transitions. So, for -example, if part of a gridcell experiences an increase in glacier area -while another part of that gridcell experiences an equal decrease in -glacier area (in the same glacier elevation class), CLM acts as if there -were no changes. As another example, consider a gridcell containing -natural vegetation, crop and glacier. If there is a decrease in glacier -area and an equal increase in crop area, CLM will assume that the crop -expands into the old glacier area, and nothing happened to the natural -vegetation area. A more realistic alternative would be that the crop -expanded into natural vegetation, and natural vegetation expanded into -glacier. The final areas will be correct in these cases, but the -adjustments of carbon and nitrogen states (section :numref:`Transient -landcover carbon and nitrogen conservation`) will be less accurate than what -would be obtained with a full tracking of gross transitions. +1. We always match CISM's glacier areas exactly, even if that means a disagreement with prescribed crop areas. This is needed for conservation when CISM is evolving in two-way-coupled mode. + +2. For land units other than crop, glacier and natural vegetation, their areas can decrease (due to encroaching crops or glaciers), but can never increase. So, for example, if a grid cell starts as 5% lake, crops expand to fill the entire grid cell, then later crop area decreases, the lake area will not return: instead, the abandoned cropland will become entirely natural vegetation. + +For all levels of the subgrid hierarchy (land unit, column and patch), we only track net changes in area, not gross transitions. So, for example, if part of a gridcell experiences an increase in glacier area while another part of that gridcell experiences an equal decrease in glacier area (in the same glacier elevation class), CLM acts as if there were no changes. As another example, consider a gridcell containing natural vegetation, crop and glacier. If there is a decrease in glacier area and an equal increase in crop area, CLM will assume that the crop expands into the old glacier area, and nothing happened to the natural vegetation area. A more realistic alternative would be that the crop expanded into natural vegetation, and natural vegetation expanded into glacier. The final areas will be correct in these cases, but the adjustments of carbon and nitrogen states (section :numref:`Transient landcover carbon and nitrogen conservation`) will be less accurate than what would be obtained with a full tracking of gross transitions. .. _Transient landcover mass and energy conservation: @@ -124,149 +43,48 @@ Mass and Energy Conservation Water and Energy Conservation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When subgrid areas change, the water and energy states remain unchanged -on a per-area basis. This can lead to changes in the total gridcell -water and energy content. - -For example, consider a gridcell with two columns: column 1 has a water -mass of 1 kg m\ :sup:`-2` and column 2 has a water mass of 2 kg m\ -:sup:`-2` for a given water state variable, where these are expressed -per unit column area. If column 1 increases in area at the expense of -column 2, then column 1 will still have a water mass of 1 kg m\ -:sup:`-2`, but now expressed over the new column area. This results in a -decrease in the total gridcell water content. - -Water and energy are conserved by summing up the total water and energy -content of each gridcell before and after a change in area. Differences -in liquid and ice water content are balanced by liquid and ice runoff -terms, which can be either positive or negative. (Negative runoff is -effectively a withdrawal of water from the ocean.) Differences in energy -content are balanced by a sensible heat flux term, which again can be -either positive or negative. These balancing fluxes are spread evenly -throughout the following year. - -There is a special case when a given crop column type newly comes into -existence - for example, when temperate corn first comes into existence -in a gridcell. In this case, the column's below-ground temperature and -water states are copied from the natural vegetated column in its -gridcell, so that these state variables begin in a close-to-spun-up -state. Other state variables (most of which spin up relatively quickly) -begin at their cold start initialization values. This initialization is -not necessary for the two other land unit types that currently can -grow - natural vegetation and glacier: Those land unit types are always -active, even when they have zero area on the gridcell, so their state -variables will be spun up immediately when they come into -existence. After this initialization, the conservation code described -above takes effect. +When subgrid areas change, the water and energy states remain unchanged on a per-area basis. This can lead to changes in the total gridcell water and energy content. + +For example, consider a gridcell with two columns: column 1 has a water mass of 1 kg m\ :sup:`-2` and column 2 has a water mass of 2 kg m\ :sup:`-2` for a given water state variable, where these are expressed per unit column area. If column 1 increases in area at the expense of column 2, then column 1 will still have a water mass of 1 kg m\ :sup:`-2`, but now expressed over the new column area. This results in a decrease in the total gridcell water content. + +Water and energy are conserved by summing up the total water and energy content of each gridcell before and after a change in area. Differences in liquid and ice water content are balanced by liquid and ice runoff terms, which can be either positive or negative. (Negative runoff is effectively a withdrawal of water from the ocean.) Differences in energy content are balanced by a sensible heat flux term, which again can be either positive or negative. These balancing fluxes are spread evenly throughout the following year. + +There is a special case when a given crop column type newly comes into existence - for example, when temperate corn first comes into existence in a gridcell. In this case, the column's below-ground temperature and water states are copied from the natural vegetated column in its gridcell, so that these state variables begin in a close-to-spun-up state. Other state variables (most of which spin up relatively quickly) begin at their cold start initialization values. This initialization is not necessary for the two other land unit types that currently can grow - natural vegetation and glacier: Those land unit types are always active, even when they have zero area on the gridcell, so their state variables will be spun up immediately when they come into existence. After this initialization, the conservation code described above takes effect. .. _Transient landcover carbon and nitrogen conservation: Carbon and Nitrogen Conservation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Because of the long timescales involved with below-ground carbon and -nitrogen dynamics, it is more important that these state variables be -adjusted properly when subgrid areas change. Carbon and nitrogen -variables are adjusted with the following three-step process: - -(1) Patch-level (i.e., vegetation) state variables are adjusted for any - changes in patch areas; this may lead to fluxes into column-level - (i.e., soil) state variables - -(2) Column-level (i.e., soil) state variables are updated based on the - fluxes generated in (1) - -(3) Column-level (i.e., soil) state variables are adjusted for any - changes in column areas - -First, patch-level (i.e., vegetation) state variables are adjusted for -any changes in patch areas. This includes changes in column or land unit -areas, even if the relative proportions of each patch remain constant: -the relevant quantities are the patch weights relative to the -gridcell. - -For a patch that decreases in area, the carbon and nitrogen density on -the remaining patch area remains the same as before (i.e., expressed as -g per m\ :sup:`2` patch area). Because the area has decreased, this -represents a decrease in total carbon or nitrogen mass (i.e., expressed -as g per m\ :sup:`2` gridcell area). The lost mass meets a variety of -fates: some is immediately lost to the atmosphere, some is sent to -product pools (which are lost to the atmosphere over longer time -scales), and some is sent to litter pools. - -For a patch that increases in area, the carbon and nitrogen density on -the new patch area is decreased in order to conserve mass. This decrease -is basically proportional to the relative increase in patch -area. However, a small amount of seed carbon and nitrogen is added to -the leaf and dead stem pools in the new patch area. - -Next, column-level (i.e., soil) state variables are updated based on any -fluxes to soil pools due to decreases in patch areas. This step is -needed so that any lost vegetation carbon and nitrogen is conserved when -column areas are changing. - -Finally, column-level state variables are adjusted for any changes in -column areas. Similarly to patches, for a column that decreases in area, -the carbon and nitrogen density on the remaining column area remains the -same as before (i.e., expressed as g per m\ :sup:`2` column area). This -represents a decrease in total carbon or nitrogen mass on the gridcell, -and this lost mass is tracked for each gridcell. After these mass losses -are summed for all shrinking columns, they are distributed amongst the -growing columns in order to conserve mass. Thus, a growing column's new -carbon density will be a weighted sum of its original carbon density and -the carbon densities of all shrinking columns in its gridcell. - -This operation makes some simplifying assumptions. First, as described -in section :numref:`Transient landcover reconciling changes in area`, we -only track net area changes, not gross changes. Second, we assume that -growing columns all grow proportionally into each of the shrinking -columns. - -Non-vegetated land units (e.g., glacier) do not typically track soil -carbon and nitrogen. When columns from these land units initially -shrink, they are assumed to contribute zero carbon and -nitrogen. However, when they grow into previously-vegetated areas, they -store any pre-existing soil carbon and nitrogen from the shrinking -columns. This stored carbon and nitrogen will remain unchanged until the -column later shrinks, at which point it will contribute to the carbon -and nitrogen in the growing columns (exactly as would happen for a -vegetated column). - -In contrast to water and energy (section :numref:`Transient landcover -water and energy conservation`), no special treatment is needed for -carbon and nitrogen states in columns that newly come into -existence. The state of a new column is derived from a weighted average -of the states of shrinking columns. This behavior falls out from the -above general rules. +Because of the long timescales involved with below-ground carbon and nitrogen dynamics, it is more important that these state variables be adjusted properly when subgrid areas change. Carbon and nitrogen variables are adjusted with the following three-step process: + +(1) Patch-level (i.e., vegetation) state variables are adjusted for any changes in patch areas; this may lead to fluxes into column-level (i.e., soil) state variables (2) Column-level (i.e., soil) state variables are updated based on the fluxes generated in (1) + +(3) Column-level (i.e., soil) state variables are adjusted for any changes in column areas First, patch-level (i.e., vegetation) state variables are adjusted for any changes in patch areas. This includes changes in column or land unit areas, even if the relative proportions of each patch remain constant: the relevant quantities are the patch weights relative to the gridcell. + +For a patch that decreases in area, the carbon and nitrogen density on the remaining patch area remains the same as before (i.e., expressed as g per m\ :sup:`2` patch area). Because the area has decreased, this represents a decrease in total carbon or nitrogen mass (i.e., expressed as g per m\ :sup:`2` gridcell area). The lost mass meets a variety of fates: some is immediately lost to the atmosphere, some is sent to product pools (which are lost to the atmosphere over longer time scales), and some is sent to litter pools. + +For a patch that increases in area, the carbon and nitrogen density on the new patch area is decreased in order to conserve mass. This decrease is basically proportional to the relative increase in patch area. However, a small amount of seed carbon and nitrogen is added to the leaf and dead stem pools in the new patch area. + +Next, column-level (i.e., soil) state variables are updated based on any fluxes to soil pools due to decreases in patch areas. This step is needed so that any lost vegetation carbon and nitrogen is conserved when column areas are changing. + +Finally, column-level state variables are adjusted for any changes in column areas. Similarly to patches, for a column that decreases in area, the carbon and nitrogen density on the remaining column area remains the same as before (i.e., expressed as g per m\ :sup:`2` column area). This represents a decrease in total carbon or nitrogen mass on the gridcell, and this lost mass is tracked for each gridcell. After these mass losses are summed for all shrinking columns, they are distributed amongst the growing columns in order to conserve mass. Thus, a growing column's new carbon density will be a weighted sum of its original carbon density and the carbon densities of all shrinking columns in its gridcell. + +This operation makes some simplifying assumptions. First, as described in section :numref:`Transient landcover reconciling changes in area`, we only track net area changes, not gross changes. Second, we assume that growing columns all grow proportionally into each of the shrinking columns. + +Non-vegetated land units (e.g., glacier) do not typically track soil carbon and nitrogen. When columns from these land units initially shrink, they are assumed to contribute zero carbon and nitrogen. However, when they grow into previously-vegetated areas, they store any pre-existing soil carbon and nitrogen from the shrinking columns. This stored carbon and nitrogen will remain unchanged until the column later shrinks, at which point it will contribute to the carbon and nitrogen in the growing columns (exactly as would happen for a vegetated column). + +In contrast to water and energy (section :numref:`Transient landcover water and energy conservation`), no special treatment is needed for carbon and nitrogen states in columns that newly come into existence. The state of a new column is derived from a weighted average of the states of shrinking columns. This behavior falls out from the above general rules. Annual Transient Land Cover Dataset Development ---------------------------------------------------- -This section describes the development of the *landuse.timeseries* dataset. -Development of this dataset involves the translation of -harmonized datasets of LULCC for the historical period and -for the different Shared Socioeconomic Pathway (SSP) - Representative -Concentration Pathway (RCP) scenarios. Additionally, LULCC time -series are to be generated for the Last Millennium and the extension beyond 2100 experiments -of CMIP6. +This section describes the development of the *landuse.timeseries* dataset. Development of this dataset involves the translation of harmonized datasets of LULCC for the historical period and for the different Shared Socioeconomic Pathway (SSP) - Representative Concentration Pathway (RCP) scenarios. Additionally, LULCC time series are to be generated for the Last Millennium and the extension beyond 2100 experiments of CMIP6. LUH2 Transient Land Use and Land Cover Change Dataset ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To coordinate the processing and consistency of LULCC data between -the historical period (1850-2015) and the six -SSP-RCP (2016-2100) scenarios derived from Integrated -Assessment Models (IAM), the University of Maryland and the University of New Hampshire -research groups (Louise Chini, George Hurtt, Steve -Frolking and Ritvik Sahajpal; luh.umd.edu) produced a new version of the Land Use Harmonized version 2 -(LUH2) transient datasets for use with Earth System Model simulations. The new data sets -are the product of the Land Use Model Intercomparison Project (LUMIP; https://cmip.ucar.edu/lumip) -as part of the Coupled Model Intercomparison Project 6 (CMIP6). The historical component of the -transient LULCC dataset has agriculture and urban -land use based on HYDE 3.2 with wood harvest based on FAO, Landsat and other sources, for the period 850-2015. -The SSP-RCP transient LULCC components (2015-2100) are -referred to as the LUH2 Future Scenario datasets. The LULCC information is provided at 0.25 degree grid resolution and includes -fractional grid cell coverage by the 12 land units of: +To coordinate the processing and consistency of LULCC data between the historical period (1850-2015) and the six SSP-RCP (2016-2100) scenarios derived from Integrated Assessment Models (IAM), the University of Maryland and the University of New Hampshire research groups (Louise Chini, George Hurtt, Steve Frolking and Ritvik Sahajpal; luh.umd.edu) produced a new version of the Land Use Harmonized version 2 (LUH2) transient datasets for use with Earth System Model simulations. The new data sets are the product of the Land Use Model Intercomparison Project (LUMIP; https://cmip.ucar.edu/lumip) as part of the Coupled Model Intercomparison Project 6 (CMIP6). The historical component of the transient LULCC dataset has agriculture and urban land use based on HYDE 3.2 with wood harvest based on FAO, Landsat and other sources, for the period 850-2015. The SSP-RCP transient LULCC components (2015-2100) are referred to as the LUH2 Future Scenario datasets. The LULCC information is provided at 0.25 degree grid resolution and includes fractional grid cell coverage by the 12 land units of: Primary Forest, Secondary Forest, Primary Non-Forest, Secondary Non-Forest, @@ -274,76 +92,24 @@ Pasture, Rangeland, Urban, C3 Annual Crop, C4 Annual Crop, C3 Perennial Crop, C4 Perennial Crop, and C3 Nitrogen Fixing Crop. -The new land unit format is an improvement on the CMIP5 LULCC -datasets as they: provide Forest and Non Forest information in combination with Primary and Secondary -land; differentiate between Pasture and Rangelands for grazing livestock; and specify annual details -on the types of Crops grown and management practices applied in each grid cell. Like the CMIP5 LULCC datasets Primary vegetation -represents the fractional area of a grid cell with vegetation undisturbed by human activities. Secondary -vegetation represents vegetated areas that have recovered from some human disturbance; this could include -re-vegetation of pasture and crop areas as well as primary vegetation areas that have been logged. -In this manner the land units can change through deforestation from Forested to Non Forested land and in the -opposite direction from Non Forested to Forested land through reforestation or afforestation without going -through the Crop, Pasture or Rangeland states. - -The LUH2 dataset provides a time series of land cover states as well as a transition matrices that describes -the annual fraction of land that is transformed from one land unit category to -another (e.g. Primary Forest to C3 Annual Crop, Pasture to C3 Perrenial Crop, etc.; Lawrence et al. -2016). Included in these transition matrices is the total conversion of one land cover type to another referred to -as Gross LULCC. This value can be larger than the sum of the changes in the state of a land unit from one time period -to the next known as the Net LULCC. This difference is possible as land unit changes can occur both from the land unit -and to the land unit at the same time. An example of this difference occurs with shifting cultivation where Secondary Forest -can be converted to C3 Annual Crop at the same time as C3 Annual Crop is abandoned to Secondary Forest. - -The transition matrices also provide harmonized prescriptions of wood harvest both in area of the grid cell harvested -and in the amount of biomass carbon harvested. The wood harvest biomass amount includes a 30% slash component inline with -the CMIP5 LULCC data described in (Hurtt et al. 2011). The harvest area and carbon amounts are prescribed for the five classes of: -Primary Forest, Primary Non-Forest, -Secondary Mature Forest, Secondary -Young Forest, and Secondary -Non-Forest. - -Additional land use management is prescribed on the Crop land units for -nitrogen fertilization and irrigation equipped land. The fertilizer application and the the irrigation fraction is -prescribed for each Crop land unit in a grid cell individually for each year of the time series. The wood harvest -and crop management are both prescribed spatially on the same 0.25 degree grid as the land use class transitions. +The new land unit format is an improvement on the CMIP5 LULCC datasets as they: provide Forest and Non Forest information in combination with Primary and Secondary land; differentiate between Pasture and Rangelands for grazing livestock; and specify annual details on the types of Crops grown and management practices applied in each grid cell. Like the CMIP5 LULCC datasets Primary vegetation represents the fractional area of a grid cell with vegetation undisturbed by human activities. Secondary vegetation represents vegetated areas that have recovered from some human disturbance; this could include re-vegetation of pasture and crop areas as well as primary vegetation areas that have been logged. In this manner the land units can change through deforestation from Forested to Non Forested land and in the opposite direction from Non Forested to Forested land through reforestation or afforestation without going through the Crop, Pasture or Rangeland states. + +The LUH2 dataset provides a time series of land cover states as well as a transition matrices that describes the annual fraction of land that is transformed from one land unit category to another (e.g. Primary Forest to C3 Annual Crop, Pasture to C3 Perrenial Crop, etc.; Lawrence et al. 2016). Included in these transition matrices is the total conversion of one land cover type to another referred to as Gross LULCC. This value can be larger than the sum of the changes in the state of a land unit from one time period to the next known as the Net LULCC. This difference is possible as land unit changes can occur both from the land unit and to the land unit at the same time. An example of this difference occurs with shifting cultivation where Secondary Forest can be converted to C3 Annual Crop at the same time as C3 Annual Crop is abandoned to Secondary Forest. + +The transition matrices also provide harmonized prescriptions of wood harvest both in area of the grid cell harvested and in the amount of biomass carbon harvested. The wood harvest biomass amount includes a 30% slash component inline with the CMIP5 LULCC data described in (Hurtt et al. 2011). The harvest area and carbon amounts are prescribed for the five classes of: Primary Forest, Primary Non-Forest, Secondary Mature Forest, Secondary Young Forest, and Secondary Non-Forest. + +Additional land use management is prescribed on the Crop land units for nitrogen fertilization and irrigation equipped land. The fertilizer application and the the irrigation fraction is prescribed for each Crop land unit in a grid cell individually for each year of the time series. The wood harvest and crop management are both prescribed spatially on the same 0.25 degree grid as the land use class transitions. Representing LUH2 Land Use and Land Cover Change in CLM5 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To represent the LUH2 transient LULCC dataset in CLM5, the annual fractional -composition of the twelve land units specified in the dataset needs to be -faithfully represented with a corresponding PFT and CFT mosaics of CLM. -CLM5 represents the land surface as a hierarchy of sub-grid types: -glacier; lake; urban; vegetated land; and crop land. The vegetated land is -further divided into a mosaic of Plant Functional Types (PFTs), while the crop land -is divided into a mosaic of Crop Functional Types (CFTs). - -To support this translation task the CLM5 Land Use Data tool has been built that extends the -methods described in Lawrence et al (2012) to include all the new functionality of CMIP6 and CLM5 LULCC. -The tool translates each of the LUH2 land units for a given year into fractional PFT and CFT values based on -the current day CLM5 data for the land unit in that grid cell. The current day land unit descriptions are generated from -from 1km resolution MODIS, MIRCA2000, ICESAT, AVHRR, SRTM, and CRU climate data products combined with reference year -LUH2 land unit data, usually set to 2005. Where the land unit does not exist in a grid cell for the current -day, the land unit description is generated from nearest neighbors with an inverse distance weighted search -algorithm. - -The Land Use Data tool produces raw vegetation, crop, and management data files which are combined with -other raw land surface data to produce the CLM5 initial surface dataset and the dynamic -*landuse.timeseries* dataset with the CLM5 mksurfdata_map tool. The schematic of this entire process from -LUH2 time series and high resolution current day data to the output of CLM5 surface datasets from the -mksurfdata_map tool is shown in Figure 21.2. - -The methodology for creating the CLM5 transient PFT and CFT dataset is based on four -steps which are applied across all of the historical and future time series. -The first step involves generating the current day descriptions of natural and managed vegetation PFTs at -1km resolution from the global source datasets, and the current day description of crop CFTs at the 10km resolution -from the MIRCA 2000 datasets. The second step combines the current day (2005) LUH2 land units with the current -day CLM5 PFT and CFT distributions to get CLM5 land unit descriptions in either PFTs or CFTs at the LUH2 resolution of -0.25 degrees. The third step involves combining the LUH2 land unit time series with the CLM5 PFT and CFT descriptions -for that land unit to generate the CLM5 raw PFT and CFT time series in the *landuse.timeseries* file. At this point in the process -management information in terms of fertilizer, irrigation and wood harvest are added to the CLM5 PFT and CFT data -to complete the CLM5 raw PFT and CFT files. The final step is to combine these files with the other raw CLM5 surface -data files in the mksurfdata_map tool. +To represent the LUH2 transient LULCC dataset in CLM5, the annual fractional composition of the twelve land units specified in the dataset needs to be faithfully represented with a corresponding PFT and CFT mosaics of CLM. CLM5 represents the land surface as a hierarchy of sub-grid types: glacier; lake; urban; vegetated land; and crop land. The vegetated land is further divided into a mosaic of Plant Functional Types (PFTs), while the crop land is divided into a mosaic of Crop Functional Types (CFTs). + +To support this translation task the CLM5 Land Use Data tool has been built that extends the methods described in Lawrence et al (2012) to include all the new functionality of CMIP6 and CLM5 LULCC. The tool translates each of the LUH2 land units for a given year into fractional PFT and CFT values based on the current day CLM5 data for the land unit in that grid cell. The current day land unit descriptions are generated from from 1km resolution MODIS, MIRCA2000, ICESAT, AVHRR, SRTM, and CRU climate data products combined with reference year LUH2 land unit data, usually set to 2005. Where the land unit does not exist in a grid cell for the current day, the land unit description is generated from nearest neighbors with an inverse distance weighted search algorithm. + +The Land Use Data tool produces raw vegetation, crop, and management data files which are combined with other raw land surface data to produce the CLM5 initial surface dataset and the dynamic *landuse.timeseries* dataset with the CLM5 mksurfdata_map tool. The schematic of this entire process from LUH2 time series and high resolution current day data to the output of CLM5 surface datasets from the mksurfdata_map tool is shown in Figure 21.2. + +The methodology for creating the CLM5 transient PFT and CFT dataset is based on four steps which are applied across all of the historical and future time series. The first step involves generating the current day descriptions of natural and managed vegetation PFTs at 1km resolution from the global source datasets, and the current day description of crop CFTs at the 10km resolution from the MIRCA 2000 datasets. The second step combines the current day (2005) LUH2 land units with the current day CLM5 PFT and CFT distributions to get CLM5 land unit descriptions in either PFTs or CFTs at the LUH2 resolution of 0.25 degrees. The third step involves combining the LUH2 land unit time series with the CLM5 PFT and CFT descriptions for that land unit to generate the CLM5 raw PFT and CFT time series in the *landuse.timeseries* file. At this point in the process management information in terms of fertilizer, irrigation and wood harvest are added to the CLM5 PFT and CFT data to complete the CLM5 raw PFT and CFT files. The final step is to combine these files with the other raw CLM5 surface data files in the mksurfdata_map tool. .. _Figure Schematic of land cover change: @@ -356,7 +122,7 @@ data files in the mksurfdata_map tool. .. figure:: image2.png Schematic of translation of annual LUH2 land units to CLM5 plant and crop functional types. - + .. _Figure Workflow of CLM5 Land Use Data Tool and Mksurfdata_map Tool: .. figure:: image3.png diff --git a/doc/source/tech_note/Urban/CLM50_Tech_Note_Urban.rst b/doc/source/tech_note/Urban/CLM50_Tech_Note_Urban.rst index e5f4ac33e5..e9bfb5eb57 100644 --- a/doc/source/tech_note/Urban/CLM50_Tech_Note_Urban.rst +++ b/doc/source/tech_note/Urban/CLM50_Tech_Note_Urban.rst @@ -3,126 +3,23 @@ Urban Model (CLMU) ====================== -At the global scale, and at the coarse spatial resolution of current -climate models, urbanization has negligible impact on climate. However, -the urban parameterization (CLMU; :ref:`Oleson et al. (2008b) `; -:ref:`Oleson et al. (2008c) `) allows -simulation of the urban environment within a climate model, and -particularly the temperature where people live. As such, the urban model -allows scientific study of how climate change affects the urban heat -island and possible urban planning and design strategies to mitigate -warming (e.g., white roofs). - -Urban areas in CLM are represented by up to three urban landunits per -gridcell according to density class. The urban landunit is based on the -“urban canyon” concept of :ref:`Oke (1987) ` in which -the canyon geometry is -described by building height (:math:`H`) and street width (:math:`W`) -(:numref:`Figure schematic representation of the urban landunit`). The canyon system -consists of roofs, walls, and canyon -floor. Walls are further divided into shaded and sunlit components. The -canyon floor is divided into pervious (e.g., to represent residential -lawns, parks) and impervious (e.g., to represent roads, parking lots, -sidewalks) fractions. Vegetation is not explicitly modeled for the -pervious fraction; instead evaporation is parameterized by a simplified -bulk scheme. - -Each of the five urban surfaces is treated as a column within the -landunit (:numref:`Figure schematic representation of the urban landunit`). -Radiation parameterizations account for trapping -of solar and longwave radiation inside the canyon. Momentum fluxes are -determined for the urban landunit using a roughness length and -displacement height appropriate for the urban canyon and stability -formulations from CLM. A one-dimensional heat conduction equation is -solved numerically for a multiple-layer (:math:`N_{levurb} =10`) column -to determine conduction fluxes into and out of canyon surfaces. - -A new building energy model has been developed for CLM5.0. It accounts -for the conduction of heat through interior surfaces (roof, sunlit and -shaded walls, and floors), convection (sensible heat exchange) between -interior surfaces and building air, longwave radiation exchange between -interior surfaces, and ventilation (natural infiltration and exfiltration). -Idealized HAC systems are assumed where the system capacity is infinite and -the system supplies the amount of energy needed to keep the indoor air -temperature (:math:`T_{iB}`) within maximum and minimum emperatures -(:math:`T_{iB,\, \max } ,\, T_{iB,\, \min }` ), thus explicitly -resolving space heating and air conditioning fluxes. Anthropogenic sources -of waste heat (:math:`Q_{H,\, waste}` ) from HAC that account for inefficiencies -in the heating and air conditioning equipment and from energy lost in the -conversion of primary energy sources to end use energy are derived from -:ref:`Sivak (2013) `. These sources of waste heat are incorporated -as modifications to the canyon energy budget. - -Turbulent [sensible heat (:math:`Q_{H,\, u}` ) and -latent heat (:math:`Q_{E,\, u}` )] and storage (:math:`Q_{S,\, u}` ) -heat fluxes and surface (:math:`T_{u,\, s}` ) and internal -(:math:`T_{u,\, i=1,\, N_{levgrnd} }` ) temperatures are determined for -each urban surface :math:`u`. Hydrology on the roof and canyon floor is -simulated and walls are hydrologically inactive. A snowpack can form on -the active surfaces. A certain amount of liquid water is allowed to pond -on these surfaces which supports evaporation. Water in excess of the -maximum ponding depth runs off -(:math:`R_{roof} ,\, R_{imprvrd} ,\, R_{prvrd}` ). - -The heat and moisture fluxes from each surface interact with each other -through a bulk air mass that represents air in the urban canopy layer -for which specific humidity (:math:`q_{ac}` ) and temperature -(:math:`T_{ac}` ) are prognosed (:numref:`Figure schematic of urban and atmospheric model coupling`). -The air temperature can -be compared with that from surrounding vegetated/soil (rural) surfaces -in the model to ascertain heat island characteristics. As with other -landunits, the CLMU is forced either with output from a host atmospheric -model (e.g., the Community Atmosphere Model (CAM)) or -observed forcing (e.g., reanalysis or field observations). The urban -model produces sensible, latent heat, and momentum fluxes, emitted -longwave, and reflected solar radiation, which are area-averaged with -fluxes from non-urban “landunits” (e.g., vegetation, lakes) to supply -grid cell averaged fluxes to the atmospheric model. - -Present day global urban extent and urban properties were developed by -:ref:`Jackson et al. (2010) `. Urban extent, defined for four classes [tall -building district (TBD), and high, medium, and low density (HD, MD, -LD)], was derived from LandScan 2004, a population density dataset -derived from census data, nighttime lights satellite observations, road -proximity, and slope (:ref:`Dobson et al. 2000 `). The urban extent data for -TBD, HD, and MD classes are aggregated from the original 1 km resolution -to both a 0.05\ :sup:`o` by 0.05\ :sup:`o` global grid -for high-resolution studies or a 0.5\ :sup:`o` by -0.5\ :sup:`o` grid. For the current implementation, the LD class -is not used because it is highly rural and better modeled as a -vegetated/soil surface. Although the TBD, HD, and MD classes are -represented as individual urban landunits, urban model history output is -currently a weighted average of the output for individual classes. - -For each of 33 distinct regions across the globe, thermal (e.g., heat -capacity and thermal conductivity), radiative (e.g., albedo and -emissivity) and morphological (e.g., height to width ratio, roof -fraction, average building height, and pervious fraction of the canyon -floor) properties are provided for each of the density classes. Building -interior minimum and maximum temperatures are prescribed based on -climate and socioeconomic considerations. The surface dataset creation -routines (see CLM5.0 User’s Guide) aggregate the data to the desired -resolution. - -An optional urban properties dataset, including a tool that allows for generating future -urban development scenarios is also available (:ref:`Oleson and Feddema (2018) `). -This will become the default dataset in future model versions. -As described in :ref:`Oleson and Feddema (2018) ` the urban properties dataset -in :ref:`Jackson et al. (2010) ` was modified with respect to wall and roof thermal -properties to correct for biases in heat transfer due to layer and building type averaging. -Further changes to the dataset reflect the need for scenario development, thus allowing for -the creation of hypothetical wall types, and the easier interchange of wall facets. -The new urban properties tool is available as part of the Toolbox for Human-Earth System -Integration & Scaling (THESIS) tool set -(http://www.cgd.ucar.edu/iam/projects/thesis/thesis-urbanproperties-tool.html; -:ref:`Feddema and Kauffman (2016) `). The driver script (urban_prop.csh) -specifies three input csv files (by default, mat_prop.csv, -lam_spec.csv, and city_spec.csv; (:numref:`Figure schematic of THESIS urban properties tool`)) -that describe the morphological, radiative, and thermal properties of urban areas, and -generates a global dataset at 0.05° latitude by longitude in NetCDF format (urban_properties_data.05deg.nc). -A standalone NCL routine (gen_data_clm.ncl) can be run separately after the mksurfdata_map tool creates -the CLM surface dataset. This creates a supplementary streams file of setpoints for the maximum -interior building temperature at yearly time resolution. +At the global scale, and at the coarse spatial resolution of current climate models, urbanization has negligible impact on climate. However, the urban parameterization (CLMU; :ref:`Oleson et al. (2008b) `; :ref:`Oleson et al. (2008c) `) allows simulation of the urban environment within a climate model, and particularly the temperature where people live. As such, the urban model allows scientific study of how climate change affects the urban heat island and possible urban planning and design strategies to mitigate warming (e.g., white roofs). + +Urban areas in CLM are represented by up to three urban landunits per gridcell according to density class. The urban landunit is based on the "urban canyon" concept of :ref:`Oke (1987) ` in which the canyon geometry is described by building height (:math:`H`) and street width (:math:`W`) (:numref:`Figure schematic representation of the urban landunit`). The canyon system consists of roofs, walls, and canyon floor. Walls are further divided into shaded and sunlit components. The canyon floor is divided into pervious (e.g., to represent residential lawns, parks) and impervious (e.g., to represent roads, parking lots, sidewalks) fractions. Vegetation is not explicitly modeled for the pervious fraction; instead evaporation is parameterized by a simplified bulk scheme. + +Each of the five urban surfaces is treated as a column within the landunit (:numref:`Figure schematic representation of the urban landunit`). Radiation parameterizations account for trapping of solar and longwave radiation inside the canyon. Momentum fluxes are determined for the urban landunit using a roughness length and displacement height appropriate for the urban canyon and stability formulations from CLM. A one-dimensional heat conduction equation is solved numerically for a multiple-layer (:math:`N_{levurb} =10`) column to determine conduction fluxes into and out of canyon surfaces. + +A new building energy model has been developed for CLM5.0. It accounts for the conduction of heat through interior surfaces (roof, sunlit and shaded walls, and floors), convection (sensible heat exchange) between interior surfaces and building air, longwave radiation exchange between interior surfaces, and ventilation (natural infiltration and exfiltration). Idealized HAC systems are assumed where the system capacity is infinite and the system supplies the amount of energy needed to keep the indoor air temperature (:math:`T_{iB}`) within maximum and minimum emperatures (:math:`T_{iB,\, \max },\, T_{iB,\, \min }` ), thus explicitly resolving space heating and air conditioning fluxes. Anthropogenic sources of waste heat (:math:`Q_{H,\, waste}` ) from HAC that account for inefficiencies in the heating and air conditioning equipment and from energy lost in the conversion of primary energy sources to end use energy are derived from :ref:`Sivak (2013) `. These sources of waste heat are incorporated as modifications to the canyon energy budget. + +Turbulent [sensible heat (:math:`Q_{H,\, u}` ) and latent heat (:math:`Q_{E,\, u}` )] and storage (:math:`Q_{S,\, u}` ) heat fluxes and surface (:math:`T_{u,\, s}` ) and internal (:math:`T_{u,\, i=1,\, N_{levgrnd} }` ) temperatures are determined for each urban surface :math:`u`. Hydrology on the roof and canyon floor is simulated and walls are hydrologically inactive. A snowpack can form on the active surfaces. A certain amount of liquid water is allowed to pond on these surfaces which supports evaporation. Water in excess of the maximum ponding depth runs off (:math:`R_{roof},\, R_{imprvrd},\, R_{prvrd}` ). + +The heat and moisture fluxes from each surface interact with each other through a bulk air mass that represents air in the urban canopy layer for which specific humidity (:math:`q_{ac}` ) and temperature (:math:`T_{ac}` ) are prognosed (:numref:`Figure schematic of urban and atmospheric model coupling`). The air temperature can be compared with that from surrounding vegetated/soil (rural) surfaces in the model to ascertain heat island characteristics. As with other landunits, the CLMU is forced either with output from a host atmospheric model (e.g., the Community Atmosphere Model (CAM)) or observed forcing (e.g., reanalysis or field observations). The urban model produces sensible, latent heat, and momentum fluxes, emitted longwave, and reflected solar radiation, which are area-averaged with fluxes from non-urban "landunits" (e.g., vegetation, lakes) to supply grid cell averaged fluxes to the atmospheric model. + +Present day global urban extent and urban properties were developed by :ref:`Jackson et al. (2010) `. Urban extent, defined for four classes [tall building district (TBD), and high, medium, and low density (HD, MD, LD)], was derived from LandScan 2004, a population density dataset derived from census data, nighttime lights satellite observations, road proximity, and slope (:ref:`Dobson et al. 2000 `). The urban extent data for TBD, HD, and MD classes are aggregated from the original 1 km resolution to both a 0.05° by 0.05° global grid for high-resolution studies or a 0.5° by 0.5° grid. For the current implementation, the LD class is not used because it is highly rural and better modeled as a vegetated/soil surface. Although the TBD, HD, and MD classes are represented as individual urban landunits, urban model history output is currently a weighted average of the output for individual classes. + +For each of 33 distinct regions across the globe, thermal (e.g., heat capacity and thermal conductivity), radiative (e.g., albedo and emissivity) and morphological (e.g., height to width ratio, roof fraction, average building height, and pervious fraction of the canyon floor) properties are provided for each of the density classes. Building interior minimum and maximum temperatures are prescribed based on climate and socioeconomic considerations. The surface dataset creation routines (see CLM5.0 User's Guide) aggregate the data to the desired resolution. + +An optional urban properties dataset, including a tool that allows for generating future urban development scenarios is also available (:ref:`Oleson and Feddema (2018) `). This will become the default dataset in future model versions. As described in :ref:`Oleson and Feddema (2018) ` the urban properties dataset in :ref:`Jackson et al. (2010) ` was modified with respect to wall and roof thermal properties to correct for biases in heat transfer due to layer and building type averaging. Further changes to the dataset reflect the need for scenario development, thus allowing for the creation of hypothetical wall types, and the easier interchange of wall facets. The new urban properties tool is available as part of the Toolbox for Human-Earth System Integration & Scaling (THESIS) tool set (http://www.cgd.ucar.edu/iam/projects/thesis/thesis-urbanproperties-tool.html; :ref:`Feddema and Kauffman (2016) `). The driver script (urban_prop.csh) specifies three input csv files (by default, mat_prop.csv, lam_spec.csv, and city_spec.csv; (:numref:`Figure schematic of THESIS urban properties tool`)) that describe the morphological, radiative, and thermal properties of urban areas, and generates a global dataset at 0.05° latitude by longitude in NetCDF format (urban_properties_data.05deg.nc). A standalone NCL routine (gen_data_clm.ncl) can be run separately after the mksurfdata_map tool creates the CLM surface dataset. This creates a supplementary streams file of setpoints for the maximum interior building temperature at yearly time resolution. .. Figure 12.1. Schematic representation of the urban land unit @@ -146,35 +43,8 @@ interior building temperature at yearly time resolution. .. Figure:: image3.png - Schematic of THESIS urban properties tool. Executable scripts are in orange, input files are blue, and output files are green. Items within the black box outline are either read in as input, executed, or output by the driver script (urban_prop.csh). - - -The urban model that was first released as a component of CLM4.0 is separately -described in the urban technical note (:ref:`Oleson et al. (2010b) `). -The main changes in the urban model from CLM4.0 to CLM4.5 were 1) -an expansion of the single urban landunit to up to three landunits per -grid cell stratified by urban density types, 2) the number of urban -layers for roofs and walls was no longer constrained to be equal to the -number of ground layers, 3) space heating and air conditioning wasteheat -factors were set to zero by default so that the user could customize -these factors for their own application, 4) the elevation threshold used -to eliminate urban areas in the surface dataset creation routines was -increased from 2200 meters to 2600 meters, 5) hydrologic and thermal -calculations for the pervious road followed CLM4.5 parameterizations. - -The main changes in the urban model from CLM4.5 to CLM5.0 are 1) a more -sophisticated and realistic building space heating and air conditioning -submodel that prognoses interior building air temperature and includes more -realistic space heating and air conditioning wasteheat factors (see above), 2) the maximum -building temperature (which determines air conditioning demand) is now read in -from a namelist-defined file which allows for dynamic control of this input -variable. The maximum building temperatures that are defined in -:ref:`Jackson et al. (2010) ` are implemented in year 1950 (thus -air conditioning is off in prior years) and air conditioning is turned off in year -2100 (because the buildings are not suitable for air conditioning in some extreme -global warming scenarios), 3) an optional updated urban properties dataset and new -scenario tool. These features are described in more detail in :ref:`Oleson and Feddema (2018) `. -In addition, a module of heat stress indices calculated online -in the model that can be used to assess human thermal comfort for rural and urban -areas has been added. This last development is described and evaluated by -:ref:`Buzan et al. (2015) `. + Schematic of THESIS urban properties tool. Executable scripts are in orange, input files are blue, and output files are green. Items within the black box outline are either read in as input, executed, or output by the driver script (urban_prop.csh). + +The urban model that was first released as a component of CLM4.0 is separately described in the urban technical note (:ref:`Oleson et al. (2010b) `). The main changes in the urban model from CLM4.0 to CLM4.5 were 1) an expansion of the single urban landunit to up to three landunits per grid cell stratified by urban density types, 2) the number of urban layers for roofs and walls was no longer constrained to be equal to the number of ground layers, 3) space heating and air conditioning wasteheat factors were set to zero by default so that the user could customize these factors for their own application, 4) the elevation threshold used to eliminate urban areas in the surface dataset creation routines was increased from 2200 meters to 2600 meters, 5) hydrologic and thermal calculations for the pervious road followed CLM4.5 parameterizations. + +The main changes in the urban model from CLM4.5 to CLM5.0 are 1) a more sophisticated and realistic building space heating and air conditioning submodel that prognoses interior building air temperature and includes more realistic space heating and air conditioning wasteheat factors (see above), 2) the maximum building temperature (which determines air conditioning demand) is now read in from a namelist-defined file which allows for dynamic control of this input variable. The maximum building temperatures that are defined in :ref:`Jackson et al. (2010) ` are implemented in year 1950 (thus air conditioning is off in prior years) and air conditioning is turned off in year 2100 (because the buildings are not suitable for air conditioning in some extreme global warming scenarios), 3) an optional updated urban properties dataset and new scenario tool. These features are described in more detail in :ref:`Oleson and Feddema (2018) `. In addition, a module of heat stress indices calculated online in the model that can be used to assess human thermal comfort for rural and urban areas has been added. This last development is described and evaluated by :ref:`Buzan et al. (2015) `. diff --git a/doc/source/tech_note/Vegetation_Phenology_Turnover/CLM50_Tech_Note_Vegetation_Phenology_Turnover.rst b/doc/source/tech_note/Vegetation_Phenology_Turnover/CLM50_Tech_Note_Vegetation_Phenology_Turnover.rst index 254d3d84c9..1665b9a00f 100644 --- a/doc/source/tech_note/Vegetation_Phenology_Turnover/CLM50_Tech_Note_Vegetation_Phenology_Turnover.rst +++ b/doc/source/tech_note/Vegetation_Phenology_Turnover/CLM50_Tech_Note_Vegetation_Phenology_Turnover.rst @@ -3,33 +3,14 @@ Vegetation Phenology and Turnover ================================= -The CLM phenology model consists of several algorithms controlling the -transfer of stored carbon and nitrogen out of storage pools for the -display of new growth and into litter pools for losses of displayed -growth. PFTs are classified into three distinct phenological types that -are represented by separate algorithms: an evergreen type, for which -some fraction of annual leaf growth persists in the displayed pool for -longer than one year; a seasonal-deciduous type with a single growing -season per year, controlled mainly by temperature and daylength; and a -stress-deciduous type with the potential for multiple growing seasons -per year, controlled by temperature and soil moisture conditions. - -The three phenology types share a common set of control variables. The -calculation of the phenology fluxes is generalized, operating -identically for all three phenology types, given a specification of the -common control variables. The following sections describe first the -general flux parameterization, followed by the algorithms for setting -the control parameters for the three phenology types. +The CLM phenology model consists of several algorithms controlling the transfer of stored carbon and nitrogen out of storage pools for the display of new growth and into litter pools for losses of displayed growth. PFTs are classified into three distinct phenological types that are represented by separate algorithms: an evergreen type, for which some fraction of annual leaf growth persists in the displayed pool for longer than one year; a seasonal-deciduous type with a single growing season per year, controlled mainly by temperature and daylength; and a stress-deciduous type with the potential for multiple growing seasons per year, controlled by temperature and soil moisture conditions. + +The three phenology types share a common set of control variables. The calculation of the phenology fluxes is generalized, operating identically for all three phenology types, given a specification of the common control variables. The following sections describe first the general flux parameterization, followed by the algorithms for setting the control parameters for the three phenology types. General Phenology Flux Parameterization -------------------------------------------- -Fluxes of carbon and nitrogen from storage pools and into displayed -tissue pools pass through a special transfer pool (denoted *\_xfer*), -maintained as a separate state variable for each tissue type. Storage -(*\_stor*) and transfer (*\_xfer*) pools are maintained separately to -reduce the complexity of accounting for transfers into and out of -storage over the course of a single growing season. +Fluxes of carbon and nitrogen from storage pools and into displayed tissue pools pass through a special transfer pool (denoted *\_xfer*), maintained as a separate state variable for each tissue type. Storage (*\_stor*) and transfer (*\_xfer*) pools are maintained separately to reduce the complexity of accounting for transfers into and out of storage over the course of a single growing season. .. _Figure annual phenology cycle: @@ -40,835 +21,626 @@ storage over the course of a single growing season. 14.1.1 Onset Periods ^^^^^^^^^^^^^^^^^^^^ -The deciduous phenology algorithms specify the occurrence of onset -growth periods (Figure 14.1). Carbon fluxes from the transfer pools into -displayed growth are calculated during these periods as: +The deciduous phenology algorithms specify the occurrence of onset growth periods (Figure 14.1). Carbon fluxes from the transfer pools into displayed growth are calculated during these periods as: .. math:: - :label: 20.1) + :label: 20.1) CF_{leaf\_ xfer,leaf} =r_{xfer\_ on} CS_{leaf\_ xfer} .. math:: - :label: 20.2) + :label: 20.2) CF_{froot\_ xfer,froot} =r_{xfer\_ on} CS_{froot\_ xfer} .. math:: - :label: 20.3) + :label: 20.3) CF_{livestem\_ xfer,livestem} =r_{xfer\_ on} CS_{livestem\_ xfer} .. math:: - :label: 20.4) + :label: 20.4) CF_{deadstem\_ xfer,deadstem} =r_{xfer\_ on} CS_{deadstem\_ xfer} .. math:: - :label: 20.5) + :label: 20.5) CF_{livecroot\_ xfer,livecroot} =r_{xfer\_ on} CS_{livecroot\_ xfer} .. math:: - :label: 20.6) + :label: 20.6) CF_{deadcroot\_ xfer,deadcroot} =r_{xfer\_ on} CS_{deadcroot\_ xfer} , with corresponding nitrogen fluxes: .. math:: - :label: 20.7) + :label: 20.7) NF_{leaf\_ xfer,leaf} =r_{xfer\_ on} NS_{leaf\_ xfer} .. math:: - :label: 20.8) + :label: 20.8) NF_{froot\_ xfer,froot} =r_{xfer\_ on} NS_{froot\_ xfer} .. math:: - :label: 20.9) + :label: 20.9) NF_{livestem\_ xfer,livestem} =r_{xfer\_ on} NS_{livestem\_ xfer} .. math:: - :label: 20.10) + :label: 20.10) NF_{deadstem\_ xfer,deadstem} =r_{xfer\_ on} NS_{deadstem\_ xfer} .. math:: - :label: 20.11) + :label: 20.11) NF_{livecroot\_ xfer,livecroot} =r_{xfer\_ on} NS_{livecroot\_ xfer} .. math:: - :label: 20.12) + :label: 20.12) NF_{deadcroot\_ xfer,deadcroot} =r_{xfer\_ on} NS_{deadcroot\_ xfer} , -where CF is the carbon flux, CS is stored carbon, NF is the nitrogen -flux, NS is stored nitrogen, :math:`{r}_{xfer\_on}` (s\ :sup:`-1`) is a time-varying rate coefficient controlling flux -out of the transfer pool: +where CF is the carbon flux, CS is stored carbon, NF is the nitrogen flux, NS is stored nitrogen, :math:`{r}_{xfer\_on}` (s\ :sup:`-1`) is a time-varying rate coefficient controlling flux out of the transfer pool: .. math:: - :label: ZEqnNum852972 + :label: ZEqnNum852972 - r_{xfer\_ on} =\left\{\begin{array}{l} {{2\mathord{\left/ {\vphantom {2 t_{onset} }} \right. \kern-\nulldelimiterspace} t_{onset} } \qquad {\rm for\; }t_{onset} \ne \Delta t} \\ {{1\mathord{\left/ {\vphantom {1 \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} \qquad {\rm for\; }t_{onset} =\Delta t} \end{array}\right. + r_{xfer\_ on} =\left\{\begin{array}{l} {{2\mathord{\left/ {\vphantom {2 t_{onset} }} \right.} t_{onset} } \qquad {\rm for\; }t_{onset} \ne \Delta t} \\ {{1\mathord{\left/ {\vphantom {1 \Delta t}} \right.} \Delta t} \qquad {\rm for\; }t_{onset} =\Delta t} \end{array}\right. -and *t*\ :sub:`onset` (s) is the number of seconds remaining in -the current phenology onset growth period (Figure 14.1). The form of Eq. :eq:`ZEqnNum852972` -produces a flux from the transfer pool which declines linearly over the -onset growth period, approaching zero flux in the final timestep. +and *t*\ :sub:`onset` (s) is the number of seconds remaining in the current phenology onset growth period (Figure 14.1). The form of Eq. :eq:`ZEqnNum852972` produces a flux from the transfer pool which declines linearly over the onset growth period, approaching zero flux in the final timestep. 14.1.2 Offset Periods ^^^^^^^^^^^^^^^^^^^^^ -The deciduous phenology algorithms also specify the occurrence of -litterfall during offset periods. In contrast to the onset periods, only -leaf and fine root state variables are subject to litterfall fluxes. -Carbon fluxes from display pools into litter are calculated during these -periods as: +The deciduous phenology algorithms also specify the occurrence of litterfall during offset periods. In contrast to the onset periods, only leaf and fine root state variables are subject to litterfall fluxes. Carbon fluxes from display pools into litter are calculated during these periods as: .. math:: - :label: 20.14) + :label: 20.14) - CF_{leaf,litter}^{n} =\left\{\begin{array}{l} {CF_{leaf,litter}^{n-1} + r_{xfer\_ off} \left(CS_{leaf} -CF_{leaf,litter}^{n-1} {\kern 1pt} t_{offset} \right)\qquad {\rm for\; }t_{offset} \ne \Delta t} - \\ {\left({CS_{leaf} \mathord{\left/ {\vphantom {CS_{leaf} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} \right) - \left( 1-biofuel\_harvfrac \right) + CF_{leaf,litter}^{n} =\left\{\begin{array}{l} {CF_{leaf,litter}^{n-1} + r_{xfer\_ off} \left(CS_{leaf} -CF_{leaf,litter}^{n-1} {\kern 1pt} t_{offset} \right)\qquad {\rm for\; }t_{offset} \ne \Delta t} + \\ {\left({CS_{leaf} \mathord{\left/ {\vphantom {CS_{leaf} \Delta t}} \right.} \Delta t} \right) + \left( 1-biofuel\_harvfrac \right) +CF_{alloc,leaf} \qquad {\rm for\; }t_{offset} =\Delta t} \end{array}\right. .. math:: - :label: 20.15) + :label: 20.15) CF_{froot,litter}^{n} =\left\{\begin{array}{l} {CF_{froot,litter}^{n-1} + - r_{xfer\_ off} \left(CS_{froot} -CF_{froot,litter}^{n-1} {\kern 1pt} t_{offset} \right)\qquad {\rm for\; }t_{offset} \ne \Delta t} \\ {\left({CS_{froot} \mathord{\left/ {\vphantom {CS_{froot} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} \right)+CF_{alloc,\, froot} \qquad \qquad \qquad {\rm for\; }t_{offset} =\Delta t} \end{array}\right. + r_{xfer\_ off} \left(CS_{froot} -CF_{froot,litter}^{n-1} {\kern 1pt} t_{offset} \right)\qquad {\rm for\; }t_{offset} \ne \Delta t} \\ {\left({CS_{froot} \mathord{\left/ {\vphantom {CS_{froot} \Delta t}} \right.} \Delta t} \right)+CF_{alloc,\, froot} \qquad \qquad \qquad {\rm for\; }t_{offset} =\Delta t} \end{array}\right. .. math:: - :label: 20.16) + :label: 20.16) r_{xfer\_ off} =\frac{2\Delta t}{t_{offset} ^{2} } -where superscripts *n* and *n-1* refer to fluxes on the current and -previous timesteps, respectively. The rate coefficient :math:`{r}_{xfer\_off}` varies with time to produce a linearly -increasing litterfall rate throughout the offset period. -The :math:`biofuel\_harvfrac` (:numref:`Table Plant functional type (PFT) parameters for harvested fraction of leaf/livestem for bioenergy production`) -is the harvested fraction of aboveground biomass (leaf & livestem) for bioenergy crops. -The special case for fluxes in the final litterfall timestep -(:math:`{t}_{offset}` = :math:`\Delta t`\ ) ensures that all of the displayed growth is sent to the litter pools or biofuel feedstock pools. The fraction (:math:`biofuel\_harvfrac`) of leaf biomass going to the biofuel feedstock pools (Equation :eq:`25.9`) is defined in Table 26.3 and is only non-zero for prognostic crops. The remaining fraction of leaf biomass (:math:`1-biofuel\_harvfrac`) for deciduous plant types is sent to the litter pools. -Similar modifications made for livestem carbon pools for prognostic crops -can be found in section :numref:`Harvest to food and seed` in Equations :eq:`25.9`-:eq:`25.14`. +where superscripts *n* and *n-1* refer to fluxes on the current and previous timesteps, respectively. The rate coefficient :math:`{r}_{xfer\_off}` varies with time to produce a linearly increasing litterfall rate throughout the offset period. The :math:`biofuel\_harvfrac` (:numref:`Table Plant functional type (PFT) parameters for harvested fraction of leaf/livestem for bioenergy production`) is the harvested fraction of aboveground biomass (leaf & livestem) for bioenergy crops. The special case for fluxes in the final litterfall timestep (:math:`{t}_{offset}` = :math:`\Delta t`\ ) ensures that all of the displayed growth is sent to the litter pools or biofuel feedstock pools. The fraction (:math:`biofuel\_harvfrac`) of leaf biomass going to the biofuel feedstock pools (Equation :eq:`25.9`) is defined in Table 26.3 and is only non-zero for prognostic crops. The remaining fraction of leaf biomass (:math:`1-biofuel\_harvfrac`) for deciduous plant types is sent to the litter pools. Similar modifications made for livestem carbon pools for prognostic crops can be found in section :numref:`Harvest to food and seed` in Equations :eq:`25.9`-:eq:`25.14`. -Corresponding nitrogen fluxes during litterfall take into account retranslocation of nitrogen out of the displayed leaf pool prior to -litterfall (:math:`{NF}_{leaf,retrans}`, gN m\ :sup:`-2` s\ :sup:`-1`). Retranslocation of nitrogen out of fine roots is -assumed to be negligible. The fluxes are: +Corresponding nitrogen fluxes during litterfall take into account retranslocation of nitrogen out of the displayed leaf pool prior to litterfall (:math:`{NF}_{leaf,retrans}`, gN m\ :sup:`-2` s\ :sup:`-1`). Retranslocation of nitrogen out of fine roots is assumed to be negligible. The fluxes are: .. math:: - :label: 20.17) + :label: 20.17) - NF_{leaf,litter} ={CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{leaf\_ litter} }} \right. \kern-\nulldelimiterspace} CN_{leaf\_ litter} } + NF_{leaf,litter} ={CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{leaf\_ litter} }} \right.} CN_{leaf\_ litter} } .. math:: - :label: 20.18) + :label: 20.18) - NF_{froot,litter} ={CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{froot} }} \right. \kern-\nulldelimiterspace} CN_{froot} } + NF_{froot,litter} ={CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{froot} }} \right.} CN_{froot} } .. math:: - :label: 20.19) + :label: 20.19) - NF_{leaf,retrans} =\left({CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{leaf} }} \right. \kern-\nulldelimiterspace} CN_{leaf} } \right)-NF_{leaf,litter} . + NF_{leaf,retrans} =\left({CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{leaf} }} \right.} CN_{leaf} } \right)-NF_{leaf,litter} . where CN is C:N. 14.1.3 Background Onset Growth ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The stress-deciduous phenology algorithm includes a provision for the -case when stress signals are absent, and the vegetation shifts from a -deciduous habit to an evergreen habit, until the next occurrence of an -offset stress trigger . In that case, the regular onset flux mechanism -is switched off and a background onset growth algorithm is invoked -(:math:`{r}_{bgtr} > 0`). During this period, small fluxes -of carbon and nitrogen from the storage pools into the associated -transfer pools are calculated on each time step, and the entire contents -of the transfer pool are added to the associated displayed growth pool -on each time step. The carbon fluxes from transfer to display pools -under these conditions are: +The stress-deciduous phenology algorithm includes a provision for the case when stress signals are absent, and the vegetation shifts from a deciduous habit to an evergreen habit, until the next occurrence of an offset stress trigger. In that case, the regular onset flux mechanism is switched off and a background onset growth algorithm is invoked (:math:`{r}_{bgtr} > 0`). During this period, small fluxes of carbon and nitrogen from the storage pools into the associated transfer pools are calculated on each time step, and the entire contents of the transfer pool are added to the associated displayed growth pool on each time step. The carbon fluxes from transfer to display pools under these conditions are: .. math:: - :label: 20.20) + :label: 20.20) - CF_{leaf\_ xfer,leaf} ={CS_{leaf\_ xfer} \mathord{\left/ {\vphantom {CS_{leaf\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{leaf\_ xfer,leaf} ={CS_{leaf\_ xfer} \mathord{\left/ {\vphantom {CS_{leaf\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.21) + :label: 20.21) - CF_{froot\_ xfer,froot} ={CS_{froot\_ xfer} \mathord{\left/ {\vphantom {CS_{froot\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{froot\_ xfer,froot} ={CS_{froot\_ xfer} \mathord{\left/ {\vphantom {CS_{froot\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.22) + :label: 20.22) - CF_{livestem\_ xfer,livestem} ={CS_{livestem\_ xfer} \mathord{\left/ {\vphantom {CS_{livestem\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{livestem\_ xfer,livestem} ={CS_{livestem\_ xfer} \mathord{\left/ {\vphantom {CS_{livestem\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.23) + :label: 20.23) - CF_{deadstem\_ xfer,deadstem} ={CS_{deadstem\_ xfer} \mathord{\left/ {\vphantom {CS_{deadstem\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{deadstem\_ xfer,deadstem} ={CS_{deadstem\_ xfer} \mathord{\left/ {\vphantom {CS_{deadstem\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.24) + :label: 20.24) - CF_{livecroot\_ xfer,livecroot} ={CS_{livecroot\_ xfer} \mathord{\left/ {\vphantom {CS_{livecroot\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{livecroot\_ xfer,livecroot} ={CS_{livecroot\_ xfer} \mathord{\left/ {\vphantom {CS_{livecroot\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.25) + :label: 20.25) - CF_{deadcroot\_ xfer,deadcroot} ={CS_{deadcroot\_ xfer} \mathord{\left/ {\vphantom {CS_{deadcroot\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} , + CF_{deadcroot\_ xfer,deadcroot} ={CS_{deadcroot\_ xfer} \mathord{\left/ {\vphantom {CS_{deadcroot\_ xfer} \Delta t}} \right.} \Delta t} , and the corresponding nitrogen fluxes are: .. math:: - :label: 20.26) + :label: 20.26) - NF_{leaf\_ xfer,leaf} ={NS_{leaf\_ xfer} \mathord{\left/ {\vphantom {NS_{leaf\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{leaf\_ xfer,leaf} ={NS_{leaf\_ xfer} \mathord{\left/ {\vphantom {NS_{leaf\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.27) + :label: 20.27) - NF_{froot\_ xfer,froot} ={NS_{froot\_ xfer} \mathord{\left/ {\vphantom {NS_{froot\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{froot\_ xfer,froot} ={NS_{froot\_ xfer} \mathord{\left/ {\vphantom {NS_{froot\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.28) + :label: 20.28) - NF_{livestem\_ xfer,livestem} ={NS_{livestem\_ xfer} \mathord{\left/ {\vphantom {NS_{livestem\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{livestem\_ xfer,livestem} ={NS_{livestem\_ xfer} \mathord{\left/ {\vphantom {NS_{livestem\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.29) + :label: 20.29) - NF_{deadstem\_ xfer,deadstem} ={NS_{deadstem\_ xfer} \mathord{\left/ {\vphantom {NS_{deadstem\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{deadstem\_ xfer,deadstem} ={NS_{deadstem\_ xfer} \mathord{\left/ {\vphantom {NS_{deadstem\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.30) + :label: 20.30) - NF_{livecroot\_ xfer,livecroot} ={NS_{livecroot\_ xfer} \mathord{\left/ {\vphantom {NS_{livecroot\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{livecroot\_ xfer,livecroot} ={NS_{livecroot\_ xfer} \mathord{\left/ {\vphantom {NS_{livecroot\_ xfer} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.31) + :label: 20.31) - NF_{deadcroot\_ xfer,deadcroot} ={NS_{deadcroot\_ xfer} \mathord{\left/ {\vphantom {NS_{deadcroot\_ xfer} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} . + NF_{deadcroot\_ xfer,deadcroot} ={NS_{deadcroot\_ xfer} \mathord{\left/ {\vphantom {NS_{deadcroot\_ xfer} \Delta t}} \right.} \Delta t} . 14.1.4 Background Litterfall ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Both evergreen and stress-deciduous phenology algorithms can specify a -litterfall flux that is not associated with a specific offset period, -but which occurs instead at a slow rate over an extended period of time, -referred to as background litterfall. For evergreen types the background -litterfall is the only litterfall flux. For stress-deciduous types -either the offset period litterfall or the background litterfall -mechanism may be active, but not both at once. Given a specification of -the background litterfall rate (:math:`{r}_{bglf}`, s\ :sup:`-1`), litterfall carbon fluxes are calculated as +Both evergreen and stress-deciduous phenology algorithms can specify a litterfall flux that is not associated with a specific offset period, but which occurs instead at a slow rate over an extended period of time, referred to as background litterfall. For evergreen types the background litterfall is the only litterfall flux. For stress-deciduous types either the offset period litterfall or the background litterfall mechanism may be active, but not both at once. Given a specification of the background litterfall rate (:math:`{r}_{bglf}`, s\ :sup:`-1`), litterfall carbon fluxes are calculated as .. math:: - :label: 20.32) + :label: 20.32) CF_{leaf,litter} =r_{bglf} CS_{leaf} .. math:: - :label: 20.33) + :label: 20.33) CS_{froot,litter} =r_{bglf} CS_{froot} , with corresponding nitrogen litterfall and retranslocation fluxes: .. math:: - :label: 20.34) + :label: 20.34) - NF_{leaf,litter} ={CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{leaf\_ litter} }} \right. \kern-\nulldelimiterspace} CN_{leaf\_ litter} } + NF_{leaf,litter} ={CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{leaf\_ litter} }} \right.} CN_{leaf\_ litter} } .. math:: - :label: 20.35) + :label: 20.35) - NF_{froot,litter} ={CF_{froot,litter} \mathord{\left/ {\vphantom {CF_{froot,litter} CN_{froot} }} \right. \kern-\nulldelimiterspace} CN_{froot} } + NF_{froot,litter} ={CF_{froot,litter} \mathord{\left/ {\vphantom {CF_{froot,litter} CN_{froot} }} \right.} CN_{froot} } .. math:: - :label: 20.36) + :label: 20.36) - NF_{leaf,retrans} =\left({CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{leaf} }} \right. \kern-\nulldelimiterspace} CN_{leaf} } \right)-NF_{leaf,litter} . + NF_{leaf,retrans} =\left({CF_{leaf,litter} \mathord{\left/ {\vphantom {CF_{leaf,litter} CN_{leaf} }} \right.} CN_{leaf} } \right)-NF_{leaf,litter} . 14.1.5 Livewood Turnover ^^^^^^^^^^^^^^^^^^^^^^^^ -The conceptualization of live wood vs. dead wood fractions for stem and -coarse root pools is intended to capture the difference in maintenance -respiration rates between these two physiologically distinct tissue -types. Unlike displayed pools for leaf and fine root, which are lost to -litterfall, live wood cells reaching the end of their lifespan are -retained as a part of the dead woody structure of stems and coarse -roots. A mechanism is therefore included in the phenology routine to -effect the transfer of live wood to dead wood pools, which also takes -into account the different nitrogen concentrations typical of these -tissue types. +The conceptualization of live wood vs. dead wood fractions for stem and coarse root pools is intended to capture the difference in maintenance respiration rates between these two physiologically distinct tissue types. Unlike displayed pools for leaf and fine root, which are lost to litterfall, live wood cells reaching the end of their lifespan are retained as a part of the dead woody structure of stems and coarse roots. A mechanism is therefore included in the phenology routine to effect the transfer of live wood to dead wood pools, which also takes into account the different nitrogen concentrations typical of these tissue types. -A live wood turnover rate (:math:`{r}_{lwt}`, s\ :sup:`-1`) is -defined as +A live wood turnover rate (:math:`{r}_{lwt}`, s\ :sup:`-1`) is defined as .. math:: - :label: 20.37) + :label: 20.37) - r_{lwt} ={p_{lwt} \mathord{\left/ {\vphantom {p_{lwt} \left(365\cdot 86400\right)}} \right. \kern-\nulldelimiterspace} \left(365\cdot 86400\right)} + r_{lwt} ={p_{lwt} \mathord{\left/ {\vphantom {p_{lwt} \left(365\cdot 86400\right)}} \right.} \left(365\cdot 86400\right)} -where :math:`{p}_{lwt} = 0.7` is the assumed annual live wood -turnover fraction. Carbon fluxes from live to dead wood pools are: +where :math:`{p}_{lwt} = 0.7` is the assumed annual live wood turnover fraction. Carbon fluxes from live to dead wood pools are: .. math:: - :label: 20.38) + :label: 20.38) CF_{livestem,deadstem} =CS_{livestem} r_{lwt} .. math:: - :label: 20.39) + :label: 20.39) CF_{livecroot,deadcroot} =CS_{livecroot} r_{lwt} , -and the associated nitrogen fluxes, including retranslocation of -nitrogen out of live wood during turnover, are: +and the associated nitrogen fluxes, including retranslocation of nitrogen out of live wood during turnover, are: .. math:: - :label: 20.40) + :label: 20.40) - NF_{livestem,deadstem} ={CF_{livestem,deadstem} \mathord{\left/ {\vphantom {CF_{livestem,deadstem} CN_{dw} }} \right. \kern-\nulldelimiterspace} CN_{dw} } + NF_{livestem,deadstem} ={CF_{livestem,deadstem} \mathord{\left/ {\vphantom {CF_{livestem,deadstem} CN_{dw} }} \right.} CN_{dw} } .. math:: - :label: 20.41) + :label: 20.41) - NF_{livestem,retrans} =\left({CF_{livestem,deadstem} \mathord{\left/ {\vphantom {CF_{livestem,deadstem} CN_{lw} }} \right. \kern-\nulldelimiterspace} CN_{lw} } \right)-NF_{livestem,deadstem} + NF_{livestem,retrans} =\left({CF_{livestem,deadstem} \mathord{\left/ {\vphantom {CF_{livestem,deadstem} CN_{lw} }} \right.} CN_{lw} } \right)-NF_{livestem,deadstem} .. math:: - :label: 20.42) + :label: 20.42) - NF_{livecroot,deadcroot} ={CF_{livecroot,deadcroot} \mathord{\left/ {\vphantom {CF_{livecroot,deadcroot} CN_{dw} }} \right. \kern-\nulldelimiterspace} CN_{dw} } + NF_{livecroot,deadcroot} ={CF_{livecroot,deadcroot} \mathord{\left/ {\vphantom {CF_{livecroot,deadcroot} CN_{dw} }} \right.} CN_{dw} } .. math:: - :label: 20.43) + :label: 20.43) - NF_{livecroot,retrans} =\left({CF_{livecroot,deadcroot} \mathord{\left/ {\vphantom {CF_{livecroot,deadcroot} CN_{lw} }} \right. \kern-\nulldelimiterspace} CN_{lw} } \right)-NF_{livecroot,deadcroot} . + NF_{livecroot,retrans} =\left({CF_{livecroot,deadcroot} \mathord{\left/ {\vphantom {CF_{livecroot,deadcroot} CN_{lw} }} \right.} CN_{lw} } \right)-NF_{livecroot,deadcroot} . Evergreen Phenology ------------------------ -The evergreen phenology algorithm is by far the simplest of the three -possible types. It is assumed for all evergreen types that all carbon -and nitrogen allocated for new growth in the current timestep goes -immediately to the displayed growth pools (i.e. f\ :math:`{f}_{cur} = 1.0` -(Chapter 13)). As such, there is never an accumulation of carbon or -nitrogen in the storage or transfer pools, and so the onset growth and -background onset growth mechanisms are never invoked for this type. -Litterfall is specified to occur only through the background litterfall -mechanism – there are no distinct periods of litterfall for evergreen -types, but rather a continuous (slow) shedding of foliage and fine -roots. This is an obvious area for potential improvements in the model, -since it is known, at least for evergreen needleleaf trees in the -temperate and boreal zones, that there are distinct periods of higher -and lower leaf litterfall (Ferrari, 1999; Gholz et al., 1985). The rate -of background litterfall (:math:`{r}_{bglf}`, section 14.1.4) -depends on the specified leaf longevity (:math:`\tau_{leaf}`\ , y), as - -.. math:: - :label: 20.44) +The evergreen phenology algorithm is by far the simplest of the three possible types. It is assumed for all evergreen types that all carbon and nitrogen allocated for new growth in the current timestep goes immediately to the displayed growth pools (i.e. f\ :math:`{f}_{cur} = 1.0` (Chapter 13)). As such, there is never an accumulation of carbon or nitrogen in the storage or transfer pools, and so the onset growth and background onset growth mechanisms are never invoked for this type. Litterfall is specified to occur only through the background litterfall mechanism – there are no distinct periods of litterfall for evergreen types, but rather a continuous (slow) shedding of foliage and fine roots. This is an obvious area for potential improvements in the model, since it is known, at least for evergreen needleleaf trees in the temperate and boreal zones, that there are distinct periods of higher and lower leaf litterfall (Ferrari, 1999; Gholz et al., 1985). The rate of background litterfall (:math:`{r}_{bglf}`, section 14.1.4) depends on the specified leaf longevity (:math:`\tau_{leaf}`\, y), as + +.. math:: + :label: 20.44) r_{bglf} =\frac{1}{\tau _{leaf} \cdot 365\cdot 86400} . Seasonal-Deciduous Phenology --------------------------------- -The seasonal-deciduous phenology algorithm derives directly from the -treatment used in the offline model Biome-BGC v. 4.1.2, (Thornton et -al., 2002), which in turn is based on the parameterizations for leaf -onset and offset for temperate deciduous broadleaf forest from White et -al. (1997). Initiation of leaf onset is triggered when a common -degree-day summation exceeds a critical value, and leaf litterfall is -initiated when daylength is shorter than a critical value. Because of -the dependence on daylength, the seasonal deciduous phenology algorithm -is only valid for latitudes outside of the tropical zone, defined here -as :math:`\left|{\rm latitude}\right|>19.5{\rm {}^\circ }`. Neither the -background onset nor background litterfall mechanism is invoked for the -seasonal-deciduous phenology algorithm. The algorithm allows a maximum -of one onset period and one offset period each year. - -The algorithms for initiation of onset and offset periods use the winter -and summer solstices as coordination signals. The period between winter -and summer solstice is identified as :math:`{dayl}_{n} > {dayl}_{n-1}`, -and the period between summer and winter -solstice is identified as :math:`{dayl}_{n} < {dayl}_{n-1}`, -where :math:`{dayl}_{n}` and :math:`{dayl}_{n-1}` are the day length(s) calculated for the -current and previous timesteps, respectively, using - -.. math:: - :label: 20.45) +The seasonal-deciduous phenology algorithm derives directly from the treatment used in the offline model Biome-BGC v. 4.1.2, (Thornton et al., 2002), which in turn is based on the parameterizations for leaf onset and offset for temperate deciduous broadleaf forest from White et al. (1997). Initiation of leaf onset is triggered when a common degree-day summation exceeds a critical value, and leaf litterfall is initiated when daylength is shorter than a critical value. Because of the dependence on daylength, the seasonal deciduous phenology algorithm is only valid for latitudes outside of the tropical zone, defined here as :math:`\left|{\rm latitude}\right|>19.5{\rm {}^\circ }`. Neither the background onset nor background litterfall mechanism is invoked for the seasonal-deciduous phenology algorithm. The algorithm allows a maximum of one onset period and one offset period each year. + +The algorithms for initiation of onset and offset periods use the winter and summer solstices as coordination signals. The period between winter and summer solstice is identified as :math:`{dayl}_{n} > {dayl}_{n-1}`, and the period between summer and winter solstice is identified as :math:`{dayl}_{n} < {dayl}_{n-1}`, where :math:`{dayl}_{n}` and :math:`{dayl}_{n-1}` are the day length(s) calculated for the current and previous timesteps, respectively, using + +.. math:: + :label: 20.45) dayl=2\cdot 13750.9871\cdot acos\left(\frac{-\sin (lat)\sin (decl)}{\cos (lat)\cos (decl)} \right), -where *lat* and *decl* are the latitude and solar declination (radians), -respectively, and the factor 13750.9871 is the number of seconds per -radian of hour-angle. +where *lat* and *decl* are the latitude and solar declination (radians), respectively, and the factor 13750.9871 is the number of seconds per radian of hour-angle. 14.3.1 Seasonal-Deciduous Onset Trigger ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The onset trigger for the seasonal-deciduous phenology algorithm is -based on an accumulated growing-degree-day approach (White et al., -1997). The growing-degree-day summation (:math:`{GDD}_{sum}`) is -initiated ( :math:`{GDD}_{sum} = 0`) when the phenological state is -dormant and the model timestep crosses the winter solstice. Once these -conditions are met, :math:`{GDD}_{sum}` is updated on each timestep as +The onset trigger for the seasonal-deciduous phenology algorithm is based on an accumulated growing-degree-day approach (White et al., 1997). The growing-degree-day summation (:math:`{GDD}_{sum}`) is initiated ( :math:`{GDD}_{sum} = 0`) when the phenological state is dormant and the model timestep crosses the winter solstice. Once these conditions are met, :math:`{GDD}_{sum}` is updated on each timestep as .. math:: - :label: ZEqnNum510730 + :label: ZEqnNum510730 GDD_{sum}^{n} =\left\{\begin{array}{l} {GDD_{sum}^{n-1} +\left(T_{s,3} -TKFRZ\right)f_{day} \qquad {\rm for\; }T_{s,3} >TKFRZ} \\ {GDD_{sum}^{n-1} \qquad \qquad \qquad {\rm for\; }T_{s,3} \le TKFRZ} \end{array}\right. -where :math:`{T}_{s,3}` (K) is the temperature of the third soil layer, and -:math:`f_{day} ={\Delta t\mathord{\left/ {\vphantom {\Delta t 86400}} \right. \kern-\nulldelimiterspace} 86400}` . -The onset period is initiated if :math:`GDD_{sum} >GDD_{sum\_ crit}` , -where +where :math:`{T}_{s,3}` (K) is the temperature of the third soil layer, and :math:`f_{day} ={\Delta t\mathord{\left/ {\vphantom {\Delta t 86400}} \right.} 86400}`. The onset period is initiated if :math:`GDD_{sum} >GDD_{sum\_ crit}`, where .. math:: - :label: ZEqnNum598907 + :label: ZEqnNum598907 GDD_{sum\_ crit} =\exp \left(4.8+0.13{\kern 1pt} \left(T_{2m,ann\_ avg} -TKFRZ\right)\right) -and where :math:`{T}_{2m,ann\_avg}` (K) is the annual average of -the 2m air temperature, and TKFRZ is the freezing point of water (273.15 K). The following control variables are set when a new onset growth -period is initiated: +and where :math:`{T}_{2m,ann\_avg}` (K) is the annual average of the 2m air temperature, and TKFRZ is the freezing point of water (273.15 K). The following control variables are set when a new onset growth period is initiated: .. math:: - :label: 20.48) + :label: 20.48) GDD_{sum} =0 .. math:: - :label: 20.49) + :label: 20.49) t_{onset} =86400\cdot n_{days\_ on} , -where :math:`{n}_{days\_on}` is set to a constant value of 30 days. -Fluxes from storage into transfer pools occur in the timestep when a new -onset growth period is initiated. Carbon fluxes are: +where :math:`{n}_{days\_on}` is set to a constant value of 30 days. Fluxes from storage into transfer pools occur in the timestep when a new onset growth period is initiated. Carbon fluxes are: .. math:: - :label: ZEqnNum904388 + :label: ZEqnNum904388 - CF_{leaf\_ stor,leaf\_ xfer} ={f_{stor,xfer} CS_{leaf\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{leaf\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{leaf\_ stor,leaf\_ xfer} ={f_{stor,xfer} CS_{leaf\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{leaf\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.51) + :label: 20.51) - CF_{froot\_ stor,froot\_ xfer} ={f_{stor,xfer} CS_{froot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{froot\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{froot\_ stor,froot\_ xfer} ={f_{stor,xfer} CS_{froot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{froot\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.52) + :label: 20.52) - CF_{livestem\_ stor,livestem\_ xfer} ={f_{stor,xfer} CS_{livestem\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{livestem\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{livestem\_ stor,livestem\_ xfer} ={f_{stor,xfer} CS_{livestem\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{livestem\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.53) + :label: 20.53) - CF_{deadstem\_ stor,deadstem\_ xfer} ={f_{stor,xfer} CS_{deadstem\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{deadstem\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{deadstem\_ stor,deadstem\_ xfer} ={f_{stor,xfer} CS_{deadstem\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{deadstem\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.54) + :label: 20.54) - CF_{livecroot\_ stor,livecroot\_ xfer} ={f_{stor,xfer} CS_{livecroot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{livecroot\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{livecroot\_ stor,livecroot\_ xfer} ={f_{stor,xfer} CS_{livecroot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{livecroot\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.55) + :label: 20.55) - CF_{deadcroot\_ stor,deadcroot\_ xfer} ={f_{stor,xfer} CS_{deadcroot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{deadcroot\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{deadcroot\_ stor,deadcroot\_ xfer} ={f_{stor,xfer} CS_{deadcroot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{deadcroot\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: ZEqnNum195642 + :label: ZEqnNum195642 - CF_{gresp\_ stor,gresp\_ xfer} ={f_{stor,xfer} CS_{gresp\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{gresp\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + CF_{gresp\_ stor,gresp\_ xfer} ={f_{stor,xfer} CS_{gresp\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} CS_{gresp\_ stor} \Delta t}} \right.} \Delta t} and the associated nitrogen fluxes are: .. math:: - :label: ZEqnNum812152 + :label: ZEqnNum812152 - NF_{leaf\_ stor,leaf\_ xfer} ={f_{stor,xfer} NS_{leaf\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{leaf\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{leaf\_ stor,leaf\_ xfer} ={f_{stor,xfer} NS_{leaf\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{leaf\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.58) + :label: 20.58) - NF_{froot\_ stor,froot\_ xfer} ={f_{stor,xfer} NS_{froot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{froot\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{froot\_ stor,froot\_ xfer} ={f_{stor,xfer} NS_{froot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{froot\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.59) + :label: 20.59) - NF_{livestem\_ stor,livestem\_ xfer} ={f_{stor,xfer} NS_{livestem\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{livestem\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{livestem\_ stor,livestem\_ xfer} ={f_{stor,xfer} NS_{livestem\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{livestem\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.60) + :label: 20.60) - NF_{deadstem\_ stor,deadstem\_ xfer} ={f_{stor,xfer} NS_{deadstem\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{deadstem\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{deadstem\_ stor,deadstem\_ xfer} ={f_{stor,xfer} NS_{deadstem\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{deadstem\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: 20.61) + :label: 20.61) - NF_{livecroot\_ stor,livecroot\_ xfer} ={f_{stor,xfer} NS_{livecroot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{livecroot\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{livecroot\_ stor,livecroot\_ xfer} ={f_{stor,xfer} NS_{livecroot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{livecroot\_ stor} \Delta t}} \right.} \Delta t} .. math:: - :label: ZEqnNum605338 + :label: ZEqnNum605338 - NF_{deadcroot\_ stor,deadcroot\_ xfer} ={f_{stor,xfer} NS_{deadcroot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{deadcroot\_ stor} \Delta t}} \right. \kern-\nulldelimiterspace} \Delta t} + NF_{deadcroot\_ stor,deadcroot\_ xfer} ={f_{stor,xfer} NS_{deadcroot\_ stor} \mathord{\left/ {\vphantom {f_{stor,xfer} NS_{deadcroot\_ stor} \Delta t}} \right.} \Delta t} -where :math:`{f}_{stor,xfer}` is the fraction of current storage -pool moved into the transfer pool for display over the incipient onset -period. This fraction is set to 0.5, based on the observation that -seasonal deciduous trees are capable of replacing their canopies from -storage reserves in the event of a severe early-season disturbance such -as frost damage or defoliation due to insect herbivory. +where :math:`{f}_{stor,xfer}` is the fraction of current storage pool moved into the transfer pool for display over the incipient onset period. This fraction is set to 0.5, based on the observation that seasonal deciduous trees are capable of replacing their canopies from storage reserves in the event of a severe early-season disturbance such as frost damage or defoliation due to insect herbivory. -If the onset criterion (:math:`{GDD}_{sum} > {GDD}_{sum\_crit}`) is not met before the summer solstice, -then :math:`{GDD}_{sum}` is set to 0.0 and the growing-degree-day -accumulation will not start again until the following winter solstice. -This mechanism prevents the initiation of very short growing seasons -late in the summer in cold climates. The onset counter is decremented on -each time step after initiation of the onset period, until it reaches -zero, signaling the end of the onset period: +If the onset criterion (:math:`{GDD}_{sum} > {GDD}_{sum\_crit}`) is not met before the summer solstice, then :math:`{GDD}_{sum}` is set to 0.0 and the growing-degree-day accumulation will not start again until the following winter solstice. This mechanism prevents the initiation of very short growing seasons late in the summer in cold climates. The onset counter is decremented on each time step after initiation of the onset period, until it reaches zero, signaling the end of the onset period: .. math:: - :label: 20.63) + :label: 20.63) t_{onfset}^{n} =t_{onfset}^{n-1} -\Delta t -14.3.2 Seasonal-Deciduous Offset Trigger +14.3.2 Seasonal-Deciduous Offset Trigger ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -After the completion of an onset period, and once past the summer -solstice, the offset (litterfall) period is triggered when daylength is -shorter than 39300 s. The offset counter is set at the initiation of the -offset period: :math:`t_{offset} =86400\cdot n_{days\_ off}` , where -:math:`{n}_{days\_off}` is set to a constant value of 15 days. The -offset counter is decremented on each time step after initiation of the -offset period, until it reaches zero, signaling the end of the offset -period: +After the completion of an onset period, and once past the summer solstice, the offset (litterfall) period is triggered when daylength is shorter than 39300 s. The offset counter is set at the initiation of the offset period: :math:`t_{offset} =86400\cdot n_{days\_ off}`, where :math:`{n}_{days\_off}` is set to a constant value of 15 days. The offset counter is decremented on each time step after initiation of the offset period, until it reaches zero, signaling the end of the offset period: .. math:: - :label: 20.64) + :label: 20.64) t_{offset}^{n} =t_{offset}^{n-1} -\Delta t Stress-Deciduous Phenology ------------------------------- -The stress-deciduous phenology algorithm was developed specifically for -the CLM based in part on the grass phenology model proposed by White et -al. (1997). The algorithm handles phenology for vegetation types such as -grasses and tropical drought-deciduous trees that respond to both cold -and drought-stress signals, and that can have multiple growing seasons -per year. The algorithm also allows for the possibility that leaves -might persist year-round in the absence of a suitable stress trigger. In -that case the phenology switches to an evergreen habit, maintaining a -marginally-deciduous leaf longevity (one year) until the occurrence of -the next stress trigger. +The stress-deciduous phenology algorithm was developed specifically for the CLM based in part on the grass phenology model proposed by White et al. (1997). The algorithm handles phenology for vegetation types such as grasses and tropical drought-deciduous trees that respond to both cold and drought-stress signals, and that can have multiple growing seasons per year. The algorithm also allows for the possibility that leaves might persist year-round in the absence of a suitable stress trigger. In that case the phenology switches to an evergreen habit, maintaining a marginally-deciduous leaf longevity (one year) until the occurrence of the next stress trigger. 14.4.1 Stress-Deciduous Onset Triggers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In climates that are warm year-round, onset triggering depends on soil -water availability. At the beginning of a dormant period (end of -previous offset period), an accumulated soil water index -(:math:`{SWI}_{sum}`, d) is initialized (:math:`{SWI}_{sum} = 0`), with subsequent accumulation calculated as: +In climates that are warm year-round, onset triggering depends on soil water availability. At the beginning of a dormant period (end of previous offset period), an accumulated soil water index (:math:`{SWI}_{sum}`, d) is initialized (:math:`{SWI}_{sum} = 0`), with subsequent accumulation calculated as: .. math:: - :label: ZEqnNum503826 + :label: ZEqnNum503826 SWI_{sum}^{n} =\left\{\begin{array}{l} {SWI_{sum}^{n-1} +f_{day} \qquad {\rm for\; }\Psi _{s,3} \ge \Psi _{onset} } \\ {SWI_{sum}^{n-1} \qquad \qquad {\rm for\; }\Psi _{s,3} <\Psi _{onset} } \end{array}\right. -where :math:`\Psi`\ :sub:`s,3` is the soil water potential (MPa) -in the third soil layer and :math:`{\Psi}_{onset} = -0.6 MPa` -is the onset soil water potential threshold. Onset triggering is -possible once :math:`{SWI}_{sum} > 15`. To avoid spurious onset triggering due to -soil moisture in the third soil layer exceeding the threshold due only to -soil water suction of water from deeper in the soil column, an additional precipitation trigger is included which requires -at least 20 mm of rain over the previous 10 days :ref:`(Dahlin et al., 2015) `. If the cold climate -growing degree-day accumulator is not active at the time when the soil moisture and precipitation -thresholds are reached (see below), and if the daylength is greater than 6 -hours, then onset is triggered. Except as noted below, -:math:`{SWI}_{sum}` continues to accumulate according to Eq. :eq:`ZEqnNum503826` during -the dormant period if the daylength criterion prevents onset triggering, -and onset is then triggered at the timestep when daylength exceeds 6 -hours. - -In climates with a cold season, onset triggering depends on both -accumulated soil temperature summation and adequate soil moisture. At -the beginning of a dormant period a freezing day accumulator -(:math:`{FD}_{sum}`, d) is initialized (:math:`{FD}_{sum} = 0`), -with subsequent accumulation calculated as: - -.. math:: - :label: 20.66) +where :math:`\Psi`\ :sub:`s,3` is the soil water potential (MPa) in the third soil layer and :math:`{\Psi}_{onset} = -0.6 MPa` is the onset soil water potential threshold. Onset triggering is possible once :math:`{SWI}_{sum} > 15`. To avoid spurious onset triggering due to soil moisture in the third soil layer exceeding the threshold due only to soil water suction of water from deeper in the soil column, an additional precipitation trigger is included which requires at least 20 mm of rain over the previous 10 days :ref:`(Dahlin et al., 2015) `. If the cold climate growing degree-day accumulator is not active at the time when the soil moisture and precipitation thresholds are reached (see below), and if the daylength is greater than 6 hours, then onset is triggered. Except as noted below, :math:`{SWI}_{sum}` continues to accumulate according to Eq. :eq:`ZEqnNum503826` during the dormant period if the daylength criterion prevents onset triggering, and onset is then triggered at the timestep when daylength exceeds 6 hours. + +In climates with a cold season, onset triggering depends on both accumulated soil temperature summation and adequate soil moisture. At the beginning of a dormant period a freezing day accumulator (:math:`{FD}_{sum}`, d) is initialized (:math:`{FD}_{sum} = 0`), with subsequent accumulation calculated as: + +.. math:: + :label: 20.66) FD_{sum}^{n} =\left\{\begin{array}{l} {FD_{sum}^{n-1} +f_{day} \qquad {\rm for\; }T_{s,3} >TKFRZ} \\ {FD_{sum}^{n-1} \qquad \qquad {\rm for\; }T_{s,3} \le TKFRZ} \end{array}\right. . -If :math:`{FD}_{sum} > 15` during the dormant period, then a -cold-climate onset triggering criterion is introduced, following exactly -the growing degree-day summation (:math:`{GDD}_{sum}`) logic of Eqs. :eq:`ZEqnNum510730` -and :eq:`ZEqnNum598907`. At that time :math:`{SWI}_{sum}` is reset -(:math:`{SWI}_{sum} = 0`). Onset triggering under these conditions -depends on meeting all three of the following criteria: -:math:`{SWI}_{sum} > 15`, :math:`{GDD}_{sum} > {GDD}_{sum\_crit}`, and daylength greater than 6 hrs. +If :math:`{FD}_{sum} > 15` during the dormant period, then a cold-climate onset triggering criterion is introduced, following exactly the growing degree-day summation (:math:`{GDD}_{sum}`) logic of Eqs. :eq:`ZEqnNum510730` and :eq:`ZEqnNum598907`. At that time :math:`{SWI}_{sum}` is reset (:math:`{SWI}_{sum} = 0`). Onset triggering under these conditions depends on meeting all three of the following criteria: :math:`{SWI}_{sum} > 15`, :math:`{GDD}_{sum} > {GDD}_{sum\_crit}`, and daylength greater than 6 hrs. -The following control variables are set when a new onset growth period -is initiated: :math:`{SWI}_{sum} = 0`, :math:`{FD}_{sum} = 0`, :math:`{GDD}_{sum} = 0`, :math:`{n}_{days\_active} = 0`, and -:math:`t_{onset} = 86400\cdot n_{days\_ on}` , where :math:`{n}_{days\_on}` is set to a constant value of 30 days. Fluxes -from storage into transfer pools occur in the timestep when a new onset growth period is initiated, and are handled identically to Eqs. :eq:`ZEqnNum904388` -:eq:`ZEqnNum195642` for -carbon fluxes, and to Eqs. :eq:`ZEqnNum812152` - :eq:`ZEqnNum605338` for nitrogen fluxes. The onset counter is decremented on each time step after initiation of the onset period, -until it reaches zero, signaling the end of the onset period: +The following control variables are set when a new onset growth period is initiated: :math:`{SWI}_{sum} = 0`, :math:`{FD}_{sum} = 0`, :math:`{GDD}_{sum} = 0`, :math:`{n}_{days\_active} = 0`, and :math:`t_{onset} = 86400\cdot n_{days\_ on}`, where :math:`{n}_{days\_on}` is set to a constant value of 30 days. Fluxes from storage into transfer pools occur in the timestep when a new onset growth period is initiated, and are handled identically to Eqs. :eq:`ZEqnNum904388` -:eq:`ZEqnNum195642` for carbon fluxes, and to Eqs. :eq:`ZEqnNum812152` - :eq:`ZEqnNum605338` for nitrogen fluxes. The onset counter is decremented on each time step after initiation of the onset period, until it reaches zero, signaling the end of the onset period: .. math:: - :label: 20.67) + :label: 20.67) t_{onfset}^{n} =t_{onfset}^{n-1} -\Delta t 14.4.2 Stress-Deciduous Offset Triggers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Any one of the following three conditions is sufficient to initiate an -offset period for the stress-deciduous phenology algorithm: sustained -period of dry soil, sustained period of cold temperature, or daylength -shorter than 6 hours. Offset triggering due to dry soil or cold -temperature conditions is only allowed once the most recent onset period -is complete. Dry soil condition is evaluated with an offset soil water -index accumulator (:math:`{OSWI}_{sum}`, d). To test for a sustained -period of dry soils, this control variable can increase or decrease, as -follows: +Any one of the following three conditions is sufficient to initiate an offset period for the stress-deciduous phenology algorithm: sustained period of dry soil, sustained period of cold temperature, or daylength shorter than 6 hours. Offset triggering due to dry soil or cold temperature conditions is only allowed once the most recent onset period is complete. Dry soil condition is evaluated with an offset soil water index accumulator (:math:`{OSWI}_{sum}`, d). To test for a sustained period of dry soils, this control variable can increase or decrease, as follows: .. math:: - :label: 20.68) + :label: 20.68) OSWI_{sum}^{n} =\left\{\begin{array}{l} {OSWI_{sum}^{n-1} +f_{day} \qquad \qquad \qquad {\rm for\; }\Psi _{s,3} \le \Psi _{offset} } \\ {{\rm max}\left(OSWI_{sum}^{n-1} -f_{day} ,0\right)\qquad {\rm for\; }\Psi _{s,3} >\Psi _{onset} } \end{array}\right. -where :math:`{\Psi}_{offset} = -0.8 MPa` is the offset soil -water potential threshold. An offset period is triggered if the previous -onset period is complete and :math:`{OSWI}_{sum}` -:math:`\mathrm{\ge}` :math:`{OSWI}_{sum\_crit}`, where :math:`{OSWI}_{sum\_crit} = 15`. +where :math:`{\Psi}_{offset} = -0.8 MPa` is the offset soil water potential threshold. An offset period is triggered if the previous onset period is complete and :math:`{OSWI}_{sum}` :math:`\mathrm{\ge}` :math:`{OSWI}_{sum\_crit}`, where :math:`{OSWI}_{sum\_crit} = 15`. -The cold temperature trigger is calculated with an offset freezing day -accumulator (:math:`{OFD}_{sum}`, d). To test for a sustained period -of cold temperature, this variable can increase or decrease, as follows: +The cold temperature trigger is calculated with an offset freezing day accumulator (:math:`{OFD}_{sum}`, d). To test for a sustained period of cold temperature, this variable can increase or decrease, as follows: .. math:: - :label: 20.69) + :label: 20.69) OFD_{sum}^{n} =\left\{\begin{array}{l} {OFD_{sum}^{n-1} +f_{day} \qquad \qquad \qquad {\rm for\; }T_{s,3} \le TKFRZ} \\ {{\rm max}\left(OFD_{sum}^{n-1} -f_{day} ,0\right)\qquad \qquad {\rm for\; }T_{s,3} >TKFRZ} \end{array}\right. -An offset period is triggered if the previous onset period is complete -and :math:`{OFD}_{sum} > {OFD}_{sum\_crit}`, -where :math:`{OFD}_{sum\_crit} = 15`. +An offset period is triggered if the previous onset period is complete and :math:`{OFD}_{sum} > {OFD}_{sum\_crit}`, where :math:`{OFD}_{sum\_crit} = 15`. -The offset counter is set at the initiation of the offset period: -:math:`t_{offset} =86400\cdot n_{days\_ off}` , where -:math:`{n}_{days\_off}` is set to a constant value of 15 days. The -offset counter is decremented on each time step after initiation of the -offset period, until it reaches zero, signaling the end of the offset -period: +The offset counter is set at the initiation of the offset period: :math:`t_{offset} =86400\cdot n_{days\_ off}`, where :math:`{n}_{days\_off}` is set to a constant value of 15 days. The offset counter is decremented on each time step after initiation of the offset period, until it reaches zero, signaling the end of the offset period: .. math:: - :label: 20.70) + :label: 20.70) t_{offset}^{n} =t_{offset}^{n-1} -\Delta t 14.4.3 Stress-Deciduous: Long Growing Season ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Under conditions when the stress-deciduous conditions triggering offset -are not met for one year or longer, the stress-deciduous algorithm -shifts toward the evergreen behavior. This can happen in cases where a -stress-deciduous vegetation type is assigned in a climate where suitably -strong stresses occur less frequently than once per year. This condition -is evaluated by tracking the number of days since the beginning of the -most recent onset period (:math:`{n}_{days\_active}`, d). At the end -of an offset period :math:`{n}_{days\_active}` is reset to 0. A long -growing season control variable (*LGS*, range 0 to 1) is calculated as: +Under conditions when the stress-deciduous conditions triggering offset are not met for one year or longer, the stress-deciduous algorithm shifts toward the evergreen behavior. This can happen in cases where a stress-deciduous vegetation type is assigned in a climate where suitably strong stresses occur less frequently than once per year. This condition is evaluated by tracking the number of days since the beginning of the most recent onset period (:math:`{n}_{days\_active}`, d). At the end of an offset period :math:`{n}_{days\_active}` is reset to 0. A long growing season control variable (*LGS*, range 0 to 1) is calculated as: .. math:: - :label: 20.71) + :label: 20.71) - LGS=\left\{\begin{array}{l} {0\qquad \qquad \qquad {\rm for\; }n_{days\_ active} <365} \\ {\left({n_{days\_ active} \mathord{\left/ {\vphantom {n_{days\_ active} 365}} \right. \kern-\nulldelimiterspace} 365} \right)-1\qquad {\rm for\; }365\le n_{days\_ active} <730} \\ {1\qquad \qquad \qquad {\rm for\; }n_{days\_ active} \ge 730} \end{array}\right. . + LGS=\left\{\begin{array}{l} {0\qquad \qquad \qquad {\rm for\; }n_{days\_ active} <365} \\ {\left({n_{days\_ active} \mathord{\left/ {\vphantom {n_{days\_ active} 365}} \right.} 365} \right)-1\qquad {\rm for\; }365\le n_{days\_ active} <730} \\ {1\qquad \qquad \qquad {\rm for\; }n_{days\_ active} \ge 730} \end{array}\right. . The rate coefficient for background litterfall (:math:`{r}_{bglf}`, s\ :sup:`-1`) is calculated as a function of *LGS*: .. math:: - :label: 20.72) + :label: 20.72) r_{bglf} =\frac{LGS}{\tau _{leaf} \cdot 365\cdot 86400} -where :math:`{\tau}_{leaf}` is the leaf longevity. The result is a shift to continuous litterfall as -:math:`{n}_{days\_active}` increases from 365 to 730. When a new offset period is triggered :math:`{r}_{bglf}` is set to 0. +where :math:`{\tau}_{leaf}` is the leaf longevity. The result is a shift to continuous litterfall as :math:`{n}_{days\_active}` increases from 365 to 730. When a new offset period is triggered :math:`{r}_{bglf}` is set to 0. The rate coefficient for background onset growth from the transfer pools ( :math:`{r}_{bgtr}`, s\ :sup:`-1`) also depends on *LGS*, as: .. math:: - :label: 20.73) + :label: 20.73) r_{bgtr} =\frac{LGS}{365\cdot 86400} . On each timestep with :math:`{r}_{bgtr}` :math:`\neq` 0, carbon fluxes from storage to transfer pools are calculated as: .. math:: - :label: 20.74) + :label: 20.74) CF_{leaf\_ stor,leaf\_ xfer} =CS_{leaf\_ stor} r_{bgtr} .. math:: - :label: 20.75) + :label: 20.75) CF_{froot\_ stor,froot\_ xfer} =CS_{froot\_ stor} r_{bgtr} .. math:: - :label: 20.76) + :label: 20.76) CF_{livestem\_ stor,livestem\_ xfer} =CS_{livestem\_ stor} r_{bgtr} .. math:: - :label: 20.77) + :label: 20.77) CF_{deadstem\_ stor,deadstem\_ xfer} =CS_{deadstem\_ stor} r_{bgtr} .. math:: - :label: 20.78) + :label: 20.78) CF_{livecroot\_ stor,livecroot\_ xfer} =CS_{livecroot\_ stor} r_{bgtr} .. math:: - :label: 20.79) + :label: 20.79) CF_{deadcroot\_ stor,deadcroot\_ xfer} =CS_{deadcroot\_ stor} r_{bgtr} , with corresponding nitrogen fluxes: .. math:: - :label: 20.80) + :label: 20.80) NF_{leaf\_ stor,leaf\_ xfer} =NS_{leaf\_ stor} r_{bgtr} .. math:: - :label: 20.81) + :label: 20.81) NF_{froot\_ stor,froot\_ xfer} =NS_{froot\_ stor} r_{bgtr} .. math:: - :label: 20.82) + :label: 20.82) NF_{livestem\_ stor,livestem\_ xfer} =NS_{livestem\_ stor} r_{bgtr} .. math:: - :label: 20.83) + :label: 20.83) NF_{deadstem\_ stor,deadstem\_ xfer} =NS_{deadstem\_ stor} r_{bgtr} .. math:: - :label: 20.84) + :label: 20.84) NF_{livecroot\_ stor,livecroot\_ xfer} =NS_{livecroot\_ stor} r_{bgtr} .. math:: - :label: 20.85) + :label: 20.85) NF_{deadcroot\_ stor,deadcroot\_ xfer} =NS_{deadcroot\_ stor} r_{bgtr} . -The result, in conjunction with the treatment of background onset -growth, is a shift to continuous transfer from storage to display pools -at a rate that would result in complete turnover of the storage pools in -one year at steady state, once *LGS* reaches 1 (i.e. after two years -without stress-deciduous offset conditions). If and when conditions -cause stress-deciduous triggering again, :math:`{r}_{bgtr}` is rest -to 0. +The result, in conjunction with the treatment of background onset growth, is a shift to continuous transfer from storage to display pools at a rate that would result in complete turnover of the storage pools in one year at steady state, once *LGS* reaches 1 (i.e. after two years without stress-deciduous offset conditions). If and when conditions cause stress-deciduous triggering again, :math:`{r}_{bgtr}` is rest to 0. Litterfall Fluxes Merged to the Column Level ------------------------------------------------- -CLM uses three litter pools, defined on the basis of commonly measured -chemical fractionation of fresh litter into labile (LIT1 = hot water and -alcohol soluble fraction), cellulose/hemicellulose (LIT2 = acid soluble -fraction) and remaining material, referred to here for convenience as -lignin (LIT3 = acid insoluble fraction) (Aber et al., 1990; Taylor et -al., 1989). While multiple plant functional types can coexist on a -single CLM soil column, each soil column includes a single instance of -the litter pools. Fluxes entering the litter pools due to litterfall are -calculated using a weighted average of the fluxes originating at the PFT -level. Carbon fluxes are calculated as: +CLM uses three litter pools, defined on the basis of commonly measured chemical fractionation of fresh litter into labile (LIT1 = hot water and alcohol soluble fraction), cellulose/hemicellulose (LIT2 = acid soluble fraction) and remaining material, referred to here for convenience as lignin (LIT3 = acid insoluble fraction) (Aber et al., 1990; Taylor et al., 1989). While multiple plant functional types can coexist on a single CLM soil column, each soil column includes a single instance of the litter pools. Fluxes entering the litter pools due to litterfall are calculated using a weighted average of the fluxes originating at the PFT level. Carbon fluxes are calculated as: .. math:: - :label: 20.86) + :label: 20.86) CF_{leaf,lit1} =\sum _{p=0}^{npfts}CF_{leaf,litter} f_{lab\_ leaf,p} wcol_{p} .. math:: - :label: 20.87) + :label: 20.87) CF_{leaf,lit2} =\sum _{p=0}^{npfts}CF_{leaf,litter} f_{cel\_ leaf,p} wcol_{p} .. math:: - :label: 20.88) + :label: 20.88) CF_{leaf,lit3} =\sum _{p=0}^{npfts}CF_{leaf,litter} f_{lig\_ leaf,p} wcol_{p} .. math:: - :label: 20.89) + :label: 20.89) CF_{froot,lit1} =\sum _{p=0}^{npfts}CF_{froot,litter} f_{lab\_ froot,p} wcol_{p} .. math:: - :label: 20.90) + :label: 20.90) CF_{froot,lit2} =\sum _{p=0}^{npfts}CF_{froot,litter} f_{cel\_ froot,p} wcol_{p} .. math:: - :label: 20.91) + :label: 20.91) CF_{froot,lit3} =\sum _{p=0}^{npfts}CF_{froot,litter} f_{lig\_ froot,p} wcol_{p} , -where :math:`{f}_{lab\_leaf,p}`, :math:`{f}_{cel\_leaf,p}`, and -:math:`{f}_{lig\_leaf,p}` are the labile, cellulose/hemicellulose, -and lignin fractions of leaf litter for PFT *p*, -:math:`{f}_{lab\_froot,p}`, :math:`{f}_{cel\_froot,p}`, and -:math:`{f}_{lig\_froot,p}` are the labile, cellulose/hemicellulose, -and lignin fractions of fine root litter for PFT *p*, -:math:`{wtcol}_{p}` is the weight relative to the column for PFT -*p*, and *p* is an index through the plant functional types occurring on -a column. Nitrogen fluxes to the litter pools are assumed to follow the -C:N of the senescent tissue, and so are distributed using the same -fractions used for carbon fluxes: +where :math:`{f}_{lab\_leaf,p}`, :math:`{f}_{cel\_leaf,p}`, and :math:`{f}_{lig\_leaf,p}` are the labile, cellulose/hemicellulose, and lignin fractions of leaf litter for PFT *p*, :math:`{f}_{lab\_froot,p}`, :math:`{f}_{cel\_froot,p}`, and :math:`{f}_{lig\_froot,p}` are the labile, cellulose/hemicellulose, and lignin fractions of fine root litter for PFT *p*, :math:`{wtcol}_{p}` is the weight relative to the column for PFT *p*, and *p* is an index through the plant functional types occurring on a column. Nitrogen fluxes to the litter pools are assumed to follow the C:N of the senescent tissue, and so are distributed using the same fractions used for carbon fluxes: .. math:: - :label: 20.92) + :label: 20.92) NF_{leaf,lit1} =\sum _{p=0}^{npfts}NF_{leaf,litter} f_{lab\_ leaf,p} wcol_{p} .. math:: - :label: 20.93) + :label: 20.93) NF_{leaf,lit2} =\sum _{p=0}^{npfts}NF_{leaf,litter} f_{cel\_ leaf,p} wcol_{p} .. math:: - :label: 20.94) + :label: 20.94) NF_{leaf,lit3} =\sum _{p=0}^{npfts}NF_{leaf,litter} f_{lig\_ leaf,p} wcol_{p} .. math:: - :label: 20.95) + :label: 20.95) NF_{froot,lit1} =\sum _{p=0}^{npfts}NF_{froot,litter} f_{lab\_ froot,p} wcol_{p} .. math:: - :label: 20.96) + :label: 20.96) NF_{froot,lit2} =\sum _{p=0}^{npfts}NF_{froot,litter} f_{cel\_ froot,p} wcol_{p} .. math:: - :label: 20.97) + :label: 20.97) NF_{froot,lit3} =\sum _{p=0}^{npfts}NF_{froot,litter} f_{lig\_ froot,p} wcol_{p} . diff --git a/doc/source/tech_note/index.rst b/doc/source/tech_note/index.rst index 5baaa61540..429788240f 100644 --- a/doc/source/tech_note/index.rst +++ b/doc/source/tech_note/index.rst @@ -11,10 +11,7 @@ CLM Technical Note .. important:: - **You are viewing the documentation for** |version_label_bold|. **There are separate - versions of this documentation for each maintained CTSM release (e.g., CLM5.0) and for - the latest development code. Use the menu at the top left to select the version of CTSM - you are using.** + **You are viewing the documentation for** |version_label_bold|. **There are separate versions of this documentation for each maintained CTSM release (e.g., CLM5.0) and for the latest development code. Use the menu at the top left to select the version of CTSM you are using.** .. toctree:: :maxdepth: 2 @@ -52,4 +49,4 @@ CLM Technical Note Isotopes/CLM50_Tech_Note_Isotopes.rst Land-Only_Mode/CLM50_Tech_Note_Land-Only_Mode.rst References/CLM50_Tech_Note_References.rst - + diff --git a/doc/source/users_guide/adding-new-resolutions/Adding-New-Resolutions-or-New-Files-to-the-build-namelist-Database.rst b/doc/source/users_guide/adding-new-resolutions/Adding-New-Resolutions-or-New-Files-to-the-build-namelist-Database.rst index a828c37e98..78edaaf629 100644 --- a/doc/source/users_guide/adding-new-resolutions/Adding-New-Resolutions-or-New-Files-to-the-build-namelist-Database.rst +++ b/doc/source/users_guide/adding-new-resolutions/Adding-New-Resolutions-or-New-Files-to-the-build-namelist-Database.rst @@ -1,26 +1,14 @@ -.. _adding-resolutions: - .. include:: ../substitutions.rst +.. _adding-resolutions: + ======================== Adding New Resolutions ======================== -In the last chapter we gave the details on how to create new files for input into CLM. -These files could be either global resolutions, regional-grids or even a single grid point. -If you want to easily have these files available for continued use in your development you will then want to include them in the build-namelist database so that build-namelist can easily find them for you. -You can deal with them, just by putting the settings in the ``user_nl_clm namelist`` file, or by using ``CLM_USRDAT_NAME``. -Another way to deal with them is to enter them into the database for build-namelist, so that build-namelist can find them for you. -This keeps one central database for all your files, rather than having multiple locations to keep track of files. -If you have a LOT of files to keep track of it also might be easier than keeping track by hand, especially if you have to periodically update your files. -If you just have a few quick experiments to try, for a short time period you might be best off using the other methods mentioned above. - -There are two parts to adding files to the build-namelist database. -The first part is adding new resolution names which is done in the ``$CTSMROOT/bld/namelist_files/namelist_definition_clm4_5.xml`` file. -You can then use the new resolution by using ``CLM_USRDAT_NAME``. -If you also want to be able to give the resolution into **create_newcase** -- you'll need to add the grid to the ``$CIMEROOT/config/cesm/config_grid.xml`` file. +In the last chapter we gave the details on how to create new files for input into CLM. These files could be either global resolutions, regional-grids or even a single grid point. If you want to easily have these files available for continued use in your development you will then want to include them in the build-namelist database so that build-namelist can easily find them for you. You can deal with them, just by putting the settings in the ``user_nl_clm namelist`` file, or by using ``CLM_USRDAT_NAME``. Another way to deal with them is to enter them into the database for build-namelist, so that build-namelist can find them for you. This keeps one central database for all your files, rather than having multiple locations to keep track of files. If you have a LOT of files to keep track of it also might be easier than keeping track by hand, especially if you have to periodically update your files. If you just have a few quick experiments to try, for a short time period you might be best off using the other methods mentioned above. -The second part is actually adding the new filenames which is done in the ``$CTSMROOT/bld/namelist_files/namelist_defaults_clm4_5.xml`` file (``$CTSMROOT/bld/namelist_files/namelist_defaults_clm4_5_tools.xml`` file for CLM tools). -If you aren't adding any new resolutions, and you are just changing the files for existing resolutions, you don't need to edit the namelist_definition file. +There are two parts to adding files to the build-namelist database. The first part is adding new resolution names which is done in the ``$CTSMROOT/bld/namelist_files/namelist_definition_clm4_5.xml`` file. You can then use the new resolution by using ``CLM_USRDAT_NAME``. If you also want to be able to give the resolution into **create_newcase** -- you'll need to add the grid to the ``$CIMEROOT/config/cesm/config_grid.xml`` file. +The second part is actually adding the new filenames which is done in the ``$CTSMROOT/bld/namelist_files/namelist_defaults_clm4_5.xml`` file (``$CTSMROOT/bld/namelist_files/namelist_defaults_clm4_5_tools.xml`` file for CLM tools). If you aren't adding any new resolutions, and you are just changing the files for existing resolutions, you don't need to edit the namelist_definition file. diff --git a/doc/source/users_guide/adding-new-resolutions/Adding-Resolution-Names.rst b/doc/source/users_guide/adding-new-resolutions/Adding-Resolution-Names.rst index 337b6dfc5a..216de19f54 100644 --- a/doc/source/users_guide/adding-new-resolutions/Adding-Resolution-Names.rst +++ b/doc/source/users_guide/adding-new-resolutions/Adding-Resolution-Names.rst @@ -1,25 +1,16 @@ -.. _adding-resolution-names: - .. include:: ../substitutions.rst +.. _adding-resolution-names: + ========================= Adding Resolution Names ========================= -If you are adding files for new resolutions which aren't covered in the namelist_definition file -- you'll need to add them in. -The list of valid resolutions is in the id="res" entry in the ``$CTSMROOT/bld/namelist_files/namelist_definition_clm4_5.xml`` file. -You need to choose a name for your new resolution and simply add it to the comma delimited list of valid_values for the id="res" entry. -The convention for global Gaussian grids is number_of_latitudes x number_of_longitudes. -The convention for global finite volume grids is latitude_grid_size x longitude_grid_size where latitude and longitude is measured in degrees. -The convention for unstructured HOMME grids is nenp4, where corresponds to the resolution. -The higher is the higher the resolution. -So for example, ne60np4 is roughly half-degree while ne240np4 is roughly a eighth degree. -For regional or single-point datasets the names have a grid size number_of_latitudes x number_of_longitudes followed by an underscore and then a descriptive name such as a City name followed by an abbreviation for the Country in caps. -The only hard requirement is that names be unique for different grid files. Here's what the entry for resolutions looks like in the file: +If you are adding files for new resolutions which aren't covered in the namelist_definition file -- you'll need to add them in. The list of valid resolutions is in the id="res" entry in the ``$CTSMROOT/bld/namelist_files/namelist_definition_clm4_5.xml`` file. You need to choose a name for your new resolution and simply add it to the comma delimited list of valid_values for the id="res" entry. The convention for global Gaussian grids is number_of_latitudes x number_of_longitudes. The convention for global finite volume grids is latitude_grid_size x longitude_grid_size where latitude and longitude is measured in degrees. The convention for unstructured HOMME grids is nenp4, where corresponds to the resolution. The higher is the higher the resolution. So for example, ne60np4 is roughly half-degree while ne240np4 is roughly a eighth degree. For regional or single-point datasets the names have a grid size number_of_latitudes x number_of_longitudes followed by an underscore and then a descriptive name such as a City name followed by an abbreviation for the Country in caps. The only hard requirement is that names be unique for different grid files. Here's what the entry for resolutions looks like in the file: :: @@ -19,23 +16,17 @@ lnd/clm2/surfdata_map/surfdata_0.9x1.25_78pfts_CMIP6_simyr1850_c170824.nc Other ``fsurdat`` files are distinguished from this one by their resolution (hgrid), simulation year (sim_year) and prognostic crop (use_crop) attributes. - -To add or change the default filenames for CLM tools edit the ``$CTSMROOT/bld/namelist_files/namelist_defaults_|version|_tools.xml`` and either change an existing filename or add a new one. -Editing this file is similar to the ``namelist_defaults_clm4_5.xml`` talked about above. - +To add or change the default filenames for CLM tools edit the ``$CTSMROOT/bld/namelist_files/namelist_defaults_|version|_tools.xml`` and either change an existing filename or add a new one. Editing this file is similar to the ``namelist_defaults_clm4_5.xml`` talked about above. ---------------------------- What are the required files? ---------------------------- -Different types of simulations and different types of configurations for CLM require different lists of files. -The |version|-BGC or Carbon Nitrogen (cn) Biogeochemistry model for example requires ``stream_fldfilename_ndep`` files, which are NOT required by CLMSP. -Transient simulations also require transient datasets, and the names of these datasets are sometimes different from the static versions (sometimes both are required as in the dynamic PFT cases). - +Different types of simulations and different types of configurations for CLM require different lists of files. The |version|-BGC or Carbon Nitrogen (cn) Biogeochemistry model for example requires ``stream_fldfilename_ndep`` files, which are NOT required by CLMSP. Transient simulations also require transient datasets, and the names of these datasets are sometimes different from the static versions (sometimes both are required as in the dynamic PFT cases). -In the following table we list the different files used by CLM, they are listed in order of importance, dependencies, and customizing. -So the required files are all near the top, and the files used only under different conditions are listed later, and files with the fewest dependencies are near the top, as are the files that are least likely to be customized. +In the following table we list the different files used by CLM, they are listed in order of importance, dependencies, and customizing. So the required files are all near the top, and the files used only under different conditions are listed later, and files with the fewest dependencies are near the top, as are the files that are least likely to be customized. +.. _reqd-files-table: Table 3-1. Required Files for Different Configurations and Simulation Types --------------------------------------------------------------------------- diff --git a/doc/source/users_guide/adding-new-resolutions/index.rst b/doc/source/users_guide/adding-new-resolutions/index.rst index 88bbda3f5b..f524461395 100644 --- a/doc/source/users_guide/adding-new-resolutions/index.rst +++ b/doc/source/users_guide/adding-new-resolutions/index.rst @@ -3,10 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. _adding-new-resolutions-section: - .. include:: ../substitutions.rst +.. _adding-new-resolutions-section: + ##################################### Adding New Resolutions ##################################### diff --git a/doc/source/users_guide/index.rst b/doc/source/users_guide/index.rst index 45afc39c72..75a2949bec 100644 --- a/doc/source/users_guide/index.rst +++ b/doc/source/users_guide/index.rst @@ -13,10 +13,7 @@ .. important:: - **You are viewing the documentation for** |version_label_bold|. **There are separate - versions of this documentation for each maintained CTSM release (e.g., CLM5.0) and for - the latest development code. Use the menu at the top left to select the version of CTSM - you are using.** + **You are viewing the documentation for** |version_label_bold|. **There are separate versions of this documentation for each maintained CTSM release (e.g., CLM5.0) and for the latest development code. Use the menu at the top left to select the version of CTSM you are using.** .. toctree:: :maxdepth: 2 diff --git a/doc/source/users_guide/overview/getting-help.rst b/doc/source/users_guide/overview/getting-help.rst index 0378852a4b..74765d1ad0 100644 --- a/doc/source/users_guide/overview/getting-help.rst +++ b/doc/source/users_guide/overview/getting-help.rst @@ -1,13 +1,11 @@ -.. _getting-help: - .. include:: ../substitutions.rst +.. _getting-help: + ============== Getting Help ============== -In addition to this users-guide there are several other resources that are available to help you use |version|. The first one is the |cesmrelease| User's-Guide, which documents the entire process of creating cases with |cesmrelease|. -And next is the CIME User's Guide which goes over the scripts and infrastructure used for running |version| in |cesmrelease|. -The CESM bulletin board which is a web-site for exchanging information between users of CESM. There are also CLM web-pages specific for CLM, and finally there is an email address to report bugs that you find in |cesmrelease|. +In addition to this users-guide there are several other resources that are available to help you use |version|. The first one is the |cesmrelease| User's-Guide, which documents the entire process of creating cases with |cesmrelease|. And next is the CIME User's Guide which goes over the scripts and infrastructure used for running |version| in |cesmrelease|. The CESM bulletin board which is a web-site for exchanging information between users of CESM. There are also CLM web-pages specific for CLM, and finally there is an email address to report bugs that you find in |cesmrelease|. --------------------------- The CESM User's-Guide @@ -21,8 +19,7 @@ The CESM User's-Guide The CIME User's-Guide --------------------------- -The CIME Users'-Guide goes into the how to use the scripts and infrastructure of the CESM. -`CIME Users Guide `_ +The CIME Users'-Guide goes into the how to use the scripts and infrastructure of the CESM. `CIME Users Guide `_ ----------------------- The CESM Bulletin Board @@ -38,32 +35,30 @@ There is a rich and diverse set of people that use the CESM, and often it is use The CLM web pages ----------------- -The main CLM web page contains information on the CLM, it's history, developers, as well as downloads for previous model versions. There are also documentation text files in the $CTSMROOT/doc directory that give some quick information on using CLM. +The main `CLM web page `_ contains information on the CLM, its history, developers, as well as downloads for previous model versions. Some other links are available at the `CESM2 land component webpage `. There are also documentation text files in the `$CTSMROOT/doc directory `_ that give some quick information on using CLM. -`CLM web page `_ -`|cesmrelease| |version| web page `_ -`CLM Documentation Text Files `_ +Also note that several of the XML database files can help with namelist options, namelist defaults, or compsets. For the most recent release: -Also note that several of the XML database files can be viewed in a web browser to get a nice table of namelist options, namelist defaults, or compsets. Simply view them as a local file and bring up one of the following files: +- `$CTSMROOT/bld/namelist_files/namelist_definition_ctsm.xml `_ -- definition of latest CTSM namelist items. +- `$CTSMROOT/bld/namelist_files/namelist_defaults_ctsm.xml `_ -- default values for latest CTSM namelist items. +- `$CTSMROOT/cime_config/config_component.xml `_ -- definition of all the CLM specific XML variables. +- `$CTSMROOT/cime_config/config_compsets.xml `_ -- definition of all the CLM compsets. + +Some archives are available for previous versions: + +- `Archive of namelist_definition_clm4_0.xml `_ -- definition of CLM4.0 namelist items. +- `Archive of namelist_definition_clm4_5.xml `_ -- definition of CLM4.5/CLM5.0 namelist items. +- `Archive of namelist_defaults_clm4_0.xml `_ -- default values for CLM4.0 namelist items. +- `Archive of namelist_defaults_clm4_5.xml `_ -- default values for CLM4.5/CLM5.0 namelist items. -- `$CTSMROOT/bld/namelist_files/namelist_definition_clm4_0.xml `_ -- definition of CLM4.0 namelist items. -- `$CTSMROOT/bld/namelist_files/namelist_definition_clm4_5.xml `_ -- definition of CLM4.5/CLM5.0 namelist items. -- `$CTSMROOT/bld/namelist_files/namelist_defaults_clm4_0.xml `_ -- default values for CLM4.0 namelist items. -- `$CTSMROOT/bld/namelist_files/namelist_defaults_clm4_5.xml `_ -- default values for CLM4.5/CLM5.0 namelist items. -- `$CTSMROOT/cime_config/config_component.xml `_ -- definition of all the CLM specific XML variables. -- `$CTSMROOT/cime_config/config_compsets.xml `_ -- definition of all the CLM compsets. -- `$CTSMROOT/bld/namelist_files/history_fields_clm4_0.xml `_ -- definition of CLM4.0 history fields. -- `$CTSMROOT/bld/namelist_files/history_fields_clm4_5.xml `_ -- definition of CLM4.5/CLM5.0 history fields. ---------------------------- Reporting bugs in |version| ---------------------------- -If you have any problems, additional questions, bug reports, or any other feedback, please report it as an issue -on GitHub https://github.com/ESCOMP/ctsm/issues or for CIME scripts and infrastructure to https://github.com/ESMCI/CIME/issues. -Or send an email to -<`cesmhelp@cgd.ucar.edu `_> or <`ctsm-software@ucar.edu `_>. -If you find bad, wrong, or misleading information in this users guide report it as an issue on CTSM. +If you have any problems, additional questions, bug reports, or any other feedback, please report it as an issue on GitHub https://github.com/ESCOMP/ctsm/issues or for CIME scripts and infrastructure to https://github.com/ESMCI/CIME/issues. Or send an email to <`cesmhelp@cgd.ucar.edu `_> or <`ctsm-software@ucar.edu `_>. If you find bad, wrong, or misleading information in this users guide report it as an issue on CTSM. + +.. _acronyms-and-terms: --------------------------------------- Some Acronym's and Terms We'll be Using @@ -76,7 +71,7 @@ CESM Community Earth System Model (CESM). The coupled earth system model that CLM is a component of. CIME - The Common Infrastructure for Modeling the Earth (CIME - pronounced “SEAM”) provides a Case Control System for configuring, compiling and executing Earth system models, data and stub model components, a driver and associated tools and libraries. + The Common Infrastructure for Modeling the Earth (CIME - pronounced "SEAM") provides a Case Control System for configuring, compiling and executing Earth system models, data and stub model components, a driver and associated tools and libraries. CLM Community Land Model (CLM). The prognostically active land model component of CESM. @@ -108,7 +103,7 @@ CRUNCEP The Climate Research Unit (CRU) analysis of the NCEP atmosphere reanalysis atmosphere forcing data. This can be used to drive CLM with atmosphere forcing from 1901 to 2016. This data is updated every year, the version we are currently using is Version-7. The las CESM1.2.2 release used Version-4 data. CTSM - The Community Terrestrial Systems Model, of which |version| and CLM4.5 are namelist option sets of. CTSM is a wider community + The Community Terrestrial Systems Model, of which |version| and CLM4.5 are namelist option sets of. CTSM is a wider community that includes using CTSM for Numerical Weather Prediction (NWP) as well as climate. DATM @@ -126,7 +121,7 @@ ESMF Earth System Modeling Framework (ESMF). They are a software project that provides a software library to support Earth System modeling. We provide interfaces for ESMF as well as use their regridding capabilities for offline CLM tools. FATES - Functionally Assembled Terrestrial Ecosystem Simulator. This is being developed by the Next Generation Ecosystem Experiment Tropics’ (NGEE-T) + Functionally Assembled Terrestrial Ecosystem Simulator. This is being developed by the Next Generation Ecosystem Experiment Tropics' (NGEE-T) project and uses both |version| and the land model component of E3SM (Energy Exascale Earth System Model). FUN diff --git a/doc/source/users_guide/overview/index.rst b/doc/source/users_guide/overview/index.rst index b03eae4034..b0dcfcdd96 100644 --- a/doc/source/users_guide/overview/index.rst +++ b/doc/source/users_guide/overview/index.rst @@ -3,10 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. _overview_section: - .. include:: ../substitutions.rst +.. _overview_section: + ##################################### Overview ##################################### diff --git a/doc/source/users_guide/overview/introduction.rst b/doc/source/users_guide/overview/introduction.rst index a7a9ef434c..bc7c1fd82c 100644 --- a/doc/source/users_guide/overview/introduction.rst +++ b/doc/source/users_guide/overview/introduction.rst @@ -1,17 +1,12 @@ -.. _introduction: - .. include:: ../substitutions.rst +.. _introduction: + **User's Guide to version |version| of the Community Land Model (CLM)** **Authors: Benjamin Andre, Erik Kluzek, William Sacks** -The National Center for Atmospheric Research (NCAR) is operated by the -nonprofit University Corporation for Atmospheric Research (UCAR) under -the sponsorship of the National Science Foundation. Any opinions, -findings, conclusions, or recommendations expressed in this publication -are those of the author(s) and do not necessarily reflect the views of -the National Science Foundation. +The National Center for Atmospheric Research (NCAR) is operated by the nonprofit University Corporation for Atmospheric Research (UCAR) under the sponsorship of the National Science Foundation. Any opinions, findings, conclusions, or recommendations expressed in this publication are those of the author(s) and do not necessarily reflect the views of the National Science Foundation. National Center for Atmospheric Research P. O. Box 3000, Boulder, Colorado 80307-3000 @@ -22,75 +17,30 @@ P. O. Box 3000, Boulder, Colorado 80307-3000 Introduction ============== -The Community Land Model (|release| in |cesmrelease|) is the latest in a -series of global land models developed by the CESM Land Model Working -Group (LMWG) and maintained at the National Center for Atmospheric -Research (NCAR). This guide is intended to instruct both the novice -and experienced user on running CLM. This guide pertains to the latest -version |version| in |cesmrelease| available for download from the public -release subversion repository as a part of |cesmrelease|. Documentation -may be different if you are using an older version, you should either -use the documentation for that release version, update to the latest -version, or use the documentation inside your own source tree. There -is information in the ChangeLog file and in the `What is new with -|version| in |cesmrelease| since previous public releases? `_ -regarding the changes from previous versions of CESM. - -.. note:: This release of |version| in |cesmrelease| includes BOTH CLM4.0 - physics and CLM4.5 physics used in previous releases as well as the updated |version| - physics. CLM allow you to trigger between the three physics modes. Most often when we refer to CLM4.0 we - are referring to the CLM4.0 physics in |version| in |cesmrelease| rather - than to a specific version of CLM4.0 (where we would give the exact - version). And when we refer to CLM4.5 we are referring to the CLM4.5 - physics in |version| in |cesmrelease| rather - than to a specific version of CLM4.5. Likewise, when referring to |version| we are referring to the - |version| physics in |version| in |cesmrelease|. - -The novice user should read `Chapter 1 `_ in detail before -beginning work, while the expert user should read `What is new with -|version| in |cesmrelease| since previous public releases? `_ and -`Quickstart to using |version| `_ chapters, and then use the -more detailed chapters as reference. Before novice users go onto more -technical problems covered in `Chapter 2 `_, `Chapter 3 -`_, `Chapter 4 `_, or `Chapter 5 `_ they -should know the material covered in `Chapter 1 `_ and be able -to replicate some of the examples given there. - -All users should read the `How to Use This Document `_ and -`Other resources to get help from `_ sections to understand -the document conventions and the various ways of getting help on using -|version|. Users should also read the `What is scientifically validated -and functional in |version| in |cesmrelease|? `_ section to see if -their planned use of the model is something that has been -scientifically validated and well tested. Users that are NOT using -NCAR machines or our list of well tested machines should also read the -What are the UNIX utilities required to use |version|? section to make -sure they have all the required UNIX utilities on the system they want -to do their work. - -Developers that are making changes to CLM either for their own -development or for development that they hope will eventually become a -part of the main CLM should read the `Chapter 8 `_ -chapter. We have a suite of test scripts that automatically test many -different model configurations and namelist options, as well as -ensuring things like restarts are bit-for-bit and the like. It's -helpful to use these scripts to ensure your changes are working -correctly. As well as being a required part of the process to bring in -new code developments. And it's far easier to use the automated -scripts rather than having to figure out, what to test, how to do it, -and then finally do it by hand. If you are using non supported -machines you may also want to use the test scripts to make sure your -machine is working correctly. - -.. _what-is-new-with-clm5_0: +The Community Land Model (|release| in |cesmrelease|) is the latest in a series of global land models developed by the CESM Land Model Working Group (LMWG) and maintained at the National Center for Atmospheric Research (NCAR). This guide is intended to instruct both the novice and experienced user on running CLM. This guide pertains to the latest version |version| in |cesmrelease| available for download from the public release subversion repository as a part of |cesmrelease|. Documentation may be different if you are using an older version, you should either use the documentation for that release version, update to the latest version, or use the documentation inside your own source tree. There is information in the ChangeLog file and in :ref:`what-is-new-with-clm` regarding the changes from previous versions of CESM. + +.. note:: This release of |version| in |cesmrelease| includes BOTH CLM4.0 physics and CLM4.5 physics used in previous releases as well as the updated |version| physics. CLM allow you to trigger between the three physics modes. Most often when we refer to CLM4.0 we are referring to the CLM4.0 physics in |version| in |cesmrelease| rather than to a specific version of CLM4.0 (where we would give the exact version). And when we refer to CLM4.5 we are referring to the CLM4.5 physics in |version| in |cesmrelease| rather than to a specific version of CLM4.5. Likewise, when referring to |version| we are referring to the |version| physics in |version| in |cesmrelease|. + +The novice user should read the :ref:`overview_section` chapter in detail before beginning work, while the expert user should read :ref:`what-is-new-with-clm` and :ref:`quickstart`, and then use the more detailed sections as reference. Before novice users go onto more technical problems covered in :ref:`customizing_section`, :ref:`using-clm-tools-section`, :ref:`adding-new-resolutions-section`, :ref:`running-special-cases-section` or :ref:`running-single-points`, they should know the material covered in :ref:`overview_section` and be able to replicate some of the examples given there. + +All users should read :ref:`how-to-use-this-document` and :ref:`getting-help` to understand the document conventions and the various ways of getting help on using |version|. Users should also read the :ref:`scientific-validiation` section to see if their planned use of the model is something that has been scientifically validated and well tested. Users that are NOT using NCAR machines or our list of well tested machines should also read the What are the UNIX utilities required to use |version|? section to make sure they have all the required UNIX utilities on the system they want to do their work. + +Developers that are making changes to CLM either for their own development or for development that they hope will eventually become a part of the main CLM should read :ref:`testing_section`. We have a suite of test scripts that automatically test many different model configurations and namelist options, as well as ensuring things like restarts are bit-for-bit and the like. It's helpful to use these scripts to ensure your changes are working correctly. As well as being a required part of the process to bring in new code developments. And it's far easier to use the automated scripts rather than having to figure out, what to test, how to do it, and then finally do it by hand. If you are using non supported machines you may also want to use the test scripts to make sure your machine is working correctly. + +.. _what-is-new-with-clm: ============================ What is New with |version| ============================ -`What's new with |version| science `_ -gives a synopsis of the changes to CLM since the CLM4.5 release. -More details are given in the `CLM ChangeLog file `_. +`What's new with |version| science `_ gives a synopsis of the changes to CLM since the CLM4.5 release. More details are given in the CLM ChangeLog files: + +- `CLM 3.0 ChangeLog file `_ +- `CLM 4.0 ChangeLog file `_ +- `CLM 4.5 ChangeLog file `_ +- `CLM 5.0 ChangeLog file `_ +- `CTSM 1.0 ChangeLog file `_ +- `Latest ChangeLog file `_ Previous release pages give similar list of changes for previous versions of the model. @@ -100,52 +50,29 @@ Previous release pages give similar list of changes for previous versions of the Overview of User's Guide ========================== -In this introduction we first give a simple guide to understand the document conventions in `How to Use This Document `_. -The next section `What is new with |version| in |cesmrelease| since previous public releases? `_ gives references to describe the differences between |version| in |cesmrelease| and previous CESM releases both from a scientific as well as a software engineering point of view. -For information on previous releases of |version| before |version| in |cesmrelease| see the CESM1.2.2 documentation. -The next section `Quickstart to using |version| `_ is for users that are already experts in using CLM and gives a quickstart guide to the bare details on how to use |version|. -The next `What is scientifically validated and functional in |version| in |cesmrelease|? `_ tells you about what has been extensively tested and scientifically validated (and maybe more importantly) what has NOT. -`What are the UNIX utilities required to use |version|? `_ lists the UNIX utilities required to use |version| and is important if you are running on non-NCAR machines, generic local machines, or machines NOT as well tested by us at NCAR. -Next we have `Important Notes and Best Practices for Usage of |version| `_ to detail some of the best practices for using |version| for science. -The last introductory section is `Other resources `_ to get help from which lists different resources for getting help with |version| and |cesmrelease|. +In this introduction we first give a simple guide to understand the document conventions in :ref:`how-to-use-this-document`. The next section, :ref:`what-is-new-with-clm`, gives references to describe the differences between |version| in |cesmrelease| and previous CESM releases both from a scientific as well as a software engineering point of view. For information on previous releases of |version| before |version| in |cesmrelease| see the CESM1.2.2 documentation. The :ref:`quickstart` section is for users that are already experts in using CLM and gives a quickstart guide to the bare details on how to use |version|. It also lists the UNIX utilities required to use |version| and is important if you are running on non-NCAR machines, generic local machines, or machines NOT as well tested by us at NCAR. The :ref:`scientific-validiation` section tells you about what has been extensively tested and scientifically validated (and maybe more importantly) what has NOT. Next we have :ref:`best-practices-for-usage` to detail some of the best practices for using |version| for science. The last introductory section is :ref:`getting-help`, which lists different resources for getting help with |version| and |cesmrelease|. -`Chapter 1 `_ goes into detail on how to setup and run simulations with |version| and especially how to customize cases. -Details of cesm_setup modes and build-namelist options as well as namelist options are given in this chapter. +:ref:`customizing_section` goes into detail on how to setup and run simulations with |version| and especially how to customize cases. Details of cesm_setup modes and build-namelist options as well as namelist options are given in this chapter. -`Chapter 2 `_ gives instructions on the CLM tools for either CLM4.5 or |version| physics for creating input datasets for use by CLM, for the expert user. -There's an overview of what each tool does, and some general notes on how to build the FORTRAN tools. -Then each tool is described in detail along with different ways in which the tool might be used. -A final section on how to customize datasets for observational sites for very savvy expert users is given as the last section of this chapter. +:ref:`using-clm-tools-section` gives instructions on the CLM tools for either CLM4.5 or |version| physics for creating input datasets for use by CLM, for the expert user. There's an overview of what each tool does, and some general notes on how to build the FORTRAN tools. Then each tool is described in detail along with different ways in which the tool might be used. A final section on how to customize datasets for observational sites for very savvy expert users is given as the last section of this chapter. -As a followup to the tools chapter, `Chapter 3 `_ tells how to add files to the XML database for build-namelist to use. -This is important if you want to use the XML database to automatically select user-created input files that you have created when you setup new cases with CLM (CLM4.0, CLM4.5 and |version| physics). +As a followup to the tools chapter, :ref:`adding-new-resolutions-section` tells how to add files to the XML database for build-namelist to use. This is important if you want to use the XML database to automatically select user-created input files that you have created when you setup new cases with CLM (CLM4.0, CLM4.5 and |version| physics). -In `Chapter 4 `_, again for the expert user, we give details on how to do some particularly difficult special cases. -For example, we give the protocol for spinning up the |version|-BGC and CLMCN models as well as CLM with dynamic vegetation active (CNDV). -We give instructions to do a spinup case from a previous case with Coupler history output for atmospheric forcing. -We also give instructions on running both the prognostic crop and irrigation models. -Lastly we tell the user how to use the DATM model to send historical CO2 data to CLM. +In :ref:`running-special-cases-section`, again for the expert user, we give details on how to do some particularly difficult special cases. For example, we give the protocol for spinning up the |version|-BGC and CLMCN models as well as CLM with dynamic vegetation active (CNDV). We give instructions to do a spinup case from a previous case with Coupler history output for atmospheric forcing. We also give instructions on running both the prognostic crop and irrigation models. Lastly we tell the user how to use the DATM model to send historical CO2 data to CLM. -`Chapter 5 `_ outlines how to do single-point or regional simulations using |version|. -This is useful to either compare |version| simulations with point observational stations, such as tower sites (which might include your own atmospheric forcing), or to do quick simulations with CLM for example to test a new parameterization. -There are several different ways given on how to perform single-point simulations which range from simple PTS_MODE to more complex where you create all your own datasets, tying into `Chapter 2 `_ and also `Chapter 3 `_ to add the files into the build-namelist XML database. -The PTCLM python script to run single-point simulations was added back in for this release (but it has bugs that don't allow it to work out of the box). -CLM4 in CESM1.0.5 has a fully working versions of PTCLM. +:ref:`running-single-points` outlines how to do single-point or regional simulations using |version|. This is useful to either compare |version| simulations with point observational stations, such as tower sites (which might include your own atmospheric forcing), or to do quick simulations with CLM for example to test a new parameterization. There are several different ways given on how to perform single-point simulations which range from simple PTS_MODE to more complex where you create all your own datasets, tying into :ref:`using-clm-tools-section` and also :ref:`adding-new-resolutions-section` to add the files into the build-namelist XML database. The PTCLM python script to run single-point simulations was added back in for this release (but it has bugs that don't allow it to work out of the box). CLM4 in CESM1.0.5 has a fully working versions of PTCLM. -Need `Chapter 6 `_ blurb... +Need :ref:`running-PTCLM` blurb... -`Chapter 7 `_ gives some guidance on trouble-shooting problems when using |version|. -It doesn't cover all possible problems with CLM, but gives you some guidelines for things that can be done for some common problems. +:ref:`troubleshooting-index` gives some guidance on trouble-shooting problems when using |version|. It doesn't cover all possible problems with CLM, but gives you some guidelines for things that can be done for some common problems. -`Chapter 8 `_ goes over the automated testing scripts for validating that the CLM is working correctly. -The test scripts run many different configurations and options with CLM4.0 physics as well and |version| physics making sure that they work, as well as doing automated testing to verify restarts are working correctly, and testing at many different resolutions. -In general this is an activity important only for a developer of |version|, but could also be used by users who are doing extensive code modifications and want to ensure that the model continues to work correctly. +:ref:`testing_section` goes over the automated testing scripts for validating that the CLM is working correctly. The test scripts run many different configurations and options with CLM4.0 physics as well and |version| physics making sure that they work, as well as doing automated testing to verify restarts are working correctly, and testing at many different resolutions. In general this is an activity important only for a developer of |version|, but could also be used by users who are doing extensive code modifications and want to ensure that the model continues to work correctly. In the appendices we talk about some issues that are useful for advanced users and developers of |version|. -Finally in `Appendix A `_ we give instructions on how to build the documentation associated with |version| (i.e. how to build this document). -This document is included in every CLM distribution and can be built so that you can view a local copy rather than having to go to the CESM website. -This also could be useful for developers who need to update the documentation due to changes they have made. +Finally on Github we give `instructions `_ on how to build the documentation associated with |version| (i.e. how to build this document). This document is included in every CLM distribution and can be built so that you can view a local copy rather than having to go to the CESM website. This also could be useful for developers who need to update the documentation due to changes they have made. + +.. _readme: ================================ README file describing |version| @@ -162,43 +89,19 @@ The README (which can be found in ``$CTSMROOT/doc``) is repeated here. Best Practices ================ -- |version| includes BOTH the old CLM4.0, CLM4.5 physics AND the new |version| physics and you can toggle between those three. - The "standard" practice for CLM4.0 is to run with CN on, and with Qian atmospheric forcing. - While the "standard" practice for CLM4.5 is to run with BGC on, and CRUNCEP atmospheric forcing. - And finally the "standard" practice for |version| is to run with BGC and Prognostic Crop on, with the MOSART model for river routing, as well as the CISM - ice sheet model, and using GSWP3 atmospheric forcing. - "BGC" is the new |version| biogeochemistry and include CENTURY-like pools, vertical resolved carbon, as well as Nitrification and de-Nitrification (see `the Section called Some Acronym's and Terms We'll be Using in Other resources to get help from `_ ). - -- When running with CLMCN (either CLM4.0 or |version| physics) or |version|-BGC, it is critical to begin with initial conditions that are provided with the release or to spin the model up following the CN spinup procedure before conducting scientific runs (see `the Section called Spinning up the |version| biogeochemistry (CLMBGC spinup) in Chapter 4 `_ for |version| or `the Section called Spinning up the CLM4.0 biogeochemistry Carbon-Nitrogen Model (CN spinup) in Chapter 4 `_ for CLM4.0). - Simulations without a proper spinup will effectively be starting from an unvegetated world. - See `the Section called Setting Your Initial Conditions File in Chapter 1 `_ for information on how to provide initial conditions for your simulation. - -- Initial condition files are provided for CLM4.0-CN as before, for fully coupled BCN and offline ICN cases for 1850 and 2000 at finite volume grids: 1deg (0.9x1.25), 2deg (1.9x2.5), and T31 resolutions. - We also have interpolated initial conditions for BCN for 1850 and 2000 for two finite volume grids: 10x15, 4x5 and two HOMME grids (ne30np4 and ne120np4). - There's also an initial condition file for ICN with the prognostic crop model for 2000 at 2deg resolution, and one with CLMSP for 2000 at 2deg resolution. - We also have initial conditions for offline CNDV for 1850. - The 1850 initial condition files are in 'reasonable' equilibrium. - The 2000 initial condition files represent the model state for the year 2000, and have been taken from transient simulations. - Therefore, by design the year 2000 initial condition files do not represent an equilibrium state. - Note also that spinning the 2000 initial conditions out to equilibrium will not reflect the best estimate of the real carbon/nitrogen state for the year 2000. - -- Initial condition files are also provided for |version| for several configurations and resolutions. - For CLM4.5-SP and CLM4.5-BGC with both CRUNCEP and GSWP3 forcing we have initial conditions at 1deg resolution for 1850. - For |version|-SP and |version|-BGC-Crop with both CRUNCEP and GSWP3 forcing we have initial conditions at 1deg resolution for 1850. - Normally, these files are interpolated to any other resolution that you run at. - -- Users can interpolate initial condition files at different resolutions at startup of a CLM4.5 or |version| simulation. And the file created can be stored for later use. - Interpolated initial condition files may no longer be in 'reasonable' equilibrium. - -- In |version| for both |version|-CN, |version|-BGC, and |version|-BGC-Crop the new fire model requires lightning frequency data, and human population density (both are read inside of CLM). - By default we have provided a climatology dataset for lightning frequency and a dataset with coverage from 1850 to 2014 for population density. - Both of these datasets are interpolated from the native resolution of the datasets to the resolution you are running the model on. - If you are running with an atmosphere model or forcing that is significantly different than present day -- the lightning frequency may NOT appropriately correspond to your atmosphere forcing and fire initiation would be inappropriate. - -- Aerosol deposition is a required field to both CLM4.0, CLM4.5 and |version| physics, sent from the atmosphere model. - Simulations without aerosol deposition will exhibit unreasonably high snow albedos. - The model sends aerosol deposition from the atmospheric model (either CAM or DATM). - When running with prescribed aerosol the atmosphere model will interpolate the aerosols from 2-degree resolution to the resolution the atmosphere model is running at. +- |version| includes BOTH the old CLM4.0, CLM4.5 physics AND the new |version| physics and you can toggle between those three. The "standard" practice for CLM4.0 is to run with CN on, and with Qian atmospheric forcing. While the "standard" practice for CLM4.5 is to run with BGC on, and CRUNCEP atmospheric forcing. And finally the "standard" practice for |version| is to run with BGC and Prognostic Crop on, with the MOSART model for river routing, as well as the CISM ice sheet model, and using GSWP3 atmospheric forcing. "BGC" is the new |version| biogeochemistry and include CENTURY-like pools, vertical resolved carbon, as well as Nitrification and de-Nitrification (see :ref:`acronyms-and-terms`). + +- When running with CLMCN (either CLM4.0 or |version| physics) or |version|-BGC, it is critical to begin with initial conditions that are provided with the release or to spin the model up following the CN spinup procedure before conducting scientific runs (see :ref:`spinning-up-clm-bgc` or :ref:`spinning-up-sp`). Simulations without a proper spinup will effectively be starting from an unvegetated world. See :ref:`setting-initial-conditions` for information on how to provide initial conditions for your simulation. + +- Initial condition files are provided for CLM4.0-CN as before, for fully coupled BCN and offline ICN cases for 1850 and 2000 at finite volume grids: 1deg (0.9x1.25), 2deg (1.9x2.5), and T31 resolutions. We also have interpolated initial conditions for BCN for 1850 and 2000 for two finite volume grids: 10x15, 4x5 and two HOMME grids (ne30np4 and ne120np4). There's also an initial condition file for ICN with the prognostic crop model for 2000 at 2deg resolution, and one with CLMSP for 2000 at 2deg resolution. We also have initial conditions for offline CNDV for 1850. The 1850 initial condition files are in 'reasonable' equilibrium. The 2000 initial condition files represent the model state for the year 2000, and have been taken from transient simulations. Therefore, by design the year 2000 initial condition files do not represent an equilibrium state. Note also that spinning the 2000 initial conditions out to equilibrium will not reflect the best estimate of the real carbon/nitrogen state for the year 2000. + +- Initial condition files are also provided for |version| for several configurations and resolutions. For CLM4.5-SP and CLM4.5-BGC with both CRUNCEP and GSWP3 forcing we have initial conditions at 1deg resolution for 1850. For |version|-SP and |version|-BGC-Crop with both CRUNCEP and GSWP3 forcing we have initial conditions at 1deg resolution for 1850. Normally, these files are interpolated to any other resolution that you run at. + +- Users can interpolate initial condition files at different resolutions at startup of a CLM4.5 or |version| simulation. And the file created can be stored for later use. Interpolated initial condition files may no longer be in 'reasonable' equilibrium. + +- In |version| for both |version|-CN, |version|-BGC, and |version|-BGC-Crop the new fire model requires lightning frequency data, and human population density (both are read inside of CLM). By default we have provided a climatology dataset for lightning frequency and a dataset with coverage from 1850 to 2014 for population density. Both of these datasets are interpolated from the native resolution of the datasets to the resolution you are running the model on. If you are running with an atmosphere model or forcing that is significantly different than present day -- the lightning frequency may NOT appropriately correspond to your atmosphere forcing and fire initiation would be inappropriate. + +- Aerosol deposition is a required field to both CLM4.0, CLM4.5 and |version| physics, sent from the atmosphere model. Simulations without aerosol deposition will exhibit unreasonably high snow albedos. The model sends aerosol deposition from the atmospheric model (either CAM or DATM). When running with prescribed aerosol the atmosphere model will interpolate the aerosols from 2-degree resolution to the resolution the atmosphere model is running at. .. _ctsm_vs_cesm_checkout: @@ -206,10 +109,7 @@ The README (which can be found in ``$CTSMROOT/doc``) is repeated here. A CTSM versus a CESM checkout ============================= -The directory structure for |version| is different depending on if it's checked out from |release| or |cesmrelease|. -If |version| is checked out from |ctsm_gh| the CLM source code is directly under the top level directory. If |cesmrelease| -is checkout out from |cesm_gh| then the CLM source directories are under "components/clm" from the top level directory. We -will refer to this directory for the CLM source directories in the User's Guide as "$CTSMROOT". +The directory structure for |version| is different depending on if it's checked out from |release| or |cesmrelease|. If |version| is checked out from |ctsm_gh| the CLM source code is directly under the top level directory. If |cesmrelease| is checkout out from |cesm_gh| then the CLM source directories are under "components/clm" from the top level directory. We will refer to this directory for the CLM source directories in the User's Guide as "$CTSMROOT". .. _how-to-use-this-document: @@ -221,17 +121,7 @@ Links to descriptions and definitions have been provided in the code below. We u :: - Throughout the document this style is used to indicate shell - commands and options, fragments of code, namelist variables, etc. - Where examples from an interactive shell session are presented, lines - starting with > indicate the shell prompt. A backslash "\" at the end - of a line means the line continues onto the next one (as it does in - standard UNIX shell). Note that $EDITOR" is used to refer to the - text editor of your choice. $EDITOR is a standard UNIX environment - variable and should be set on most UNIX systems. Comment lines are - signaled with a "#" sign, which is the standard UNIX comment sign as well. - $CSMDATA is used to denote the path to the inputdata directory for - your CESM data. + Throughout the document this style is used to indicate shell commands and options, fragments of code, namelist variables, etc. Where examples from an interactive shell session are presented, lines starting with > indicate the shell prompt. A backslash "\" at the end of a line means the line continues onto the next one (as it does in standard UNIX shell). Note that $EDITOR" is used to refer to the text editor of your choice. $EDITOR is a standard UNIX environment variable and should be set on most UNIX systems. Comment lines are signaled with a "#" sign, which is the standard UNIX comment sign as well. $CSMDATA is used to denote the path to the inputdata directory for your CESM data. > This is a shell prompt with commands \ that continues to the following line. diff --git a/doc/source/users_guide/overview/quickstart.rst b/doc/source/users_guide/overview/quickstart.rst index 65398200e9..5414963c4d 100644 --- a/doc/source/users_guide/overview/quickstart.rst +++ b/doc/source/users_guide/overview/quickstart.rst @@ -1,33 +1,29 @@ -.. _quickstart: - .. include:: ../substitutions.rst +.. _quickstart: + ============ Quickstart ============ -Running the CLM requires a suite of UNIX utilities and programs and you should make sure you have all of these available before trying to go forward with using it. -If you are missing one of these you should contact the systems administrator for the machine you wish to run on and make sure they are installed. +Running the CLM requires a suite of UNIX utilities and programs and you should make sure you have all of these available before trying to go forward with using it. If you are missing one of these you should contact the systems administrator for the machine you wish to run on and make sure they are installed. + +List of utilities required for CESM in the `Software/OS Prerequisites `_ section of the CESM User's Guide. -List of utilities required for CESM in the "|cesmrelease| Software/Operating System Prerequisites" section in `http://www.cesm.ucar.edu/models/cesm1.2//cesm/doc/usersguide/book1.html `_ - UNIX bash shell (for some of the CLM tools scripts) -- NCL (for some of the offline tools for creating/modifying CLM input datasets see `Chapter 2 `_ for more information on NCL) +- NCL (for some of the offline tools for creating/modifying CLM input datasets; see :ref:`using-ncl` for more information) - Python -Before working with |version| read the QuickStart Guide in the `|cesmrelease| Scripts User's Guide `_. Once you are familiar with how to setup cases for any type of simulation with CESM you will want to direct your attention to the specifics of using CLM. - -For some of the details of setting up cases for |version| read the README and text files available from the "$CTSMROOT/doc" directory (see the "CLM Web pages" section for a link to the list of these files). Here are the important ones that you should be familiar with. - -`README file `_ describing the directory structure. - -The IMPORTANT_NOTES file talks about important things for users to know about using the model scientifically. It content is given in the next chapter on `"What is scientifically validated and functional in |version| in |cesmrelease|?" `_. +Before working with |version| read the `CESM QuickStart Guide `_. Once you are familiar with how to setup cases for any type of simulation with CESM you will want to direct your attention to the specifics of using CLM. -The ChangeLog/ChangeSum talk about advances in different versions of CLM. The content of these files is largely explained in the previous chapter on `"What is new with |version| in |cesmrelease| since previous public releases?" `_. +For some of the details of setting up cases for |version| read the README and text files available from the "$CTSMROOT/doc" directory (see the "CLM Web pages" section for a link to the list of these files). Here are the important ones that you should be familiar with: -The release-clm5.0.ChangeLog gives the specific changes that have gone on the release-clm5.0 branch. clm3_0_ChangeLog, clm4_0_ChangeLog, clm4_5_ChangeLog gives the changes that -culimated in that given version of the CLM. +- :ref:`readme` describing the directory structure. +- The IMPORTANT_NOTES file talks about important things for users to know about using the model scientifically. It content is given in the next chapter on :ref:`scientific-validiation`. +- The ChangeLog/ChangeSum talk about advances in different versions of CLM. The content of these files is largely explained in the previous chapter on :ref:`what-is-new-with-clm`. +- The release-clm5.0.ChangeLog gives the specific changes that have gone on the release-clm5.0 branch. clm3_0_ChangeLog, clm4_0_ChangeLog, clm4_5_ChangeLog gives the changes that culimated in that given version of the CLM. -Note other directories have README files that explain different components and tools used when running CLM and are useful in understanding how those parts of the model work and should be consulted when using tools in those directories. For more details on configuring and customizing a case with CLM see `Chapter 1 `_. +Note other directories have README files that explain different components and tools used when running CLM and are useful in understanding how those parts of the model work and should be consulted when using tools in those directories. For more details on configuring and customizing a case with CLM see :ref:`customizing_section`. The Quickstart.GUIDE (which can be found in ``$CTSMROOT/doc``) is repeated here. diff --git a/doc/source/users_guide/overview/scientific_validation.rst b/doc/source/users_guide/overview/scientific_validation.rst index d05497a645..348bd97250 100644 --- a/doc/source/users_guide/overview/scientific_validation.rst +++ b/doc/source/users_guide/overview/scientific_validation.rst @@ -1,7 +1,7 @@ -.. _scientific-validiation: - .. include:: ../substitutions.rst +.. _scientific-validiation: + ======================== Scientific Validation ======================== @@ -12,8 +12,7 @@ In this section we go over what has been extensively tested and scientifically v Standard Configuration and Namelist Options that are Validated -------------------------------------------------------------- -See -`http://www.cesm.ucar.edu/models/cesm1.2/clm/CLM_configurations_CESM1.2.pdf `_ for an explanation of what configurations are scientifically validated for |version|. For CLM4.0 changes to the science of the model are minimal since CESM1.1.1 so we expect answers to be very similar to using it. +See `http://www.cesm.ucar.edu/models/cesm1.2/clm/CLM_configurations_CESM1.2.pdf `_ for an explanation of what configurations are scientifically validated for |version|. For CLM4.0 changes to the science of the model are minimal since CESM1.1.1 so we expect answers to be very similar to using it. In the sections below we go through configuration and/or namelist options or modes that the user should be especially wary of using. You are of course free to use these options, and you may find that they work functionally. Although in some cases you will find issues even with functionality of using them. If so you will need to test, debug and find solutions for these issues on your own. But in every case you will need to go through more extensive work to validate these options from a scientific standpoint. Some of these options are only for |version| while others are for both CLM4.0 AND |version| we explicitly say which they apply to. @@ -21,9 +20,7 @@ In the sections below we go through configuration and/or namelist options or mod Configurations that should be used with caution ----------------------------------------------- -There are some options in |version| that are available but either not tested extensively, or not scientifically evaluated. These -options should be used with caution. And any options that deviate from the scientifically supported configurations can have issues. -The IMPORTANT_NODES file goes into more details on this. +There are some options in |version| that are available but either not tested extensively, or not scientifically evaluated. These options should be used with caution. And any options that deviate from the scientifically supported configurations can have issues. The IMPORTANT_NODES file goes into more details on this. The IMPORTANT_NOTES (which can be found in ``$CTSMROOT/doc``) is repeated here. diff --git a/doc/source/users_guide/running-PTCLM/adding-ptclm-site-data.rst b/doc/source/users_guide/running-PTCLM/adding-ptclm-site-data.rst index b643e79a28..d085c2f689 100644 --- a/doc/source/users_guide/running-PTCLM/adding-ptclm-site-data.rst +++ b/doc/source/users_guide/running-PTCLM/adding-ptclm-site-data.rst @@ -1,19 +1,12 @@ -.. _adding-ptclm-site-data: - .. include:: ../substitutions.rst +.. _adding-ptclm-site-data: + ============================ Adding PTCLMmkdata Site Data ============================ -The "sitegroupname" option to PTCLMmkdata looks for groups of sites in the files in the ``PTCLM_sitedata`` directory under the PTCLMmkdata directory. -You can add new names available for this option including your own lists of sites, by adding more files in this directory. -There are three files for each "sitegroupname": ``$SITEGROUP_sitedata.txt``, ``$SITEGROUP_soildata.txt`` and ``$SITEGROUP_pftdata.txt`` (where ``$SITEGROUP`` is the name that would be entered as "sitegroupname" to PTCLMmkdata). -Each file needs to have the same list of sites, but gives different information: site data, PFT data, and soil data respectively. -Although the site codes need to be the same between the three files, the files do NOT have to be in the same order. -Each file has a one-line header that lists the contents of each column which are separated by commas. -The first column for each of the files is the "site_code" which must be consistent between the three files. -The site code can be any unique character string, but in general we use the AmeriFlux site code. +The "sitegroupname" option to PTCLMmkdata looks for groups of sites in the files in the ``PTCLM_sitedata`` directory under the PTCLMmkdata directory. You can add new names available for this option including your own lists of sites, by adding more files in this directory. There are three files for each "sitegroupname": ``$SITEGROUP_sitedata.txt``, ``$SITEGROUP_soildata.txt`` and ``$SITEGROUP_pftdata.txt`` (where ``$SITEGROUP`` is the name that would be entered as "sitegroupname" to PTCLMmkdata). Each file needs to have the same list of sites, but gives different information: site data, PFT data, and soil data respectively. Although the site codes need to be the same between the three files, the files do NOT have to be in the same order. Each file has a one-line header that lists the contents of each column which are separated by commas. The first column for each of the files is the "site_code" which must be consistent between the three files. The site code can be any unique character string, but in general we use the AmeriFlux site code. Site data file:`` $SITEGROUP_sitedata.txt``): The header for this file is: :: @@ -47,55 +40,41 @@ There is a mechanism for giving site-specific land-use change in PTCLMmkdata. Ad This file only requires a line for each year where a transition or harvest happens. As in the "pftdata" file above "pft_f" refers to the fraction and "pft_c" refers to the PFT index, and only up to five vegetation types are allowed to co-exist. The last eight columns have to do with harvesting and grazing. The last two columns are whether to hold harvesting and/or grazing constant until the next transition year and will just be either 1 or 0. This file will be converted by the **PTCLM_sitedata/cnvrt_trnsyrs2_pftdyntxtfile.pl** script in the PTCLMmkdata directory to a format that **mksurfdata_map** can read that has an entry for each year for the range of years valid for the compset in question. +.. _converting-ameriflux-for-ptclmmkdata: + ------------------------------------------------ Converting AmeriFlux Data for use by PTCLMmkdata ------------------------------------------------ -AmeriFlux data comes in comma separated format and is available from: -`http://public.ornl.gov/ameriflux/dataproducts.shtml `_. Before you download the data you need to agree to the usage terms. +AmeriFlux data comes in comma separated format and is available from: `http://public.ornl.gov/ameriflux/dataproducts.shtml `_. Before you download the data you need to agree to the usage terms. Here is a copy of the usage terms from the web-site on June/13/2011. -"The AmeriFlux data provided on this site are freely available and were furnished by individual AmeriFlux scientists who encourage their use. -Please kindly inform the appropriate AmeriFlux scientist(s) of how you are using the data and of any publication plans. -Please acknowledge the data source as a citation or in the acknowledgments if the data are not yet published. -If the AmeriFlux Principal Investigators (PIs) feel that they should be acknowledged or offered participation as authors, they will let you know and we assume that an agreement on such matters will be reached before publishing and/or use of the data for publication. -If your work directly competes with the PI's analysis they may ask that they have the opportunity to submit a manuscript before you submit one that uses unpublished data. -In addition, when publishing, please acknowledge the agency that supported the research. -Lastly, we kindly request that those publishing papers using AmeriFlux data provide preprints to the PIs providing the data and to the data archive at the Carbon Dioxide Information Analysis Center (CDIAC)." +"The AmeriFlux data provided on this site are freely available and were furnished by individual AmeriFlux scientists who encourage their use. Please kindly inform the appropriate AmeriFlux scientist(s) of how you are using the data and of any publication plans. Please acknowledge the data source as a citation or in the acknowledgments if the data are not yet published. If the AmeriFlux Principal Investigators (PIs) feel that they should be acknowledged or offered participation as authors, they will let you know and we assume that an agreement on such matters will be reached before publishing and/or use of the data for publication. If your work directly competes with the PI's analysis they may ask that they have the opportunity to submit a manuscript before you submit one that uses unpublished data. In addition, when publishing, please acknowledge the agency that supported the research. Lastly, we kindly request that those publishing papers using AmeriFlux data provide preprints to the PIs providing the data and to the data archive at the Carbon Dioxide Information Analysis Center (CDIAC)." The above agreement applies to the "US-UMB" dataset imported into our repository as well, and Gil Bohrer is the PI on record for that dataset. - -The CESM can NOT handle missing data, so we recommend using the "Level 4" Gap filled datasets. -The fields will also need to be renamed. -The "WS" column becomes "WIND", "PREC" becomes "PRECmms", "RH" stays as "RH", "TA" becomes "TBOT", "Rg" becomes "FSDS", "Rgl" becomes "FLDS", "PRESS" becomes "PSRF". -"ZBOT" can just be set to the constant of "30" (m). -The units of Temperature need to be converted from "Celsius" to "Kelvin" (use the value in ``SHR_CONST_TKFRZ`` in the file ``models/csm_share/shr/shr_const.F90`` of ``273.15``. -The units of Pressure also need to be converted from "kPa" to "Pa". LATIXY, and LONGXY should also be set to the latitude and longitude of the site. - +The CESM can NOT handle missing data, so we recommend using the "Level 4" Gap filled datasets. The fields will also need to be renamed. The "WS" column becomes "WIND", "PREC" becomes "PRECmms", "RH" stays as "RH", "TA" becomes "TBOT", "Rg" becomes "FSDS", "Rgl" becomes "FLDS", "PRESS" becomes "PSRF". "ZBOT" can just be set to the constant of "30" (m). The units of Temperature need to be converted from "Celsius" to "Kelvin" (use the value in ``SHR_CONST_TKFRZ`` in the file ``models/csm_share/shr/shr_const.F90`` of ``273.15``. The units of Pressure also need to be converted from "kPa" to "Pa". LATIXY, and LONGXY should also be set to the latitude and longitude of the site. ----------------------------------------------------------------- Example: PTCLMmkdata transient example over a shorter time period ----------------------------------------------------------------- -This is an example of using PTCLMmkdata for Harvard Forest (AmeriFlux site code US-Ha1) for transient land use 1991-2006. -In order to do this we would've needed to have converted the AmeriFlux data into NetCDF format as show in the `the Section called Converting AmeriFlux Data for use by PTCLMmkdata `_ section above. -Also note that this site has a site-specific dynamic land-use change file for it ``PTCLM_sitedata/US-Ha1_dynpftdata.txt`` in the PTCLMmkdata directory and this file will be used for land-use change and harvesting rather than the global dataset. +This is an example of using PTCLMmkdata for Harvard Forest (AmeriFlux site code US-Ha1) for transient land use 1991-2006. In order to do this we would've needed to have converted the AmeriFlux data into NetCDF format as shown in :ref:`converting-ameriflux-for-ptclmmkdata` section above. Also note that this site has a site-specific dynamic land-use change file for it ``PTCLM_sitedata/US-Ha1_dynpftdata.txt`` in the PTCLMmkdata directory and this file will be used for land-use change and harvesting rather than the global dataset. :: > cd $CTSMROOT/tools/PTCLM # We are going to use forcing data over 1991 to 2006, but we need to start with # a transient compset to do so, so we use the 20th Century transient: 1850-2000 - # Note: When creating the fpftdyn dataset for this site it will use the + # Note: When creating the fpftdyn dataset for this site it will use the # PTCLM_sitedata/US-Ha1_dynpftdata.txt # file for land-use change and harvesting > ./PTCLMmkdata -s US-Ha1 -d $MYCSMDATA --sitegroupname AmeriFlux > mkdir $MYCSMDATA/atm/datm7/CLM1PT_data/1x1pt_US-Ha1 > cd $MYCSMDATA/atm/datm7/CLM1PT_data/1x1pt_US-Ha1 # Copy data in NetCDF format to this directory, filenames should be YYYY-MM.nc - # The fieldnames on the file should be: + # The fieldnames on the file should be: # FLDS,FSDS,LATIXY, LONGXY, PRECTmms,PSRF,RH,TBOT,WIND,ZBOT # With units # W/m2,W/m2,degrees_N,degrees_E,mm/s, Pa, %, K, m/s, m diff --git a/doc/source/users_guide/running-PTCLM/index.rst b/doc/source/users_guide/running-PTCLM/index.rst index 26e18d3a73..0b44c01b49 100644 --- a/doc/source/users_guide/running-PTCLM/index.rst +++ b/doc/source/users_guide/running-PTCLM/index.rst @@ -3,10 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. _running-PTCLM: - .. include:: ../substitutions.rst +.. _running-PTCLM: + ##################################### Running PTCLM ##################################### diff --git a/doc/source/users_guide/running-PTCLM/introduction-to-ptclm.rst b/doc/source/users_guide/running-PTCLM/introduction-to-ptclm.rst index 04fad02db2..9ae4186d2c 100644 --- a/doc/source/users_guide/running-PTCLM/introduction-to-ptclm.rst +++ b/doc/source/users_guide/running-PTCLM/introduction-to-ptclm.rst @@ -8,24 +8,22 @@ What is PTCLMmkdata? ===================== -PTCLMmkdata (pronounced Pee-Tee Cee-L-M make data is a Python script to help you set up PoinT CLM simulations. +PTCLMmkdata (pronounced Pee-Tee Cee-L-M make data is a Python script to help you set up PoinT CLM simulations. -It runs the CLM tools for you to get datasets set up, and copies them to a location you can use them including the changes -needed for a case to use the dataset with namelist and XML changes. +It runs the CLM tools for you to get datasets set up, and copies them to a location you can use them including the changes needed for a case to use the dataset with namelist and XML changes. Then you run **create_newcase** and point to the directory so that the namelist and XML changes are automatically applied. -PTCLMmkdata has a simple ASCII text file for storing basic information for your sites. +PTCLMmkdata has a simple ASCII text file for storing basic information for your sites. -We also have complete lists for AmeriFlux and Fluxnet-Canada sites, although we only have the meteorology data for one site. +We also have complete lists for AmeriFlux and Fluxnet-Canada sites, although we only have the meteorology data for one site. -For other sites you will need to obtain the meteorology data and translate it to a format that the CESM datm model can use. +For other sites you will need to obtain the meteorology data and translate it to a format that the CESM datm model can use. But, even without meteorology data PTCLMmkdata is useful to setup datasets to run with standard ``CLM_QIAN`` data. The original authors of PTCLMmkdata are: Daniel M. Ricciuto, Dali Wang, Peter E. Thornton, Wilfred M. Post all at Environmental Sciences Division, Oak Ridge National Laboratory (ORNL) and R. Quinn Thomas at Cornell University. It was then modified fairly extensively by Erik Kluzek at NCAR. We want to thank all of these individuals for this contribution to the CESM effort. We also want to thank the folks at University of Michigan Biological Stations (US-UMB) who allowed us to use their Fluxnet station data and import it into our inputdata repository, especially Gil Bohrer the PI on record for this site. - .. _details-of-ptclm: ======================= @@ -107,8 +105,7 @@ The output to the above command is as follows: Main Script Version Id: $Id: PTCLM.py 47576 2013-05-29 19:11:16Z erik $ Scripts URL: $HeadURL: https://svn-ccsm-models.cgd.ucar.edu/PTCLM/trunk_tags/PTCLM1_130529/PTCLM.py $: -Here we give a simple example of using PTCLMmkdata for a straightforward case of running at the US-UMB Fluxnet site on cheyenne where we already have the meteorology data on the machine. -Note, see `the Section called Converting AmeriFlux Data for use by PTCLMmkdata `_ for permission information to use this data. +Here we give a simple example of using PTCLMmkdata for a straightforward case of running at the US-UMB Fluxnet site on cheyenne where we already have the meteorology data on the machine. Note, see :ref:`converting-ameriflux-for-ptclmmkdata` for permission information to use this data. Example 6-1. Example of running PTCLMmkdata for US-UMB on cheyenne ------------------------------------------------------------------ @@ -118,7 +115,7 @@ Example 6-1. Example of running PTCLMmkdata for US-UMB on cheyenne > setenv MYDATAFILES `pwd`/mydatafiles > setenv SITE US-UMB > setenv MYCASE testPTCLM - + # Next build all of the clm tools you will need > cd $CTSMROOT/tools/PTCLM > buildtools @@ -132,7 +129,6 @@ Example 6-1. Example of running PTCLMmkdata for US-UMB on cheyenne > cd $MYCASE > ./case.setup - PTCLMmkdata includes a README file that gives some extra details and a simple example. .. include:: ../../../../tools/PTCLM/README diff --git a/doc/source/users_guide/running-PTCLM/ptclm-examples.rst b/doc/source/users_guide/running-PTCLM/ptclm-examples.rst index e36b1e686a..6801c5f3d8 100644 --- a/doc/source/users_guide/running-PTCLM/ptclm-examples.rst +++ b/doc/source/users_guide/running-PTCLM/ptclm-examples.rst @@ -1,7 +1,7 @@ -.. _ptclm-examples: - .. include:: ../substitutions.rst +.. _ptclm-examples: + ============================== Examples of using PTCLMmkdata ============================== @@ -13,7 +13,7 @@ Now, let's demonstrate using a different group list, doing a spinup, running wit Example: Running PTCLMmkdata without tower years ------------------------------------------------ :: - + > cd $CTSMROOT/tools/PTCLM > ./PTCLMmkdata -s US-Ha1 -d $CSMDATA --sitegroupname AmeriFlux --donot_use_tower_yrs > cd ../../../../../US-Ha1_ICRUCLM45BGC_QIAN diff --git a/doc/source/users_guide/running-PTCLM/using-ptclm.rst b/doc/source/users_guide/running-PTCLM/using-ptclm.rst index e657608b67..e7be79bee6 100644 --- a/doc/source/users_guide/running-PTCLM/using-ptclm.rst +++ b/doc/source/users_guide/running-PTCLM/using-ptclm.rst @@ -1,22 +1,18 @@ -.. _using-ptclm.rst: - .. include:: ../substitutions.rst +.. _using-ptclm.rst: + ************************** Using PTCLMmkdata ************************** -There are two types of options to PTCLMmkdata: required and optional. -The three required options are the three settings that MUST be specified for PTCLMmkdata to work at all. The other settings have default values that will default to something useful. Most options use a double dash "--" "longname" such as "--list", but the most common options also have a short-name with a single dash. +There are two types of options to PTCLMmkdata: required and optional. The three required options are the three settings that MUST be specified for PTCLMmkdata to work at all. The other settings have default values that will default to something useful. Most options use a double dash "--" "longname" such as "--list", but the most common options also have a short-name with a single dash. -The required options to PTCLMmkdata are: inputdata directory (-d) and site-name (-s). -Inputdata directory is the directory where you have the CESM inputdata files. Finally site-name is the name of the site that you want to run for. Site-name is a Fluxnet site name from the list of sites you are running on (see the --sitegroupname for more information about the site lists). +The required options to PTCLMmkdata are: inputdata directory (-d) and site-name (-s). Inputdata directory is the directory where you have the CESM inputdata files. Finally site-name is the name of the site that you want to run for. Site-name is a Fluxnet site name from the list of sites you are running on (see the --sitegroupname for more information about the site lists). -After PTCLMmkdata is run you can run **create_newcase** to setup a case to use the datasets created. -It also creates a ``README.PTCLM`` in that directory that documents the commandline options to PTCLMmkdata that were used to create it. +After PTCLMmkdata is run you can run **create_newcase** to setup a case to use the datasets created. It also creates a ``README.PTCLM`` in that directory that documents the commandline options to PTCLMmkdata that were used to create it. -After "help" the "list" option is one of the most useful options for getting help on using PTCLMmkdata. -This option gives you information about some of the other options to PTCLMmkdata. To get a list of the sites that can be used for PTCLMmkdata use the "--list" option as follows. +After "help" the "list" option is one of the most useful options for getting help on using PTCLMmkdata. This option gives you information about some of the other options to PTCLMmkdata. To get a list of the sites that can be used for PTCLMmkdata use the "--list" option as follows. :: > cd $CTSMROOT/tools/PTCLM @@ -30,23 +26,21 @@ The output to the above command is as follows: Steps in running PTCLMmkdata ============================ -1. Build the CLM tools - Next you need to make sure all the CLM FORTRAN tools are built. +1. Build the CLM tools Next you need to make sure all the CLM FORTRAN tools are built. :: > cd $CTSMROOT/tools/PTCLM > ./buildtools > gmake clean -2. Run PTCLMmkdata - Next you actually run PTCLMmkdata which does the different things listed below: +2. Run PTCLMmkdata Next you actually run PTCLMmkdata which does the different things listed below: - a. PTCLMmkdata names your output file directory based on your input + a. PTCLMmkdata names your output file directory based on your input :: [Prefix_]SiteCode - Where: + Where: ``Prefix`` is from the caseidprefix option (or blank if not used). ``SiteCode`` is the site name you entered with the -s option. @@ -57,26 +51,17 @@ Steps in running PTCLMmkdata > cd scripts > ./PTCLMmkdata -s US-UMB -d $MYCSMDATA - b. PTCLMmkdata creates datasets for you - It will populate $MYCSMDATA with new datasets it creates using the CLM tools. + b. PTCLMmkdata creates datasets for you It will populate $MYCSMDATA with new datasets it creates using the CLM tools. - c. If a transient compset and PTCLMmkdata finds a _dynpftdata.txt file - If you are running a transient compset (such as the "I_1850-2000_CN" compset) AND you there is a file in the PTCLM_sitedata directory under the PTCLMmkdata directory called $SITE_dynpftdata.txt it will use this file for the land-use changes. - Otherwise it will leave land-use constant, unless you use the pftgrid option so it uses the global dataset for landuse changes. - See the Section called Dynamic Land-Use Change Files for use by PTCLMmkdata for more information on this. - There is a sample transient dataset called US-Ha1_dynpftdata.txt. - Transient compsets, are compsets that create transient land-use change and forcing conditions such as: 'I_1850-2000', 'I_1850-2000_CN', 'I_RCP8.5_CN', 'I_RCP6.0_CN', 'I_RCP4.5_CN', or 'I_RCP2.6_CN'. + c. If a transient compset and PTCLMmkdata finds a _dynpftdata.txt file If you are running a transient compset (such as the "I_1850-2000_CN" compset) AND you there is a file in the PTCLM_sitedata directory under the PTCLMmkdata directory called $SITE_dynpftdata.txt it will use this file for the land-use changes. Otherwise it will leave land-use constant, unless you use the pftgrid option so it uses the global dataset for landuse changes. See the Section called Dynamic Land-Use Change Files for use by PTCLMmkdata for more information on this. There is a sample transient dataset called US-Ha1_dynpftdata.txt. Transient compsets, are compsets that create transient land-use change and forcing conditions such as: 'I_1850-2000', 'I_1850-2000_CN', 'I_RCP8.5_CN', 'I_RCP6.0_CN', 'I_RCP4.5_CN', or 'I_RCP2.6_CN'. - d. PTCLMmkdata creates a pft-physiology for you - PTCLMmkdata will create a local copy of the pft-physiology specific for your site that you could then customize with changes specific for that site. + d. PTCLMmkdata creates a pft-physiology for you PTCLMmkdata will create a local copy of the pft-physiology specific for your site that you could then customize with changes specific for that site. - e. PTCLMmkdata creates a README.PTCLM for you - PTCLMmkdata will create a simple text file with the command line for it in a file called README.PTCLM in the case directory it creates for you. + e. PTCLMmkdata creates a README.PTCLM for you PTCLMmkdata will create a simple text file with the command line for it in a file called README.PTCLM in the case directory it creates for you. 3. Run create_newcase pointing to the directory created -4. Customize, setup, build and run case as normal - You then customize your case as you would normally. See the Chapter 1 chapter for more information on doing this. +4. Customize, setup, build and run case as normal You then customize your case as you would normally. See the Chapter 1 chapter for more information on doing this. PTCLMmkdata options ========================= @@ -93,8 +78,7 @@ Configure options include: This option is for running PTCLMmkdata with a different root directory to CESM than the version PTCLMmkdata exists in. Normally you do NOT need to use this option. ``--sitegroupname`` - In the PTCLMmkdata directory there is a subdirectory "PTCLM_sitedata" that contains files with the site, PFT and soil data information for groups of sites. - These site groups are all separate ASCII files with the same prefix followed by a "_*data.txt" name. See `the Section called PTCLMmkdata Group Site Lists `_ for more information on these files. By default we have provided three different valid group names: + In the PTCLMmkdata directory there is a subdirectory "PTCLM_sitedata" that contains files with the site, PFT and soil data information for groups of sites. These site groups are all separate ASCII files with the same prefix followed by a "_*data.txt" name. See :ref:`adding-ptclm-site-data` for more information on these files. By default we have provided three different valid group names: EXAMPLE ------- @@ -102,11 +86,7 @@ AmeriFlux Fluxnet-Canada -The EXAMPLE is the group used by default and ONLY includes the US-UMB site as that is the only site we have data provided for. -The other two site groups include the site information for all of both the AmeriFlux and Fluxnet-Canada sites. -You can use the "sitegroupname" option to use one of the other lists, or you can create your own lists using the EXAMPLE file as an example. -Your list of sites could be real world locations or could be theoretical "virtual" sites given to exercise CLM on differing biomes for example. -Note, see `the Section called Converting AmeriFlux Data for use by PTCLMmkdata `_ with permission information to use the US-UMB data. +The EXAMPLE is the group used by default and ONLY includes the US-UMB site as that is the only site we have data provided for. The other two site groups include the site information for all of both the AmeriFlux and Fluxnet-Canada sites. You can use the "sitegroupname" option to use one of the other lists, or you can create your own lists using the EXAMPLE file as an example. Your list of sites could be real world locations or could be theoretical "virtual" sites given to exercise CLM on differing biomes for example. Note, see :ref:`converting-ameriflux-for-ptclmmkdata` with permission information to use the US-UMB data. ``--donot_use_tower_yrs`` This option is used with the "useQIAN" option to set the years to cycle over for the Qian data. In this case Qian atmospheric forcing will be used, but the simulation will run over the same years that tower site is available for this site. @@ -127,10 +107,8 @@ The options that with a "grid" suffix all mean to create datasets using the glob Because supported single-point datasets already have the data created for them, you MUST use the "nopointdata" and "ndepgrid" options when you are using a supported single-point site. You must use "ndepgrid" even for a compset without CN. You also can NOT use the options: "soilgrid", "pftgrid", "aerdepgrid", or "owritesrfaer". ``--pftgrid`` - This option says to use the PFT values provided on the global dataset rather than using the specific site based values from the PTCLM_sitedata/\*_pftdata.txt file when creating the surface dataset. - This option must NOT be used when you you are using a site that is a supported single point dataset. + This option says to use the PFT values provided on the global dataset rather than using the specific site based values from the PTCLM_sitedata/\*_pftdata.txt file when creating the surface dataset. This option must NOT be used when you you are using a site that is a supported single point dataset. ``--soilgrid`` - This option says to use the soil values provided on the global dataset rather than using the specific site based values from the PTCLM_sitedata/\*_soildata.txt file when creating the surface dataset. - This option must NOT be used when you you are using a site that is a supported single point dataset. + This option says to use the soil values provided on the global dataset rather than using the specific site based values from the PTCLM_sitedata/\*_soildata.txt file when creating the surface dataset. This option must NOT be used when you you are using a site that is a supported single point dataset. diff --git a/doc/source/users_guide/running-single-points/index.rst b/doc/source/users_guide/running-single-points/index.rst index e1d955ac0a..ba342d0ba9 100644 --- a/doc/source/users_guide/running-single-points/index.rst +++ b/doc/source/users_guide/running-single-points/index.rst @@ -3,10 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. _running-single-points: - .. include:: ../substitutions.rst +.. _running-single-points: + ##################################### Running Single Point Regional Cases ##################################### diff --git a/doc/source/users_guide/running-single-points/running-pts_mode-configurations.rst b/doc/source/users_guide/running-single-points/running-pts_mode-configurations.rst index b5ce4eadb7..d3b14e6184 100644 --- a/doc/source/users_guide/running-single-points/running-pts_mode-configurations.rst +++ b/doc/source/users_guide/running-single-points/running-pts_mode-configurations.rst @@ -1,13 +1,12 @@ -.. _pts_mode: - .. include:: ../substitutions.rst +.. _pts_mode: + **************************************************** Running a single point using global data - PTS_MODE **************************************************** -``PTS_MODE`` enables you to run the model using global datasets, but just picking a single point from those datasets and operating on it. -It can be a very quick way to do fast simulations and get a quick turnaround. +``PTS_MODE`` enables you to run the model using global datasets, but just picking a single point from those datasets and operating on it. It can be a very quick way to do fast simulations and get a quick turnaround. To setup a ``PTS_MODE`` simulation you use the "-pts_lat" and "-pts_lon" arguments to **create_newcase** to give the latitude and longitude of the point you want to simulate for (the code will pick the point on the global grid nearest to the point you give. Here's an example to setup a simulation for the nearest point at 2-degree resolution to Boulder Colorado. :: @@ -29,11 +28,6 @@ Then setup, build and run as normal. We make sure initial conditions are NOT use Running in a single processor ============================== -Note, that when running with ``PTS_MODE`` the number of processors is automatically set to one. -When running a single grid point you can only use a single processor. -You might also want to set the ``env_build.xml`` variable: ``MPILIB=mpi-serial`` to ``TRUE`` so that you can also run interactively without having to use MPI to start up your job. +Note, that when running with ``PTS_MODE`` the number of processors is automatically set to one. When running a single grid point you can only use a single processor. You might also want to set the ``env_build.xml`` variable: ``MPILIB=mpi-serial`` to ``TRUE`` so that you can also run interactively without having to use MPI to start up your job. -On many machines, batch queues have a minimum number of nodes or processors that can be used. -On these machines you may have to change the queue and possibly the time-limits of the job, to get it to run in the batch queue. -On the NCAR machine, cheyenne, this is done for you automatically, and the "share" or "caldera" queue is used for such single-processor simulations. -For single point mode you also may want to consider using a smaller workstation or cluster, rather than a super-computer, because you can't take advantage of the multi-processing power of the super-computer anyway. +On many machines, batch queues have a minimum number of nodes or processors that can be used. On these machines you may have to change the queue and possibly the time-limits of the job, to get it to run in the batch queue. On the NCAR machine, cheyenne, this is done for you automatically, and the "share" or "caldera" queue is used for such single-processor simulations. For single point mode you also may want to consider using a smaller workstation or cluster, rather than a super-computer, because you can't take advantage of the multi-processing power of the super-computer anyway. diff --git a/doc/source/users_guide/running-single-points/running-single-point-configurations.rst b/doc/source/users_guide/running-single-points/running-single-point-configurations.rst index 4705148f8c..8588da8b99 100644 --- a/doc/source/users_guide/running-single-points/running-single-point-configurations.rst +++ b/doc/source/users_guide/running-single-points/running-single-point-configurations.rst @@ -1,14 +1,12 @@ -.. _running-single-point-datasets: - .. include:: ../substitutions.rst +.. _running-single-point-datasets: + ****************************************** Running Single Point Configurations ****************************************** -In addition to ``PTS_MODE``, CLM supports running using single-point or regional datasets that are customized to a particular region. -CLM supports a a small number of out-of-the-box single-point and regional datasets. -However, users can create their own dataset. +In addition to ``PTS_MODE`` (Sect. :numref:`pts_mode`), CLM supports running using single-point or regional datasets that are customized to a particular region. CLM supports a a small number of out-of-the-box single-point and regional datasets. However, users can create their own dataset. To get the list of supported dataset resolutions do this: :: @@ -16,73 +14,71 @@ To get the list of supported dataset resolutions do this: > cd $CTSMROOT/doc > ../bld/build-namelist -res list - Which results in the following: :: CLM build-namelist - valid values for res (Horizontal resolutions Note: 0.5x0.5, 5x5min, 10x10min, 3x3min and 0.33x0.33 are only used for CLM tools): - Values: default 512x1024 360x720cru 128x256 64x128 48x96 32x64 8x16 94x192 \ - 0.23x0.31 0.47x0.63 0.9x1.25 1.9x2.5 2.5x3.33 4x5 10x15 5x5_amazon 1x1_tropicAtl \ - 1x1_vancouverCAN 1x1_mexicocityMEX 1x1_asphaltjungleNJ 1x1_brazil 1x1_urbanc_alpha 1x1_numaIA \ - 1x1_smallvilleIA 0.5x0.5 3x3min 5x5min 10x10min 0.33x0.33 ne4np4 ne16np4 ne30np4 ne60np4 \ + Values: default 512x1024 360x720cru 128x256 64x128 48x96 32x64 8x16 94x192 \ + 0.23x0.31 0.47x0.63 0.9x1.25 1.9x2.5 2.5x3.33 4x5 10x15 5x5_amazon 1x1_tropicAtl \ + 1x1_vancouverCAN 1x1_mexicocityMEX 1x1_asphaltjungleNJ 1x1_brazil 1x1_urbanc_alpha 1x1_numaIA \ + 1x1_smallvilleIA 0.5x0.5 3x3min 5x5min 10x10min 0.33x0.33 ne4np4 ne16np4 ne30np4 ne60np4 \ ne120np4 ne240np4 wus12 us20 Default = 1.9x2.5 (NOTE: resolution and mask and other settings may influence what the default is) The resolution names that have an underscore in them ("_") are all single-point or regional resolutions. -To run for the Brazil test site do the following: +.. note:: When running a single point, the number of processors is automatically set to one, which is the only value allowed. + +.. warning:: + Just like ``PTS_MODE`` (Sect. :numref:`pts_mode`), by default these setups sometimes run with ``MPILIB=mpi-serial`` (in the ``env_build.xml`` file) turned on, which allows you to run the model interactively. On some machines this mode is NOT supported and you may need to change it to FALSE before you are able to build. + +.. _single-point-global-climate: + +Single-point runs with global climate forcings +============================================== + +Example: Use global forcings at a site without its own special forcings +----------------------------------------------------------------------- -Example: Running CLM over a single-point test site in Brazil ------------------------------------------------------------- +This example uses the single-point site in Brazil. :: > cd scripts > set SITE=1x1_brazil - > ./create_newcase -case testSPDATASET -res $SITE -compset I2000Clm50SpGs + > ./create_newcase -case testSPDATASET -res $SITE -compset I2000Clm50SpGs > cd testSPDATASET Then setup, build and run normally. -Then to run for the urban Mexico City Mexico test site that also has atmosphere forcing data, but to run it with the Qian forcing data, but over the period for which it's own forcing data is provided do the following: +Example: Use global forcings at a site WITH its own special forcings +-------------------------------------------------------------------- + +The urban Mexico City test site has its own atmosphere forcing data (see Sect. :numref:`single-point-with-own-forcing`). To ignore that and run it with the default global forcing data, but over the period for which its own forcing data is provided, do the following: -Example: Running CLM over the single-point of Mexicocity Mexico with the default Qian atmosphere data forcing. -------------------------------------------------------------------------------------------------------------------------- :: > cd scripts # Set a variable to the site you want to use (as it's used several times below) > set SITE=1x1_mexicocityMEX - > ./create_newcase -case testSPDATASET -res $SITE -compset I1PtClm50SpGs + > ./create_newcase -case testSPDATASET -res $SITE -compset I1PtClm50SpGs > cd testSPDATASET -Then setup, build and run normally. - -**Important:** Just like PTS_MODE above, By default it sets up to run with ``MPILIB=mpi-serial`` (in the ``env_build.xml`` file) turned on, which allows you to run the model interactively. On some machines this mode is NOT supported and you may need to change it to FALSE before you are able to build. - -.. warning:: See `the Section called Warning about Running with a Single-Processor on a Batch Machine `_ for a warning about running single-point jobs on batch machines. +(Note the use of ``I1Pt`` instead of ``I2000`` as in the example above.) Then setup, build and run normally. -.. note:: When running a ``pt1_pt1`` resolution the number of processors is automatically set to one. When running a single grid point you can only use a single processor. You might also want to set the ``env_build.xml`` variable: ``MPILIB=mpi-serial`` to TRUE so that you can also run interactively without having to use mpi to start up your job. +.. _single-point-with-own-forcing: -Using Supported Single-point Datasets that have their own Atmospheric Forcing -================================================================================ +Supported single-point runs for sites with their own atmospheric forcing +======================================================================== -Of the supported single-point datasets we have three that also have atmospheric forcing data that go with them: Mexico City (Mexico), Vancouver, (Canada, British Columbia), and ``urbanc_alpha`` (test data for an Urban inter-comparison project). -Mexico city and Vancouver also have namelist options in the source code for them to work with modified urban data parameters that are particular to these locations. -To turn on the atmospheric forcing for these datasets, you set the ``env_run.xml DATM_MODE`` variable to ``CLM1PT``, and then the atmospheric forcing datasets will be used for the point picked. -If you use one of the compsets that has "I1Pt" in the name that will be set automatically. +Of the supported single-point datasets we have three that also have atmospheric forcing data that go with them: Mexico City (Mexico), Vancouver, (Canada, British Columbia), and ``urbanc_alpha`` (test data for an Urban inter-comparison project). Mexico city and Vancouver also have namelist options in the source code for them to work with modified urban data parameters that are particular to these locations. To turn on the atmospheric forcing for these datasets, you set the ``env_run.xml DATM_MODE`` variable to ``CLM1PT``, and then the atmospheric forcing datasets will be used for the point picked. If you use one of the compsets that has "I1Pt" in the name that will be set automatically. -When running with datasets that have their own atmospheric forcing you need to be careful to run over the period that data is available. -If you have at least one year of forcing it will cycle over the available data over and over again no matter how long of a simulation you run. -However, if you have less than a years worth of data (or if the start date doesn't start at the beginning of the year, or the end date doesn't end at the end of the year) then you won't be able to run over anything but the data extent. -In this case you will need to carefully set the ``RUN_STARTDATE``, ``START_TOD`` and ``STOP_N/STOP_OPTION`` variables for your case to run over the entire time extent of your data. -For the supported data points, these values are in the XML database and you can use the **queryDefaultNamelist.pl** script to query the values and set them for your case (they are set for the three urban test cases: Mexicocity, Vancouver, and urbanc_alpha). +When running with datasets that have their own atmospheric forcing you need to be careful to run over the period that data is available. If you have at least one year of forcing it will cycle over the available data over and over again no matter how long of a simulation you run. However, if you have less than a years worth of data (or if the start date doesn't start at the beginning of the year, or the end date doesn't end at the end of the year) then you won't be able to run over anything but the data extent. In this case you will need to carefully set the ``RUN_STARTDATE``, ``START_TOD`` and ``STOP_N/STOP_OPTION`` variables for your case to run over the entire time extent of your data. For the supported data points, these values are in the XML database and you can use the **queryDefaultNamelist.pl** script to query the values and set them for your case (they are set for the three urban test cases: Mexicocity, Vancouver, and urbanc_alpha). -In the example below we will show how to do this for the Vancouver, Canada point. - -Example: Running CLM over the single-point of Vancouver Canada with supplied atmospheric forcing data for Vancouver. -------------------------------------------------------------------------------------------------------------------------- +Example: Use site-specific atmospheric forcings +----------------------------------------------- +In this example, we show how to use the atmospheric forcings specific to the Vancouver, Canada point. :: > cd scripts @@ -112,19 +108,14 @@ Example: Running CLM over the single-point of Vancouver Canada with supplied atm > ./case.setup - .. warning:: If you don't set the start-year and run-length carefully as shown above the model will abort with a "dtlimit error" in the atmosphere model. Since, the forcing data for this site (and the MexicoCity site) is less than a year, the model won't be able to run for a full year. The ``1x1_urbanc_alpha`` site has data for more than a full year, but neither year is complete hence, it has the same problem (see the problem for this site above). -.. note:: Just like ``PTS_MODE`` above, By default it sets up to run with ``MPILIB=mpi-serial`` (in the env_build.xml file) turned on, which allows you to run the model interactively. - -.. note:: When running a ``pt1_pt1`` resolution the number of processors is automatically set to one. When running a single grid point you can only use a single processor. You might also want to set the ``env_build.xml`` variable: ``MPILIB=mpi-serial`` to ``TRUE`` so that you can also run interactively without having to use mpi to start up your job. - +.. _creating-your-own-singlepoint-dataset: Creating your own single-point dataset =================================================== -The following provides an example of setting up a case using ``CLM_USRDAT_NAME`` where you rename the files according to the ``CLM_USRDAT_NAME`` convention. -We have an example of such datafiles in the repository for a specific region over Alaska (actually just a sub-set of the global f19 grid). +The following provides an example of setting up a case using ``CLM_USRDAT_NAME`` where you rename the files according to the ``CLM_USRDAT_NAME`` convention. We have an example of such datafiles in the repository for a specific region over Alaska (actually just a sub-set of the global f19 grid). Example: Using CLM_USRDAT_NAME to run a simulation using user datasets for a specific region over Alaska ----------------------------------------------------------------------------------------------------------------------- @@ -147,7 +138,7 @@ Example: Using CLM_USRDAT_NAME to run a simulation using user datasets for a spe #> svn export $SVN_INP_URL/lnd/clm2/surfdata_map/surfdata_${GRIDNAME}_simyr2000.nc $CSMDATA/lnd/clm2/surfdata_map/surfdata_${GRIDNAME}_simyr2000.nc > ./case.setup -The first step is to create the domain and surface datasets using the process outlined in `the Section called The File Creation Process in Chapter 2 `_. Below we show an example of the process. +The first step is to create the domain and surface datasets using the process outlined in :ref:`using-clm-tools-section`. Below we show an example of the process. Example: Creating a surface dataset for a single point --------------------------------------------------------------------- @@ -173,7 +164,7 @@ Example: Creating a surface dataset for a single point > setenv OCNDOM domain.ocn_noocean.nc > setenv ATMDOM domain.lnd.{$GRIDNAME}_noocean.nc > ./gen_domain -m $MAPFILE -o $OCNDOM -l $ATMDOM - # Save the location where the domain file was created + # Save the location where the domain file was created > setenv GENDOM_PATH `pwd` # Finally create the surface dataset > cd ../../../../lnd/clm/tools/|version|/mksurfdata_map/src @@ -189,11 +180,11 @@ Example: Setting up a case from the single-point surface dataset just created # First setup an environment variable that points to the top of the CESM directory. > setenv CESMROOT - # Next make sure you have a inputdata location that you can write to + # Next make sure you have a inputdata location that you can write to # You only need to do this step once, so you won't need to do this in the future > setenv MYCSMDATA $HOME/inputdata # Set env var for the directory for input data > ./link_dirtree $CSMDATA $MYCSMDATA - # Copy the file you created above to your new $MYCSMDATA location following the CLMUSRDAT + # Copy the file you created above to your new $MYCSMDATA location following the CLMUSRDAT # naming convention (leave off the creation date) > cp $CESMROOT/$CTSMROOT/tools/mksurfdata_map/surfdata_${GRIDNAME}_simyr1850_$CDATE.nc \ $MYCSMDATA/lnd/clm2/surfdata_map/surfdata_${GRIDNAME}_simyr1850.nc diff --git a/doc/source/users_guide/running-single-points/single-point-and-regional-grid-configurations.rst b/doc/source/users_guide/running-single-points/single-point-and-regional-grid-configurations.rst index 9564b1649c..34a199ebe8 100644 --- a/doc/source/users_guide/running-single-points/single-point-and-regional-grid-configurations.rst +++ b/doc/source/users_guide/running-single-points/single-point-and-regional-grid-configurations.rst @@ -1,13 +1,12 @@ -.. _single-point-configurations: - .. include:: ../substitutions.rst +.. _single-point-configurations: + ***************************************** Single and Regional Grid Configurations ***************************************** -CLM allows you to set up and run cases with a single-point or a local region as well as global resolutions. -This is often useful for running quick cases for testing, evaluating specific vegetation types, or land-units, or running with observed data for a specific site. +CLM allows you to set up and run cases with a single-point or a local region as well as global resolutions. This is often useful for running quick cases for testing, evaluating specific vegetation types, or land-units, or running with observed data for a specific site. There are three different ways to do this for normal-supported site @@ -28,26 +27,13 @@ There are three different ways to do this for normal-supported site Choosing the right single point options ========================================= -Running for a *normal supported site* is a great solution, if one of the supported single-point/regional datasets, is your region of interest (see `the Section called Running Supported Single-point/Regional Datasets `_). -All the datasets are created for you, and you can easily select one and run, out of the box with it using a supported resolution from the top level of the CESM scripts. -The problem is that there is a very limited set of supported datasets. -You can also use this method for your own datasets, but you have to create the datasets, and add them to the XML database in scripts, CLM and to the DATM. This is worthwhile if you want to repeat many multiple cases for a given point or region. +Running for a *normal supported site* is a great solution, if one of the supported single-point/regional datasets, is your region of interest (see :ref:`running-single-point-datasets`). All the datasets are created for you, and you can easily select one and run, out of the box with it using a supported resolution from the top level of the CESM scripts. The problem is that there is a very limited set of supported datasets. You can also use this method for your own datasets, but you have to create the datasets, and add them to the XML database in scripts, CLM and to the DATM. This is worthwhile if you want to repeat many multiple cases for a given point or region. -In general `the Section called Running PTS_MODE configurations `_ is the quick and dirty method that gets you started without having to create datasets -- but has limitations. -It's good for an initial attempt at seeing results for a point of interest, but since you can NOT restart with it, it's usage is limited. -It is the quickest method as you can create a case for it directly from **create_newcase**. Although you can't restart, running a single point is very fast, and you can run for long simulation times even without restarts. +In general :ref:`pts_mode` is the quick and dirty method that gets you started without having to create datasets -- but has limitations. It's good for an initial attempt at seeing results for a point of interest, but since you can NOT restart with it, it's usage is limited. It is the quickest method as you can create a case for it directly from **create_newcase**. Although you can't restart, running a single point is very fast, and you can run for long simulation times even without restarts. -Next, ``CLM_USRDAT_NAME`` is the best way to setup cases quickly where you have to create your own datasets (see `the Section called Creating your own single-point/regional surface datasets `_). -With this method you don't have to change DATM or add files to the XML database -- but you have to follow a strict naming convention for files. -However, once the files are named and in the proper location, you can easily setup new cases that use these datasets. -This is good for treating all the required datasets as a "group" and for a particular model version. For advanced CLM developers who need to track dataset changes with different model versions you would be best off adding these datasets as supported datasets with the "normal supported datasets" method. +Next, ``CLM_USRDAT_NAME`` is the best way to setup cases quickly where you have to create your own datasets (see :ref:`running-single-point-datasets`). With this method you don't have to change DATM or add files to the XML database -- but you have to follow a strict naming convention for files. However, once the files are named and in the proper location, you can easily setup new cases that use these datasets. This is good for treating all the required datasets as a "group" and for a particular model version. For advanced CLM developers who need to track dataset changes with different model versions you would be best off adding these datasets as supported datasets with the "normal supported datasets" method. -Lastly *PTCLMmkdata* is a great way to easily create datasets, setup simulations and run simulations for tower sites. -It takes advantage of both normal supported site functionality and CLM_USRDAT_NAME internally. -A big advantage to it, is that it's one-stop shopping, it runs tools to create datasets, and runs **create_newcase** and sets the appropriate env variables for you. So you only have to learn how to run one tool, rather than work with many different ones. PTCLMmkdata is described in the next chapter `Chapter 6 `_. +Lastly *PTCLMmkdata* is a great way to easily create datasets, setup simulations and run simulations for tower sites. It takes advantage of both normal supported site functionality and CLM_USRDAT_NAME internally. A big advantage to it, is that it's one-stop shopping, it runs tools to create datasets, and runs **create_newcase** and sets the appropriate env variables for you. So you only have to learn how to run one tool, rather than work with many different ones. PTCLMmkdata is described in the next chapter, :ref:`running-PTCLM`. -Finally, if you also have meteorology data that you want to force your CLM simulations with you'll need to setup cases as described in `the Section called Running with your own atmosphere forcing `_. -You'll need to create CLM datasets either according to ``CLM_USRDAT_NAME``. -You may also need to modify DATM to use your forcing data. -And you'll need to change your forcing data to be in a format that DATM can use. In the PTCLMmkdata chapter `the Section called Converting AmeriFlux Data for use by PTCLMmkdata in Chapter 6 `_ section tells you how to use AmeriFlux data for atmospheric forcing. +Finally, if you also have meteorology data that you want to force your CLM simulations with you'll need to setup cases as described in :ref:`creating-your-own-singlepoint-dataset`. You'll need to create CLM datasets either according to ``CLM_USRDAT_NAME``. You may also need to modify DATM to use your forcing data. And you'll need to change your forcing data to be in a format that DATM can use. :ref:`converting-ameriflux-for-ptclmmkdata` tells you how to use AmeriFlux data for atmospheric forcing. diff --git a/doc/source/users_guide/running-special-cases/Running-stand-alone-CLM-with-transient-historical-CO2-concentration.rst b/doc/source/users_guide/running-special-cases/Running-stand-alone-CLM-with-transient-historical-CO2-concentration.rst index 9883662a4b..f93bcca2f0 100644 --- a/doc/source/users_guide/running-special-cases/Running-stand-alone-CLM-with-transient-historical-CO2-concentration.rst +++ b/doc/source/users_guide/running-special-cases/Running-stand-alone-CLM-with-transient-historical-CO2-concentration.rst @@ -1,43 +1,29 @@ -.. _running-with-historical-co2-forcing: - .. include:: ../substitutions.rst +.. _running-with-historical-co2-forcing: + ===================================== Running with historical CO2 forcing ===================================== -In this case you want to run a simulation with stand-alone CLM responding to changes in CO2 for a historical period. -For this example, we will start with the "I_1850-2000_CN" compset that has transient: land-use, Nitrogen and Aerosol deposition already. -You could also use another compset if you didn't want these other features to be transient. -In order to get CO2 to be transient we need to add a new streams file and add it to the list of streams in the user_nl_datm file. -You also need a NetCDF datafile that datm can read that gives the variation. You could supply your own file, but we have a standard file that is used by CAM for this and our example will make use of this file. +In this case you want to run a simulation with stand-alone CLM responding to changes in CO2 for a historical period. For this example, we will start with the "I_1850-2000_CN" compset that has transient: land-use, Nitrogen and Aerosol deposition already. You could also use another compset if you didn't want these other features to be transient. In order to get CO2 to be transient we need to add a new streams file and add it to the list of streams in the user_nl_datm file. You also need a NetCDF datafile that datm can read that gives the variation. You could supply your own file, but we have a standard file that is used by CAM for this and our example will make use of this file. -.. note:: Most everything here has to do with changing datm rather than CLM to allow this to happen. As such the user that wishes to do this should first become more familiar with datm and read the `CESM Data Model User's Guide `_ especially as it pertains to the datm. +.. note:: Most everything here has to do with changing datm rather than CLM to allow this to happen. As such the user that wishes to do this should first become more familiar with datm and read the `CESM Data Model User's Guide `_ especially as it pertains to the datm. .. warning:: This section documents the process for doing something that is non-standard. There may be errors with the documentation and process, and you may have to do some work before all of this works for you. If that is the case, we recommend that you do further research into understanding the process and the files, as well as understanding the datm and how it works. You may have to read documentation found in the code for datm as well as "csm_share". -The datm has "streams" files that have rough XML-like syntax and specify the location and file to get data from, as well as information on the variable names and the data locations of the grid points. -The datm expects specific variable names and the datm "maps" the expected variable names from the file to the names expected by datm. -The file we are working with here is a file with a single-point, that covers the entire globe (so the vertices go from -90 to 90 degrees in latitude and 0 to 360 degrees in longitude). -Since it's a single point it's a little easier to work with than datasets that may be at a given horizontal resolution. -The datm also expects that variables will be in certain units, and only expects a limited number of variables so arbitrary fields can NOT be exchanged this way. -However, the process would be similar for datasets that do contain more than one point. - -The three things that are needed: a domain file, a data file, and a streams text file. -The domain file is a CF-compliant NetCDF file that has information on the grid points (latitudes and longitudes for cell-centers and vertices, mask , fraction, and areas). -The datafile is a CF-compliant NetCDF file with the data that will be mapped. -The streams text file is the XML-like file that tells datm how to find the files and how to map the variables datm knows about to the variable names on the NetCDF files. Note, that in our case the domain file and the data file are the same file. In other cases, the domain file may be separate from the data file. +The datm has "streams" files that have rough XML-like syntax and specify the location and file to get data from, as well as information on the variable names and the data locations of the grid points. The datm expects specific variable names and the datm "maps" the expected variable names from the file to the names expected by datm. The file we are working with here is a file with a single-point, that covers the entire globe (so the vertices go from -90 to 90 degrees in latitude and 0 to 360 degrees in longitude). Since it's a single point it's a little easier to work with than datasets that may be at a given horizontal resolution. The datm also expects that variables will be in certain units, and only expects a limited number of variables so arbitrary fields can NOT be exchanged this way. However, the process would be similar for datasets that do contain more than one point. -First we are going to create a case, and we will edit the ``user_nl_datm`` so that we add a CO2 data stream in. -There is a streams text file available in ``$CTSMROOT/doc/UsersGuide/co2_streams.txt``, that includes file with a CO2 time-series from 1765 to 2007. +The three things that are needed: a domain file, a data file, and a streams text file. The domain file is a CF-compliant NetCDF file that has information on the grid points (latitudes and longitudes for cell-centers and vertices, mask, fraction, and areas). The datafile is a CF-compliant NetCDF file with the data that will be mapped. The streams text file is the XML-like file that tells datm how to find the files and how to map the variables datm knows about to the variable names on the NetCDF files. Note, that in our case the domain file and the data file are the same file. In other cases, the domain file may be separate from the data file. +First we are going to create a case, and we will edit the ``user_nl_datm`` so that we add a CO2 data stream in. There is a streams text file available in ``$CTSMROOT/doc/UsersGuide/co2_streams.txt``, that includes file with a CO2 time-series from 1765 to 2007. Example: Transient Simulation with Historical CO2 -------------------------------------------------------------- :: > cd scripts - > ./create_newcase -case DATM_CO2_TSERIES -res f19_g17_gl4 -compset IHistClm50BgcCrop + > ./create_newcase -case DATM_CO2_TSERIES -res f19_g17_gl4 -compset IHistClm50BgcCrop > cd DATM_CO2_TSERIES # Historical CO2 will already be setup correctly for this compset diff --git a/doc/source/users_guide/running-special-cases/Running-with-MOAR-data-as-atmospheric-forcing-to-spinup-the-model.rst b/doc/source/users_guide/running-special-cases/Running-with-MOAR-data-as-atmospheric-forcing-to-spinup-the-model.rst index 12903a18b5..dbe01c497c 100644 --- a/doc/source/users_guide/running-special-cases/Running-with-MOAR-data-as-atmospheric-forcing-to-spinup-the-model.rst +++ b/doc/source/users_guide/running-special-cases/Running-with-MOAR-data-as-atmospheric-forcing-to-spinup-the-model.rst @@ -1,16 +1,14 @@ -.. _running-with-moar-data: - .. include:: ../substitutions.rst +.. _running-with-moar-data: + ======================== Running with MOAR data ======================== -Because it takes so long to spinup the CN model (as we just saw previously), if you are doing fully coupled simulations with active atmosphere and ocean, you will want to do the spinup portion of this "offline". -So instead of doing expensive fully coupled simulations for the spinup duration, you run CLM in a very cheap "I" compset using atmospheric forcing from a shorter fully coupled simulation (or a simulation run previously by someone else). +Because it takes so long to spinup the CN model (as we just saw previously), if you are doing fully coupled simulations with active atmosphere and ocean, you will want to do the spinup portion of this "offline". So instead of doing expensive fully coupled simulations for the spinup duration, you run CLM in a very cheap "I" compset using atmospheric forcing from a shorter fully coupled simulation (or a simulation run previously by someone else). -In this example we will use the ``I1850Clm50BgcSpinup compset`` to setup CLM to run with atmospheric forcing from a previous fully coupled simulation with data that is already stored on disk on cheyenne. -There are several simulations that have high frequency data for which we can do this. You can also do this on a machine other than cheyenne, but would need to download the data from the Earth System Grid and change the datapath similar to `Example 4-11 `_. +In this example we will use the ``I1850Clm50BgcSpinup compset`` to setup CLM to run with atmospheric forcing from a previous fully coupled simulation with data that is already stored on disk on Cheyenne. There are several simulations that have high frequency data for which we can do this. You can also do this on a machine other than Cheyenne, but would need to download the data from the Earth System Grid and change the datapath similar to Example :numref:`eg-sim-data-from-prev-sim`. Example: Simulation with MOAR Data on cheyenne ------------------------------------------------------------- @@ -21,7 +19,7 @@ Example: Simulation with MOAR Data on cheyenne > cd MOARforce1850 # The following sets the casename to point to for atm forcing (you could also use an editor) > ./xmlchange DATM_CPL_CASE=b40.1850.track1.1deg.006a - # The following sets the align year and years to run over for atm forcing + # The following sets the align year and years to run over for atm forcing # (you could also use an editor) > ./xmlchange DATM_CPL_YR_ALIGN=1,DATM_CPL_YR_START=960,DATM_CPL_YR_END=1030 > ./case.setup diff --git a/doc/source/users_guide/running-special-cases/Running-with-anomaly-forcing.rst b/doc/source/users_guide/running-special-cases/Running-with-anomaly-forcing.rst index 10af089814..0c6009f3fe 100644 --- a/doc/source/users_guide/running-special-cases/Running-with-anomaly-forcing.rst +++ b/doc/source/users_guide/running-special-cases/Running-with-anomaly-forcing.rst @@ -1,11 +1,11 @@ -.. _running-with-anomaly-forcing: - .. include:: ../substitutions.rst +.. _running-with-anomaly-forcing: + ============================== Running with anomaly forcing ============================== -Because performing fully coupled climate simulations is computationally expensive, an alternate method of running land-only simulations forced by future climate projections was developed for CTSM called 'anomaly forcing'. The anomaly forcing method uses a previously completed fully coupled simulation to create monthly anomalies, relative to the present day, of near-surface atmospheric states and fluxes. These anomalies, representing the evolution of future climate projections, are applied to a repeating cycle of present day atmospheric forcing data, either as an additive (for states) or multiplicative (for fluxes) quantity. Thus, high-frequency variability is obtained from the present day atmospheric forcing data, while the long-term evolution of the climate is determined by the anomaly forcing dataset. +Because performing fully coupled climate simulations is computationally expensive, an alternate method of running land-only simulations forced by future climate projections was developed for CTSM called 'anomaly forcing'. The anomaly forcing method uses a previously completed fully coupled simulation to create monthly anomalies, relative to the present day, of near-surface atmospheric states and fluxes. These anomalies, representing the evolution of future climate projections, are applied to a repeating cycle of present day atmospheric forcing data, either as an additive (for states) or multiplicative (for fluxes) quantity. Thus, high-frequency variability is obtained from the present day atmospheric forcing data, while the long-term evolution of the climate is determined by the anomaly forcing dataset. To enable anomaly forcing in a CTSM simulation, the following namelist variable can be added to the user\_nl\_datm file: @@ -15,7 +15,7 @@ Any combination or subset of forcing variables can be used, e.g. to modify only anomaly\_forcing = 'Anomaly.Forcing.Temperature' -which will only adjust the temperature (TBOT). +which will only adjust the temperature (TBOT). After the namelist has been created, the run directory will be populated with files such as these: diff --git a/doc/source/users_guide/running-special-cases/Running-with-excess-ground-ice.rst b/doc/source/users_guide/running-special-cases/Running-with-excess-ground-ice.rst new file mode 100644 index 0000000000..d0b29f8682 --- /dev/null +++ b/doc/source/users_guide/running-special-cases/Running-with-excess-ground-ice.rst @@ -0,0 +1,40 @@ +.. _running-with-excess-ground-ice: + +.. include:: ../substitutions.rst + +=================================== + Running with excess ground ice +=================================== + + +Excess ground ice can be toggled with ``use_excess_ice`` namelist option. By default this option is ``.false.``. When +``use_excess_ice`` is true, CTSM needs initial excess ice amount within soil layers to initialize. A second namelist option ``use_excess_ice_streams`` exists to control this process (``.false.`` is default). If ``.true.`` and ``use_excess_ice`` is ``.true.``, +initial conditions will be read from a data-stream file (default is based :ref:`on IPA map from 1997 `). +This is useful, since in this way, a run with excess ground ice can be started from a restart or initial dataset, that does not include excess ground ice. +If the run is a continue-run, excess ice variables will **always** be expected on a restart file. + +.. note:: Excess ice amount provided by the stream file is expressed in excess ice concentration (%) and does not have a vertical distribution. Each soil layer beneath 0.5 m (or maximum active layer depth from the previous year if it is greater) down to bedrock will receive the same concentration, but the ice mass will be scaled by the soil layer depth. Both naturally vegetated and crop columns get excess ice. + + +Since presence of excess ice within the soil significantly alters heat diffusion within it, when starting from initial conditions where excess ice was not present, an additional spinup is required. +Usually such spinup takes 100-150 years (depending on your climate) to completely equilibrate soil temperatures. + + + +Example: Crop Simulation +------------------------------------ +:: + + > cd cime/scripts + > ./create_newcase -case I1850Clm50BgcCrop_with_exice -res f19_g17_gl4 -compset I1850Clm50BgcCrop + > cd I1850Clm50BgcCrop_with_exice + + > ./case.setup + + # turn on excess ice and its "stream" initialization + > echo "use_excess_ice=.true." >> user_nl_clm + > echo "use_excess_ice_streams=.true." >> user_nl_clm + + # Now build and run normally + > ./case.build + > ./case.submit diff --git a/doc/source/users_guide/running-special-cases/Running-with-tillage.rst b/doc/source/users_guide/running-special-cases/Running-with-tillage.rst new file mode 100644 index 0000000000..77309aea07 --- /dev/null +++ b/doc/source/users_guide/running-special-cases/Running-with-tillage.rst @@ -0,0 +1,38 @@ +.. _running-with-tillage: + +.. include:: ../substitutions.rst + +===================== + Running with tillage +===================== + + +Cropland tillage (Sect. :numref:`decomp_mgmt_modifiers`) can be toggled by specifying a value of ``'low'`` (low intensity) or ``'high'`` (high intensity) for the ``tillage_mode`` namelist option. By default this option is ``'off'``. + +Depth of tillage can be changed with the ``max_tillage_depth`` parameter (meters; default 0.26). + +Tillage multipliers for different soil pools and time since planting are defined on the parameter file, in variables ``bgc_till_decompk_multipliers`` (for CENTURY soil) and ``mimics_till_decompk_multipliers`` (for MIMICS soil). These variables were originally added with the script at ``tools/contrib/add_tillage_to_paramsfile.py``, which can be modified as needed to change tillage multipliers. + + +Example: Crop simulation with tillage +------------------------------------- +:: + + > cime/scripts/create_newcase -case IHistClm51BgcCrop_till -res f19_g17_gl4 -compset IHistClm51BgcCrop + + + > cd IHistClm51BgcCrop_till + > ./case.setup + + # turn on tillage ('low' or 'high'; default 'off') + > echo "tillage_mode = 'high'" >> user_nl_clm + +Reverting fixes relative to original tillage implementation +----------------------------------------------------------- + +The current implementation of tillage in CTSM is based on work by :ref:`Graham et al. (2021) `, but with fixes to how days after planting is calculated and to default tillage depth. To run without those changes: + +:: + + > echo "use_original_tillage_phases = .true." >> user_nl_clm + > echo "max_tillage_depth = 0.32" >> user_nl_clm diff --git a/doc/source/users_guide/running-special-cases/Running-with-your-own-previous-simulation-as-atmospheric-forcing-to-spinup-the-model.rst b/doc/source/users_guide/running-special-cases/Running-with-your-own-previous-simulation-as-atmospheric-forcing-to-spinup-the-model.rst index 34bb12ab49..ff05836f6e 100644 --- a/doc/source/users_guide/running-special-cases/Running-with-your-own-previous-simulation-as-atmospheric-forcing-to-spinup-the-model.rst +++ b/doc/source/users_guide/running-special-cases/Running-with-your-own-previous-simulation-as-atmospheric-forcing-to-spinup-the-model.rst @@ -1,13 +1,12 @@ -.. _running-with-previous-simulation-forcing: - .. include:: ../substitutions.rst +.. _running-with-previous-simulation-forcing: + ============================================================= Running with atmospheric forcing from a previous simulation ============================================================= -Another way that you might want to spinup the model is to run your own simulation for a relatively short period (either a B, E, or F compset) and then use it as forcing for your "I" case later. -By only running 20 to 50 years for the fully coupled case, you'll save a substantial amount of computer time rather than running the entire spinup period with a fully coupled model. +Another way that you might want to spinup the model is to run your own simulation for a relatively short period (either a B, E, or F compset) and then use it as forcing for your "I" case later. By only running 20 to 50 years for the fully coupled case, you'll save a substantial amount of computer time rather than running the entire spinup period with a fully coupled model. The first thing we need to do is to run a fully coupled case and save the atmospheric coupling fields on a three hourly basis. In this example, we will run on cheyenne and archive the data to a local disk that we can then use in the next simulation. @@ -19,7 +18,7 @@ Example: Fully Coupled Simulation to Create Data to Force Next Example Simulatio > ./create_newcase -case myB1850 -res f09_g17_gl4 -compset B1850 > cd myB1850 > ./case.setup - # Set histaux_a2x3hr to .true. in your user_nl_cpl output from the atmosphere model + # Set histaux_a2x3hr to .true. in your user_nl_cpl output from the atmosphere model # will be saved 3 hourly echo "histaux_a2x3hr=.true." >> user_nl_cpl # edit the driver code in order to save the correct list of fields (see note below) @@ -29,7 +28,7 @@ Example: Fully Coupled Simulation to Create Data to Force Next Example Simulatio > ./case.build # The following sets the archival disk space (you could also use an editor) > ./xmlchange DOUT_S_ROOT='/glade/home/$USER/$CASE' - # Make sure files are archived to disk, but NOT to long term storage + # Make sure files are archived to disk, but NOT to long term storage # (you could also use an editor) > ./xmlchange DOUT_S=TRUE,DOUT_L_MS=FALSE # Set the run length to run a total of 20 years (you could also use an editor) @@ -37,7 +36,9 @@ Example: Fully Coupled Simulation to Create Data to Force Next Example Simulatio # Now run as normal > ./case.submit -Now we run an I compset forced with the data from the previous simulation using the CPLHISTForcing`` option to DATM_MODE. See `the Section called CPLHISTForcing mode and it's DATM settings in Chapter 1 `_ for more information on the DATM settings for ``CPLHISTForcing`` mode. +Now we run an I compset forced with the data from the previous simulation using the ``CPLHISTForcing`` option to DATM_MODE. See :ref:`cplhistforcing` for more information. + +.. _eg-sim-data-from-prev-sim: Example: Simulation Forced with Data from the Previous Simulation ------------------------------------------------------------------------------ @@ -48,7 +49,7 @@ Example: Simulation Forced with Data from the Previous Simulation > cd frcWmyB1850 # The following sets the casename to point to for atm forcing (you could also use an editor) > ./xmlchange DATM_CPLHIST_CASE="myB1850" - # The following sets the align year and years to run over for atm forcing + # The following sets the align year and years to run over for atm forcing # (you could also use an editor) > ./xmlchange DATM_CPLHIST_YR_ALIGN="1",DATM_CPLHIST_YR_START=1,DATM_CPLHIST_YR_END=20 # Set the strm_datdir in the namelist_defaults_datm.xml @@ -60,7 +61,6 @@ Example: Simulation Forced with Data from the Previous Simulation > ./case.build > ./case.submit - .. note:: We did this by editing the "namelist_defaults_datm.xml" which will change the settings for ALL future ``I1850Clm50BgcSpinup`` cases you run. You could also do this by editing the path in the resulting streams text files in the CaseDocs directory, and then create a "user\_" streams file with the correct path. This would change the streams file JUST for this case. The steps do it this way are: :: diff --git a/doc/source/users_guide/running-special-cases/Spinning-up-the-Satellite-Phenology-Model-CLMSP-spinup.rst b/doc/source/users_guide/running-special-cases/Spinning-up-the-Satellite-Phenology-Model-CLMSP-spinup.rst index d6b6c74d91..eb482efdd6 100644 --- a/doc/source/users_guide/running-special-cases/Spinning-up-the-Satellite-Phenology-Model-CLMSP-spinup.rst +++ b/doc/source/users_guide/running-special-cases/Spinning-up-the-Satellite-Phenology-Model-CLMSP-spinup.rst @@ -1,21 +1,14 @@ -.. _spinning-up-sp: - .. include:: ../substitutions.rst +.. _spinning-up-sp: + =========================================== Spinning up the Satellite Phenology Model =========================================== -To spin-up the CLMSP model you merely need to run CLMSP for about 50 simulation years starting from arbitrary initial conditions. -You then use the final restart file for initial conditions in other simulations. -Because, this is a straight forward operation we will NOT give the details on how to do that here, but leave it as an exercise for the reader. -See the `Example 4-7 `_ as an example of doing this as the last step for CLMCN. +To spin-up the CLMSP model you merely need to run CLMSP for about 50 simulation years starting from arbitrary initial conditions. You then use the final restart file for initial conditions in other simulations. Because this is a straight forward operation we will NOT give the details on how to do that here, but leave it as an exercise for the reader. See the Example :numref:`eg-final-clmbgc-spinup` as an example of doing this as the last step for CLMCN. -You can also start from a default initial file that is setup as part of the selected compset. :numref:`Figure SP spinup plot for 1850` shows -spinup behavior for an 1850 SP case that loops over one year of coupler history output for atmospheric forcing (generated from the fully coupled model), -initialized with an initial file generated from a GSWP3 atmospheric forcing case. Note that it takes less than 10 years for state variables -such as FSH (sensible heat flux), EFLX_LH_TOT (latent heat flux), GPP (photosynthesis), H2OSOI (soil water), and TSOI (soil temperature) to reach -a specified equilibrium state (denoted by the dotted lines) due to the different atmospheric forcing. TWS (total water storage) may take a bit longer. +You can also start from a default initial file that is setup as part of the selected compset. :numref:`Figure SP spinup plot for 1850` shows spinup behavior for an 1850 SP case that loops over one year of coupler history output for atmospheric forcing (generated from the fully coupled model), initialized with an initial file generated from a GSWP3 atmospheric forcing case. Note that it takes less than 10 years for state variables such as FSH (sensible heat flux), EFLX_LH_TOT (latent heat flux), GPP (photosynthesis), H2OSOI (soil water), and TSOI (soil temperature) to reach a specified equilibrium state (denoted by the dotted lines) due to the different atmospheric forcing. TWS (total water storage) may take a bit longer. .. _Figure SP spinup plot for 1850: @@ -23,8 +16,7 @@ a specified equilibrium state (denoted by the dotted lines) due to the different SP spinup plot for year 1850. Variables examined are FSH (sensible heat flux), EFLX_LH_TOT (latent heat flux), GPP (photosynthesis), TWS (total water storage), H2OSOI (volumetric soil water in layer 8) and TSOI (soil temperature in layer 10). Generated using .../tools/contrib/SpinupStability_SP.ncl. -:numref:`Figure SP spinup plot for 2000 CO2` shows spinup behavior for the same case but also changes CO2 to present-day conditions (379ppmv). -Again, it takes about 10 years to reach equilibrium. +:numref:`Figure SP spinup plot for 2000 CO2` shows spinup behavior for the same case but also changes CO2 to present-day conditions (379ppmv). Again, it takes about 10 years to reach equilibrium. .. _Figure SP spinup plot for 2000 CO2: diff --git a/doc/source/users_guide/running-special-cases/Spinning-up-the-biogeochemistry-BGC-spinup.rst b/doc/source/users_guide/running-special-cases/Spinning-up-the-biogeochemistry-BGC-spinup.rst index 2ce977fc7e..cc266506a8 100644 --- a/doc/source/users_guide/running-special-cases/Spinning-up-the-biogeochemistry-BGC-spinup.rst +++ b/doc/source/users_guide/running-special-cases/Spinning-up-the-biogeochemistry-BGC-spinup.rst @@ -1,14 +1,12 @@ -.. _spinning-up-clm50-bgc: - .. include:: ../substitutions.rst -========================== +.. _spinning-up-clm-bgc: + +============================= Spinup of |version|-BGC-Crop -========================== +============================= -To get the |version|-BGC model to a steady state, you first run it from arbitrary initial conditions using the "accelerated decomposition spinup" (-bgc_spinup on in CLM **configure**, see example below) mode for about 200 simulation years. :numref:`Figure BGC AD spinup plot for 1850 GSWP3` shows spinup behavior for an 1850 -BGC accelerated decomposition (AD) case using GSWP3 atmospheric forcing. Generally, the criteria that less than 3% of the land surface be in -total ecosystem carbon disequilibrium takes the longest to satisfy due to slow soil carbon (TOTSOMC) turnover times in the Arctic. +To get the |version|-BGC model to a steady state, you first run it from arbitrary initial conditions using the "accelerated decomposition spinup" (-bgc_spinup on in CLM **configure**, see example below) mode for about 200 simulation years. :numref:`Figure BGC AD spinup plot for 1850 GSWP3` shows spinup behavior for an 1850 BGC accelerated decomposition (AD) case using GSWP3 atmospheric forcing. Generally, the criteria that less than 3% of the land surface be in total ecosystem carbon disequilibrium takes the longest to satisfy due to slow soil carbon (TOTSOMC) turnover times in the Arctic. .. _Figure BGC AD spinup plot for 1850 GSWP3: @@ -16,11 +14,7 @@ total ecosystem carbon disequilibrium takes the longest to satisfy due to slow s BGC AD spinup plot for a year 1850 case with GSWP3 atmospheric forcing. Variables examined are TOTECOSYSC (total ecosystem carbon), TOTSOMC (total soil organic matter carbon), TOTVEGC (total vegetation carbon), TLAI (total leaf area index), GPP (gross primary production) and TWS (total water storage). Generated using .../tools/contrib/SpinupStability.ncl. -After this you branch from this mode in the "final spinup" (-bgc_spinup off in CLM **configure**, see example below), and run for several hundred simulation years. -:numref:`Figure BGC pAD spinup plot for 1850 GSWP3` shows spinup behavior for an 1850 -BGC post accelerated decomposition (pAD) case using GSWP3 atmospheric forcing. As before, the criteria that less than 3% of the land surface be in -total ecosystem carbon disequilibrium takes the longest to satisfy. It can be difficult to meet this strict criteria in less than 1000 years and users may want to relax this -criteria depending on their application. +After this you branch from this mode in the "final spinup" (-bgc_spinup off in CLM **configure**, see example below), and run for several hundred simulation years. :numref:`Figure BGC pAD spinup plot for 1850 GSWP3` shows spinup behavior for an 1850 BGC post accelerated decomposition (pAD) case using GSWP3 atmospheric forcing. As before, the criteria that less than 3% of the land surface be in total ecosystem carbon disequilibrium takes the longest to satisfy. It can be difficult to meet this strict criteria in less than 1000 years and users may want to relax this criteria depending on their application. .. _Figure BGC pAD spinup plot for 1850 GSWP3: @@ -28,10 +22,7 @@ criteria depending on their application. BGC pAD spinup plot for a year 1850 case with GSWP3 atmospheric forcing and initialization from the end of the BGC AD spinup case. Variables examined are TOTECOSYSC (total ecosystem carbon), TOTSOMC (total soil organic matter carbon), TOTVEGC (total vegetation carbon), TLAI (total leaf area index), GPP (gross primary production) and TWS (total water storage). Generated using .../tools/contrib/SpinupStability.ncl. -You can also start from a default initial file that is setup as part of the selected compset. :numref:`Figure BGC initialized spinup plot for 1850` shows -spinup behavior for an 1850 pAD BGC case that loops over one year of coupler history output for atmospheric forcing (generated from the fully coupled model), -initialized with a BGC initial file generated from a GSWP3 atmospheric forcing case. Note that it takes about 10 years for variables -such as TLAI (total leaf area index), GPP (gross primary production), and TWS (total water storage) to reach a specified equilibrium state (denoted by the dotted lines) due to the different atmospheric forcing. +You can also start from a default initial file that is setup as part of the selected compset. :numref:`Figure BGC initialized spinup plot for 1850` shows spinup behavior for an 1850 pAD BGC case that loops over one year of coupler history output for atmospheric forcing (generated from the fully coupled model), initialized with a BGC initial file generated from a GSWP3 atmospheric forcing case. Note that it takes about 10 years for variables such as TLAI (total leaf area index), GPP (gross primary production), and TWS (total water storage) to reach a specified equilibrium state (denoted by the dotted lines) due to the different atmospheric forcing. .. _Figure BGC initialized spinup plot for 1850: @@ -39,8 +30,7 @@ such as TLAI (total leaf area index), GPP (gross primary production), and TWS (t BGC initialized spinup plot for year 1850. Variables examined are TOTECOSYSC (total ecosystem carbon), TOTSOMC (total soil organic matter carbon), TOTVEGC (total vegetation carbon), TLAI (total leaf area index), GPP (gross primary production) and TWS (total water storage). Generated using .../tools/contrib/SpinupStability.ncl. -:numref:`Figure BGC initialized spinup plot for 2000 CO2` shows spinup behavior for the same case but also changes CO2 to present-day conditions (379ppmv). -Again, it takes about 10 years to reach equilibrium for TLAI, GPP, and TWS. +:numref:`Figure BGC initialized spinup plot for 2000 CO2` shows spinup behavior for the same case but also changes CO2 to present-day conditions (379ppmv). Again, it takes about 10 years to reach equilibrium for TLAI, GPP, and TWS. .. _Figure BGC initialized spinup plot for 2000 CO2: @@ -48,14 +38,11 @@ Again, it takes about 10 years to reach equilibrium for TLAI, GPP, and TWS. BGC initialized spinup plot for year 2000 CO2. Variables examined are TOTECOSYSC (total ecosystem carbon), TOTSOMC (total soil organic matter carbon), TOTVEGC (total vegetation carbon), TLAI (total leaf area index), GPP (gross primary production) and TWS (total water storage). Generated using .../tools/contrib/SpinupStability.ncl. -If you use the default initial file and you signficantly change model behavior or atmospheric forcing, and you are concerned about the carbon -equilibrium (e.g., TOTECOSYSC, TOTSOMC, TOTVEGC), particularly at high latitudes, then we recommend you put the model back into AD mode to -reach a new equilibrium. In this configuration, this will also automatically reseed "dead" plant functional types in the initial file with a -bit of leaf carbon to give those plant functional types another chance to grow under the new atmospheric forcing or model conditions. +If you use the default initial file and you signficantly change model behavior or atmospheric forcing, and you are concerned about the carbon equilibrium (e.g., TOTECOSYSC, TOTSOMC, TOTVEGC), particularly at high latitudes, then we recommend you put the model back into AD mode to reach a new equilibrium. In this configuration, this will also automatically reseed "dead" plant functional types in the initial file with a bit of leaf carbon to give those plant functional types another chance to grow under the new atmospheric forcing or model conditions. **1. |version| accelerated-decomposition (AD) spinup** For the first step of running 200+ years in "-bgc_spinup on" mode, you will setup a case, and then edit the values in env_build.xml and env_run.xml so that the right configuration is turned on and the simulation is setup to run for the required length of simulation time. So do the following: - + Example:: AD_SPINUP Simulation for |version|-BGC -------------------------------------------------------- :: @@ -82,6 +69,8 @@ Afterwards save the last restart file from this simulation to use in the next st **2. Final spinup for |version|-BGC** Next save the last restart file from this step and use it as the "finidat" file to use for one more spinup for at least 400+ years in normal mode. So do the following: +.. _eg-final-clmbgc-spinup: + Example: Final CLMBGC Spinup Simulation for |version|-BGC ------------------------------------------------------------------ :: @@ -112,5 +101,3 @@ To assess if the model is spunup, plot trends for CLMBGC variables of interest u .. note:: This same final spinup procedure works for |version|-CN as well. - - diff --git a/doc/source/users_guide/running-special-cases/index.rst b/doc/source/users_guide/running-special-cases/index.rst index f84b7706fb..7ead8f1551 100644 --- a/doc/source/users_guide/running-special-cases/index.rst +++ b/doc/source/users_guide/running-special-cases/index.rst @@ -3,10 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. _running-special-cases-section: - .. include:: ../substitutions.rst +.. _running-special-cases-section: + ##################################### Running Special Cases ##################################### @@ -17,8 +17,10 @@ Running Special Cases what-is-a-special-case.rst running-the-prognostic-crop-model.rst running-with-irrigation.rst + running-with-custom-crop-calendars.rst Spinning-up-the-Satellite-Phenology-Model-CLMSP-spinup.rst Spinning-up-the-biogeochemistry-BGC-spinup.rst + Running-with-excess-ground-ice.rst Running-with-MOAR-data-as-atmospheric-forcing-to-spinup-the-model.rst Running-with-your-own-previous-simulation-as-atmospheric-forcing-to-spinup-the-model.rst Running-stand-alone-CLM-with-transient-historical-CO2-concentration.rst diff --git a/doc/source/users_guide/running-special-cases/running-the-prognostic-crop-model.rst b/doc/source/users_guide/running-special-cases/running-the-prognostic-crop-model.rst index 20ea12e91f..7e19af8678 100644 --- a/doc/source/users_guide/running-special-cases/running-the-prognostic-crop-model.rst +++ b/doc/source/users_guide/running-special-cases/running-the-prognostic-crop-model.rst @@ -1,22 +1,19 @@ -.. running-prognostic-crop-model: - .. include:: ../substitutions.rst +.. _running-prognostic-crop-model: + =================================== Running the prognostic crop model =================================== -The prognostic crop model is setup to work with CLM4.0, CLM4.5 or |version| with either BGC or CN (with or without DV). -In order to use the initial condition file, we need to set the ``RUN_TYPE`` to startup rather than ``hybrid`` since the compset for f19 sets up to use an initial condition file without crop active. -To activate the crop model you can choose a compset that has "Crop" in the name such as "I1850Clm50BgcCropCru" or simply add -"-crop" to ``CLM_BLDNML_OPTS`` (or for CLM4.0 add "-crop on" to ``CLM_CONFIG_OPTS``). +The prognostic crop model is setup to work with CLM4.0, CLM4.5 or |version| with either BGC or CN (with or without DV). In order to use the initial condition file, we need to set the ``RUN_TYPE`` to startup rather than ``hybrid`` since the compset for f19 sets up to use an initial condition file without crop active. To activate the crop model you can choose a compset that has "Crop" in the name such as "I1850Clm50BgcCropCru" or simply add "-crop" to ``CLM_BLDNML_OPTS`` (or for CLM4.0 add "-crop on" to ``CLM_CONFIG_OPTS``). Example: Crop Simulation ------------------------------------ :: > cd scripts - > ./create_newcase -case CROP -res f19_g17_gl4 -compset I1850Clm50BgcCropCru + > ./create_newcase -case CROP -res f19_g17_gl4 -compset I1850Clm50BgcCropCru > cd CROP > ./case.setup diff --git a/doc/source/users_guide/running-special-cases/running-with-custom-crop-calendars.rst b/doc/source/users_guide/running-special-cases/running-with-custom-crop-calendars.rst new file mode 100644 index 0000000000..b19ebcdb09 --- /dev/null +++ b/doc/source/users_guide/running-special-cases/running-with-custom-crop-calendars.rst @@ -0,0 +1,96 @@ +.. running-with-custom-crop-calendars: + +.. include:: ../substitutions.rst + +======================================= + Running with custom crop calendars +======================================= + +Since CLM5.1, functionality has been added to enable the customization of crop sowing window and maturity requirements. + +Sowing window +------------- +Crops are allowed to be sown only within certain date windows. By default, these are static in time and specific to each crop in each hemisphere. These values can be inspected by looking at (and changed by modifying) the following variables on the parameter file: + +- ``min_NH_planting_date`` (start of sowing window, northern hemisphere) +- ``max_NH_planting_date`` (end of sowing window, northern hemisphere) +- ``min_SH_planting_date`` (start of sowing window, southern hemisphere) +- ``max_SH_planting_date`` (end of sowing window, southern hemisphere) + +However, geographically- and temporally-varying maps can also be used to prescribe sowing window like so (in ``user_nl_clm``): +:: + + ! Input files with maps of the start and end date (1-365) of every crop + stream_fldFileName_swindow_start = '/glade/p/cesmdata/cseg/inputdata/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' + stream_fldFileName_swindow_end = '/glade/p/cesmdata/cseg/inputdata/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' + + ! A mesh file matching the resolution of the sowing window datasets + stream_meshfile_cropcal = '/glade/p/cesmdata/cseg/inputdata/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc' + + ! First and last years on the sowing window datasets + stream_year_first_cropcal = 2000 + stream_year_last_cropcal = 2000 + +Sowing date +----------- +Specific sowing *dates* can be prescribed for any crop in any gridcell by setting the start and end dates of its sowing windows to the same value. The simplest way to do this for all crops in all gridcells is to specify the same file for both ``stream_fldFileName_swindow_start`` and ``stream_fldFileName_swindow_end``. + +.. note:: In cells with prescribed sowing dates, the usual weather- and climate-based criteria for determining whether planting is allowed are ignored. The crop will be planted on the prescribed day no matter what. + +Maturity requirements +--------------------- +The heat unit accumulation required for a crop to reach maturity (and thus be harvested) is typically determined by a formula with crop-specific parameters that are specified on the parameter file. However, geographically- and temporally-varying maps of maturity requirement (in units of degree-days) can also be specified using the ``user_nl_clm`` input variable ``stream_fldFileName_cultivar_gdds``. (Note that ``stream_meshfile_cropcal``, ``stream_year_first_cropcal``, and ``stream_year_last_cropcal``---see above---are all also required.) + +Generating maturity requirements +-------------------------------- +For phase 3 of the Global Gridded Crop Model Intercomparison (GGCMI), maturity requirements should be the average (over the 1980--2009 growing seasons) growing degree-days accumulated between specified observation-derived sowing and harvest dates. In CLM, this requires the use of a special "GDD-generating" run and some postprocessing. + +In a GDD-generating run, crops are planted on the specified sowing dates and are then left in the field---regardless of when they reach maturity and ignoring maximum growing season length---for 364 days. This is set up like so in ``user_nl_clm``: +:: + + ! Variables that we introduced above + stream_fldFileName_swindow_start = '/path/to/sowing_date_file.nc' + stream_fldFileName_swindow_end = '/path/to/sowing_date_file.nc' + stream_meshfile_cropcal = '/path/to/mesh_file.nc' + stream_year_first_cropcal = YEAR + stream_year_last_cropcal = YEAR + + ! Special settings for "GDD-generating" run + generate_crop_gdds = .true. + use_mxmat = .false. + + ! (h0) Save default variables monthly instead of daily to save space + hist_nhtfrq = 0 + hist_mfilt = 12 + + ! (h1) Annual outputs for GDD generation + hist_fincl2 = 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV' + hist_nhtfrq(2) = 17520 + hist_mfilt(2) = 999 + hist_type1d_pertape(2) = 'PFTS' + hist_dov2xy(2) = .false. + + ! (h2) Daily outputs for GDD generation + hist_fincl3 = 'GDDACCUM', 'GDDHARV' + hist_nhtfrq(3) = -24 + hist_mfilt(3) = 365 + hist_type1d_pertape(3) = 'PFTS' + hist_dov2xy(3) = .false. + +Once the GDD-generating run completes, calling the following Python script will generate a file that can serve as ``stream_fldFileName_cultivar_gdds`` in subsequent runs (along with maps illustrating the results): +:: + + python3 python/ctsm/crop_calendars/generate_gdds.py \ + --input-dir /path/to/output/dir/from/gdd-generating-run \ + --first-season 1980 \ + --last-season 2009 \ + --sdates-file '/path/to/sowing_date_file.nc' \ + --hdates-file '/path/to/harvest_date_file.nc' \ + --output-dir '/path/where/you/want/results/saved' \ + --skip-crops miscanthus,irrigated_miscanthus + +The entire process can be illustrated with the RXCROPMATURITY system test. E.g.: + +:: + + run_sys_tests -t RXCROPMATURITY_Lm61.f10_f10_mg37.IHistClm51BgcCrop.cheyenne_intel.clm-cropMonthOutput --skip-generate --skip-compare \ No newline at end of file diff --git a/doc/source/users_guide/running-special-cases/running-with-irrigation.rst b/doc/source/users_guide/running-special-cases/running-with-irrigation.rst index aa99179971..12fa76af5b 100644 --- a/doc/source/users_guide/running-special-cases/running-with-irrigation.rst +++ b/doc/source/users_guide/running-special-cases/running-with-irrigation.rst @@ -1,17 +1,12 @@ -.. running-with-irrigation: - .. include:: ../substitutions.rst +.. _running-with-irrigation: + =================================== Running with irrigation =================================== -In CLM4.0 irrigation isn't an allowed option. -In CLM4.5 irrigation can ONLY be used WITH crop. With CLM5.0 irrigation can be used whether crop is on or not -- **BUT** -if crop is off, your surface datasets **HAVE** to have irrigation defined appropriately. Right now *ALL* surface -datasets without crop enabled have irrigation hard-wired on. In order to create datasets with irrigation off, you'd need -to make changes to ``mksurfdata_map`` in order to have all generic crops to be non-irrigated. -To turn on irrigation in |version| we simply add "-irrig on" to ``CLM_BLDNML_OPTS``. +In CLM4.0 irrigation isn't an allowed option. In CLM4.5 irrigation can ONLY be used WITH crop. With CLM5.0 irrigation can be used whether crop is on or not -- **BUT** if crop is off, your surface datasets **HAVE** to have irrigation defined appropriately. Right now *ALL* surface datasets without crop enabled have irrigation hard-wired on. In order to create datasets with irrigation off, you'd need to make changes to ``mksurfdata_map`` in order to have all generic crops to be non-irrigated. To turn on irrigation in |version| we simply add "-irrig on" to ``CLM_BLDNML_OPTS``. Example: Irrigation Simulation ------------------------------------------ @@ -31,4 +26,3 @@ Example: Irrigation Simulation > ./case.build > ./case.submit - diff --git a/doc/source/users_guide/running-special-cases/what-is-a-special-case.rst b/doc/source/users_guide/running-special-cases/what-is-a-special-case.rst index 5a42858818..eece6aab51 100644 --- a/doc/source/users_guide/running-special-cases/what-is-a-special-case.rst +++ b/doc/source/users_guide/running-special-cases/what-is-a-special-case.rst @@ -1,14 +1,12 @@ -.. _what is a special case: - .. include:: ../substitutions.rst +.. _what is a special case: + ========================= What is a special case? ========================= -All of the following special cases cases that take more than one step to do. -The straightforward cases have compsets and/or build-namelist use-cases setup for them or require simple editing of a single-case. -All of the cases here require you to do at least two simulations with different configurations, or require more complex editing of the case (changing the streams files). +All of the following special cases cases that take more than one step to do. The straightforward cases have compsets and/or build-namelist use-cases setup for them or require simple editing of a single-case. All of the cases here require you to do at least two simulations with different configurations, or require more complex editing of the case (changing the streams files). .. note:: The cases in this chapter are more sophisticated and require more technical knowledge and skill than cases in previous chapters. The user should be very familiar with doing simple cases before moving onto the cases described here. diff --git a/doc/source/users_guide/setting-up-and-running-a-case/README_history_fields_files b/doc/source/users_guide/setting-up-and-running-a-case/README_history_fields_files new file mode 100644 index 0000000000..c611c19ae2 --- /dev/null +++ b/doc/source/users_guide/setting-up-and-running-a-case/README_history_fields_files @@ -0,0 +1,11 @@ +2021/9/8 slevis +2023/8/15 samsrabin + +The files history_fields_nofates.rst and history_fields_fates.rst each contain a +table of the history fields, active and inactive, available in the CTSM cases +that get generated by these tests: +SMS_Ld1.f10_f10_mg37.I1850Clm50BgcCrop.cheyenne_intel.clm-SaveHistFieldList +SMS_Ld1.f10_f10_mg37.I2000Clm50FatesCru.cheyenne_intel.clm-SaveHistFieldList + +To reproduce these .rst files, run the above tests and the files +will appear in the corresponding run directories. diff --git a/doc/source/users_guide/setting-up-and-running-a-case/README_master_list_files b/doc/source/users_guide/setting-up-and-running-a-case/README_master_list_files deleted file mode 100644 index 61f8ef44d4..0000000000 --- a/doc/source/users_guide/setting-up-and-running-a-case/README_master_list_files +++ /dev/null @@ -1,10 +0,0 @@ -2021/9/8 slevis - -The files master_list_nofates.rst and master_list_fates.rst each contain a -table of the history fields, active and inactive, available in the CTSM cases -that get generated by these tests: -ERP_P36x2_D_Ld3.f10_f10_mg37.I1850Clm50BgcCrop.cheyenne_gnu.clm-extra_outputs -ERS_Ld9.f10_f10_mg37.I2000Clm50FatesCru.cheyenne_intel.clm-FatesColdDefCH4 - -To reproduce these .rst files, run the above tests and the files -will appear in the corresponding run directories. diff --git a/doc/source/users_guide/setting-up-and-running-a-case/choosing-a-compset.rst b/doc/source/users_guide/setting-up-and-running-a-case/choosing-a-compset.rst index c2bb0b606b..a3fb4770f7 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/choosing-a-compset.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/choosing-a-compset.rst @@ -1,34 +1,23 @@ -.. _choosing-a-compset: - .. include:: ../substitutions.rst +.. _choosing-a-compset: + ==================== Choosing a compset ==================== -When setting up a new case one of the first choices to make is which "component set" (or compset) to use. -The compset refers to which component models are used as well as specific settings for them. -We label the different types of compsets with a different letter of the alphabet from "A" (for all data model) to "X" (for all dead model). -The compsets of interest when working with CLM are the "I" compsets (which contain CLM with a data atmosphere model and a stub ocean, and stub sea-ice models), "E" and "F" compsets (which contain CLM with the active atmosphere model (CAM), prescribed sea-ice model, and a data ocean model), and "B" compsets which have all active components. -Below we go into details on the "I" compsets which emphasize CLM as the only active model, and just mention the two other categories. +When setting up a new case one of the first choices to make is which "component set" (or compset) to use. The compset refers to which component models are used as well as specific settings for them. We label the different types of compsets with a different letter of the alphabet from "A" (for all data model) to "X" (for all dead model). The compsets of interest when working with CLM are the "I" compsets (which contain CLM with a data atmosphere model and a stub ocean, and stub sea-ice models), "E" and "F" compsets (which contain CLM with the active atmosphere model (CAM), prescribed sea-ice model, and a data ocean model), and "B" compsets which have all active components. Below we go into details on the "I" compsets which emphasize CLM as the only active model, and just mention the two other categories. -To run CLM coupled to CAM ("E" or "F" compsets) or fully coupled ("B compsets) you need to be running CLM from a CESM checkout rather than a CTSM checkout (see :ref:`ctsm_vs_cesm_checkout`). +To run CLM coupled to CAM ("E" or "F" compsets) or fully coupled ("B" compsets) you need to be running CLM from a CESM checkout rather than a CTSM checkout (see :ref:`ctsm_vs_cesm_checkout`). -When working with CLM you usually want to start with a relevant "I" compset before moving to the more complex cases that involve other active model components. -The "I" compsets can exercise CLM in a way that is similar to the coupled modes, but with much lower computational cost and faster turnaround times. +When working with CLM you usually want to start with a relevant "I" compset before moving to the more complex cases that involve other active model components. The "I" compsets can exercise CLM in a way that is similar to the coupled modes, but with much lower computational cost and faster turnaround times. Compsets coupled to data atmosphere and stub ocean/sea-ice ("I" compsets) ------------------------------------------------------------------------- -`Supported CLM Configurations `_ are listed in `Table 1-1 `_ for the Scientifically Supported compsets (have been scientifically validated with long simulations) and in `Table 1-2 `_ for the Functionally Supported compsets (we've only checked that they function). - - -Here is the entire list of compsets available. - -`CESM compsets `_ +The entire list of compsets available out-of-the-box can be browsed at the `CESM Component Set Definitions `_ page. Note that using the compset longnames, even more combinations are possible than those listed. That webpage also includes information on whether each compset has been tested and/or scientifically validated. -Note that using the "-user_compset" option even more combinations are possible. To get a list of the compsets use the "query_config" -command as follows: +To get a list of the compsets use the ``query_config`` command as follows: :: $CTSMROOT/cime/scripts/query_config --compsets clm @@ -43,6 +32,4 @@ Fully coupled compsets are compsets that start with "B" in the name. They are de Conclusion to choosing a compset -------------------------------- -We've introduced the basic type of compsets that use CLM and given some further details for the "standalone CLM" (or "I" compsets). -The `$CTSMROOT/cime_config/config_compsets.xml `_ lists all of the compsets and gives a full description of each of them. -In the next section we look into customizing the setup time options for compsets using CLM. +We've introduced the basic type of compsets that use CLM and given some further details for the "standalone CLM" (or "I" compsets). `$CTSMROOT/cime_config/config_compsets.xml `_ lists all of the compsets and gives a full description of each of them. In the next section we look into customizing the setup time options for compsets using CLM. diff --git a/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-configuration.rst b/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-configuration.rst index 08041c522a..f8b5fee002 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-configuration.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-configuration.rst @@ -1,27 +1,24 @@ -.. _configuring-clm: - .. include:: ../substitutions.rst +.. _configuring-clm: + ******************************** Customizing CLM's Configuration ******************************** -The section of the |cesmrelease| Quickstart `CESM Create a Case `_ gives instructions on creating a case. -Also see a similar section in the CIME User's-Guide `CIME Create a case `_. -What is of interest here is how to customize your use of CLM for the case that you created. +The section of the |cesmrelease| Quickstart `CESM Create a Case `_ gives instructions on creating a case. Also see a similar section in the CIME User's-Guide `CIME Create a case `_. What is of interest here is how to customize your use of CLM for the case that you created. For CLM when **preview_namelist**, **case.build**, or **case.run** are called there are two steps that take place: -1. The CLM "**configure**" script is called to setup the build-time configuration for CLM (more information on **configure** is given in `the Section called More information on the CLM configure script `_). The env variables for **configure** are locked after the **case.build** step. So the results of the CLM **configure** are locked after the build has taken place. +1. The CLM "**configure**" script is called to setup the build-time configuration for CLM (see :ref:`more-info-clm-config-script`). The env variables for **configure** are locked after the **case.build** step. So the results of the CLM **configure** are locked after the build has taken place. -2. The CLM "**build-namelist**" script is called to generate the run-time namelist for CLM (more information on **build-namelist** is given below in `the Section called Definition of Namelist items and their default values `_. +2. The CLM "**build-namelist**" script is called to generate the run-time namelist for CLM (more information on **build-namelist** is given below in :ref:`def-nl-items-and-defaults`). When customizing your case at the **case.setup** step you are able to modify the process by effecting either one or both of these steps. The CLM "**configure**" and "**build-namelist**" scripts are both available in the "$CTSMROOT/bld" directory in the distribution. Both of these scripts have a "-help" option that is useful to examine to see what types of options you can give either of them. There are five different types of customization for the configuration that we will discuss: |version| in |cesmrelease| build-time options, |version| in |cesmrelease| run-time options, User Namelist, other noteworthy |cesmrelease| configuration items, the CLM **configure** script options, and the CLM **build-namelist** script options. -Information on all of the CLM script, configuration, build and run items is found under ``$CTSMROOT/cime_config/config_component.xml``. -See `CLM CASEROOT Variable Definitions `_. +Information on all of the CLM script, configuration, build and run items is found under ``$CTSMROOT/cime_config/config_component.xml``. See `CLM CASEROOT Variable Definitions `_. ================================ CLM Script configuration items @@ -46,17 +43,10 @@ For the precedence of the different options to **build-namelist** see the sectio The first item ``CLM_CONFIG_OPTS`` has to do with customizing the CLM build-time options for your case, the rest all have to do with generating the namelist. CLM_CONFIG_OPTS - The option ``CLM_CONFIG_OPTS`` is all about passing command line arguments to the CLM **configure** script. - It is important to note that some compsets, may already put a value into the ``CLM_CONFIG_OPTS`` variable. - You can still add more options to your ``CLM_CONFIG_OPTS`` but make sure you add to what is already there rather than replacing it. - Hence, we recommend using the "-append" option to the xmlchange script. - In `the Section called More information on the CLM configure script `_ below we will go into more details on options that can be customized in the CLM "**configure**" script. - It's also important to note that the **$CTSMROOT/cime_config/buildnml** script may already invoke certain CLM **configure** options and as such those command line options are NOT going to be available to change at this step (nor would you want to change them). - The options to CLM **configure** are given with the "-help" option which is given in `the Section called More information on the CLM configure script `_. - .. note:: ``CLM_CONFIG_OPTS`` is locked after the **case.build** script is run. If you want to change something in ``CLM_CONFIG_OPTS`` you'll need to clean the build and rerun **case.build**. The other env variables can be changed at run-time so are never locked. + The option ``CLM_CONFIG_OPTS`` is all about passing command line arguments to the CLM **configure** script. It is important to note that some compsets, may already put a value into the ``CLM_CONFIG_OPTS`` variable. You can still add more options to your ``CLM_CONFIG_OPTS`` but make sure you add to what is already there rather than replacing it. Hence, we recommend using the "-append" option to the xmlchange script. In :ref:`more-info-clm-config-script` below we will go into more details on options that can be customized in the CLM "**configure**" script. It's also important to note that the **$CTSMROOT/cime_config/buildnml** script may already invoke certain CLM **configure** options and as such those command line options are NOT going to be available to change at this step (nor would you want to change them). The options to CLM **configure** are given with the "-help" option which is given in :ref:`more-info-clm-config-script`... note:: ``CLM_CONFIG_OPTS`` is locked after the **case.build** script is run. If you want to change something in ``CLM_CONFIG_OPTS`` you'll need to clean the build and rerun **case.build**. The other env variables can be changed at run-time so are never locked. CLM_NML_USE_CASE - ``CLM_NML_USE_CASE`` is used to set a particular set of conditions that set multiple namelist items, all centering around a particular usage of the model. To list the valid options do the following: + ``CLM_NML_USE_CASE`` is used to set a particular set of conditions that set multiple namelist items, all centering around a particular usage of the model. (See :ref:`precedence-of-opts` for the precedence of this option relative to the others.) To list the valid options do the following: :: > cd $CTSMROOT @@ -65,56 +55,53 @@ CLM_NML_USE_CASE The output of the above command is: :: - CLM build-namelist - use cases: 1850-2100_rcp2.6_glacierMEC_transient 1850-2100_rcp2.6_transient \ - 1850-2100_rcp4.5_glacierMEC_transient 1850-2100_rcp4.5_transient \ - 1850-2100_rcp6_glacierMEC_transient 1850-2100_rcp6_transient \ - 1850-2100_rcp8.5_glacierMEC_transient 1850-2100_rcp8.5_transient 1850_control \ - 1850_glacierMEC_control 2000-2100_rcp8.5_transient 2000_control 2000_glacierMEC_control \ + CLM build-namelist - use cases: 1850-2100_rcp2.6_glacierMEC_transient 1850-2100_rcp2.6_transient \ + 1850-2100_rcp4.5_glacierMEC_transient 1850-2100_rcp4.5_transient \ + 1850-2100_rcp6_glacierMEC_transient 1850-2100_rcp6_transient \ + 1850-2100_rcp8.5_glacierMEC_transient 1850-2100_rcp8.5_transient 1850_control \ + 1850_glacierMEC_control 2000-2100_rcp8.5_transient 2000_control 2000_glacierMEC_control \ 20thC_glacierMEC_transient 20thC_transient glacierMEC_pd stdurbpt_pd Use cases are:... - - 1850-2100_rcp2.6_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes \ + + 1850-2100_rcp2.6_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes \ with historical data from 1850 to 2005 and then with the RCP2.6 scenario from IMAGE - - 1850-2100_rcp2.6_transient = Simulate transient land-use, and aerosol deposition changes with \ + + 1850-2100_rcp2.6_transient = Simulate transient land-use, and aerosol deposition changes with \ historical data from 1850 to 2005 and then with the RCP2.6 scenario from IMAGE - - 1850-2100_rcp4.5_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes \ + + 1850-2100_rcp4.5_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes \ with historical data from 1850 to 2005 and then with the RCP4.5 scenario from MINICAM - - 1850-2100_rcp4.5_transient = Simulate transient land-use, and aerosol deposition changes with \ + + 1850-2100_rcp4.5_transient = Simulate transient land-use, and aerosol deposition changes with \ historical data from 1850 to 2005 and then with the RCP4.5 scenario from MINICAM - - 1850-2100_rcp6_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes \ + + 1850-2100_rcp6_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes \ with historical data from 1850 to 2005 and then with the RCP6 scenario from AIM - - 1850-2100_rcp6_transient = Simulate transient land-use, and aerosol deposition changes with \ + + 1850-2100_rcp6_transient = Simulate transient land-use, and aerosol deposition changes with \ historical data from 1850 to 2005 and then with the RCP6 scenario from AIM - - 1850-2100_rcp8.5_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes \ + + 1850-2100_rcp8.5_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes \ with historical data from 1850 to 2005 and then with the RCP8.5 scenario from MESSAGE - - 1850-2100_rcp8.5_transient = Simulate transient land-use, and aerosol deposition changes with \ + + 1850-2100_rcp8.5_transient = Simulate transient land-use, and aerosol deposition changes with \ historical data from 1850 to 2005 and then with the RCP8.5 scenario from MESSAGE - + 1850_control = Conditions to simulate 1850 land-use 1850_glacierMEC_control = Running an IG case for 1850 conditions with the ice sheet model glimmer - 2000-2100_rcp8.5_transient = Simulate transient land-use, and aerosol deposition changes with \ + 2000-2100_rcp8.5_transient = Simulate transient land-use, and aerosol deposition changes with \ historical data from 2000 to 2005 and then with the RCP8.5 scenario from MESSAGE - + 2000_control = Conditions to simulate 2000 land-use 2000_glacierMEC_control = Running an IG case for 2000 conditions with the ice sheet model glimmer - 20thC_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes from 1850 \ + 20thC_glacierMEC_transient = Simulate transient land-use, and aerosol deposition changes from 1850 \ to 2005 20thC_transient = Simulate transient land-use, and aerosol deposition changes from 1850 to 2005 glacierMEC_pd = Running an IG case with the ice sheet model glimmer stdurbpt_pd = Standard Urban Point Namelist Settings - .. note::See `the Section called Precedence of Options `_ section for the precedence of this option relative to the others. - CLM_BLDNML_OPTS - The option CLM_BLDNML_OPTS is for passing options to the CLM "build-namelist" script. - As with the CLM "configure" script the CLM $CTSMROOT/cime_config/buildnml may already invoke certain options and as such those options will NOT be available to be set here. The best way to see what options can be sent to the "build-namelist" script is to do + The option CLM_BLDNML_OPTS is for passing options to the CLM "build-namelist" script. As with the CLM "configure" script the CLM $CTSMROOT/cime_config/buildnml may already invoke certain options and as such those options will NOT be available to be set here. The best way to see what options can be sent to the "build-namelist" script is to do :: > cd $CTSMROOT/bld @@ -128,26 +115,26 @@ CLM_BLDNML_OPTS Create the namelist for CLM OPTIONS - -[no-]chk_res Also check [do NOT check] to make sure the resolution and + -[no-]chk_res Also check [do NOT check] to make sure the resolution and land-mask is valid. -clm_demand "list" List of variables to require on clm namelist besides the usuals. "-clm_demand list" to list valid options. (can include a list member "null" which does nothing) -clm_startfile "file" CLM restart file to start from. - -clm_start_type "type" Start type of simulation + -clm_start_type "type" Start type of simulation (default, cold, arb_ic, startup, continue, or branch) (default=do the default type for this configuration) (cold=always start with arbitrary initial conditions) - (arb_ic=start with arbitrary initial conditions if + (arb_ic=start with arbitrary initial conditions if initial conditions don't exist) (startup=ensure that initial conditions are being used) - -clm_usr_name "name" Dataset resolution/descriptor for personal datasets. + -clm_usr_name "name" Dataset resolution/descriptor for personal datasets. Default: not used Example: 1x1pt_boulderCO_c090722 to describe location, number of pts, and date files created -co2_type "value" Set CO2 the type of CO2 variation to use. -co2_ppmv "value" Set CO2 concentration to use when co2_type is constant (ppmv). - -config "filepath" Read the given CLM configuration cache file. + -config "filepath" Read the given CLM configuration cache file. Default: "config_cache.xml". -csmdata "dir" Root directory of CESM input data. Can also be set by using the CSMDATA environment variable. @@ -157,11 +144,11 @@ CLM_BLDNML_OPTS "drv_flds_in" file for the driver to pass dry-deposition to the atm. Default: -no-drydep (Note: buildnml.csh copies the file for use by the driver) - -glc_grid "grid" Glacier model grid and resolution when glacier model, - Only used if glc_nec > 0 for determining fglcmask + -glc_grid "grid" Glacier model grid and resolution when glacier model, + Only used if glc_nec > 0 for determining fglcmask Default: gland5UM (i.e. gland20, gland10 etcetera) - -glc_nec Glacier number of elevation classes [0 | 3 | 5 | 10 | 36] + -glc_nec Glacier number of elevation classes [0 | 3 | 5 | 10 | 36] (default is 0) (standard option with land-ice model is 10) -glc_smb Only used if glc_nec > 0 If .true., pass surface mass balance info to GLC @@ -170,10 +157,10 @@ CLM_BLDNML_OPTS -help [or -h] Print usage to STDOUT. -ignore_ic_date Ignore the date on the initial condition files when determining what input initial condition file to use. - -ignore_ic_year Ignore just the year part of the date on the initial condition files + -ignore_ic_year Ignore just the year part of the date on the initial condition files when determining what input initial condition file to use. - -infile "filepath" Specify a file (or list of files) containing namelists to - read values from. + -infile "filepath" Specify a file (or list of files) containing namelists to + read values from. If used with a CLM build with multiple ensembles (ninst_lnd>1) and the filename entered is a directory to files of the @@ -188,14 +175,14 @@ CLM_BLDNML_OPTS file specified. -irrig "value" If .true. turn irrigation on with namelist logical irrigate (for |version| physics) - (requires crop to be on in the clm configuration) + (requires crop to be on in the clm configuration) Seek surface datasets with irrigation turned on. (for CLM4.0 physics) Default: .false. -l_ncpl "LND_NCPL" Number of CLM coupling time-steps in a day. -lnd_frac "domainfile" Land fraction file (the input domain file) -mask "landmask" Type of land-mask (default, navy, gx3v5, gx1v5 etc.) "-mask list" to list valid land masks. - -namelist "namelist" Specify namelist settings directly on the commandline by supplying + -namelist "namelist" Specify namelist settings directly on the commandline by supplying a string containing FORTRAN namelist syntax, e.g., -namelist "&clm_inparm dt=1800 /" -no-megan DO NOT PRODUCE a megan_emis_nl namelist that will go into the @@ -204,7 +191,7 @@ CLM_BLDNML_OPTS (Note: buildnml.csh copies the file for use by the driver) -[no-]note Add note to output namelist [do NOT add note] about the arguments to build-namelist. - -rcp "value" Representative concentration pathway (rcp) to use for + -rcp "value" Representative concentration pathway (rcp) to use for future scenarios. "-rcp list" to list valid rcp settings. -res "resolution" Specify horizontal grid. Use nlatxnlon for spectral grids; @@ -212,7 +199,7 @@ CLM_BLDNML_OPTS in degrees for latitude and longitude respectively) "-res list" to list valid resolutions. -s Turns on silent mode - only fatal messages issued. - -sim_year "year" Year to simulate for input datasets + -sim_year "year" Year to simulate for input datasets (i.e. 1850, 2000, 1850-2000, 1850-2100) "-sim_year list" to list valid simulation years -bgc_spinup "on|off" CLM 4.5 Only. For CLM 4.0, spinup is controlled from configure. @@ -229,14 +216,14 @@ CLM_BLDNML_OPTS mode. The spinup state is saved to the restart file. - If the values match between the model and the restart - file it proceeds as directed. + If the values match between the model and the restart + file it proceeds as directed. If the restart file is in spinup mode and the model is in - normal mode, then it performs the exit spinup step - and proceeds in normal mode after that. + normal mode, then it performs the exit spinup step + and proceeds in normal mode after that. - If the restart file has normal mode and the model is in + If the restart file has normal mode and the model is in spinup, then it enters spinup. This is useful if you change a parameter and want to rapidly re-equilibrate without doing a cold start. @@ -247,26 +234,22 @@ CLM_BLDNML_OPTS "-use_case list" to list valid use-cases. -version Echo the SVN tag name used to check out this CLM distribution. - - Note: The precedence for setting the values of namelist variables is (highest to lowest): - 0. namelist values set by specific command-line options, like, -d, -sim_year + 1. namelist values set by specific command-line options, like, -d, -sim_year (i.e. CLM_BLDNML_OPTS env_run variable) - 1. values set on the command-line using the -namelist option, + 2. values set on the command-line using the -namelist option, (i.e. CLM_NAMELIST_OPTS env_run variable) - 2. values read from the file(s) specified by -infile, + 3. values read from the file(s) specified by -infile, (i.e. user_nl_clm files) - 3. datasets from the -clm_usr_name option, + 4. datasets from the -clm_usr_name option, (i.e. CLM_USRDAT_NAME env_run variable) - 4. values set from a use-case scenario, e.g., -use_case + 5. values set from a use-case scenario, e.g., -use_case (i.e. CLM_NML_USE_CASE env_run variable) - 5. values from the namelist defaults file. - + 6. values from the namelist defaults file. -The **$CTSMROOT/cime_config/buildnml** script already sets the resolution and mask as well as the CLM **configure** file, and defines an input namelist and namelist input file, and the output namelist directory, and sets the start-type (from ``RUN_TYPE``), namelist options (from ``CLM_NAMELIST_OPTS``), co2_ppmv (from ``CCSM_CO2_PPMV``, co2_type (from ``CLM_CO2_TYPE``), lnd_frac (from ``LND_DOMAIN_PATH`` and ``LND_DOMAIN_FILE``), l_ncpl (from ``LND_NCPL``, glc_grid, glc_smb, glc_nec (from ``GLC_GRID``, ``GLC_SMB``, and ``GLC_NEC``), and "clm_usr_name" is set (to ``CLM_USRDAT_NAME >``when the grid is set to ``CLM_USRDAT_NAME``. -Hence only the following different options can be set: +The **$CTSMROOT/cime_config/buildnml** script already sets the resolution and mask as well as the CLM **configure** file, and defines an input namelist and namelist input file, and the output namelist directory, and sets the start-type (from ``RUN_TYPE``), namelist options (from ``CLM_NAMELIST_OPTS``), co2_ppmv (from ``CCSM_CO2_PPMV``, co2_type (from ``CLM_CO2_TYPE``), lnd_frac (from ``LND_DOMAIN_PATH`` and ``LND_DOMAIN_FILE``), l_ncpl (from ``LND_NCPL``, glc_grid, glc_smb, glc_nec (from ``GLC_GRID``, ``GLC_SMB``, and ``GLC_NEC``), and "clm_usr_name" is set (to ``CLM_USRDAT_NAME >``when the grid is set to ``CLM_USRDAT_NAME``. Hence only the following different options can be set: -1. +1. -bgc_spinup #. -chk_res @@ -291,8 +274,7 @@ Hence only the following different options can be set: #. -verbose - -"-bgc_spinup" is an option only available for |version| for any configuration when CN is turned on (so either CLMCN or CLMBGC). It can be set to "on" or "off". If "on" the model will go into Accelerated Decomposition mode, while for "off" (the default) it will have standard decomposition rates. If you are starting up from initial condition files the model will check what mode the initial condition file is in and do the appropriate action on the first time-step to change the Carbon pools to the appropriate spinup setting. See `the Section called Spinning up the |version| biogeochemistry (CLMBGC spinup) in Chapter 4 `_ for an example using this option. +"-bgc_spinup" is an option only available for |version| for any configuration when CN is turned on (so either CLMCN or CLMBGC). It can be set to "on" or "off". If "on" the model will go into Accelerated Decomposition mode, while for "off" (the default) it will have standard decomposition rates. If you are starting up from initial condition files the model will check what mode the initial condition file is in and do the appropriate action on the first time-step to change the Carbon pools to the appropriate spinup setting. See :ref:`spinning-up-clm-bgc` for an example using this option. "-chk_res" ensures that the resolution chosen is supported by CLM. If the resolution is NOT supported it will cause the CLM **build-namelist** to abort when run. So when either **preview_namelist**, **case.build** or **case.run** is executed it will abort early. Since, the CESM scripts only support certain resolutions anyway, in general this option is NOT needed in the context of running CESM cases. @@ -307,7 +289,6 @@ To see a list of valid variables that you could set do this: > cd $CTSMROOT/doc > ../bld/build-namelist -clm_demand list - .. note:: Using a 20th-Century transient compset or the ``20thC_transient`` use-case using ``CLM_NML_USE_CASE`` would set this as well, but would also use dynamic nitrogen and aerosol deposition files, so using ``-clm_demand`` would be a way to get *just* dynamic vegetation types and NOT the other files as well. "-drydep" adds the dry-deposition namelist to the driver. This is a driver namelist, but adding the option here has CLM **build-namelist** create the ``drv_flds_in`` file that the driver will copy over and use. Invoking this option does have an impact on performance even for I compsets and will slow the model down. It's also only useful when running with an active atmosphere model that makes use of this information. @@ -316,9 +297,7 @@ To see a list of valid variables that you could set do this: "-ignore_ic_year" ignores the Initial Conditions (IC) year for finding initial condition files to startup from. The date is used, but the year is ignored. Without this option or the "-ignore_ic_date" option below, the date and year of the file comes into play. -When "-irrig on" is used **build-namelist** will try to find surface datasets that have the irrigation model enabled (when running -with Sattellitte Phenology). When running with the prognostic crop model on, "-irrig on" will turn irrigate crops on, while "-irrig off" -will manage all crop areas as rain-fed without irrigation. +When "-irrig on" is used **build-namelist** will try to find surface datasets that have the irrigation model enabled (when running with Sattellitte Phenology). When running with the prognostic crop model on, "-irrig on" will turn irrigate crops on, while "-irrig off" will manage all crop areas as rain-fed without irrigation. "no-megan" means do NOT add the MEGAN model Biogenic Volatile Organic Compounds (BVOC) namelist to the driver. This namelist is created by default, so normally this WILL be done. This is a driver namelist, so unless "no-megan" is specified the CLM **build-namelist** will create the ``drv_flds_in`` file that the driver will copy over and use (if you are running with CAM and CAM produces this file as well, it's file will have precedence). @@ -336,8 +315,8 @@ will manage all crop areas as rain-fed without irrigation. > cd $CTSMROOT/doc > ../bld/build-namelist -sim_year list -``CLM_NAMELIST_OPTS`` - passes namelist items into one of the CLM namelists. +``CLM_NAMELIST_OPTS`` + passes namelist items into one of the CLM namelists. (See :ref:`precedence-of-opts` for the precedence of this option relative to the others.) .. note:: For character namelist items you need to use "'" as quotes for strings so that the scripts don't get confused with other quotes they use. @@ -348,35 +327,25 @@ will manage all crop areas as rain-fed without irrigation. Example, you want to set ``hist_fincl1`` to add the variable 'HK' to your history files. To do so edit ``env_run.xml`` and add a setting for ``hist_fincl1``. So do the following: :: - - > ./xmlchange CLM_NAMELIST_OPTS="hist_fincl1='HK'" - For a list of the history fields available see `CLM History Fields `_. + > ./xmlchange CLM_NAMELIST_OPTS="hist_fincl1='HK'" - .. note::See `the Section called Precedence of Options `_ section for the precedence of this option relative to the others. + For lists of the history fields available see :ref:`customizing_section`. -``CLM_FORCE_COLDSTART`` +``CLM_FORCE_COLDSTART`` when set to on, *requires* that your simulation do a cold start from arbitrary initial conditions. If this is NOT set, it will use an initial condition file if it can find an appropriate one, and otherwise do a cold start. ``CLM_FORCE_COLDSTART`` is a good way to ensure that you are doing a cold start if that is what you want to do. -``CLM_USRDAT_NAME`` - Provides a way to enter your own datasets into the namelist. - The files you create must be named with specific naming conventions outlined in: `the Section called Creating your own single-point/regional surface datasets in Chapter 5 `_. - To see what the expected names of the files are, use the **queryDefaultNamelist.pl** to see what the names will need to be. - For example if your ``CLM_USRDAT_NAME`` will be "1x1_boulderCO", with a "navy" land-mask, constant simulation year range, for 1850, the following will list what your filenames should be: +``CLM_USRDAT_NAME`` + Provides a way to enter your own datasets into the namelist. The files you create must be named with specific naming conventions outlined in :ref:`creating-your-own-singlepoint-dataset`. To see what the expected names of the files are, use the **queryDefaultNamelist.pl** to see what the names will need to be. For example if your ``CLM_USRDAT_NAME`` will be "1x1_boulderCO", with a "navy" land-mask, constant simulation year range, for 1850, the following will list what your filenames should be: :: > cd $CTSMROOT/bld > queryDefaultNamelist.pl -usrname "1x1_boulderCO" -options mask=navy,sim_year=1850,sim_year_range="constant" -csmdata $CSMDATA - An example of using ``CLM_USRDAT_NAME`` for a simulation is given in `Example 5-4 `_. - - .. note: See `the Section called Precedence of Options `_ section for the precedence of this option relative to the others. + An example of using ``CLM_USRDAT_NAME`` for a simulation is given in Example :numref:`creating-your-own-singlepoint-dataset`. -``CLM_CO2_TYPE`` - sets the type of input CO2 for either "constant", "diagnostic" or prognostic". - If "constant" the value from ``CCSM_CO2_PPMV`` will be used. - If "diagnostic" or "prognostic" the values MUST be sent from the atmosphere model. - For more information on how to send CO2 from the data atmosphere model see `the Section called Running stand-alone CLM with transient historical CO2 concentration in Chapter 4 `_. +``CLM_CO2_TYPE`` + sets the type of input CO2 for either "constant", "diagnostic" or prognostic". If "constant" the value from ``CCSM_CO2_PPMV`` will be used. If "diagnostic" or "prognostic" the values MUST be sent from the atmosphere model. See :ref:`running-with-historical-co2-forcing` for more information on how to send CO2 from the data atmosphere model. =============== User Namelist @@ -413,7 +382,7 @@ Example: user_nl_clm namelist file 'FSDSVD','FSDSND','FSDSVI','FSDSNI', 'FSRVD','FSRND','FSRVI','FSRNI', 'TSA','FCTR','FCEV','QBOT','RH2M','H2OSOI', - 'H2OSNO','SOILLIQ','SOILICE', + 'H2OSNO','SOILLIQ','SOILICE', 'TSA_U', 'TSA_R', 'TREFMNAV_U', 'TREFMNAV_R', 'TREFMXAV_U', 'TREFMXAV_R', @@ -428,15 +397,14 @@ Example: user_nl_clm namelist file hist_mfilt = 1, 30, 28, 24 hist_nhtfrq = 0, -24, -6, -1 - **Note:** The comments at the top are some guidance given in the default user_nl_clm and just give some guidance on how to set variables and use the file. -**Note:** See `the Section called Precedence of Options `_ section for the precedence of this option relative to the others. - **Note:** You do NOT need to specify the namelist group that the variables are in because the CLM **build-namelist** knows the namelist that specific variable names belong to, and it puts them there. Obviously, all of this would be difficult to put in the CLM_NAMELIST_OPTS variable, especially having to put ' around all the character strings. For more information on the namelist variables being set here and what they mean, see the section on CLM namelists below, as well as the namelist definition that gives details on each variable. +.. _precedence-of-opts: + --------------------- Precedence of Options --------------------- @@ -457,19 +425,23 @@ Note: The precedence for setting the values of namelist variables with the diffe Thus a setting in ``CLM_BLDNML_OPTS`` will override a setting for the same thing given in a use case with ``CLM_NML_USE_CASE``. Likewise, a setting in ``CLM_NAMELIST_OPTS`` will override a setting in ``user_nl_clm``. +.. _setting-initial-conditions: + ------------------------------------ Setting Your Initial Conditions File ------------------------------------ Especially with CLMBGC and CLMCN starting from initial conditions is very important. Even with CLMSP it takes many simulation years to get the model fully spunup. There are a couple different ways to provide an initial condition file. -- `the Section called Doing a hybrid simulation to provide initial conditions `_ -- `the Section called Doing a branch simulation to provide initial conditions `_ -- `the Section called Providing a finidat file in your user_nl_clm file `_ -- `the Section called Adding a finidat file to the XML database `_ +- :ref:`doing-a-hybrid-sim-for-init-conds` +- :ref:`doing-a-branch-sim-for-init-conds` +- :ref:`providing-finidat-in-usernlclm` +- :ref:`adding-finidat-to-xml` **Note:** Your initial condition file MUST agree with the surface dataset you are using to run the simulation. If the two files do NOT agree you will get a run-time about a mis-match in PFT weights, or in the number of PFT's or columns. To get around this you'll need to add the "use_init_interp=T" namelist flag in your namelist so that the initial conditions will be interpolated on startup.** +.. _doing-a-hybrid-sim-for-init-conds: + ------------------------------------------------------- Doing a hybrid simulation to provide initial conditions ------------------------------------------------------- @@ -480,12 +452,16 @@ The first option is to setup a hybrid simulation and give a ``RUN_REFCASE`` and Setting the ``GET_REFCASE`` option to ``TRUE means`` it will copy the files from the RUN_REFDIR usually under: ``$DIN_LOC_ROOT/cesm2_init/$RUN_REFCASE/$RUN_REFDATE`` directory. Note, that the ``RUN_REFCASE`` and ``RUN_REFDATE`` variables are expanded to get the directory name above. If you do NOT set ``GET_REFCASE`` to ``TRUE`` then you will need to have placed the file in your run directory yourself. In either case, the file is expected to be named: ``$RUN_REFCASE.clm2.r.$RUN_REFDATE-00000.nc`` with the variables expanded of course. +.. _doing-a-branch-sim-for-init-conds: + ------------------------------------------------------- Doing a branch simulation to provide initial conditions ------------------------------------------------------- The setup for running a branch simulation is essentially the same as for a hybrid. With the exception of setting ``RUN_TYPE`` to branch rather than hybrid. A branch simulation runs the case essentially as restarting from it's place before to exactly reproduce it (but possibly output more or different fields on the history files). While a hybrid simulation allows you to change the configuration or run-time options, as well as use a different code base than the original case that may have fewer fields on it than a full restart file. The ``GET_REFCASE`` option works similarly for a branch case as for a hybrid. +.. _providing-finidat-in-usernlclm: + ------------------------------------------------- Providing a finidat file in your user_nl_clm file ------------------------------------------------- @@ -497,6 +473,8 @@ Setting up a branch or hybrid simulation requires the initial condition file to Note, if you provide an initial condition file -- you can NOT set ``CLM_FORCE_COLDSTART`` to ``TRUE``. +.. _adding-finidat-to-xml: + ------------------------------------------- Adding a finidat file to the XML database ------------------------------------------- @@ -507,10 +485,7 @@ Like other datasets, if you want to use a given initial condition file to be use Other noteworthy configuration items ------------------------------------ -For running "I" cases there are several other noteworthy configuration items that you may want to work with. -Most of these involve settings for the DATM, but one ``CCSM_CO2_PPMV`` applies to all models. The list of DATM -settings is `here `_. -If you are running an B, E, or F case that doesn't use the DATM obviously the DATM_* settings will not be used. All of the settings below are in your ``env_build.xml`` and ``env_run.xml`` files +For running "I" cases there are several other noteworthy configuration items that you may want to work with. Most of these involve settings for the DATM, but one ``CCSM_CO2_PPMV`` applies to all models. The list of DATM settings is `here `_. If you are running an B, E, or F case that doesn't use the DATM obviously the DATM_* settings will not be used. All of the settings below are in your ``env_build.xml`` and ``env_run.xml`` files :: CCSM_CO2_PPMV @@ -525,7 +500,7 @@ If you are running an B, E, or F case that doesn't use the DATM obviously the DA DATM_CPL_YR_START DATM_CPL_YR_END -``CCSM_CO2_PPMV`` +``CCSM_CO2_PPMV`` Sets the mixing ratio of CO2 in parts per million by volume for ALL CESM components to use. Note that most compsets already set this value to something reasonable. Also note that some compsets may tell the atmosphere model to override this value with either historic or ramped values. If the CCSM_BGC variable is set to something other than "none" the atmosphere model will determine CO2, and CLM will listen and use what the atmosphere sends it. On the CLM side the namelist item co2_type tells CLM to use the value sent from the atmosphere rather than a value set on it's own namelist. ``DATM_MODE`` @@ -539,21 +514,25 @@ If you are running an B, E, or F case that doesn't use the DATM obviously the DA CLM1PT CPLHISTForcing -``CLMCRUNCEP`` - The standard mode for CLM4.5 of using global atmospheric data that was developed by CRU using NCEP data from 1901 to 2010 (version 4 of this series). - See `the Section called CLMCRUNCEP mode and it's DATM settings `_ for more information on the DATM settings for ``CLMCRUNCEP`` mode. +``CLMCRUNCEP`` + The standard mode for CLM4.5 of using global atmospheric data that was developed by CRU using NCEP data from 1901 to 2010 (version 4 of this series). See :ref:`clmcruncep-and-its-datm` for more information. -``CLMCRUNCEPv7`` - Version 7 of the CRUNCEP data from 1901 to 2016. - See `the Section called CLMCRUNCEPv7 mode and it's DATM settings `_ for more information on the DATM settings for ``CLMCRUNCEP`` mode. +``CLMCRUNCEPv7`` + Version 7 of the CRUNCEP data from 1901 to 2016. See :ref:`clmcruncep-and-its-datm` for more information. ``CLMGSWP3v1`` GSWP3 version 1 forcing data based on NCEP reanalysis with bias corrections by GSWP3 from 1901 to 2010. -``CLM_QIAN`` - The standard mode for CLM4.0 of using global atmospheric data that was developed by Qian et. al. for CLM using NCEP data from 1948 to 2004. See the `Section called CLM_QIAN mode and it's DATM settings `_ for more information on the DATM settings for ``CLM_QIAN`` mode. ``CLM1PT`` is for the special cases where we have single-point tower data for particular sites. Right now we only have data for three urban locations: MexicoCity Mexico, Vancouver Canada, and the urban-c alpha site. And we have data for the US-UMB AmeriFlux tower site for University of Michigan Biological Station. See `the Section called CLM1PT mode and it's DATM settings `_ for more information on the DATM settings for ``CLM1PT`` mode. ``CPLHISTForcing`` is for running with atmospheric forcing from a previous CESM simulation. See `the Section called CPLHISTForcing mode and it's DATM settings `_ for more information on the DATM settings for ``CPLHISTForcing`` mode. +``CLM_QIAN`` + The standard mode for CLM4.0 of using global atmospheric data that was developed by Qian et. al. for CLM using NCEP data from 1948 to 2004. See :ref:`clmqian-and-its-datm` for more information. + +``CLM1PT`` + This is for the special cases where we have single-point tower data for particular sites. Right now we only have data for three urban locations: MexicoCity Mexico, Vancouver Canada, and the urban-c alpha site. And we have data for the US-UMB AmeriFlux tower site for University of Michigan Biological Station. See :ref:`clm1pt-and-its-datm` for more information. -``DATM_PRESAERO`` +``CPLHISTForcing`` + This is for running with atmospheric forcing from a previous CESM simulation. See :ref:`cplhistforcing` for more information. + +``DATM_PRESAERO`` sets the prescribed aerosol mode for the data atmosphere model. The list of valid options include: ``clim_1850`` = constant year 1850 conditions @@ -614,11 +593,13 @@ The final thing that the user may wish to do before **case.setup** is run is to **datm.buildexe.csh** **datm.buildnml.csh** +.. _more-info-clm-config-script: + -------------------------------------------- More information on the CLM configure script -------------------------------------------- -The CLM **configure** script defines the details of a clm configuration and summarizes it into a ``config_cache.xml`` file. The ``config_cache.xml`` will be placed in your case directory under ``Buildconf/clmconf``. The `config_definition.xml `_ in ``$CTSMROOT/bld/config_files`` gives a definition of each CLM configuration item, it is viewable in a web-browser. Many of these items are things that you would NOT change, but looking through the list gives you the valid options, and a good description of each. Below we repeat the ``config_definition.xml`` files contents: +The CLM ``configure`` script defines the details of a clm configuration and summarizes it into a ``config_cache.xml`` file. The ``config_cache.xml`` will be placed in your case directory under ``Buildconf/clmconf``. The `config_definition_ctsm.xml `_ in ``$CTSMROOT/bld/config_files`` gives a definition of each CLM configuration item, it is viewable in a web-browser. Many of these items are things that you would NOT change, but looking through the list gives you the valid options, and a good description of each. Help on CLM configure --------------------- @@ -642,23 +623,23 @@ The output to the above command is as follows: or double leading dashes. A consequence of this is that single letter options may NOT be bundled. - -bgc Build CLM with BGC package [ none | cn | cndv ] + -bgc Build CLM with BGC package [ none | cn | cndv ] (default is none). -cache Name of output cache file (default: config_cache.xml). - -cachedir Name of directory where output cache file is written + -cachedir Name of directory where output cache file is written (default: CLM build directory). -clm4me Turn Methane model: [on | off] Requires bgc=cn/cndv (Carbon Nitrogen model) (ONLY valid for |version|!) - -clm_root Root directory of clm source code + -clm_root Root directory of clm source code (default: directory above location of this script) -cppdefs A string of user specified CPP defines. Appended to Makefile defaults. e.g. -cppdefs '-DVAR1 -DVAR2' -vichydro Turn VIC hydrologic parameterizations : [on | off] (default is off) - -crop Toggle for prognostic crop model. [on | off] (default is off) + -crop Toggle for prognostic crop model. [on | off] (default is off) (can ONLY be turned on when BGC type is CN or CNDV) -comp_intf Component interface to use (ESMF or MCT) (default MCT) - -defaults Specify full path to a configuration file which will be used + -defaults Specify full path to a configuration file which will be used to supply defaults instead of the defaults in bld/config_files. This file is used to specify model configuration parameters only. Parameters relating to the build which are system dependent will @@ -666,13 +647,13 @@ The output to the above command is as follows: -exlaklayers Turn on extra lake layers (25 layers instead of 10) [on | off] (ONLY valid for |version|!) -help [or -h] Print usage to STDOUT. - -nofire Turn off wildfires for BGC setting of CN + -nofire Turn off wildfires for BGC setting of CN (default includes fire for CN) -noio Turn history output completely off (typically for testing). - -phys Value of clm4_0 or |version| (default is clm4_0) + -phys Value of clm4_0 or |version| (default is clm4_0) -silent [or -s] Turns on silent mode - only fatal messages issued. -sitespf_pt Setup for the given site specific single-point resolution. - -snicar_frc Turn on SNICAR radiative forcing calculation. [on | off] + -snicar_frc Turn on SNICAR radiative forcing calculation. [on | off] (default is off) -spinup CLM 4.0 Only. For CLM 4.5, spinup is controlled from build-namelist. Turn on given spinup mode for BGC setting of CN (level) @@ -686,11 +667,11 @@ The output to the above command is as follows: Directories containing user source code. -verbose [or -v] Turn on verbose echoing of settings made by configure. -version Echo the SVN tag name used to check out this CLM distribution. - -vsoilc_centbgc Turn on vertical soil Carbon profile, CENTURY model decomposition, \ - - split Nitrification/de-Nitrification into two mineral + -vsoilc_centbgc Turn on vertical soil Carbon profile, CENTURY model decomposition, \ + + split Nitrification/de-Nitrification into two mineral pools for NO3 and NH4 (requires clm4me Methane model), and - eliminate inconsistent duplicate soil hydraulic + eliminate inconsistent duplicate soil hydraulic parameters used in soil biogeochem. (requires either CN or CNDV) (ONLY valid for |version|!) @@ -700,6 +681,4 @@ The output to the above command is as follows: no-nitrif Turn the Nitrification/denitrification off [no-vert,no-cent,no-nitrif,no-vert:no-cent] - -We've given details on how to use the options in env_build.xml and env_run.xml to interact with the CLM "configure" and "build-namelist" scripts, as well as giving a good understanding of how these scripts work and the options to them. -In the next section we give further details on the CLM namelist. You could customize the namelist for these options after "case.setup" is run. +We've given details on how to use the options in env_build.xml and env_run.xml to interact with the CLM "configure" and "build-namelist" scripts, as well as giving a good understanding of how these scripts work and the options to them. In the next section we give further details on the CLM namelist. You could customize the namelist for these options after "case.setup" is run. diff --git a/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.rst b/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.rst index 47274d8480..ff76a841c9 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-clm-namelist.rst @@ -1,16 +1,16 @@ -.. _customizing-a-case: - .. include:: ../substitutions.rst +.. _customizing-a-case: + ============================ Customizing CLM's namelist ============================ Once a case has run **case.setup**, we can then customize the case further, by editing the run-time namelist for CLM. First let's list the definition of each namelist item and their valid values, and then we'll list the default values for them. Next for some of the most used or tricky namelist items we'll give examples of their use, and give you example namelists that highlight these features. -In the following, various examples of namelists are provided that feature the use of different namelist options to customize a case for particular uses. -Most the examples revolve around how to customize the output history fields. -This should give you a good basis for setting up your own CLM namelist. +In the following, various examples of namelists are provided that feature the use of different namelist options to customize a case for particular uses. Most the examples revolve around how to customize the output history fields. This should give you a good basis for setting up your own CLM namelist. + +.. _def-nl-items-and-defaults: ----------------------------------------------------- Definition of Namelist items and their default values @@ -18,38 +18,14 @@ Definition of Namelist items and their default values Here we point to you where you can find the definition of each namelist item and separately the default values for them. The default values may change depending on the resolution, land-mask, simulation-year and other attributes. Both of these files are viewable in your web browser, and then expand each in turn. -1. `Definition of Namelists Relevant for |version| `_ +1. `Definition of Namelists `_ -2. `Default values of each CLM4.0 Namelist Item `_ - -3. `Default values of each |version| Namelist Item `_ +2. `Default values of each Namelist Item `_ List of fields that can be added to your output history files by namelist ------------------------------------------------------------------------- -One set of the namelist items allows you to add fields to the output history files: ``hist_fincl1``, ``hist_fincl2``, ``hist_fincl3``, ``hist_fincl4``, ``hist_fincl5``, and ``hist_fincl6``. The following links for `CLM4.0 History Fields `_ and `|version| History Fields `_ documents all of the history fields available and gives the long-name and units for each. The table below lists all the |version| history fields. - -Definition of CLM history variables ------------------------------------ - -Included in the table are the following pieces of information: - -- Variable name. - -- Long name description. - -- units - - -Table 1-3. CLM History Fields from a BgcCrop case -------------------------------------------------- -For Table from a BgcCrop case, please see :doc:`master_list_nofates`. - - -Table 1-4. CLM History Fields from a Fates case ------------------------------------------------ -For Table from a Fates case, please see :doc:`master_list_fates`. - +One set of the namelist items allows you to add fields to the output history files: ``hist_fincl1``, ``hist_fincl2``, ``hist_fincl3``, ``hist_fincl4``, ``hist_fincl5``, and ``hist_fincl6``. The :doc:`history_fields_nofates` and :doc:`history_fields_fates` files list all of the history fields available and gives the long-name and units for each. --------------------------------------------- Examples of using different namelist features @@ -114,8 +90,7 @@ Example 1-2. Default CLM Namelist Adding/removing fields on your primary history file --------------------------------------------------- -The primary history files are output monthly, and contain an extensive list of fieldnames, but the list of fieldnames can be added to using ``hist_fincl1`` or removed from by adding fieldnames to ``hist_fexcl1``. -A sample user namelist ``user_nl_clm`` adding few new fields (cosine of solar zenith angle, and solar declination) and excluding a few standard fields is (ground temperature, vegetation temperature, soil temperature and soil water).: +The primary history files are output monthly, and contain an extensive list of fieldnames, but the list of fieldnames can be added to using ``hist_fincl1`` or removed from by adding fieldnames to ``hist_fexcl1``. A sample user namelist ``user_nl_clm`` adding few new fields (cosine of solar zenith angle, and solar declination) and excluding a few standard fields is (ground temperature, vegetation temperature, soil temperature and soil water).: Example 1-3. Example user_nl_clm namelist adding and removing fields on primary history file -------------------------------------------------------------------------------------------- @@ -124,22 +99,14 @@ Example 1-3. Example user_nl_clm namelist adding and removing fields on primary hist_fincl1 = 'COSZEN', 'DECL' hist_fexcl1 = 'TG', 'TV', 'TSOI', 'H2OSOI' - Adding auxiliary history files and changing output frequency ------------------------------------------------------------ -The ``hist_fincl2`` through ``hist_fincl6`` set of namelist variables add given history fieldnames to auxiliary history file "streams", and ``hist_fexcl2`` through ``hist_fexcl6`` set of namelist variables remove given history fieldnames from history file auxiliary "streams". -A history "stream" is a set of history files that are produced at a given frequency. -By default there is only one stream of monthly data files. -To add more streams you add history fieldnames to ``hist_fincl2`` through ``hist_fincl6``. -The output frequency and the way averaging is done can be different for each history file stream. -By default the primary history files are monthly and any others are daily. You can have up to six active history streams, but you need to activate them in order. So if you activate stream "6" by setting ``hist_fincl6``, but if any of ``hist_fincl2`` through ``hist_fincl5`` are unset, only the history streams up to the first blank one will be activated. +The ``hist_fincl2`` through ``hist_fincl6`` set of namelist variables add given history fieldnames to auxiliary history file "streams", and ``hist_fexcl2`` through ``hist_fexcl6`` set of namelist variables remove given history fieldnames from history file auxiliary "streams". A history "stream" is a set of history files that are produced at a given frequency. By default there is only one stream of monthly data files. To add more streams you add history fieldnames to ``hist_fincl2`` through ``hist_fincl6``. The output frequency and the way averaging is done can be different for each history file stream. By default the primary history files are monthly and any others are daily. You can have up to six active history streams, but you need to activate them in order. So if you activate stream "6" by setting ``hist_fincl6``, but if any of ``hist_fincl2`` through ``hist_fincl5`` are unset, only the history streams up to the first blank one will be activated. The frequency of the history file streams is given by the namelist variable ``hist_nhtfrq`` which is an array of rank six for each history stream. The values of the array ``hist_nhtfrq`` must be integers, where the following values have the given meaning: -*Positive value* means the output frequency is the number of model steps between output. -*Negative value* means the output frequency is the absolute value in hours given (i.e -1 would mean an hour and -24 would mean a full day). Daily (-24) is the default value for all auxiliary files. -*Zero* means the output frequency is monthly. This is the default for the primary history files. +*Positive value* means the output frequency is the number of model steps between output. *Negative value* means the output frequency is the absolute value in hours given (i.e -1 would mean an hour and -24 would mean a full day). Daily (-24) is the default value for all auxiliary files. *Zero* means the output frequency is monthly. This is the default for the primary history files. The number of samples on each history file stream is given by the namelist variable ``hist_mfilt`` which is an array of rank six for each history stream. The values of the array ``hist_mfilt`` must be positive integers. By default the primary history file stream has one time sample on it (i.e. output is to separate monthly files), and all other streams have thirty time samples on them. @@ -159,12 +126,7 @@ Example: user_nl_clm namelist adding auxiliary history files and changing output Removing all history fields --------------------------- -Sometimes for various reasons you want to remove all the history fields either because you want to do testing without any output, or you only want a very small custom list of output fields rather than the default extensive list of fields. -By default only the primary history files are active, so technically using ``hist_fexcl1`` explained in the first example, you could list ALL of the history fields that are output in ``hist_fexcl1`` and then you wouldn't get any output. -However, as the list is very extensive this would be a cumbersome thing to do. -So to facilitate this ``hist_empty_htapes`` allows you to turn off all default output. -You can still use ``hist_fincl1`` to turn your own list of fields on, but you then start from a clean slate. -A sample user namelist ``user_nl_clm`` turning off all history fields and then activating just a few selected fields (ground and vegetation temperatures and absorbed solar radiation) is: +Sometimes for various reasons you want to remove all the history fields either because you want to do testing without any output, or you only want a very small custom list of output fields rather than the default extensive list of fields. By default only the primary history files are active, so technically using ``hist_fexcl1`` explained in the first example, you could list ALL of the history fields that are output in ``hist_fexcl1`` and then you wouldn't get any output. However, as the list is very extensive this would be a cumbersome thing to do. So to facilitate this ``hist_empty_htapes`` allows you to turn off all default output. You can still use ``hist_fincl1`` to turn your own list of fields on, but you then start from a clean slate. A sample user namelist ``user_nl_clm`` turning off all history fields and then activating just a few selected fields (ground and vegetation temperatures and absorbed solar radiation) is: Example 1-5. Example user_nl_clm namelist removing all history fields --------------------------------------------------------------------- @@ -173,62 +135,46 @@ Example 1-5. Example user_nl_clm namelist removing all history fields hist_empty_htapes = .true. hist_fincl1 = 'TG', 'TV', 'FSA' - Various ways to change history output averaging flags ----------------------------------------------------- -There are two ways to change the averaging of output history fields. -The first is using ``hist_avgflag_pertape`` which gives a default value for each history stream, the second is when you add fields using ``hist_fincl*``, you add an averaging flag to the end of the field name after a colon (for example 'TSOI:X', would output the maximum of TSOI). -The types of averaging that can be done are: +There are two ways to change the averaging of output history fields. The first is using ``hist_avgflag_pertape`` which gives a default value for each history stream, the second is when you add fields using ``hist_fincl*``, you add an averaging flag to the end of the field name after a colon (for example 'TSOI:X', would output the maximum of TSOI). The types of averaging that can be done are: - *A* Average, over the output interval. - *I* Instantaneous, output the value at the output interval. - *X* Maximum, over the output interval. - *M* Minimum, over the output interval. -The default averaging depends on the specific fields, but for most fields is an average. -A sample user namelist ``user_nl_clm`` making the monthly output fields all averages (except TSOI for the first two streams and FIRE for the 5th stream), and adding auxiliary file streams for instantaneous (6-hourly), maximum (daily), minimum (daily), and average (daily). -For some of the fields we diverge from the per-tape value given and customize to some different type of optimization. +The default averaging depends on the specific fields, but for most fields is an average. A sample user namelist ``user_nl_clm`` making the monthly output fields all averages (except TSOI for the first two streams and FIRE for the 5th stream), and adding auxiliary file streams for instantaneous (6-hourly), maximum (daily), minimum (daily), and average (daily). For some of the fields we diverge from the per-tape value given and customize to some different type of optimization. Example: user_nl_clm namelist with various ways to average history fields ------------------------------------------------------------------------------------- :: hist_empty_htapes = .true. - hist_fincl1 = 'TSOI:X', 'TG', 'TV', 'FIRE', 'FSR', 'FSH', + hist_fincl1 = 'TSOI:X', 'TG', 'TV', 'FIRE', 'FSR', 'FSH', 'EFLX_LH_TOT', 'WT' - hist_fincl2 = 'TSOI:X', 'TG', 'TV', 'FIRE', 'FSR', 'FSH', + hist_fincl2 = 'TSOI:X', 'TG', 'TV', 'FIRE', 'FSR', 'FSH', 'EFLX_LH_TOT', 'WT' - hist_fincl3 = 'TSOI', 'TG:I', 'TV', 'FIRE', 'FSR', 'FSH', + hist_fincl3 = 'TSOI', 'TG:I', 'TV', 'FIRE', 'FSR', 'FSH', 'EFLX_LH_TOT', 'WT' - hist_fincl4 = 'TSOI', 'TG', 'TV:I', 'FIRE', 'FSR', 'FSH', + hist_fincl4 = 'TSOI', 'TG', 'TV:I', 'FIRE', 'FSR', 'FSH', 'EFLX_LH_TOT', 'WT' - hist_fincl5 = 'TSOI', 'TG', 'TV', 'FIRE:I', 'FSR', 'FSH', + hist_fincl5 = 'TSOI', 'TG', 'TV', 'FIRE:I', 'FSR', 'FSH', 'EFLX_LH_TOT', 'WT' hist_avgflag_pertape = 'A', 'I', 'X', 'M', 'A' hist_nhtfrq = 0, -6, -24, -24, -24 -In the example we put the same list of fields on each of the tapes: soil-temperature, ground temperature, vegetation temperature, emitted longwave radiation, reflected solar radiation, sensible heat, total latent-heat, and total water storage. -We also modify the soil-temperature for the primary and secondary auxiliary tapes by outputting them for a maximum instead of the prescribed per-tape of average and instantaneous respectively. -For the tertiary auxiliary tape we output ground temperature instantaneous instead of as a maximum, and for the fourth auxiliary tape we output vegetation temperature instantaneous instead of as a minimum. -Finally, for the fifth auxiliary tapes we output ``FIRE`` instantaneously instead of as an average. +In the example we put the same list of fields on each of the tapes: soil-temperature, ground temperature, vegetation temperature, emitted longwave radiation, reflected solar radiation, sensible heat, total latent-heat, and total water storage. We also modify the soil-temperature for the primary and secondary auxiliary tapes by outputting them for a maximum instead of the prescribed per-tape of average and instantaneous respectively. For the tertiary auxiliary tape we output ground temperature instantaneous instead of as a maximum, and for the fourth auxiliary tape we output vegetation temperature instantaneous instead of as a minimum. Finally, for the fifth auxiliary tapes we output ``FIRE`` instantaneously instead of as an average. .. note:: We also use ``hist_empty_htapes`` as in the previous example, so we can list ONLY the fields that we want on the primary history tapes. Outputting history files as a vector in order to analyze the plant function types within gridcells -------------------------------------------------------------------------------------------------- -By default the output to history files are the grid-cell average of all land-units, and vegetation types within that grid-cell, and output is on the full 2D latitude/longitude grid with ocean masked out. -Sometimes it's important to understand how different land-units or vegetation types are acting within a grid-cell. -The way to do this is to output history files as a 1D-vector of all land-units and vegetation types. -In order to display this, you'll need to do extensive post-processing to make sense of the output. -Often you may only be interested in a few points, so once you figure out the 1D indices for the grid-cells of interest, you can easily view that data. -1D vector output can also be useful for single point datasets, since it's then obvious that all data is for the same grid cell. +By default the output to history files are the grid-cell average of all land-units, and vegetation types within that grid-cell, and output is on the full 2D latitude/longitude grid with ocean masked out. Sometimes it's important to understand how different land-units or vegetation types are acting within a grid-cell. The way to do this is to output history files as a 1D-vector of all land-units and vegetation types. In order to display this, you'll need to do extensive post-processing to make sense of the output. Often you may only be interested in a few points, so once you figure out the 1D indices for the grid-cells of interest, you can easily view that data. 1D vector output can also be useful for single point datasets, since it's then obvious that all data is for the same grid cell. -To do this you use ``hist_dov2xy`` which is an array of rank six for each history stream. -Set it to ``.false.`` if you want one of the history streams to be a 1D vector. -You can also use ``hist_type1d_pertape`` if you want to average over all the: Plant-Function-Types, columns, land-units, or grid-cells. -A sample user namelist ``user_nl_clm`` leaving the primary monthly files as 2D, and then doing grid-cell (GRID), column (COLS), and no averaging over auxiliary tapes output daily for a single field (ground temperature) is: +To do this you use ``hist_dov2xy`` which is an array of rank six for each history stream. Set it to ``.false.`` if you want one of the history streams to be a 1D vector. You can also use ``hist_type1d_pertape`` if you want to average over all the: Plant-Function-Types, columns, land-units, or grid-cells. A sample user namelist ``user_nl_clm`` leaving the primary monthly files as 2D, and then doing grid-cell (GRID), column (COLS), and no averaging over auxiliary tapes output daily for a single field (ground temperature) is: Example: user_nl_clm namelist outputting some files in 1D Vector format ----------------------------------------------------------------------- @@ -247,10 +193,4 @@ Example: user_nl_clm namelist outputting some files in 1D Vector format .. note:: Technically the default for hist_nhtfrq is for primary files output monthly and the other auxiliary tapes for daily, so we don't actually have to include hist_nhtfrq, we could use the default for it. Here we specify it for clarity. -Visualizing global 1D vector files will take effort. -You'll probably want to do some post-processing and possibly just extract out single points of interest to see what is going on. -Since, the output is a 1D vector, of only land-points traditional plots won't be helpful. -The number of points per grid-cell will also vary for anything, but grid-cell averaging. -You'll need to use the output fields pfts1d_ixy, and pfts1d_jxy, to get the mapping of the fields to the global 2D array. -pfts1d_itype_veg gives you the PFT number for each PFT. -Most likely you'll want to do this analysis in a data processing tool (such as NCL, Matlab, Mathmatica, IDL, etcetera that is able to read and process NetCDF data files). +Visualizing global 1D vector files will take effort. You'll probably want to do some post-processing and possibly just extract out single points of interest to see what is going on. Since, the output is a 1D vector, of only land-points traditional plots won't be helpful. The number of points per grid-cell will also vary for anything, but grid-cell averaging. You'll need to use the output fields pfts1d_ixy, and pfts1d_jxy, to get the mapping of the fields to the global 2D array. pfts1d_itype_veg gives you the PFT number for each PFT. Most likely you'll want to do this analysis in a data processing tool (such as NCL, Matlab, Mathmatica, IDL, etcetera that is able to read and process NetCDF data files). diff --git a/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-datm-namelist.rst b/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-datm-namelist.rst index dcc7fa3cbb..92aa8b4aaa 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-datm-namelist.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/customizing-the-datm-namelist.rst @@ -1,7 +1,7 @@ -.. customizing-the-datm-namelist: - .. include:: ../substitutions.rst +.. _customizing-the-datm-namelist: + =============================== Customizing the DATM namelist =============================== @@ -11,11 +11,7 @@ When running "I" compsets with CLM you use the DATM model to give atmospheric fo 1. **DATM Main Namelist and Stream Namlist gorup** (``datm_in``) 2. **DATM stream files** -The `Data Model Documentation `_ gives the details of all the options for the data models and for DATM specifically. -It goes into detail on all namelist items both for DATM and for DATM streams. -So here we won't list ALL of the DATM namelist options, nor go into great details about stream files. -But, we will talk about a few of the different options that are relevant for running with CLM. -All of the options for changing the namelists or stream files is done by editing the ``user_nl_datm`` file. +The `Data Model Documentation `_ gives the details of all the options for the data models and for DATM specifically. It goes into detail on all namelist items both for DATM and for DATM streams. So here we won't list ALL of the DATM namelist options, nor go into great details about stream files. But, we will talk about a few of the different options that are relevant for running with CLM. All of the options for changing the namelists or stream files is done by editing the ``user_nl_datm`` file. Because, they aren't useful for work with CLM we will NOT discuss any of the options for the main DATM namelist. Use the DATM Users Guide at the link above to find details of that. For the streams namelist we will discuss three items: @@ -32,9 +28,7 @@ mapalgo ``mapalgo`` sets the spatial interpolation method to go from the DATM input data to the output DATM model grid. The default is ``bilinear``. For ``CLM1PT`` we set it to ``nn`` to just select the nearest neighbor. This saves time and we also had problems running the interpolation for single-point mode. taxmode - ``taxmode`` is the time axis mode. - For CLM we usually have it set to ``cycle`` which means that once the end of the data is reached it will start over at the beginning. - The extend modes is used have it use the last time-step of the forcing data once it reaches the end of forcing data (or use the first time-step before it reaches where the forcing data starts). See the warning below about the extend mode. + ``taxmode`` is the time axis mode. For CLM we usually have it set to ``cycle`` which means that once the end of the data is reached it will start over at the beginning. The extend modes is used have it use the last time-step of the forcing data once it reaches the end of forcing data (or use the first time-step before it reaches where the forcing data starts). See the warning below about the extend mode. .. warning:: *THE extend OPTION NEEDS TO BE USED WITH CAUTION!* It is only invoked by default for the CLM1PT mode and is only intended for the supported urban datasets to extend the data for a single time-step. If you have the model *run extensively through periods in this mode you will effectively be repeating that last time-step over that entire period*. This means the output of your simulation will be worthless. @@ -54,74 +48,50 @@ In the sections below we go over each of the relevant ``DATM_MODE`` options and CLMGSWP3v1 mode and it's DATM settings -------------------------------------- -In ``CLMGSWP3v1`` mode the GSWP3 NCEP forcing dataset is used and all of it's data is on a 3-hourly interval. -Like ``CLM_QIAN`` the dataset is divided into those three data streams: solar, precipitation, and everything else (temperature, pressure, humidity, Long-Wave down and wind). -The time-stamps of the data were also adjusted so that they are the beginning of the interval for solar, and the middle for the other two. -Because, of this the ``offset`` is set to zero, and the ``tintalgo`` is: ``coszen``, ``nearest``, and ``linear`` for the solar, precipitation and other data respectively. -``taxmode`` is set to ``cycle`` and ``mapalgo`` is set to ``bilinear`` so that the data is spatially interpolated from the input exact half degree grid to the grid the atmosphere model is being run at (to run at this same model resolution use the 360x720cru_360x720cru resolution). +In ``CLMGSWP3v1`` mode the GSWP3 NCEP forcing dataset is used and all of it's data is on a 3-hourly interval. Like ``CLM_QIAN`` the dataset is divided into those three data streams: solar, precipitation, and everything else (temperature, pressure, humidity, Long-Wave down and wind). The time-stamps of the data were also adjusted so that they are the beginning of the interval for solar, and the middle for the other two. Because, of this the ``offset`` is set to zero, and the ``tintalgo`` is: ``coszen``, ``nearest``, and ``linear`` for the solar, precipitation and other data respectively. ``taxmode`` is set to ``cycle`` and ``mapalgo`` is set to ``bilinear`` so that the data is spatially interpolated from the input exact half degree grid to the grid the atmosphere model is being run at (to run at this same model resolution use the 360x720cru_360x720cru resolution). ---------------------------------------- CLMCRUNCEPv7 mode and it's DATM settings ---------------------------------------- -In ``CLMCRUNCEPv7`` mode the CRUNCEP dataset is used and all of it's data is on a 6-hourly interval. -Like ``CLM_QIAN`` the dataset is divided into those three data streams: solar, precipitation, and everything else (temperature, pressure, humidity and wind). -The time-stamps of the data were also adjusted so that they are the beginning of the interval for solar, and the middle for the other two. -Because, of this the ``offset`` is set to zero, and the ``tintalgo`` is: ``coszen``, ``nearest``, and ``linear`` for the solar, precipitation and other data respectively. -``taxmode`` is set to ``cycle`` and ``mapalgo`` is set to ``bilinear`` so that the data is spatially interpolated from the input exact half degree grid to the grid the atmosphere model is being run at (to run at this same model resolution use the 360x720cru_360x720cru resolution). - -.. note:: The "everything else" data stream (of temperature, pressure, humidity and wind) also includes the data for longwave downward forcing as well. Our simulations showed sensitivity to this field, so we backed off in using it, and let DATM calculate longwave down from the other fields. +In ``CLMCRUNCEPv7`` mode the CRUNCEP dataset is used and all of it's data is on a 6-hourly interval. Like ``CLM_QIAN`` the dataset is divided into those three data streams: solar, precipitation, and everything else (temperature, pressure, humidity and wind). The time-stamps of the data were also adjusted so that they are the beginning of the interval for solar, and the middle for the other two. Because, of this the ``offset`` is set to zero, and the ``tintalgo`` is: ``coszen``, ``nearest``, and ``linear`` for the solar, precipitation and other data respectively. ``taxmode`` is set to ``cycle`` and ``mapalgo`` is set to ``bilinear`` so that the data is spatially interpolated from the input exact half degree grid to the grid the atmosphere model is being run at (to run at this same model resolution use the 360x720cru_360x720cru resolution)... note:: The "everything else" data stream (of temperature, pressure, humidity and wind) also includes the data for longwave downward forcing as well. Our simulations showed sensitivity to this field, so we backed off in using it, and let DATM calculate longwave down from the other fields. For more information on CRUNCEP forcing see `http://dods.extra.cea.fr/data/p529viov/cruncep/ `_. +.. _clmcruncep-and-its-datm: + -------------------------------------- CLMCRUNCEP mode and it's DATM settings -------------------------------------- ``CLMCRUNCEP`` is similar to the ``CLMCRUNCEPv7`` mode above, except it uses Version 4 of the CRUNCEP data rather than version 7. +.. _clmqian-and-its-datm: + ------------------------------------ CLM_QIAN mode and it's DATM settings ------------------------------------ -In ``CLM_QIAN`` mode the Qian dataset is used which has 6-hourly solar and precipitation data, and 3-hourly for everything else. -The dataset is divided into those three data streams: solar, precipitation, and everything else (temperature, pressure, humidity and wind). -The time-stamps of the data were also adjusted so that they are the beginning of the interval for solar, and the middle for the other two. -Because, of this the ``offset`` is set to zero, and the ``tintalgo`` is: ``coszen``, ``nearest``, and ``linear`` for the solar, precipitation and other data respectively. -``taxmode`` is set to ``cycle`` and ``mapalgo`` is set to ``bilinear`` so that the data is spatially interpolated from the input T62 grid to the grid the atmosphere model is being run at. +In ``CLM_QIAN`` mode the Qian dataset is used which has 6-hourly solar and precipitation data, and 3-hourly for everything else. The dataset is divided into those three data streams: solar, precipitation, and everything else (temperature, pressure, humidity and wind). The time-stamps of the data were also adjusted so that they are the beginning of the interval for solar, and the middle for the other two. Because, of this the ``offset`` is set to zero, and the ``tintalgo`` is: ``coszen``, ``nearest``, and ``linear`` for the solar, precipitation and other data respectively. ``taxmode`` is set to ``cycle`` and ``mapalgo`` is set to ``bilinear`` so that the data is spatially interpolated from the input T62 grid to the grid the atmosphere model is being run at. Normally you wouldn't customize the ``CLM_QIAN`` settings, but you might replicate it's use for your own global data that had similar temporal characteristics. +.. _clm1pt-and-its-datm: + ---------------------------------- CLM1PT mode and it's DATM settings ---------------------------------- -In ``CLM1PT`` mode the model is assumed to have half-hourly or hourly data for a single-point. -For the supported datasets that is exactly what it has. -But, if you add your own data you may need to make adjustments accordingly. -Using the ``CLM_USRDAT_NAME`` resolution you can easily extend this mode for your own datasets that may be regional or even global and could be at different temporal frequencies. -If you do so you'll need to make adjustments to your DATM settings. -The dataset has all data in a single stream file. -The time-stamps of the data were also adjusted so that they are at the middle of the interval. -Because, of this the ``offset`` is set to zero, and the ``tintalgo`` is set to ``nearest``. -``taxmode`` is set to ``extend`` and ``mapalgo`` is set to ``nn`` so that simply the nearest point is used. +In ``CLM1PT`` mode the model is assumed to have half-hourly or hourly data for a single-point. For the supported datasets that is exactly what it has. But, if you add your own data you may need to make adjustments accordingly. Using the ``CLM_USRDAT_NAME`` resolution you can easily extend this mode for your own datasets that may be regional or even global and could be at different temporal frequencies. If you do so you'll need to make adjustments to your DATM settings. The dataset has all data in a single stream file. The time-stamps of the data were also adjusted so that they are at the middle of the interval. Because, of this the ``offset`` is set to zero, and the ``tintalgo`` is set to ``nearest``. ``taxmode`` is set to ``extend`` and ``mapalgo`` is set to ``nn`` so that simply the nearest point is used. -If you are using your own data for this mode and it's not at least hourly you'll want to adjust the DATM settings for it. If the data is three or six hourly, you'll need to divide it up into separate streams like in ``CLM_QIAN`` mode which will require fairly extensive changes to the DATM namelist and streams files. For an example of doing this see `Example 5-8 `_. +If you are using your own data for this mode and it's not at least hourly you'll want to adjust the DATM settings for it. If the data is three or six hourly, you'll need to divide it up into separate streams like in ``CLM_QIAN`` mode which will require fairly extensive changes to the DATM namelist and streams files. For an example of doing this see :ref:`eg-sim-data-from-prev-sim`. + +.. _cplhistforcing: ------------------------------------------ CPLHISTForcing mode and it's DATM settings ------------------------------------------ -In ``CPLHISTForcing`` mode the model is assumed to have 3-hourly for a global grid from a previous CESM simulation. -Like ``CLM_QIAN`` mode the data is divided into three streams: one for precipitation, one for solar, and one for everything else. -The time-stamps for Coupler history files for CESM is at the end of the interval, so the ``offset`` needs to be set in order to adjust the time-stamps to what it needs to be for the ``tintalgo`` settings. -For precipitation ``taxmode`` is set to ``nearest`` so the ``offset`` is set to ``-5400`` seconds so that the ending time-step is adjusted by an hour and half to the middle of the interval. -For solar ``taxmode`` is set to ``coszen`` so the offset is set to ``-10800`` seconds so that the ending time-step is adjust by three hours to the beginning of the interval. -For everything else ``taxmode`` is set to ``linear`` so the offset is set to ``-5400`` seconds so that the ending time-step is adjusted by an hour and half to the middle of the interval. -For an example of such a case see `the Section called Running with MOAR data as atmospheric forcing to spinup the model in Chapter 4 `_. - +In ``CPLHISTForcing`` mode the model is assumed to have 3-hourly for a global grid from a previous CESM simulation. Like ``CLM_QIAN`` mode the data is divided into three streams: one for precipitation, one for solar, and one for everything else. The time-stamps for Coupler history files for CESM is at the end of the interval, so the ``offset`` needs to be set in order to adjust the time-stamps to what it needs to be for the ``tintalgo`` settings. For precipitation ``taxmode`` is set to ``nearest`` so the ``offset`` is set to ``-5400`` seconds so that the ending time-step is adjusted by an hour and half to the middle of the interval. For solar ``taxmode`` is set to ``coszen`` so the offset is set to ``-10800`` seconds so that the ending time-step is adjust by three hours to the beginning of the interval. For everything else ``taxmode`` is set to ``linear`` so the offset is set to ``-5400`` seconds so that the ending time-step is adjusted by an hour and half to the middle of the interval. For an example of such a case see :ref:`running-with-moar-data`. -Normally you wouldn't modify the DATM settings for this mode. -However, if you had data at a different frequency than 3-hours you would need to modify the ``offset`` and possibly the ``taxmode``. -The other two things that you might modify would be the path to the data or the domain file for the resolution (which is currently hardwired to f09). -For data at a different input resolution you would need to change the domain file in the streams file to use a domain file to the resolution that the data comes in on. +Normally you wouldn't modify the DATM settings for this mode. However, if you had data at a different frequency than 3-hours you would need to modify the ``offset`` and possibly the ``taxmode``. The other two things that you might modify would be the path to the data or the domain file for the resolution (which is currently hardwired to f09). For data at a different input resolution you would need to change the domain file in the streams file to use a domain file to the resolution that the data comes in on. diff --git a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst new file mode 100644 index 0000000000..ec10de5080 --- /dev/null +++ b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_fates.rst @@ -0,0 +1,1064 @@ +============================= +CTSM History Fields (fates) +============================= + +CAUTION: Not all variables are relevant / present for all CTSM cases. +Key flags used in this CTSM case: +use_cn = F +use_crop = F +use_fates = T + +=================================== ================ ============================================================================================== ================================================================= ======= +CTSM History Fields +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Variable Name Level Dim. Long Description Units Active? +=================================== ================ ============================================================================================== ================================================================= ======= +A5TMIN - 5-day running mean of min 2-m temperature K F +ACTUAL_IMMOB - actual N immobilization gN/m^2/s T +AGLB - Aboveground leaf biomass kg/m^2 F +AGSB - Aboveground stem biomass kg/m^2 F +ALT - current active layer thickness m F +ALTMAX - maximum annual active layer thickness m F +ALTMAX_LASTYEAR - maximum prior year active layer thickness m F +ATM_O3 - atmospheric ozone partial pressure mol/mol F +ATM_TOPO - atmospheric surface height m T +AnnET - Annual ET mm/s F +BCDEP - total BC deposition (dry+wet) from atmosphere kg/m^2/s T +BTRAN - transpiration beta factor unitless T +BTRANMN - daily minimum of transpiration beta factor unitless T +COL_CTRUNC - column-level sink for C truncation gC/m^2 F +COL_NTRUNC - column-level sink for N truncation gN/m^2 F +COSZEN - cosine of solar zenith angle none F +CROPPROD1C - 1-yr crop product (grain+biofuel) C gC/m^2 T +CROPPROD1C_LOSS - loss from 1-yr crop product pool gC/m^2/s T +CROPPROD1N - 1-yr crop product (grain+biofuel) N gN/m^2 T +CROPPROD1N_LOSS - loss from 1-yr crop product pool gN/m^2/s T +CWDC_HR - cwd C heterotrophic respiration gC/m^2/s T +DENIT - total rate of denitrification gN/m^2/s T +DGNETDT - derivative of net ground heat flux wrt soil temp W/m^2/K F +DISPLA - displacement height (vegetated landunits only) m F +DPVLTRB1 - turbulent deposition velocity 1 m/s F +DPVLTRB2 - turbulent deposition velocity 2 m/s F +DPVLTRB3 - turbulent deposition velocity 3 m/s F +DPVLTRB4 - turbulent deposition velocity 4 m/s F +DSL - dry surface layer thickness mm T +DSTDEP - total dust deposition (dry+wet) from atmosphere kg/m^2/s T +DSTFLXT - total surface dust emission kg/m2/s T +DWT_CROPPROD1C_GAIN - landcover change-driven addition to 1-year crop product pool gC/m^2/s T +DWT_CROPPROD1N_GAIN - landcover change-driven addition to 1-year crop product pool gN/m^2/s T +DWT_PROD100C_GAIN - landcover change-driven addition to 100-yr wood product pool gC/m^2/s F +DWT_PROD100N_GAIN - landcover change-driven addition to 100-yr wood product pool gN/m^2/s F +DWT_PROD10C_GAIN - landcover change-driven addition to 10-yr wood product pool gC/m^2/s F +DWT_PROD10N_GAIN - landcover change-driven addition to 10-yr wood product pool gN/m^2/s F +DWT_WOODPRODC_GAIN - landcover change-driven addition to wood product pools gC/m^2/s T +DWT_WOODPRODN_GAIN - landcover change-driven addition to wood product pools gN/m^2/s T +DYN_COL_SOIL_ADJUSTMENTS_C - Adjustments in soil carbon due to dynamic column areas; only makes sense at the column level: gC/m^2 F +DYN_COL_SOIL_ADJUSTMENTS_N - Adjustments in soil nitrogen due to dynamic column areas; only makes sense at the column level gN/m^2 F +DYN_COL_SOIL_ADJUSTMENTS_NH4 - Adjustments in soil NH4 due to dynamic column areas; only makes sense at the column level: sho gN/m^2 F +DYN_COL_SOIL_ADJUSTMENTS_NO3 - Adjustments in soil NO3 due to dynamic column areas; only makes sense at the column level: sho gN/m^2 F +EFLXBUILD - building heat flux from change in interior building air temperature W/m^2 T +EFLX_DYNBAL - dynamic land cover change conversion energy flux W/m^2 T +EFLX_GNET - net heat flux into ground W/m^2 F +EFLX_GRND_LAKE - net heat flux into lake/snow surface, excluding light transmission W/m^2 T +EFLX_LH_TOT - total latent heat flux [+ to atm] W/m^2 T +EFLX_LH_TOT_ICE - total latent heat flux [+ to atm] (ice landunits only) W/m^2 F +EFLX_LH_TOT_R - Rural total evaporation W/m^2 T +EFLX_LH_TOT_U - Urban total evaporation W/m^2 F +EFLX_SOIL_GRND - soil heat flux [+ into soil] W/m^2 F +ELAI - exposed one-sided leaf area index m^2/m^2 T +ERRH2O - total water conservation error mm T +ERRH2OSNO - imbalance in snow depth (liquid water) mm T +ERRSEB - surface energy conservation error W/m^2 T +ERRSOI - soil/lake energy conservation error W/m^2 T +ERRSOL - solar radiation conservation error W/m^2 T +ESAI - exposed one-sided stem area index m^2/m^2 T +FATES_AR - autotrophic respiration gC/m^2/s T +FATES_AREA_PLANTS - area occupied by all plants per m2 land area m2 m-2 T +FATES_AREA_TREES - area occupied by woody plants per m2 land area m2 m-2 T +FATES_AR_CANOPY - autotrophic respiration of canopy plants gC/m^2/s T +FATES_AR_UNDERSTORY - autotrophic respiration of understory plants gC/m^2/s T +FATES_AUTORESP - autotrophic respiration in kg carbon per m2 per second kg m-2 s-1 T +FATES_AUTORESP_CANOPY - autotrophic respiration of canopy plants in kg carbon per m2 per second kg m-2 s-1 T +FATES_AUTORESP_SECONDARY - autotrophic respiration in kg carbon per m2 per second, secondary patches kg m-2 s-1 T +FATES_AUTORESP_USTORY - autotrophic respiration of understory plants in kg carbon per m2 per second kg m-2 s-1 T +FATES_BA_WEIGHTED_HEIGHT - basal area-weighted mean height of woody plants m T +FATES_BURNFRAC - burned area fraction per second s-1 T +FATES_CANOPY_SPREAD - scaling factor (0-1) between tree basal area and canopy area T +FATES_CANOPY_VEGC - biomass of canopy plants in kg carbon per m2 land area kg m-2 T +FATES_CA_WEIGHTED_HEIGHT - crown area-weighted mean height of canopy plants m T +FATES_CBALANCE_ERROR - total carbon error in kg carbon per second kg s-1 T +FATES_COLD_STATUS - site-level cold status, 0=not cold-dec, 1=too cold for leaves, 2=not too cold T +FATES_CROOTMAINTAR - live coarse root maintenance autotrophic respiration in kg carbon per m2 per second kg m-2 s-1 T +FATES_CROOT_ALLOC - allocation to coarse roots in kg carbon per m2 per second kg m-2 s-1 T +FATES_DAYSINCE_COLDLEAFOFF - site-level days elapsed since cold leaf drop days T +FATES_DAYSINCE_COLDLEAFON - site-level days elapsed since cold leaf flush days T +FATES_DEMOTION_CARBONFLUX - demotion-associated biomass carbon flux from canopy to understory in kg carbon per m2 per seco kg m-2 s-1 T +FATES_DISTURBANCE_RATE_FIRE - disturbance rate from fire m2 m-2 yr-1 T +FATES_DISTURBANCE_RATE_LOGGING - disturbance rate from logging m2 m-2 yr-1 T +FATES_DISTURBANCE_RATE_P2P - disturbance rate from primary to primary lands m2 m-2 yr-1 T +FATES_DISTURBANCE_RATE_P2S - disturbance rate from primary to secondary lands m2 m-2 yr-1 T +FATES_DISTURBANCE_RATE_POTENTIAL - potential (i.e., including unresolved) disturbance rate m2 m-2 yr-1 T +FATES_DISTURBANCE_RATE_S2S - disturbance rate from secondary to secondary lands m2 m-2 yr-1 T +FATES_DISTURBANCE_RATE_TREEFALL - disturbance rate from treefall m2 m-2 yr-1 T +FATES_EFFECT_WSPEED - effective wind speed for fire spread in meters per second m s-1 T +FATES_EXCESS_RESP - respiration of un-allocatable carbon gain kg m-2 s-1 T +FATES_FDI - Fire Danger Index (probability that an ignition will lead to a fire) 1 T +FATES_FIRE_CLOSS - carbon loss to atmosphere from fire in kg carbon per m2 per second kg m-2 s-1 T +FATES_FIRE_INTENSITY - spitfire surface fireline intensity in J per m per second J m-1 s-1 T +FATES_FIRE_INTENSITY_BURNFRAC - product of surface fire intensity and burned area fraction -- divide by FATES_BURNFRAC to get J m-1 s-1 T +FATES_FRACTION - total gridcell fraction which FATES is running over m2 m-2 T +FATES_FROOTC - total biomass in live plant fine roots in kg carbon per m2 kg m-2 T +FATES_FROOTMAINTAR - fine root maintenance autotrophic respiration in kg carbon per m2 per second kg m-2 s-1 T +FATES_FROOT_ALLOC - allocation to fine roots in kg carbon per m2 per second kg m-2 s-1 T +FATES_FUELCONSUMED - total fuel consumed in kg carbon per m2 land area kg m-2 T +FATES_FUEL_AMOUNT - total ground fuel related to FATES_ROS (omits 1000hr fuels) in kg C per m2 land area kg m-2 T +FATES_FUEL_BULKD - fuel bulk density in kg per m3 kg m-3 T +FATES_FUEL_EFF_MOIST - spitfire fuel moisture (volumetric) m3 m-3 T +FATES_FUEL_MEF - fuel moisture of extinction (volumetric) m3 m-3 T +FATES_FUEL_SAV - spitfire fuel surface area to volume ratio m-1 T +FATES_GDD - site-level growing degree days degree_Celsius T +FATES_GPP - gross primary production in kg carbon per m2 per second kg m-2 s-1 T +FATES_GPP_CANOPY - gross primary production of canopy plants in kg carbon per m2 per second kg m-2 s-1 T +FATES_GPP_SECONDARY - gross primary production in kg carbon per m2 per second, secondary patches kg m-2 s-1 T +FATES_GPP_USTORY - gross primary production of understory plants in kg carbon per m2 per second kg m-2 s-1 T +FATES_GROWTH_RESP - growth respiration in kg carbon per m2 per second kg m-2 s-1 T +FATES_GROWTH_RESP_SECONDARY - growth respiration in kg carbon per m2 per second, secondary patches kg m-2 s-1 T +FATES_HARVEST_CARBON_FLUX - harvest carbon flux in kg carbon per m2 per year kg m-2 yr-1 T +FATES_HARVEST_DEBT - Accumulated carbon failed to be harvested kg C T +FATES_HARVEST_DEBT_SEC - Accumulated carbon failed to be harvested from secondary patches kg C T +FATES_HET_RESP - heterotrophic respiration in kg carbon per m2 per second kg m-2 s-1 T +FATES_IGNITIONS - number of successful fire ignitions per m2 land area per second m-2 s-1 T +FATES_LAI - leaf area index per m2 land area m2 m-2 T +FATES_LAI_SECONDARY - leaf area index per m2 land area, secondary patches m2 m-2 T +FATES_LBLAYER_COND - mean leaf boundary layer conductance mol m-2 s-1 T +FATES_LEAFC - total biomass in live plant leaves in kg carbon per m2 kg m-2 T +FATES_LEAFMAINTAR - leaf maintenance autotrophic respiration in kg carbon per m2 per second kg m-2 s-1 T +FATES_LEAF_ALLOC - allocation to leaves in kg carbon per m2 per second kg m-2 s-1 T +FATES_LITTER_IN - litter flux in kg carbon per m2 per second kg m-2 s-1 T +FATES_LITTER_OUT - litter flux out in kg carbon (exudation, fragmentation, seed decay) kg m-2 s-1 T +FATES_LSTEMMAINTAR - live stem maintenance autotrophic respiration in kg carbon per m2 per second kg m-2 s-1 T +FATES_MAINT_RESP - maintenance respiration in kg carbon per m2 land area per second kg m-2 s-1 T +FATES_MAINT_RESP_SECONDARY - maintenance respiration in kg carbon per m2 land area per second, secondary patches kg m-2 s-1 T +FATES_MAINT_RESP_UNREDUCED - diagnostic maintenance respiration if the low-carbon-storage reduction is ignored kg m-2 s-1 F +FATES_MORTALITY_CFLUX_CANOPY - flux of biomass carbon from live to dead pools from mortality of canopy plants in kg carbon pe kg m-2 s-1 T +FATES_MORTALITY_CFLUX_USTORY - flux of biomass carbon from live to dead pools from mortality of understory plants in kg carbo kg m-2 s-1 T +FATES_NCHILLDAYS - site-level number of chill days days T +FATES_NCOHORTS - total number of cohorts per site T +FATES_NCOHORTS_SECONDARY - total number of cohorts per site T +FATES_NCOLDDAYS - site-level number of cold days days T +FATES_NEP - net ecosystem production in kg carbon per m2 per second kg m-2 s-1 T +FATES_NESTEROV_INDEX - nesterov fire danger index T +FATES_NONSTRUCTC - non-structural biomass (sapwood + leaf + fineroot) in kg carbon per m2 kg m-2 T +FATES_NPATCHES - total number of patches per site T +FATES_NPATCHES_SECONDARY - total number of patches per site T +FATES_NPP - net primary production in kg carbon per m2 per second kg m-2 s-1 T +FATES_NPP_SECONDARY - net primary production in kg carbon per m2 per second, secondary patches kg m-2 s-1 T +FATES_PRIMARY_PATCHFUSION_ERR - error in total primary lands associated with patch fusion m2 m-2 yr-1 T +FATES_PROMOTION_CARBONFLUX - promotion-associated biomass carbon flux from understory to canopy in kg carbon per m2 per sec kg m-2 s-1 T +FATES_RAD_ERROR - radiation error in FATES RTM W m-2 T +FATES_REPROC - total biomass in live plant reproductive tissues in kg carbon per m2 kg m-2 T +FATES_ROS - fire rate of spread in meters per second m s-1 T +FATES_SAPWOODC - total biomass in live plant sapwood in kg carbon per m2 kg m-2 T +FATES_SECONDARY_FOREST_FRACTION - secondary forest fraction m2 m-2 T +FATES_SECONDARY_FOREST_VEGC - biomass on secondary lands in kg carbon per m2 land area (mult by FATES_SECONDARY_FOREST_FRACT kg m-2 T +FATES_SEEDLING_POOL - total seedling (ie germinated seeds) mass of all PFTs in kg carbon per m2 land area kg m-2 T +FATES_SEEDS_IN - seed production rate in kg carbon per m2 second kg m-2 s-1 T +FATES_SEEDS_IN_LOCAL - local seed production rate in kg carbon per m2 second kg m-2 s-1 T +FATES_SEED_ALLOC - allocation to seeds in kg carbon per m2 per second kg m-2 s-1 T +FATES_SEED_BANK - total seed mass of all PFTs in kg carbon per m2 land area kg m-2 T +FATES_STEM_ALLOC - allocation to stem in kg carbon per m2 per second kg m-2 s-1 T +FATES_STOMATAL_COND - mean stomatal conductance mol m-2 s-1 T +FATES_STOREC - total biomass in live plant storage in kg carbon per m2 land area kg m-2 T +FATES_STOREC_TF - Storage C fraction of target kg kg-1 T +FATES_STORE_ALLOC - allocation to storage tissues in kg carbon per m2 per second kg m-2 s-1 T +FATES_STRUCTC - structural biomass in kg carbon per m2 land area kg m-2 T +FATES_TGROWTH - fates long-term running mean vegetation temperature by site degree_Celsius F +FATES_TLONGTERM - fates 30-year running mean vegetation temperature by site degree_Celsius F +FATES_TRIMMING - degree to which canopy expansion is limited by leaf economics (0-1) 1 T +FATES_TVEG - fates instantaneous mean vegetation temperature by site degree_Celsius T +FATES_TVEG24 - fates 24-hr running mean vegetation temperature by site degree_Celsius T +FATES_UNGERM_SEED_BANK - ungerminated seed mass of all PFTs in kg carbon per m2 land area kg m-2 T +FATES_USTORY_VEGC - biomass of understory plants in kg carbon per m2 land area kg m-2 T +FATES_VEGC - total biomass in live plants in kg carbon per m2 land area kg m-2 T +FATES_VEGC_ABOVEGROUND - aboveground biomass in kg carbon per m2 land area kg m-2 T +FATES_WOOD_PRODUCT - total wood product from logging in kg carbon per m2 land area kg m-2 T +FCEV - canopy evaporation W/m^2 T +FCO2 - CO2 flux to atmosphere (+ to atm) kgCO2/m2/s F +FCOV - fractional impermeable area unitless T +FCTR - canopy transpiration W/m^2 T +FGEV - ground evaporation W/m^2 T +FGR - heat flux into soil/snow including snow melt and lake / snow light transmission W/m^2 T +FGR12 - heat flux between soil layers 1 and 2 W/m^2 T +FGR_ICE - heat flux into soil/snow including snow melt and lake / snow light transmission (ice landunits W/m^2 F +FGR_R - Rural heat flux into soil/snow including snow melt and snow light transmission W/m^2 F +FGR_U - Urban heat flux into soil/snow including snow melt W/m^2 F +FH2OSFC - fraction of ground covered by surface water unitless T +FH2OSFC_NOSNOW - fraction of ground covered by surface water (if no snow present) unitless F +FIRA - net infrared (longwave) radiation W/m^2 T +FIRA_ICE - net infrared (longwave) radiation (ice landunits only) W/m^2 F +FIRA_R - Rural net infrared (longwave) radiation W/m^2 T +FIRA_U - Urban net infrared (longwave) radiation W/m^2 F +FIRE - emitted infrared (longwave) radiation W/m^2 T +FIRE_ICE - emitted infrared (longwave) radiation (ice landunits only) W/m^2 F +FIRE_R - Rural emitted infrared (longwave) radiation W/m^2 T +FIRE_U - Urban emitted infrared (longwave) radiation W/m^2 F +FLDS - atmospheric longwave radiation (downscaled to columns in glacier regions) W/m^2 T +FLDS_ICE - atmospheric longwave radiation (downscaled to columns in glacier regions) (ice landunits only) W/m^2 F +FPG - fraction of potential gpp proportion T +FPI - fraction of potential immobilization proportion T +FROST_TABLE - frost table depth (natural vegetated and crop landunits only) m F +FSA - absorbed solar radiation W/m^2 T +FSAT - fractional area with water table at surface unitless T +FSA_ICE - absorbed solar radiation (ice landunits only) W/m^2 F +FSA_R - Rural absorbed solar radiation W/m^2 F +FSA_U - Urban absorbed solar radiation W/m^2 F +FSD24 - direct radiation (last 24hrs) K F +FSD240 - direct radiation (last 240hrs) K F +FSDS - atmospheric incident solar radiation W/m^2 T +FSDSND - direct nir incident solar radiation W/m^2 T +FSDSNDLN - direct nir incident solar radiation at local noon W/m^2 T +FSDSNI - diffuse nir incident solar radiation W/m^2 T +FSDSVD - direct vis incident solar radiation W/m^2 T +FSDSVDLN - direct vis incident solar radiation at local noon W/m^2 T +FSDSVI - diffuse vis incident solar radiation W/m^2 T +FSDSVILN - diffuse vis incident solar radiation at local noon W/m^2 T +FSH - sensible heat not including correction for land use change and rain/snow conversion W/m^2 T +FSH_G - sensible heat from ground W/m^2 T +FSH_ICE - sensible heat not including correction for land use change and rain/snow conversion (ice landu W/m^2 F +FSH_PRECIP_CONVERSION - Sensible heat flux from conversion of rain/snow atm forcing W/m^2 T +FSH_R - Rural sensible heat W/m^2 T +FSH_RUNOFF_ICE_TO_LIQ - sensible heat flux generated from conversion of ice runoff to liquid W/m^2 T +FSH_TO_COUPLER - sensible heat sent to coupler (includes corrections for land use change, rain/snow conversion W/m^2 T +FSH_U - Urban sensible heat W/m^2 F +FSH_V - sensible heat from veg W/m^2 T +FSI24 - indirect radiation (last 24hrs) K F +FSI240 - indirect radiation (last 240hrs) K F +FSM - snow melt heat flux W/m^2 T +FSM_ICE - snow melt heat flux (ice landunits only) W/m^2 F +FSM_R - Rural snow melt heat flux W/m^2 F +FSM_U - Urban snow melt heat flux W/m^2 F +FSNO - fraction of ground covered by snow unitless T +FSNO_EFF - effective fraction of ground covered by snow unitless T +FSNO_ICE - fraction of ground covered by snow (ice landunits only) unitless F +FSR - reflected solar radiation W/m^2 T +FSRND - direct nir reflected solar radiation W/m^2 T +FSRNDLN - direct nir reflected solar radiation at local noon W/m^2 T +FSRNI - diffuse nir reflected solar radiation W/m^2 T +FSRVD - direct vis reflected solar radiation W/m^2 T +FSRVDLN - direct vis reflected solar radiation at local noon W/m^2 T +FSRVI - diffuse vis reflected solar radiation W/m^2 T +FSR_ICE - reflected solar radiation (ice landunits only) W/m^2 F +FSUN - sunlit fraction of canopy proportion F +FSUN24 - fraction sunlit (last 24hrs) K F +FSUN240 - fraction sunlit (last 240hrs) K F +F_DENIT - denitrification flux gN/m^2/s T +F_N2O_DENIT - denitrification N2O flux gN/m^2/s T +F_N2O_NIT - nitrification N2O flux gN/m^2/s T +F_NIT - nitrification flux gN/m^2/s T +GROSS_NMIN - gross rate of N mineralization gN/m^2/s T +GRU_PROD100C_GAIN - gross unrepresented landcover change addition to 100-yr wood product pool gC/m^2/s F +GRU_PROD100N_GAIN - gross unrepresented landcover change addition to 100-yr wood product pool gN/m^2/s F +GRU_PROD10C_GAIN - gross unrepresented landcover change addition to 10-yr wood product pool gC/m^2/s F +GRU_PROD10N_GAIN - gross unrepresented landcover change addition to 10-yr wood product pool gN/m^2/s F +GSSHA - shaded leaf stomatal conductance umol H20/m2/s T +GSSHALN - shaded leaf stomatal conductance at local noon umol H20/m2/s T +GSSUN - sunlit leaf stomatal conductance umol H20/m2/s T +GSSUNLN - sunlit leaf stomatal conductance at local noon umol H20/m2/s T +H2OCAN - intercepted water mm T +H2OSFC - surface water depth mm T +H2OSNO - snow depth (liquid water) mm T +H2OSNO_ICE - snow depth (liquid water, ice landunits only) mm F +H2OSNO_TOP - mass of snow in top snow layer kg/m2 T +HBOT - canopy bottom m F +HEAT_CONTENT1 - initial gridcell total heat content J/m^2 T +HEAT_CONTENT1_VEG - initial gridcell total heat content - natural vegetated and crop landunits only J/m^2 F +HEAT_CONTENT2 - post land cover change total heat content J/m^2 F +HEAT_FROM_AC - sensible heat flux put into canyon due to heat removed from air conditioning W/m^2 T +HIA - 2 m NWS Heat Index C T +HIA_R - Rural 2 m NWS Heat Index C T +HIA_U - Urban 2 m NWS Heat Index C T +HR - total heterotrophic respiration gC/m^2/s T +HTOP - canopy top m T +HUMIDEX - 2 m Humidex C T +HUMIDEX_R - Rural 2 m Humidex C T +HUMIDEX_U - Urban 2 m Humidex C T +ICE_CONTENT1 - initial gridcell total ice content mm T +ICE_CONTENT2 - post land cover change total ice content mm F +ICE_MODEL_FRACTION - Ice sheet model fractional coverage unitless F +INT_SNOW - accumulated swe (natural vegetated and crop landunits only) mm F +INT_SNOW_ICE - accumulated swe (ice landunits only) mm F +IWUELN - local noon intrinsic water use efficiency umolCO2/molH2O T +LAI240 - 240hr average of leaf area index m^2/m^2 F +LAISHA - shaded projected leaf area index m^2/m^2 T +LAISUN - sunlit projected leaf area index m^2/m^2 T +LAKEICEFRAC_SURF - surface lake layer ice mass fraction unitless T +LAKEICETHICK - thickness of lake ice (including physical expansion on freezing) m T +LIQCAN - intercepted liquid water mm T +LIQUID_CONTENT1 - initial gridcell total liq content mm T +LIQUID_CONTENT2 - post landuse change gridcell total liq content mm F +LIQUID_WATER_TEMP1 - initial gridcell weighted average liquid water temperature K F +LITTERC_HR - litter C heterotrophic respiration gC/m^2/s T +LIT_CEL_C - LIT_CEL C gC/m^2 T +LIT_CEL_C_1m - LIT_CEL C to 1 meter gC/m^2 F +LIT_CEL_C_TO_SOM_ACT_C - decomp. of cellulosic litter C to active soil organic C gC/m^2/s F +LIT_CEL_HR - Het. Resp. from cellulosic litter gC/m^2/s F +LIT_CEL_N - LIT_CEL N gN/m^2 T +LIT_CEL_N_1m - LIT_CEL N to 1 meter gN/m^2 F +LIT_CEL_N_TO_SOM_ACT_N - decomp. of cellulosic litter N to active soil organic N gN/m^2 F +LIT_LIG_C - LIT_LIG C gC/m^2 T +LIT_LIG_C_1m - LIT_LIG C to 1 meter gC/m^2 F +LIT_LIG_C_TO_SOM_SLO_C - decomp. of lignin litter C to slow soil organic ma C gC/m^2/s F +LIT_LIG_HR - Het. Resp. from lignin litter gC/m^2/s F +LIT_LIG_N - LIT_LIG N gN/m^2 T +LIT_LIG_N_1m - LIT_LIG N to 1 meter gN/m^2 F +LIT_LIG_N_TO_SOM_SLO_N - decomp. of lignin litter N to slow soil organic ma N gN/m^2 F +LIT_MET_C - LIT_MET C gC/m^2 T +LIT_MET_C_1m - LIT_MET C to 1 meter gC/m^2 F +LIT_MET_C_TO_SOM_ACT_C - decomp. of metabolic litter C to active soil organic C gC/m^2/s F +LIT_MET_HR - Het. Resp. from metabolic litter gC/m^2/s F +LIT_MET_N - LIT_MET N gN/m^2 T +LIT_MET_N_1m - LIT_MET N to 1 meter gN/m^2 F +LIT_MET_N_TO_SOM_ACT_N - decomp. of metabolic litter N to active soil organic N gN/m^2 F +LNC - leaf N concentration gN leaf/m^2 T +LWdown - atmospheric longwave radiation (downscaled to columns in glacier regions) W/m^2 F +LWup - upwelling longwave radiation W/m^2 F +MORTALITY_CROWNAREA_CANOPY - Crown area of canopy trees that died m2/ha/year T +MORTALITY_CROWNAREA_UNDERSTORY - Crown aera of understory trees that died m2/ha/year T +M_LIT_CEL_C_TO_LEACHING - cellulosic litter C leaching loss gC/m^2/s F +M_LIT_CEL_N_TO_LEACHING - cellulosic litter N leaching loss gN/m^2/s F +M_LIT_LIG_C_TO_LEACHING - lignin litter C leaching loss gC/m^2/s F +M_LIT_LIG_N_TO_LEACHING - lignin litter N leaching loss gN/m^2/s F +M_LIT_MET_C_TO_LEACHING - metabolic litter C leaching loss gC/m^2/s F +M_LIT_MET_N_TO_LEACHING - metabolic litter N leaching loss gN/m^2/s F +M_SOM_ACT_C_TO_LEACHING - active soil organic C leaching loss gC/m^2/s F +M_SOM_ACT_N_TO_LEACHING - active soil organic N leaching loss gN/m^2/s F +M_SOM_PAS_C_TO_LEACHING - passive soil organic C leaching loss gC/m^2/s F +M_SOM_PAS_N_TO_LEACHING - passive soil organic N leaching loss gN/m^2/s F +M_SOM_SLO_C_TO_LEACHING - slow soil organic ma C leaching loss gC/m^2/s F +M_SOM_SLO_N_TO_LEACHING - slow soil organic ma N leaching loss gN/m^2/s F +NDEP_TO_SMINN - atmospheric N deposition to soil mineral N gN/m^2/s T +NET_NMIN - net rate of N mineralization gN/m^2/s T +NFIX_TO_SMINN - symbiotic/asymbiotic N fixation to soil mineral N gN/m^2/s T +NSUBSTEPS - number of adaptive timesteps in CLM timestep unitless F +OBU - Monin-Obukhov length m F +OCDEP - total OC deposition (dry+wet) from atmosphere kg/m^2/s T +PARVEGLN - absorbed par by vegetation at local noon W/m^2 T +PBOT - atmospheric pressure at surface (downscaled to columns in glacier regions) Pa T +PCO2 - atmospheric partial pressure of CO2 Pa T +POTENTIAL_IMMOB - potential N immobilization gN/m^2/s T +POT_F_DENIT - potential denitrification flux gN/m^2/s T +POT_F_NIT - potential nitrification flux gN/m^2/s T +PROD100C - 100-yr wood product C gC/m^2 F +PROD100C_LOSS - loss from 100-yr wood product pool gC/m^2/s F +PROD100N - 100-yr wood product N gN/m^2 F +PROD100N_LOSS - loss from 100-yr wood product pool gN/m^2/s F +PROD10C - 10-yr wood product C gC/m^2 F +PROD10C_LOSS - loss from 10-yr wood product pool gC/m^2/s F +PROD10N - 10-yr wood product N gN/m^2 F +PROD10N_LOSS - loss from 10-yr wood product pool gN/m^2/s F +PSurf - atmospheric pressure at surface (downscaled to columns in glacier regions) Pa F +Q2M - 2m specific humidity kg/kg T +QAF - canopy air humidity kg/kg F +QBOT - atmospheric specific humidity (downscaled to columns in glacier regions) kg/kg T +QDIRECT_THROUGHFALL - direct throughfall of liquid (rain + above-canopy irrigation) mm/s F +QDIRECT_THROUGHFALL_SNOW - direct throughfall of snow mm/s F +QDRAI - sub-surface drainage mm/s T +QDRAI_PERCH - perched wt drainage mm/s T +QDRAI_XS - saturation excess drainage mm/s T +QDRIP - rate of excess canopy liquid falling off canopy mm/s F +QDRIP_SNOW - rate of excess canopy snow falling off canopy mm/s F +QFLOOD - runoff from river flooding mm/s T +QFLX_EVAP_TOT - qflx_evap_soi + qflx_evap_can + qflx_tran_veg kg m-2 s-1 T +QFLX_EVAP_VEG - vegetation evaporation mm H2O/s F +QFLX_ICE_DYNBAL - ice dynamic land cover change conversion runoff flux mm/s T +QFLX_LIQDEW_TO_TOP_LAYER - rate of liquid water deposited on top soil or snow layer (dew) mm H2O/s T +QFLX_LIQEVAP_FROM_TOP_LAYER - rate of liquid water evaporated from top soil or snow layer mm H2O/s T +QFLX_LIQ_DYNBAL - liq dynamic land cover change conversion runoff flux mm/s T +QFLX_LIQ_GRND - liquid (rain+irrigation) on ground after interception mm H2O/s F +QFLX_SNOW_DRAIN - drainage from snow pack mm/s T +QFLX_SNOW_DRAIN_ICE - drainage from snow pack melt (ice landunits only) mm/s T +QFLX_SNOW_GRND - snow on ground after interception mm H2O/s F +QFLX_SOLIDDEW_TO_TOP_LAYER - rate of solid water deposited on top soil or snow layer (frost) mm H2O/s T +QFLX_SOLIDEVAP_FROM_TOP_LAYER - rate of ice evaporated from top soil or snow layer (sublimation) (also includes bare ice subli mm H2O/s T +QFLX_SOLIDEVAP_FROM_TOP_LAYER_ICE - rate of ice evaporated from top soil or snow layer (sublimation) (also includes bare ice subli mm H2O/s F +QH2OSFC - surface water runoff mm/s T +QH2OSFC_TO_ICE - surface water converted to ice mm/s F +QHR - hydraulic redistribution mm/s T +QICE - ice growth/melt mm/s T +QICE_FRZ - ice growth mm/s T +QICE_MELT - ice melt mm/s T +QINFL - infiltration mm/s T +QINTR - interception mm/s T +QIRRIG_DEMAND - irrigation demand mm/s F +QIRRIG_DRIP - water added via drip irrigation mm/s F +QIRRIG_FROM_GW_CONFINED - water added through confined groundwater irrigation mm/s T +QIRRIG_FROM_GW_UNCONFINED - water added through unconfined groundwater irrigation mm/s T +QIRRIG_FROM_SURFACE - water added through surface water irrigation mm/s T +QIRRIG_SPRINKLER - water added via sprinkler irrigation mm/s F +QOVER - total surface runoff (includes QH2OSFC) mm/s T +QPHSNEG - net negative hydraulic redistribution flux mm/s F +QRGWL - surface runoff at glaciers (liquid only), wetlands, lakes; also includes melted ice runoff fro mm/s T +QRUNOFF - total liquid runoff not including correction for land use change mm/s T +QRUNOFF_ICE - total liquid runoff not incl corret for LULCC (ice landunits only) mm/s T +QRUNOFF_ICE_TO_COUPLER - total ice runoff sent to coupler (includes corrections for land use change) mm/s T +QRUNOFF_ICE_TO_LIQ - liquid runoff from converted ice runoff mm/s F +QRUNOFF_R - Rural total runoff mm/s F +QRUNOFF_TO_COUPLER - total liquid runoff sent to coupler (includes corrections for land use change) mm/s T +QRUNOFF_U - Urban total runoff mm/s F +QSNOCPLIQ - excess liquid h2o due to snow capping not including correction for land use change mm H2O/s T +QSNOEVAP - evaporation from snow (only when snl<0, otherwise it is equal to qflx_ev_soil) mm/s T +QSNOFRZ - column-integrated snow freezing rate kg/m2/s T +QSNOFRZ_ICE - column-integrated snow freezing rate (ice landunits only) mm/s T +QSNOMELT - snow melt rate mm/s T +QSNOMELT_ICE - snow melt (ice landunits only) mm/s T +QSNOUNLOAD - canopy snow unloading mm/s T +QSNO_TEMPUNLOAD - canopy snow temp unloading mm/s T +QSNO_WINDUNLOAD - canopy snow wind unloading mm/s T +QSNWCPICE - excess solid h2o due to snow capping not including correction for land use change mm H2O/s T +QSOIL - Ground evaporation (soil/snow evaporation + soil/snow sublimation - dew) mm/s T +QSOIL_ICE - Ground evaporation (ice landunits only) mm/s T +QTOPSOIL - water input to surface mm/s F +QVEGE - canopy evaporation mm/s T +QVEGT - canopy transpiration mm/s T +Qair - atmospheric specific humidity (downscaled to columns in glacier regions) kg/kg F +Qh - sensible heat W/m^2 F +Qle - total evaporation W/m^2 F +Qstor - storage heat flux (includes snowmelt) W/m^2 F +Qtau - momentum flux kg/m/s^2 F +RAH1 - aerodynamical resistance s/m F +RAH2 - aerodynamical resistance s/m F +RAIN - atmospheric rain, after rain/snow repartitioning based on temperature mm/s T +RAIN_FROM_ATM - atmospheric rain received from atmosphere (pre-repartitioning) mm/s T +RAIN_ICE - atmospheric rain, after rain/snow repartitioning based on temperature (ice landunits only) mm/s F +RAM_LAKE - aerodynamic resistance for momentum (lakes only) s/m F +RAW1 - aerodynamical resistance s/m F +RAW2 - aerodynamical resistance s/m F +RB - leaf boundary resistance s/m F +RH - atmospheric relative humidity % F +RH2M - 2m relative humidity % T +RH2M_R - Rural 2m specific humidity % F +RH2M_U - Urban 2m relative humidity % F +RHAF - fractional humidity of canopy air fraction F +RH_LEAF - fractional humidity at leaf surface fraction F +RSCANOPY - canopy resistance s m-1 T +RSSHA - shaded leaf stomatal resistance s/m T +RSSUN - sunlit leaf stomatal resistance s/m T +Rainf - atmospheric rain, after rain/snow repartitioning based on temperature mm/s F +Rnet - net radiation W/m^2 F +SABG - solar rad absorbed by ground W/m^2 T +SABG_PEN - Rural solar rad penetrating top soil or snow layer watt/m^2 T +SABV - solar rad absorbed by veg W/m^2 T +SMINN - soil mineral N gN/m^2 T +SMINN_TO_PLANT - plant uptake of soil mineral N gN/m^2/s T +SMINN_TO_S1N_L1 - mineral N flux for decomp. of LIT_METto SOM_ACT gN/m^2 F +SMINN_TO_S1N_L2 - mineral N flux for decomp. of LIT_CELto SOM_ACT gN/m^2 F +SMINN_TO_S1N_S2 - mineral N flux for decomp. of SOM_SLOto SOM_ACT gN/m^2 F +SMINN_TO_S1N_S3 - mineral N flux for decomp. of SOM_PASto SOM_ACT gN/m^2 F +SMINN_TO_S2N_L3 - mineral N flux for decomp. of LIT_LIGto SOM_SLO gN/m^2 F +SMINN_TO_S2N_S1 - mineral N flux for decomp. of SOM_ACTto SOM_SLO gN/m^2 F +SMINN_TO_S3N_S1 - mineral N flux for decomp. of SOM_ACTto SOM_PAS gN/m^2 F +SMINN_TO_S3N_S2 - mineral N flux for decomp. of SOM_SLOto SOM_PAS gN/m^2 F +SMIN_NH4 - soil mineral NH4 gN/m^2 T +SMIN_NO3 - soil mineral NO3 gN/m^2 T +SMIN_NO3_LEACHED - soil NO3 pool loss to leaching gN/m^2/s T +SMIN_NO3_RUNOFF - soil NO3 pool loss to runoff gN/m^2/s T +SNOBCMCL - mass of BC in snow column kg/m2 T +SNOBCMSL - mass of BC in top snow layer kg/m2 T +SNOCAN - intercepted snow mm T +SNODSTMCL - mass of dust in snow column kg/m2 T +SNODSTMSL - mass of dust in top snow layer kg/m2 T +SNOFSDSND - direct nir incident solar radiation on snow W/m^2 F +SNOFSDSNI - diffuse nir incident solar radiation on snow W/m^2 F +SNOFSDSVD - direct vis incident solar radiation on snow W/m^2 F +SNOFSDSVI - diffuse vis incident solar radiation on snow W/m^2 F +SNOFSRND - direct nir reflected solar radiation from snow W/m^2 T +SNOFSRNI - diffuse nir reflected solar radiation from snow W/m^2 T +SNOFSRVD - direct vis reflected solar radiation from snow W/m^2 T +SNOFSRVI - diffuse vis reflected solar radiation from snow W/m^2 T +SNOINTABS - Fraction of incoming solar absorbed by lower snow layers - T +SNOLIQFL - top snow layer liquid water fraction (land) fraction F +SNOMELT_ACCUM - accumulated snow melt for z0 m T +SNOOCMCL - mass of OC in snow column kg/m2 T +SNOOCMSL - mass of OC in top snow layer kg/m2 T +SNORDSL - top snow layer effective grain radius m^-6 F +SNOTTOPL - snow temperature (top layer) K F +SNOTTOPL_ICE - snow temperature (top layer, ice landunits only) K F +SNOTXMASS - snow temperature times layer mass, layer sum; to get mass-weighted temperature, divide by (SNO K kg/m2 T +SNOTXMASS_ICE - snow temperature times layer mass, layer sum (ice landunits only); to get mass-weighted temper K kg/m2 F +SNOW - atmospheric snow, after rain/snow repartitioning based on temperature mm/s T +SNOWDP - gridcell mean snow height m T +SNOWICE - snow ice kg/m2 T +SNOWICE_ICE - snow ice (ice landunits only) kg/m2 F +SNOWLIQ - snow liquid water kg/m2 T +SNOWLIQ_ICE - snow liquid water (ice landunits only) kg/m2 F +SNOW_5D - 5day snow avg m F +SNOW_DEPTH - snow height of snow covered area m T +SNOW_DEPTH_ICE - snow height of snow covered area (ice landunits only) m F +SNOW_FROM_ATM - atmospheric snow received from atmosphere (pre-repartitioning) mm/s T +SNOW_ICE - atmospheric snow, after rain/snow repartitioning based on temperature (ice landunits only) mm/s F +SNOW_PERSISTENCE - Length of time of continuous snow cover (nat. veg. landunits only) seconds T +SNOW_SINKS - snow sinks (liquid water) mm/s T +SNOW_SOURCES - snow sources (liquid water) mm/s T +SNOdTdzL - top snow layer temperature gradient (land) K/m F +SOIL10 - 10-day running mean of 12cm layer soil K F +SOILC_HR - soil C heterotrophic respiration gC/m^2/s T +SOILRESIS - soil resistance to evaporation s/m T +SOILWATER_10CM - soil liquid water + ice in top 10cm of soil (veg landunits only) kg/m2 T +SOMC_FIRE - C loss due to peat burning gC/m^2/s T +SOM_ACT_C - SOM_ACT C gC/m^2 T +SOM_ACT_C_1m - SOM_ACT C to 1 meter gC/m^2 F +SOM_ACT_C_TO_SOM_PAS_C - decomp. of active soil organic C to passive soil organic C gC/m^2/s F +SOM_ACT_C_TO_SOM_SLO_C - decomp. of active soil organic C to slow soil organic ma C gC/m^2/s F +SOM_ACT_HR_S2 - Het. Resp. from active soil organic gC/m^2/s F +SOM_ACT_HR_S3 - Het. Resp. from active soil organic gC/m^2/s F +SOM_ACT_N - SOM_ACT N gN/m^2 T +SOM_ACT_N_1m - SOM_ACT N to 1 meter gN/m^2 F +SOM_ACT_N_TO_SOM_PAS_N - decomp. of active soil organic N to passive soil organic N gN/m^2 F +SOM_ACT_N_TO_SOM_SLO_N - decomp. of active soil organic N to slow soil organic ma N gN/m^2 F +SOM_C_LEACHED - total flux of C from SOM pools due to leaching gC/m^2/s T +SOM_N_LEACHED - total flux of N from SOM pools due to leaching gN/m^2/s F +SOM_PAS_C - SOM_PAS C gC/m^2 T +SOM_PAS_C_1m - SOM_PAS C to 1 meter gC/m^2 F +SOM_PAS_C_TO_SOM_ACT_C - decomp. of passive soil organic C to active soil organic C gC/m^2/s F +SOM_PAS_HR - Het. Resp. from passive soil organic gC/m^2/s F +SOM_PAS_N - SOM_PAS N gN/m^2 T +SOM_PAS_N_1m - SOM_PAS N to 1 meter gN/m^2 F +SOM_PAS_N_TO_SOM_ACT_N - decomp. of passive soil organic N to active soil organic N gN/m^2 F +SOM_SLO_C - SOM_SLO C gC/m^2 T +SOM_SLO_C_1m - SOM_SLO C to 1 meter gC/m^2 F +SOM_SLO_C_TO_SOM_ACT_C - decomp. of slow soil organic ma C to active soil organic C gC/m^2/s F +SOM_SLO_C_TO_SOM_PAS_C - decomp. of slow soil organic ma C to passive soil organic C gC/m^2/s F +SOM_SLO_HR_S1 - Het. Resp. from slow soil organic ma gC/m^2/s F +SOM_SLO_HR_S3 - Het. Resp. from slow soil organic ma gC/m^2/s F +SOM_SLO_N - SOM_SLO N gN/m^2 T +SOM_SLO_N_1m - SOM_SLO N to 1 meter gN/m^2 F +SOM_SLO_N_TO_SOM_ACT_N - decomp. of slow soil organic ma N to active soil organic N gN/m^2 F +SOM_SLO_N_TO_SOM_PAS_N - decomp. of slow soil organic ma N to passive soil organic N gN/m^2 F +SUPPLEMENT_TO_SMINN - supplemental N supply gN/m^2/s T +SWBGT - 2 m Simplified Wetbulb Globe Temp C T +SWBGT_R - Rural 2 m Simplified Wetbulb Globe Temp C T +SWBGT_U - Urban 2 m Simplified Wetbulb Globe Temp C T +SWdown - atmospheric incident solar radiation W/m^2 F +SWup - upwelling shortwave radiation W/m^2 F +SoilAlpha - factor limiting ground evap unitless F +SoilAlpha_U - urban factor limiting ground evap unitless F +T10 - 10-day running mean of 2-m temperature K F +TAF - canopy air temperature K F +TAUX - zonal surface stress kg/m/s^2 T +TAUY - meridional surface stress kg/m/s^2 T +TBOT - atmospheric air temperature (downscaled to columns in glacier regions) K T +TBUILD - internal urban building air temperature K T +TBUILD_MAX - prescribed maximum interior building temperature K F +TFLOOR - floor temperature K F +TG - ground temperature K T +TG_ICE - ground temperature (ice landunits only) K F +TG_R - Rural ground temperature K F +TG_U - Urban ground temperature K F +TH2OSFC - surface water temperature K T +THBOT - atmospheric air potential temperature (downscaled to columns in glacier regions) K T +TKE1 - top lake level eddy thermal conductivity W/(mK) T +TLAI - total projected leaf area index m^2/m^2 T +TOPO_COL - column-level topographic height m F +TOPO_COL_ICE - column-level topographic height (ice landunits only) m F +TOTCOLC - total column carbon, incl veg and cpool but excl product pools gC/m^2 T +TOTCOLN - total column-level N, excluding product pools gN/m^2 T +TOTECOSYSC - total ecosystem carbon, incl veg but excl cpool and product pools gC/m^2 T +TOTECOSYSN - total ecosystem N, excluding product pools gN/m^2 T +TOTLITC - total litter carbon gC/m^2 T +TOTLITC_1m - total litter carbon to 1 meter depth gC/m^2 T +TOTLITN - total litter N gN/m^2 T +TOTLITN_1m - total litter N to 1 meter gN/m^2 T +TOTSOILICE - vertically summed soil ice (veg landunits only) kg/m2 T +TOTSOILLIQ - vertically summed soil liquid water (veg landunits only) kg/m2 T +TOTSOMC - total soil organic matter carbon gC/m^2 T +TOTSOMC_1m - total soil organic matter carbon to 1 meter depth gC/m^2 T +TOTSOMN - total soil organic matter N gN/m^2 T +TOTSOMN_1m - total soil organic matter N to 1 meter gN/m^2 T +TOT_WOODPRODC - total wood product C gC/m^2 T +TOT_WOODPRODC_LOSS - total loss from wood product pools gC/m^2/s T +TOT_WOODPRODN - total wood product N gN/m^2 T +TOT_WOODPRODN_LOSS - total loss from wood product pools gN/m^2/s T +TRAFFICFLUX - sensible heat flux from urban traffic W/m^2 F +TREFMNAV - daily minimum of average 2-m temperature K T +TREFMNAV_R - Rural daily minimum of average 2-m temperature K F +TREFMNAV_U - Urban daily minimum of average 2-m temperature K F +TREFMXAV - daily maximum of average 2-m temperature K T +TREFMXAV_R - Rural daily maximum of average 2-m temperature K F +TREFMXAV_U - Urban daily maximum of average 2-m temperature K F +TROOF_INNER - roof inside surface temperature K F +TSA - 2m air temperature K T +TSAI - total projected stem area index m^2/m^2 T +TSA_ICE - 2m air temperature (ice landunits only) K F +TSA_R - Rural 2m air temperature K F +TSA_U - Urban 2m air temperature K F +TSHDW_INNER - shadewall inside surface temperature K F +TSKIN - skin temperature K T +TSL - temperature of near-surface soil layer (natural vegetated and crop landunits only) K T +TSOI_10CM - soil temperature in top 10cm of soil K T +TSUNW_INNER - sunwall inside surface temperature K F +TV - vegetation temperature K T +TV24 - vegetation temperature (last 24hrs) K F +TV240 - vegetation temperature (last 240hrs) K F +TWS - total water storage mm T +Tair - atmospheric air temperature (downscaled to columns in glacier regions) K F +Tair_from_atm - atmospheric air temperature received from atmosphere (pre-downscaling) K F +U10 - 10-m wind m/s T +U10_DUST - 10-m wind for dust model m/s T +U10_ICE - 10-m wind (ice landunits only) m/s F +UAF - canopy air speed m/s F +UM - wind speed plus stability effect m/s F +URBAN_AC - urban air conditioning flux W/m^2 T +URBAN_HEAT - urban heating flux W/m^2 T +USTAR - aerodynamical resistance s/m F +UST_LAKE - friction velocity (lakes only) m/s F +VA - atmospheric wind speed plus convective velocity m/s F +VENTILATION - sensible heat flux from building ventilation W/m^2 T +VOLR - river channel total water storage m3 T +VOLRMCH - river channel main channel water storage m3 T +VPD - vpd Pa F +VPD2M - 2m vapor pressure deficit Pa T +VPD_CAN - canopy vapor pressure deficit kPa T +WASTEHEAT - sensible heat flux from heating/cooling sources of urban waste heat W/m^2 T +WBT - 2 m Stull Wet Bulb C T +WBT_R - Rural 2 m Stull Wet Bulb C T +WBT_U - Urban 2 m Stull Wet Bulb C T +WIND - atmospheric wind velocity magnitude m/s T +Wind - atmospheric wind velocity magnitude m/s F +Z0HG - roughness length over ground, sensible heat (vegetated landunits only) m F +Z0MG - roughness length over ground, momentum (vegetated landunits only) m F +Z0MV_DENSE - roughness length over vegetation, momentum, for dense canopy m F +Z0M_TO_COUPLER - roughness length, momentum: gridcell average sent to coupler m F +Z0QG - roughness length over ground, latent heat (vegetated landunits only) m F +ZBOT - atmospheric reference height m T +ZETA - dimensionless stability parameter unitless F +ZII - convective boundary height m F +ZWT - water table depth (natural vegetated and crop landunits only) m T +ZWT_PERCH - perched water table depth (natural vegetated and crop landunits only) m T +num_iter - number of iterations unitless F +QICE_FORC elevclas qice forcing sent to GLC mm/s F +TOPO_FORC elevclas topograephic height sent to GLC m F +TSRF_FORC elevclas surface temperature sent to GLC K F +FATES_BURNFRAC_AP fates_levage spitfire fraction area burnt (per second) by patch age s-1 T +FATES_CANOPYAREA_AP fates_levage canopy area by age bin per m2 land area m2 m-2 T +FATES_FIRE_INTENSITY_BURNFRAC_AP fates_levage product of fire intensity and burned fraction, resolved by patch age (so divide by FATES_BURNF J m-1 s-1 T +FATES_FUEL_AMOUNT_AP fates_levage spitfire ground fuel (kg carbon per m2) related to FATES_ROS (omits 1000hr fuels) within each kg m-2 T +FATES_GPP_AP fates_levage gross primary productivity by age bin in kg carbon per m2 per second kg m-2 s-1 F +FATES_LAI_AP fates_levage leaf area index by age bin per m2 land area m2 m-2 T +FATES_LBLAYER_COND_AP fates_levage mean leaf boundary layer conductance - by patch age mol m-2 s-1 F +FATES_NCL_AP fates_levage number of canopy levels by age bin F +FATES_NPATCH_AP fates_levage number of patches by age bin F +FATES_NPP_AP fates_levage net primary productivity by age bin in kg carbon per m2 per second kg m-2 s-1 F +FATES_PATCHAREA_AP fates_levage patch area by age bin per m2 land area m2 m-2 T +FATES_SECONDAREA_ANTHRODIST_AP fates_levage secondary forest patch area age distribution since anthropgenic disturbance m2 m-2 F +FATES_SECONDAREA_DIST_AP fates_levage secondary forest patch area age distribution since any kind of disturbance m2 m-2 F +FATES_STOMATAL_COND_AP fates_levage mean stomatal conductance - by patch age mol m-2 s-1 F +FATES_VEGC_AP fates_levage total biomass within a given patch age bin in kg carbon per m2 land area kg m-2 F +FATES_ZSTAR_AP fates_levage product of zstar and patch area by age bin (divide by FATES_PATCHAREA_AP to get mean zstar) m F +FATES_FUEL_AMOUNT_APFC fates_levagefuel spitfire fuel quantity in each age x fuel class in kg carbon per m2 land area kg m-2 F +FATES_NPP_APPF fates_levagepft NPP per PFT in each age bin in kg carbon per m2 per second kg m-2 s-1 F +FATES_SCORCH_HEIGHT_APPF fates_levagepft SPITFIRE flame Scorch Height (calculated per PFT in each patch age bin) m F +FATES_VEGC_APPF fates_levagepft biomass per PFT in each age bin in kg carbon per m2 kg m-2 F +FATES_MORTALITY_AGESCEN_AC fates_levcacls age senescence mortality by cohort age in number of plants per m2 per year m-2 yr-1 T +FATES_NPLANT_AC fates_levcacls number of plants per m2 by cohort age class m-2 T +FATES_CROWNAREA_CL fates_levcan total crown area in each canopy layer m2 m-2 T +FATES_FABD_SHA_TOPLF_CL fates_levcan shade fraction of direct light absorbed by the top leaf layer of each canopy layer 1 F +FATES_FABD_SUN_TOPLF_CL fates_levcan sun fraction of direct light absorbed by the top leaf layer of each canopy layer 1 F +FATES_FABI_SHA_TOPLF_CL fates_levcan shade fraction of indirect light absorbed by the top leaf layer of each canopy layer 1 F +FATES_FABI_SUN_TOPLF_CL fates_levcan sun fraction of indirect light absorbed by the top leaf layer of each canopy layer 1 F +FATES_LAISHA_TOP_CL fates_levcan LAI in the shade by the top leaf layer of each canopy layer m2 m-2 F +FATES_LAISUN_TOP_CL fates_levcan LAI in the sun by the top leaf layer of each canopy layer m2 m-2 F +FATES_PARSHA_Z_CL fates_levcan PAR absorbed in the shade by top leaf layer in each canopy layer W m-2 F +FATES_PARSUN_Z_CL fates_levcan PAR absorbed in the sun by top leaf layer in each canopy layer W m-2 F +FATES_MORTALITY_AGESCEN_ACPF fates_levcapf age senescence mortality by pft/cohort age in number of plants per m2 per year m-2 yr-1 F +FATES_NPLANT_ACPF fates_levcapf stem number density by pft and age class m-2 F +FATES_CROWNAREA_CLLL fates_levcnlf total crown area that is occupied by leaves in each canopy and leaf layer m2 m-2 F +FATES_FABD_SHA_CLLL fates_levcnlf shade fraction of direct light absorbed by each canopy and leaf layer 1 F +FATES_FABD_SUN_CLLL fates_levcnlf sun fraction of direct light absorbed by each canopy and leaf layer 1 F +FATES_FABI_SHA_CLLL fates_levcnlf shade fraction of indirect light absorbed by each canopy and leaf layer 1 F +FATES_FABI_SUN_CLLL fates_levcnlf sun fraction of indirect light absorbed by each canopy and leaf layer 1 F +FATES_LAISHA_Z_CLLL fates_levcnlf LAI in the shade by each canopy and leaf layer m2 m-2 F +FATES_LAISUN_Z_CLLL fates_levcnlf LAI in the sun by each canopy and leaf layer m2 m-2 F +FATES_NET_C_UPTAKE_CLLL fates_levcnlf net carbon uptake in kg carbon per m2 per second by each canopy and leaf layer per unit ground kg m-2 s-1 F +FATES_PARPROF_DIF_CLLL fates_levcnlf radiative profile of diffuse PAR through each canopy and leaf layer (averaged across PFTs) W m-2 F +FATES_PARPROF_DIR_CLLL fates_levcnlf radiative profile of direct PAR through each canopy and leaf layer (averaged across PFTs) W m-2 F +FATES_PARSHA_Z_CLLL fates_levcnlf PAR absorbed in the shade by each canopy and leaf layer W m-2 F +FATES_PARSUN_Z_CLLL fates_levcnlf PAR absorbed in the sun by each canopy and leaf layer W m-2 F +FATES_FABD_SHA_CLLLPF fates_levcnlfpf shade fraction of direct light absorbed by each canopy, leaf, and PFT 1 F +FATES_FABD_SUN_CLLLPF fates_levcnlfpf sun fraction of direct light absorbed by each canopy, leaf, and PFT 1 F +FATES_FABI_SHA_CLLLPF fates_levcnlfpf shade fraction of indirect light absorbed by each canopy, leaf, and PFT 1 F +FATES_FABI_SUN_CLLLPF fates_levcnlfpf sun fraction of indirect light absorbed by each canopy, leaf, and PFT 1 F +FATES_LAISHA_Z_CLLLPF fates_levcnlfpf LAI in the shade by each canopy, leaf, and PFT m2 m-2 F +FATES_LAISUN_Z_CLLLPF fates_levcnlfpf LAI in the sun by each canopy, leaf, and PFT m2 m-2 F +FATES_PARPROF_DIF_CLLLPF fates_levcnlfpf radiative profile of diffuse PAR through each canopy, leaf, and PFT W m-2 F +FATES_PARPROF_DIR_CLLLPF fates_levcnlfpf radiative profile of direct PAR through each canopy, leaf, and PFT W m-2 F +FATES_PARSHA_Z_CLLLPF fates_levcnlfpf PAR absorbed in the shade by each canopy, leaf, and PFT W m-2 F +FATES_PARSUN_Z_CLLLPF fates_levcnlfpf PAR absorbed in the sun by each canopy, leaf, and PFT W m-2 F +FATES_CWD_ABOVEGROUND_DC fates_levcwdsc debris class-level aboveground coarse woody debris stocks in kg carbon per m2 kg m-2 F +FATES_CWD_ABOVEGROUND_IN_DC fates_levcwdsc debris class-level aboveground coarse woody debris input in kg carbon per m2 per second kg m-2 s-1 F +FATES_CWD_ABOVEGROUND_OUT_DC fates_levcwdsc debris class-level aboveground coarse woody debris output in kg carbon per m2 per second kg m-2 s-1 F +FATES_CWD_BELOWGROUND_DC fates_levcwdsc debris class-level belowground coarse woody debris stocks in kg carbon per m2 kg m-2 F +FATES_CWD_BELOWGROUND_IN_DC fates_levcwdsc debris class-level belowground coarse woody debris input in kg carbon per m2 per second kg m-2 s-1 F +FATES_CWD_BELOWGROUND_OUT_DC fates_levcwdsc debris class-level belowground coarse woody debris output in kg carbon per m2 per second kg m-2 s-1 F +FATES_LITTER_CWD_ELDC fates_levelcwd total mass of litter in coarse woody debris by element and coarse woody debris size kg m-2 T +FATES_ERROR_EL fates_levelem total mass-balance error in kg per second by element kg s-1 T +FATES_FIRE_FLUX_EL fates_levelem loss to atmosphere from fire by element in kg element per m2 per s kg m-2 s-1 T +FATES_LITTER_AG_CWD_EL fates_levelem mass of aboveground litter in coarse woody debris (trunks/branches/twigs) by element kg m-2 T +FATES_LITTER_AG_FINE_EL fates_levelem mass of aboveground litter in fines (leaves, nonviable seed) by element kg m-2 T +FATES_LITTER_BG_CWD_EL fates_levelem mass of belowground litter in coarse woody debris (coarse roots) by element kg m-2 T +FATES_LITTER_BG_FINE_EL fates_levelem mass of belowground litter in fines (fineroots) by element kg m-2 T +FATES_LITTER_IN_EL fates_levelem litter flux in in kg element per m2 per second kg m-2 s-1 T +FATES_LITTER_OUT_EL fates_levelem litter flux out (exudation, fragmentation and seed decay) in kg element kg m-2 s-1 T +FATES_SEEDS_IN_EXTERN_EL fates_levelem external seed influx rate in kg element per m2 per second kg m-2 s-1 T +FATES_SEEDS_IN_LOCAL_EL fates_levelem within-site, element-level seed production rate in kg element per m2 per second kg m-2 s-1 T +FATES_SEED_BANK_EL fates_levelem element-level total seed mass of all PFTs in kg element per m2 kg m-2 T +FATES_SEED_DECAY_EL fates_levelem seed mass decay (germinated and un-germinated) in kg element per m2 per second kg m-2 s-1 T +FATES_SEED_GERM_EL fates_levelem element-level total germinated seed mass of all PFTs in kg element per m2 kg m-2 T +FATES_FUEL_AMOUNT_FC fates_levfuel spitfire fuel-class level fuel amount in kg carbon per m2 land area kg m-2 T +FATES_FUEL_BURNT_BURNFRAC_FC fates_levfuel product of fraction (0-1) of fuel burnt and burnt fraction (divide by FATES_BURNFRAC to get bu 1 T +FATES_FUEL_MOISTURE_FC fates_levfuel spitfire fuel class-level fuel moisture (volumetric) m3 m-3 T +FATES_CANOPYAREA_HT fates_levheight canopy area height distribution m2 m-2 T +FATES_LEAFAREA_HT fates_levheight leaf area height distribution m2 m-2 T +FATES_CANOPYCROWNAREA_PF fates_levpft total PFT-level canopy-layer crown area per m2 land area m2 m-2 T +FATES_CROWNAREA_PF fates_levpft total PFT-level crown area per m2 land area m2 m-2 T +FATES_DAYSINCE_DROUGHTLEAFOFF_PF fates_levpft PFT-level days elapsed since drought leaf drop days T +FATES_DAYSINCE_DROUGHTLEAFON_PF fates_levpft PFT-level days elapsed since drought leaf flush days T +FATES_DROUGHT_STATUS_PF fates_levpft PFT-level drought status, <2 too dry for leaves, >=2 not too dry T +FATES_ELONG_FACTOR_PF fates_levpft PFT-level mean elongation factor (partial flushing/abscission) 1 T +FATES_GPP_PF fates_levpft total PFT-level GPP in kg carbon per m2 land area per second kg m-2 s-1 T +FATES_GPP_SE_PF fates_levpft total PFT-level GPP in kg carbon per m2 land area per second, secondary patches kg m-2 s-1 T +FATES_LEAFC_PF fates_levpft total PFT-level leaf biomass in kg carbon per m2 land area kg m-2 T +FATES_MEANLIQVOL_DROUGHTPHEN_PF fates_levpft PFT-level mean liquid water volume for drought phenolgy m3 m-3 T +FATES_MEANSMP_DROUGHTPHEN_PF fates_levpft PFT-level mean soil matric potential for drought phenology Pa T +FATES_MORTALITY_CFLUX_PF fates_levpft PFT-level flux of biomass carbon from live to dead pool from mortality kg m-2 s-1 T +FATES_MORTALITY_CSTARV_CFLUX_PF fates_levpft PFT-level flux of biomass carbon from live to dead pool from carbon starvation mortality kg m-2 s-1 T +FATES_MORTALITY_FIRE_CFLUX_PF fates_levpft PFT-level flux of biomass carbon from live to dead pool from fire mortality kg m-2 s-1 T +FATES_MORTALITY_HYDRO_CFLUX_PF fates_levpft PFT-level flux of biomass carbon from live to dead pool from hydraulic failure mortality kg m-2 s-1 T +FATES_MORTALITY_PF fates_levpft PFT-level mortality rate in number of individuals per m2 land area per year m-2 yr-1 T +FATES_NPLANT_PF fates_levpft total PFT-level number of individuals per m2 land area m-2 T +FATES_NPLANT_SEC_PF fates_levpft total PFT-level number of individuals per m2 land area, secondary patches m-2 T +FATES_NPP_PF fates_levpft total PFT-level NPP in kg carbon per m2 land area per second kg m-2 s-1 T +FATES_NPP_SE_PF fates_levpft total PFT-level NPP in kg carbon per m2 land area per second, secondary patches kg m-2 s-1 T +FATES_RECRUITMENT_PF fates_levpft PFT-level recruitment rate in number of individuals per m2 land area per year m-2 yr-1 T +FATES_SEEDS_IN_GRIDCELL_PF fates_levpft Site-level seed mass input from neighboring gridcells per pft kg F +FATES_SEEDS_OUT_GRIDCELL_PF fates_levpft Site-level seed mass output to neighboring gridcells per pft kg F +FATES_STOREC_PF fates_levpft total PFT-level stored biomass in kg carbon per m2 land area kg m-2 T +FATES_VEGC_PF fates_levpft total PFT-level biomass in kg of carbon per land area kg m-2 T +FATES_VEGC_SE_PF fates_levpft total PFT-level biomass in kg of carbon per land area, secondary patches kg m-2 T +FATES_DDBH_CANOPY_SZAP fates_levscag growth rate of canopy plants in meters DBH per m2 per year in canopy in each size x age class m m-2 yr-1 F +FATES_DDBH_USTORY_SZAP fates_levscag growth rate of understory plants in meters DBH per m2 per year in each size x age class m m-2 yr-1 F +FATES_MORTALITY_CANOPY_SZAP fates_levscag mortality rate of canopy plants in number of plants per m2 per year in each size x age class m-2 yr-1 F +FATES_MORTALITY_USTORY_SZAP fates_levscag mortality rate of understory plants in number of plants per m2 per year in each size x age cla m-2 yr-1 F +FATES_NPLANT_CANOPY_SZAP fates_levscag number of plants per m2 in canopy in each size x age class m-2 F +FATES_NPLANT_SZAP fates_levscag number of plants per m2 in each size x age class m-2 F +FATES_NPLANT_USTORY_SZAP fates_levscag number of plants per m2 in understory in each size x age class m-2 F +FATES_NPLANT_SZAPPF fates_levscagpf number of plants per m2 in each size x age x pft class m-2 F +FATES_BASALAREA_SZ fates_levscls basal area by size class m2 m-2 T +FATES_CROOTMAINTAR_CANOPY_SZ fates_levscls live coarse root maintenance autotrophic respiration for canopy plants in kg carbon per m2 per kg m-2 s-1 F +FATES_CROOTMAINTAR_USTORY_SZ fates_levscls live coarse root maintenance autotrophic respiration for understory plants in kg carbon per m2 kg m-2 s-1 F +FATES_CROWNAREA_CANOPY_SZ fates_levscls total crown area of canopy plants by size class m2 m-2 F +FATES_CROWNAREA_USTORY_SZ fates_levscls total crown area of understory plants by size class m2 m-2 F +FATES_DDBH_CANOPY_SZ fates_levscls diameter growth increment by size of canopy plants m m-2 yr-1 T +FATES_DDBH_USTORY_SZ fates_levscls diameter growth increment by size of understory plants m m-2 yr-1 T +FATES_DEMOTION_RATE_SZ fates_levscls demotion rate from canopy to understory by size class in number of plants per m2 per year m-2 yr-1 F +FATES_FROOTCTURN_CANOPY_SZ fates_levscls fine root turnover (non-mortal) for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_FROOTCTURN_USTORY_SZ fates_levscls fine root turnover (non-mortal) for understory plants by size class in kg carbon per m2 per se kg m-2 s-1 F +FATES_FROOTMAINTAR_CANOPY_SZ fates_levscls live coarse root maintenance autotrophic respiration for canopy plants in kg carbon per m2 per kg m-2 s-1 F +FATES_FROOTMAINTAR_USTORY_SZ fates_levscls fine root maintenance autotrophic respiration for understory plants in kg carbon per m2 per se kg m-2 s-1 F +FATES_FROOT_ALLOC_CANOPY_SZ fates_levscls allocation to fine root C for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_FROOT_ALLOC_USTORY_SZ fates_levscls allocation to fine roots for understory plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_GROWAR_CANOPY_SZ fates_levscls growth autotrophic respiration of canopy plants in kg carbon per m2 per second by size kg m-2 s-1 F +FATES_GROWAR_USTORY_SZ fates_levscls growth autotrophic respiration of understory plants in kg carbon per m2 per second by size kg m-2 s-1 F +FATES_LAI_CANOPY_SZ fates_levscls leaf area index (LAI) of canopy plants by size class m2 m-2 T +FATES_LAI_USTORY_SZ fates_levscls leaf area index (LAI) of understory plants by size class m2 m-2 T +FATES_LEAFCTURN_CANOPY_SZ fates_levscls leaf turnover (non-mortal) for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_LEAFCTURN_USTORY_SZ fates_levscls leaf turnover (non-mortal) for understory plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_LEAF_ALLOC_CANOPY_SZ fates_levscls allocation to leaves for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_LEAF_ALLOC_USTORY_SZ fates_levscls allocation to leaves for understory plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_LSTEMMAINTAR_CANOPY_SZ fates_levscls live stem maintenance autotrophic respiration for canopy plants in kg carbon per m2 per second kg m-2 s-1 F +FATES_LSTEMMAINTAR_USTORY_SZ fates_levscls live stem maintenance autotrophic respiration for understory plants in kg carbon per m2 per se kg m-2 s-1 F +FATES_M3_MORTALITY_CANOPY_SZ fates_levscls C starvation mortality of canopy plants by size N/ha/yr F +FATES_M3_MORTALITY_USTORY_SZ fates_levscls C starvation mortality of understory plants by size N/ha/yr F +FATES_MAINTAR_CANOPY_SZ fates_levscls maintenance autotrophic respiration of canopy plants in kg carbon per m2 per second by size kg m-2 s-1 F +FATES_MAINTAR_USTORY_SZ fates_levscls maintenance autotrophic respiration of understory plants in kg carbon per m2 per second by siz kg m-2 s-1 F +FATES_MORTALITY_AGESCEN_SE_SZ fates_levscls age senescence mortality by size in number of plants per m2 per year, secondary patches m-2 yr-1 T +FATES_MORTALITY_AGESCEN_SZ fates_levscls age senescence mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_BACKGROUND_SE_SZ fates_levscls background mortality by size in number of plants per m2 per year, secondary patches m-2 yr-1 T +FATES_MORTALITY_BACKGROUND_SZ fates_levscls background mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_CANOPY_SE_SZ fates_levscls total mortality of canopy trees by size class in number of plants per m2, secondary patches m-2 yr-1 T +FATES_MORTALITY_CANOPY_SZ fates_levscls total mortality of canopy trees by size class in number of plants per m2 m-2 yr-1 T +FATES_MORTALITY_CSTARV_SE_SZ fates_levscls carbon starvation mortality by size in number of plants per m2 per year, secondary patches m-2 yr-1 T +FATES_MORTALITY_CSTARV_SZ fates_levscls carbon starvation mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_FIRE_SZ fates_levscls fire mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_FREEZING_SE_SZ fates_levscls freezing mortality by size in number of plants per m2 per event, secondary patches m-2 event-1 T +FATES_MORTALITY_FREEZING_SZ fates_levscls freezing mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_HYDRAULIC_SE_SZ fates_levscls hydraulic mortality by size in number of plants per m2 per year, secondary patches m-2 yr-1 T +FATES_MORTALITY_HYDRAULIC_SZ fates_levscls hydraulic mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_IMPACT_SZ fates_levscls impact mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_LOGGING_SE_SZ fates_levscls logging mortality by size in number of plants per m2 per event, secondary patches m-2 yr-1 T +FATES_MORTALITY_LOGGING_SZ fates_levscls logging mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_SENESCENCE_SE_SZ fates_levscls senescence mortality by size in number of plants per m2 per event, secondary patches m-2 yr-1 T +FATES_MORTALITY_SENESCENCE_SZ fates_levscls senescence mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_TERMINATION_SZ fates_levscls termination mortality by size in number of plants per m2 per year m-2 yr-1 T +FATES_MORTALITY_USTORY_SZ fates_levscls total mortality of understory trees by size class in individuals per m2 per year m-2 yr-1 T +FATES_NPLANT_CANOPY_SZ fates_levscls number of canopy plants per m2 by size class m-2 T +FATES_NPLANT_SZ fates_levscls number of plants per m2 by size class m-2 T +FATES_NPLANT_USTORY_SZ fates_levscls number of understory plants per m2 by size class m-2 T +FATES_NPP_CANOPY_SZ fates_levscls NPP of canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_NPP_USTORY_SZ fates_levscls NPP of understory plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_PROMOTION_RATE_SZ fates_levscls promotion rate from understory to canopy by size class m-2 yr-1 F +FATES_RDARK_CANOPY_SZ fates_levscls dark respiration for canopy plants in kg carbon per m2 per second by size kg m-2 s-1 F +FATES_RDARK_USTORY_SZ fates_levscls dark respiration for understory plants in kg carbon per m2 per second by size kg m-2 s-1 F +FATES_SAI_CANOPY_SZ fates_levscls stem area index (SAI) of canopy plants by size class m2 m-2 F +FATES_SAI_USTORY_SZ fates_levscls stem area index (SAI) of understory plants by size class m2 m-2 F +FATES_SAPWOODCTURN_CANOPY_SZ fates_levscls sapwood turnover (non-mortal) for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_SAPWOODCTURN_USTORY_SZ fates_levscls sapwood C turnover (non-mortal) for understory plants by size class in kg carbon per m2 per se kg m-2 s-1 F +FATES_SAPWOOD_ALLOC_CANOPY_SZ fates_levscls allocation to sapwood C for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_SAPWOOD_ALLOC_USTORY_SZ fates_levscls allocation to sapwood C for understory plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_SEED_ALLOC_CANOPY_SZ fates_levscls allocation to reproductive C for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_SEED_ALLOC_USTORY_SZ fates_levscls allocation to reproductive C for understory plants by size class in kg carbon per m2 per secon kg m-2 s-1 F +FATES_SEED_PROD_CANOPY_SZ fates_levscls seed production of canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_SEED_PROD_USTORY_SZ fates_levscls seed production of understory plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_STORECTURN_CANOPY_SZ fates_levscls storage turnover (non-mortal) for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_STORECTURN_USTORY_SZ fates_levscls storage C turnover (non-mortal) for understory plants by size class in kg carbon per m2 per se kg m-2 s-1 F +FATES_STORE_ALLOC_CANOPY_SZ fates_levscls allocation to storage C for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_STORE_ALLOC_USTORY_SZ fates_levscls allocation to storage C for understory plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_STRUCTCTURN_CANOPY_SZ fates_levscls structural C turnover (non-mortal) for canopy plants by size class in kg carbon per m2 per sec kg m-2 s-1 F +FATES_STRUCTCTURN_USTORY_SZ fates_levscls structural C turnover (non-mortal) for understory plants by size class in kg carbon per m2 per kg m-2 s-1 F +FATES_STRUCT_ALLOC_CANOPY_SZ fates_levscls allocation to structural C for canopy plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_STRUCT_ALLOC_USTORY_SZ fates_levscls allocation to structural C for understory plants by size class in kg carbon per m2 per second kg m-2 s-1 F +FATES_TRIMMING_CANOPY_SZ fates_levscls trimming term of canopy plants weighted by plant density, by size class m-2 F +FATES_TRIMMING_USTORY_SZ fates_levscls trimming term of understory plants weighted by plant density, by size class m-2 F +FATES_VEGC_ABOVEGROUND_SZ fates_levscls aboveground biomass by size class in kg carbon per m2 kg m-2 T +FATES_VEGC_SZ fates_levscls total biomass by size class in kg carbon per m2 kg m-2 F +FATES_YESTCANLEV_CANOPY_SZ fates_levscls yesterdays canopy level for canopy plants by size class in number of plants per m2 m-2 F +FATES_YESTCANLEV_USTORY_SZ fates_levscls yesterdays canopy level for understory plants by size class in number of plants per m2 m-2 F +FATES_ABOVEGROUND_MORT_SZPF fates_levscpf Aboveground flux of carbon from AGB to necromass due to mortality kg m-2 s-1 F +FATES_ABOVEGROUND_PROD_SZPF fates_levscpf Aboveground carbon productivity kg m-2 s-1 F +FATES_AGSAPMAINTAR_SZPF fates_levscpf above-ground sapwood maintenance autotrophic respiration in kg carbon per m2 per second by pft kg m-2 s-1 F +FATES_AGSAPWOOD_ALLOC_SZPF fates_levscpf allocation to above-ground sapwood by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_AGSTRUCT_ALLOC_SZPF fates_levscpf allocation to above-ground structural (deadwood) by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_AUTORESP_CANOPY_SZPF fates_levscpf autotrophic respiration of canopy plants by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_AUTORESP_SZPF fates_levscpf total autotrophic respiration in kg carbon per m2 per second by pft/size kg m-2 s-1 F +FATES_AUTORESP_USTORY_SZPF fates_levscpf autotrophic respiration of understory plants by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_BASALAREA_SZPF fates_levscpf basal area by pft/size m2 m-2 F +FATES_BGSAPMAINTAR_SZPF fates_levscpf below-ground sapwood maintenance autotrophic respiration in kg carbon per m2 per second by pft kg m-2 s-1 F +FATES_BGSAPWOOD_ALLOC_SZPF fates_levscpf allocation to below-ground sapwood by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_BGSTRUCT_ALLOC_SZPF fates_levscpf allocation to below-ground structural (deadwood) by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_C13DISC_SZPF fates_levscpf C13 discrimination by pft/size per mil F +FATES_DDBH_CANOPY_SZPF fates_levscpf diameter growth increment by pft/size m m-2 yr-1 F +FATES_DDBH_SZPF fates_levscpf diameter growth increment by pft/size m m-2 yr-1 F +FATES_DDBH_USTORY_SZPF fates_levscpf diameter growth increment by pft/size m m-2 yr-1 F +FATES_FROOTC_SZPF fates_levscpf fine-root carbon mass by size-class x pft in kg carbon per m2 kg m-2 F +FATES_FROOTMAINTAR_SZPF fates_levscpf fine root maintenance autotrophic respiration in kg carbon per m2 per second by pft/size kg m-2 s-1 F +FATES_FROOT_ALLOC_SZPF fates_levscpf allocation to fine roots by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_GPP_CANOPY_SZPF fates_levscpf gross primary production of canopy plants by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_GPP_SZPF fates_levscpf gross primary production by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_GPP_USTORY_SZPF fates_levscpf gross primary production of understory plants by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_GROWAR_SZPF fates_levscpf growth autotrophic respiration in kg carbon per m2 per second by pft/size kg m-2 s-1 F +FATES_GROWTHFLUX_FUSION_SZPF fates_levscpf flux of individuals into a given size class bin via fusion m-2 yr-1 F +FATES_GROWTHFLUX_SZPF fates_levscpf flux of individuals into a given size class bin via growth and recruitment m-2 yr-1 F +FATES_LAI_CANOPY_SZPF fates_levscpf Leaf area index (LAI) of canopy plants by pft/size m2 m-2 F +FATES_LAI_USTORY_SZPF fates_levscpf Leaf area index (LAI) of understory plants by pft/size m2 m-2 F +FATES_LEAFC_CANOPY_SZPF fates_levscpf biomass in leaves of canopy plants by pft/size in kg carbon per m2 kg m-2 F +FATES_LEAFC_SZPF fates_levscpf leaf carbon mass by size-class x pft in kg carbon per m2 kg m-2 F +FATES_LEAFC_USTORY_SZPF fates_levscpf biomass in leaves of understory plants by pft/size in kg carbon per m2 kg m-2 F +FATES_LEAF_ALLOC_SZPF fates_levscpf allocation to leaves by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_M3_MORTALITY_CANOPY_SZPF fates_levscpf C starvation mortality of canopy plants by pft/size N/ha/yr F +FATES_M3_MORTALITY_USTORY_SZPF fates_levscpf C starvation mortality of understory plants by pft/size N/ha/yr F +FATES_MAINTAR_SZPF fates_levscpf maintenance autotrophic respiration in kg carbon per m2 per second by pft/size kg m-2 s-1 F +FATES_MORTALITY_AGESCEN_SZPF fates_levscpf age senescence mortality by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_BACKGROUND_SZPF fates_levscpf background mortality by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_CAMBIALBURN_SZPF fates_levscpf fire mortality from cambial burn by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_CANOPY_SZPF fates_levscpf total mortality of canopy plants by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_CROWNSCORCH_SZPF fates_levscpf fire mortality from crown scorch by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_CSTARV_SZPF fates_levscpf carbon starvation mortality by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_FIRE_SZPF fates_levscpf fire mortality by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_FREEZING_SZPF fates_levscpf freezing mortality by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_HYDRAULIC_SZPF fates_levscpf hydraulic mortality by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_IMPACT_SZPF fates_levscpf impact mortality by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_LOGGING_SZPF fates_levscpf logging mortality by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_SENESCENCE_SZPF fates_levscpf senescence mortality by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_TERMINATION_SZPF fates_levscpf termination mortality by pft/size in number pf plants per m2 per year m-2 yr-1 F +FATES_MORTALITY_USTORY_SZPF fates_levscpf total mortality of understory plants by pft/size in number of plants per m2 per year m-2 yr-1 F +FATES_NPLANT_CANOPY_SZPF fates_levscpf number of canopy plants by size/pft per m2 m-2 F +FATES_NPLANT_SZPF fates_levscpf stem number density by pft/size m-2 F +FATES_NPLANT_USTORY_SZPF fates_levscpf density of understory plants by pft/size in number of plants per m2 m-2 F +FATES_NPP_SZPF fates_levscpf total net primary production by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_RDARK_SZPF fates_levscpf dark portion of maintenance autotrophic respiration in kg carbon per m2 per second by pft/size kg m-2 s-1 F +FATES_REPROC_SZPF fates_levscpf reproductive carbon mass (on plant) by size-class x pft in kg carbon per m2 kg m-2 F +FATES_SAPWOODC_SZPF fates_levscpf sapwood carbon mass by size-class x pft in kg carbon per m2 kg m-2 F +FATES_SEED_ALLOC_SZPF fates_levscpf allocation to seeds by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_STOREC_CANOPY_SZPF fates_levscpf biomass in storage pools of canopy plants by pft/size in kg carbon per m2 kg m-2 F +FATES_STOREC_SZPF fates_levscpf storage carbon mass by size-class x pft in kg carbon per m2 kg m-2 F +FATES_STOREC_TF_CANOPY_SZPF fates_levscpf Storage C fraction of target by size x pft, in the canopy kg kg-1 F +FATES_STOREC_TF_USTORY_SZPF fates_levscpf Storage C fraction of target by size x pft, in the understory kg kg-1 F +FATES_STOREC_USTORY_SZPF fates_levscpf biomass in storage pools of understory plants by pft/size in kg carbon per m2 kg m-2 F +FATES_STORE_ALLOC_SZPF fates_levscpf allocation to storage C by pft/size in kg carbon per m2 per second kg m-2 s-1 F +FATES_VEGC_ABOVEGROUND_SZPF fates_levscpf aboveground biomass by pft/size in kg carbon per m2 kg m-2 F +FATES_VEGC_SZPF fates_levscpf total vegetation biomass in live plants by size-class x pft in kg carbon per m2 kg m-2 F +ACTUAL_IMMOB_NH4 levdcmp immobilization of NH4 gN/m^3/s F +ACTUAL_IMMOB_NO3 levdcmp immobilization of NO3 gN/m^3/s F +ACTUAL_IMMOB_vr levdcmp actual N immobilization gN/m^3/s F +FMAX_DENIT_CARBONSUBSTRATE levdcmp FMAX_DENIT_CARBONSUBSTRATE gN/m^3/s F +FMAX_DENIT_NITRATE levdcmp FMAX_DENIT_NITRATE gN/m^3/s F +FPI_vr levdcmp fraction of potential immobilization proportion F +F_DENIT_BASE levdcmp F_DENIT_BASE gN/m^3/s F +F_DENIT_vr levdcmp denitrification flux gN/m^3/s F +F_NIT_vr levdcmp nitrification flux gN/m^3/s F +GROSS_NMIN_vr levdcmp gross rate of N mineralization gN/m^3/s F +K_LIT_CEL levdcmp cellulosic litter potential loss coefficient 1/s F +K_LIT_LIG levdcmp lignin litter potential loss coefficient 1/s F +K_LIT_MET levdcmp metabolic litter potential loss coefficient 1/s F +K_NITR levdcmp K_NITR 1/s F +K_NITR_H2O levdcmp K_NITR_H2O unitless F +K_NITR_PH levdcmp K_NITR_PH unitless F +K_NITR_T levdcmp K_NITR_T unitless F +K_SOM_ACT levdcmp active soil organic potential loss coefficient 1/s F +K_SOM_PAS levdcmp passive soil organic potential loss coefficient 1/s F +K_SOM_SLO levdcmp slow soil organic ma potential loss coefficient 1/s F +L1_PATHFRAC_S1_vr levdcmp PATHFRAC from metabolic litter to active soil organic fraction F +L1_RESP_FRAC_S1_vr levdcmp respired from metabolic litter to active soil organic fraction F +L2_PATHFRAC_S1_vr levdcmp PATHFRAC from cellulosic litter to active soil organic fraction F +L2_RESP_FRAC_S1_vr levdcmp respired from cellulosic litter to active soil organic fraction F +L3_PATHFRAC_S2_vr levdcmp PATHFRAC from lignin litter to slow soil organic ma fraction F +L3_RESP_FRAC_S2_vr levdcmp respired from lignin litter to slow soil organic ma fraction F +LIT_CEL_C_TNDNCY_VERT_TR levdcmp cellulosic litter C tendency due to vertical transport gC/m^3/s F +LIT_CEL_C_TO_SOM_ACT_C_v levdcmp decomp. of cellulosic litter C to active soil organic C gC/m^3/s F +LIT_CEL_HR_vr levdcmp Het. Resp. from cellulosic litter gC/m^3/s F +LIT_CEL_N_TNDNCY_VERT_TR levdcmp cellulosic litter N tendency due to vertical transport gN/m^3/s F +LIT_CEL_N_TO_SOM_ACT_N_v levdcmp decomp. of cellulosic litter N to active soil organic N gN/m^3 F +LIT_CEL_N_vr levdcmp LIT_CEL N (vertically resolved) gN/m^3 T +LIT_LIG_C_TNDNCY_VERT_TR levdcmp lignin litter C tendency due to vertical transport gC/m^3/s F +LIT_LIG_C_TO_SOM_SLO_C_v levdcmp decomp. of lignin litter C to slow soil organic ma C gC/m^3/s F +LIT_LIG_HR_vr levdcmp Het. Resp. from lignin litter gC/m^3/s F +LIT_LIG_N_TNDNCY_VERT_TR levdcmp lignin litter N tendency due to vertical transport gN/m^3/s F +LIT_LIG_N_TO_SOM_SLO_N_v levdcmp decomp. of lignin litter N to slow soil organic ma N gN/m^3 F +LIT_LIG_N_vr levdcmp LIT_LIG N (vertically resolved) gN/m^3 T +LIT_MET_C_TNDNCY_VERT_TR levdcmp metabolic litter C tendency due to vertical transport gC/m^3/s F +LIT_MET_C_TO_SOM_ACT_C_v levdcmp decomp. of metabolic litter C to active soil organic C gC/m^3/s F +LIT_MET_HR_vr levdcmp Het. Resp. from metabolic litter gC/m^3/s F +LIT_MET_N_TNDNCY_VERT_TR levdcmp metabolic litter N tendency due to vertical transport gN/m^3/s F +LIT_MET_N_TO_SOM_ACT_N_v levdcmp decomp. of metabolic litter N to active soil organic N gN/m^3 F +LIT_MET_N_vr levdcmp LIT_MET N (vertically resolved) gN/m^3 T +NDEP_PROF levdcmp profile for atmospheric N deposition 1/m F +NET_NMIN_vr levdcmp net rate of N mineralization gN/m^3/s F +NFIXATION_PROF levdcmp profile for biological N fixation 1/m F +POTENTIAL_IMMOB_vr levdcmp potential N immobilization gN/m^3/s F +POT_F_DENIT_vr levdcmp potential denitrification flux gN/m^3/s F +POT_F_NIT_vr levdcmp potential nitrification flux gN/m^3/s F +S1_PATHFRAC_S2_vr levdcmp PATHFRAC from active soil organic to slow soil organic ma fraction F +S1_PATHFRAC_S3_vr levdcmp PATHFRAC from active soil organic to passive soil organic fraction F +S1_RESP_FRAC_S2_vr levdcmp respired from active soil organic to slow soil organic ma fraction F +S1_RESP_FRAC_S3_vr levdcmp respired from active soil organic to passive soil organic fraction F +S2_PATHFRAC_S1_vr levdcmp PATHFRAC from slow soil organic ma to active soil organic fraction F +S2_PATHFRAC_S3_vr levdcmp PATHFRAC from slow soil organic ma to passive soil organic fraction F +S2_RESP_FRAC_S1_vr levdcmp respired from slow soil organic ma to active soil organic fraction F +S2_RESP_FRAC_S3_vr levdcmp respired from slow soil organic ma to passive soil organic fraction F +S3_PATHFRAC_S1_vr levdcmp PATHFRAC from passive soil organic to active soil organic fraction F +S3_RESP_FRAC_S1_vr levdcmp respired from passive soil organic to active soil organic fraction F +SMINN_TO_PLANT_vr levdcmp plant uptake of soil mineral N gN/m^3/s F +SMINN_TO_S1N_L1_vr levdcmp mineral N flux for decomp. of LIT_METto SOM_ACT gN/m^3 F +SMINN_TO_S1N_L2_vr levdcmp mineral N flux for decomp. of LIT_CELto SOM_ACT gN/m^3 F +SMINN_TO_S1N_S2_vr levdcmp mineral N flux for decomp. of SOM_SLOto SOM_ACT gN/m^3 F +SMINN_TO_S1N_S3_vr levdcmp mineral N flux for decomp. of SOM_PASto SOM_ACT gN/m^3 F +SMINN_TO_S2N_L3_vr levdcmp mineral N flux for decomp. of LIT_LIGto SOM_SLO gN/m^3 F +SMINN_TO_S2N_S1_vr levdcmp mineral N flux for decomp. of SOM_ACTto SOM_SLO gN/m^3 F +SMINN_TO_S3N_S1_vr levdcmp mineral N flux for decomp. of SOM_ACTto SOM_PAS gN/m^3 F +SMINN_TO_S3N_S2_vr levdcmp mineral N flux for decomp. of SOM_SLOto SOM_PAS gN/m^3 F +SMIN_NH4_TO_PLANT levdcmp plant uptake of NH4 gN/m^3/s F +SMIN_NO3_LEACHED_vr levdcmp soil NO3 pool loss to leaching gN/m^3/s F +SMIN_NO3_MASSDENS levdcmp SMIN_NO3_MASSDENS ugN/cm^3 soil F +SMIN_NO3_RUNOFF_vr levdcmp soil NO3 pool loss to runoff gN/m^3/s F +SMIN_NO3_TO_PLANT levdcmp plant uptake of NO3 gN/m^3/s F +SOILN_vr levdcmp SOIL N (vertically resolved) gN/m^3 T +SOM_ACT_C_TNDNCY_VERT_TR levdcmp active soil organic C tendency due to vertical transport gC/m^3/s F +SOM_ACT_C_TO_SOM_PAS_C_v levdcmp decomp. of active soil organic C to passive soil organic C gC/m^3/s F +SOM_ACT_C_TO_SOM_SLO_C_v levdcmp decomp. of active soil organic C to slow soil organic ma C gC/m^3/s F +SOM_ACT_HR_S2_vr levdcmp Het. Resp. from active soil organic gC/m^3/s F +SOM_ACT_HR_S3_vr levdcmp Het. Resp. from active soil organic gC/m^3/s F +SOM_ACT_N_TNDNCY_VERT_TR levdcmp active soil organic N tendency due to vertical transport gN/m^3/s F +SOM_ACT_N_TO_SOM_PAS_N_v levdcmp decomp. of active soil organic N to passive soil organic N gN/m^3 F +SOM_ACT_N_TO_SOM_SLO_N_v levdcmp decomp. of active soil organic N to slow soil organic ma N gN/m^3 F +SOM_ACT_N_vr levdcmp SOM_ACT N (vertically resolved) gN/m^3 T +SOM_ADV_COEF levdcmp advection term for vertical SOM translocation m/s F +SOM_DIFFUS_COEF levdcmp diffusion coefficient for vertical SOM translocation m^2/s F +SOM_PAS_C_TNDNCY_VERT_TR levdcmp passive soil organic C tendency due to vertical transport gC/m^3/s F +SOM_PAS_C_TO_SOM_ACT_C_v levdcmp decomp. of passive soil organic C to active soil organic C gC/m^3/s F +SOM_PAS_HR_vr levdcmp Het. Resp. from passive soil organic gC/m^3/s F +SOM_PAS_N_TNDNCY_VERT_TR levdcmp passive soil organic N tendency due to vertical transport gN/m^3/s F +SOM_PAS_N_TO_SOM_ACT_N_v levdcmp decomp. of passive soil organic N to active soil organic N gN/m^3 F +SOM_PAS_N_vr levdcmp SOM_PAS N (vertically resolved) gN/m^3 T +SOM_SLO_C_TNDNCY_VERT_TR levdcmp slow soil organic ma C tendency due to vertical transport gC/m^3/s F +SOM_SLO_C_TO_SOM_ACT_C_v levdcmp decomp. of slow soil organic ma C to active soil organic C gC/m^3/s F +SOM_SLO_C_TO_SOM_PAS_C_v levdcmp decomp. of slow soil organic ma C to passive soil organic C gC/m^3/s F +SOM_SLO_HR_S1_vr levdcmp Het. Resp. from slow soil organic ma gC/m^3/s F +SOM_SLO_HR_S3_vr levdcmp Het. Resp. from slow soil organic ma gC/m^3/s F +SOM_SLO_N_TNDNCY_VERT_TR levdcmp slow soil organic ma N tendency due to vertical transport gN/m^3/s F +SOM_SLO_N_TO_SOM_ACT_N_v levdcmp decomp. of slow soil organic ma N to active soil organic N gN/m^3 F +SOM_SLO_N_TO_SOM_PAS_N_v levdcmp decomp. of slow soil organic ma N to passive soil organic N gN/m^3 F +SOM_SLO_N_vr levdcmp SOM_SLO N (vertically resolved) gN/m^3 T +SUPPLEMENT_TO_SMINN_vr levdcmp supplemental N supply gN/m^3/s F +WFPS levdcmp WFPS percent F +anaerobic_frac levdcmp anaerobic_frac m3/m3 F +diffus levdcmp diffusivity m^2/s F +fr_WFPS levdcmp fr_WFPS fraction F +n2_n2o_ratio_denit levdcmp n2_n2o_ratio_denit gN/gN F +r_psi levdcmp r_psi m F +ratio_k1 levdcmp ratio_k1 none F +ratio_no3_co2 levdcmp ratio_no3_co2 ratio F +soil_bulkdensity levdcmp soil_bulkdensity kg/m3 F +soil_co2_prod levdcmp soil_co2_prod ug C / g soil / day F +FGR_SOIL_R levgrnd Rural downward heat flux at interface below each soil layer watt/m^2 F +HK levgrnd hydraulic conductivity (natural vegetated and crop landunits only) mm/s F +SMP levgrnd soil matric potential (natural vegetated and crop landunits only) mm T +SOILPSI levgrnd soil water potential in each soil layer MPa F +TSOI levgrnd soil temperature (natural vegetated and crop landunits only) K T +TSOI_ICE levgrnd soil temperature (ice landunits only) K T +LAKEICEFRAC levlak lake layer ice mass fraction unitless F +TLAKE levlak lake temperature K T +SNO_ABS levsno Absorbed solar radiation in each snow layer W/m^2 F +SNO_ABS_ICE levsno Absorbed solar radiation in each snow layer (ice landunits only) W/m^2 F +SNO_BW levsno Partial density of water in the snow pack (ice + liquid) kg/m3 F +SNO_BW_ICE levsno Partial density of water in the snow pack (ice + liquid, ice landunits only) kg/m3 F +SNO_EXISTENCE levsno Fraction of averaging period for which each snow layer existed unitless F +SNO_FRZ levsno snow freezing rate in each snow layer kg/m2/s F +SNO_FRZ_ICE levsno snow freezing rate in each snow layer (ice landunits only) mm/s F +SNO_GS levsno Mean snow grain size Microns F +SNO_GS_ICE levsno Mean snow grain size (ice landunits only) Microns F +SNO_ICE levsno Snow ice content kg/m2 F +SNO_LIQH2O levsno Snow liquid water content kg/m2 F +SNO_MELT levsno snow melt rate in each snow layer mm/s F +SNO_MELT_ICE levsno snow melt rate in each snow layer (ice landunits only) mm/s F +SNO_T levsno Snow temperatures K F +SNO_TK levsno Thermal conductivity W/m-K F +SNO_TK_ICE levsno Thermal conductivity (ice landunits only) W/m-K F +SNO_T_ICE levsno Snow temperatures (ice landunits only) K F +SNO_Z levsno Snow layer thicknesses m F +SNO_Z_ICE levsno Snow layer thicknesses (ice landunits only) m F +FATES_FRAGMENTATION_SCALER_SL levsoi factor (0-1) by which litter/cwd fragmentation proceeds relative to max rate by soil layer T +FATES_FROOTC_SL levsoi Total carbon in live plant fine-roots over depth kg m-3 T +H2OSOI levsoi volumetric soil water (natural vegetated and crop landunits only) mm3/mm3 T +HR_vr levsoi total vertically resolved heterotrophic respiration gC/m^3/s T +KROOT levsoi root conductance each soil layer 1/s F +KSOIL levsoi soil conductance in each soil layer 1/s F +LIT_CEL_C_vr levsoi LIT_CEL C (vertically resolved) gC/m^3 T +LIT_LIG_C_vr levsoi LIT_LIG C (vertically resolved) gC/m^3 T +LIT_MET_C_vr levsoi LIT_MET C (vertically resolved) gC/m^3 T +O_SCALAR levsoi fraction by which decomposition is reduced due to anoxia unitless T +QROOTSINK levsoi water flux from soil to root in each soil-layer mm/s F +SMINN_vr levsoi soil mineral N gN/m^3 T +SMIN_NH4_vr levsoi soil mineral NH4 (vert. res.) gN/m^3 T +SMIN_NO3_vr levsoi soil mineral NO3 (vert. res.) gN/m^3 T +SOILC_vr levsoi SOIL C (vertically resolved) gC/m^3 T +SOILICE levsoi soil ice (natural vegetated and crop landunits only) kg/m2 T +SOILLIQ levsoi soil liquid water (natural vegetated and crop landunits only) kg/m2 T +SOM_ACT_C_vr levsoi SOM_ACT C (vertically resolved) gC/m^3 T +SOM_PAS_C_vr levsoi SOM_PAS C (vertically resolved) gC/m^3 T +SOM_SLO_C_vr levsoi SOM_SLO C (vertically resolved) gC/m^3 T +T_SCALAR levsoi temperature inhibition of decomposition unitless T +W_SCALAR levsoi Moisture (dryness) inhibition of decomposition unitless T +ALBD numrad surface albedo (direct) proportion F +ALBGRD numrad ground albedo (direct) proportion F +ALBGRI numrad ground albedo (indirect) proportion F +ALBI numrad surface albedo (indirect) proportion F +=================================== ================ ============================================================================================== ================================================================= ======= diff --git a/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst new file mode 100644 index 0000000000..89e7dd23fc --- /dev/null +++ b/doc/source/users_guide/setting-up-and-running-a-case/history_fields_nofates.rst @@ -0,0 +1,1357 @@ +============================= +CTSM History Fields (nofates) +============================= + +CAUTION: Not all variables are relevant / present for all CTSM cases. +Key flags used in this CTSM case: +use_cn = T +use_crop = T +use_fates = F + +=================================== ================ ============================================================================================== ================================================================= ======= +CTSM History Fields +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Variable Name Level Dim. Long Description Units Active? +=================================== ================ ============================================================================================== ================================================================= ======= +A10TMIN - 10-day running mean of min 2-m temperature K F +A5TMIN - 5-day running mean of min 2-m temperature K F +ACTUAL_IMMOB - actual N immobilization gN/m^2/s T +AGLB - Aboveground leaf biomass kg/m^2 F +AGNPP - aboveground NPP gC/m^2/s T +AGSB - Aboveground stem biomass kg/m^2 F +ALPHA - alpha coefficient for VOC calc non F +ALT - current active layer thickness m T +ALTMAX - maximum annual active layer thickness m T +ALTMAX_LASTYEAR - maximum prior year active layer thickness m F +ANNAVG_T2M - annual average 2m air temperature K F +ANNMAX_RETRANSN - annual max of retranslocated N pool gN/m^2 F +ANNSUM_COUNTER - seconds since last annual accumulator turnover s F +ANNSUM_NPP - annual sum of NPP gC/m^2/yr F +ANNSUM_POTENTIAL_GPP - annual sum of potential GPP gN/m^2/yr F +APPAR_TEMP - 2 m apparent temperature C T +APPAR_TEMP_R - Rural 2 m apparent temperature C T +APPAR_TEMP_U - Urban 2 m apparent temperature C T +AR - autotrophic respiration (MR + GR) gC/m^2/s T +ATM_O3 - atmospheric ozone partial pressure mol/mol F +ATM_TOPO - atmospheric surface height m T +AVAILC - C flux available for allocation gC/m^2/s F +AVAIL_RETRANSN - N flux available from retranslocation pool gN/m^2/s F +AnnET - Annual ET mm/s F +BAF_CROP - fractional area burned for crop s-1 T +BAF_PEATF - fractional area burned in peatland s-1 T +BCDEP - total BC deposition (dry+wet) from atmosphere kg/m^2/s T +BETA - coefficient of convective velocity none F +BGLFR - background litterfall rate 1/s F +BGNPP - belowground NPP gC/m^2/s T +BGTR - background transfer growth rate 1/s F +BTRANMN - daily minimum of transpiration beta factor unitless T +CANNAVG_T2M - annual average of 2m air temperature K F +CANNSUM_NPP - annual sum of column-level NPP gC/m^2/s F +CGRND - deriv. of soil energy flux wrt to soil temp W/m^2/K F +CGRNDL - deriv. of soil latent heat flux wrt soil temp W/m^2/K F +CGRNDS - deriv. of soil sensible heat flux wrt soil temp W/m^2/K F +CH4PROD - Gridcell total production of CH4 gC/m2/s T +CH4_EBUL_TOTAL_SAT - ebullition surface CH4 flux; (+ to atm) mol/m2/s F +CH4_EBUL_TOTAL_UNSAT - ebullition surface CH4 flux; (+ to atm) mol/m2/s F +CH4_SURF_AERE_SAT - aerenchyma surface CH4 flux for inundated area; (+ to atm) mol/m2/s T +CH4_SURF_AERE_UNSAT - aerenchyma surface CH4 flux for non-inundated area; (+ to atm) mol/m2/s T +CH4_SURF_DIFF_SAT - diffusive surface CH4 flux for inundated / lake area; (+ to atm) mol/m2/s T +CH4_SURF_DIFF_UNSAT - diffusive surface CH4 flux for non-inundated area; (+ to atm) mol/m2/s T +CH4_SURF_EBUL_SAT - ebullition surface CH4 flux for inundated / lake area; (+ to atm) mol/m2/s T +CH4_SURF_EBUL_UNSAT - ebullition surface CH4 flux for non-inundated area; (+ to atm) mol/m2/s T +COL_CTRUNC - column-level sink for C truncation gC/m^2 F +COL_FIRE_CLOSS - total column-level fire C loss for non-peat fires outside land-type converted region gC/m^2/s T +COL_FIRE_NLOSS - total column-level fire N loss gN/m^2/s T +COL_NTRUNC - column-level sink for N truncation gN/m^2 F +COST_NACTIVE - Cost of active uptake gN/gC T +COST_NFIX - Cost of fixation gN/gC T +COST_NRETRANS - Cost of retranslocation gN/gC T +COSZEN - cosine of solar zenith angle none F +CPHASE - crop phenology phase 0-not planted, 1-planted, 2-leaf emerge, 3-grain fill, 4-harvest T +CPOOL - temporary photosynthate C pool gC/m^2 T +CPOOL_DEADCROOT_GR - dead coarse root growth respiration gC/m^2/s F +CPOOL_DEADCROOT_STORAGE_GR - dead coarse root growth respiration to storage gC/m^2/s F +CPOOL_DEADSTEM_GR - dead stem growth respiration gC/m^2/s F +CPOOL_DEADSTEM_STORAGE_GR - dead stem growth respiration to storage gC/m^2/s F +CPOOL_FROOT_GR - fine root growth respiration gC/m^2/s F +CPOOL_FROOT_STORAGE_GR - fine root growth respiration to storage gC/m^2/s F +CPOOL_LEAF_GR - leaf growth respiration gC/m^2/s F +CPOOL_LEAF_STORAGE_GR - leaf growth respiration to storage gC/m^2/s F +CPOOL_LIVECROOT_GR - live coarse root growth respiration gC/m^2/s F +CPOOL_LIVECROOT_STORAGE_GR - live coarse root growth respiration to storage gC/m^2/s F +CPOOL_LIVESTEM_GR - live stem growth respiration gC/m^2/s F +CPOOL_LIVESTEM_STORAGE_GR - live stem growth respiration to storage gC/m^2/s F +CPOOL_TO_DEADCROOTC - allocation to dead coarse root C gC/m^2/s F +CPOOL_TO_DEADCROOTC_STORAGE - allocation to dead coarse root C storage gC/m^2/s F +CPOOL_TO_DEADSTEMC - allocation to dead stem C gC/m^2/s F +CPOOL_TO_DEADSTEMC_STORAGE - allocation to dead stem C storage gC/m^2/s F +CPOOL_TO_FROOTC - allocation to fine root C gC/m^2/s F +CPOOL_TO_FROOTC_STORAGE - allocation to fine root C storage gC/m^2/s F +CPOOL_TO_GRESP_STORAGE - allocation to growth respiration storage gC/m^2/s F +CPOOL_TO_LEAFC - allocation to leaf C gC/m^2/s F +CPOOL_TO_LEAFC_STORAGE - allocation to leaf C storage gC/m^2/s F +CPOOL_TO_LIVECROOTC - allocation to live coarse root C gC/m^2/s F +CPOOL_TO_LIVECROOTC_STORAGE - allocation to live coarse root C storage gC/m^2/s F +CPOOL_TO_LIVESTEMC - allocation to live stem C gC/m^2/s F +CPOOL_TO_LIVESTEMC_STORAGE - allocation to live stem C storage gC/m^2/s F +CROPPROD1C - 1-yr crop product (grain+biofuel) C gC/m^2 T +CROPPROD1C_LOSS - loss from 1-yr crop product pool gC/m^2/s T +CROPPROD1N - 1-yr crop product (grain+biofuel) N gN/m^2 T +CROPPROD1N_LOSS - loss from 1-yr crop product pool gN/m^2/s T +CROPSEEDC_DEFICIT - C used for crop seed that needs to be repaid gC/m^2 T +CROPSEEDN_DEFICIT - N used for crop seed that needs to be repaid gN/m^2 F +CROP_SEEDC_TO_LEAF - crop seed source to leaf gC/m^2/s F +CROP_SEEDN_TO_LEAF - crop seed source to leaf gN/m^2/s F +CURRENT_GR - growth resp for new growth displayed in this timestep gC/m^2/s F +CWDC_HR - cwd C heterotrophic respiration gC/m^2/s T +CWDC_LOSS - coarse woody debris C loss gC/m^2/s T +CWD_C - CWD C gC/m^2 T +CWD_C_1m - CWD C to 1 meter gC/m^2 F +CWD_C_TO_LIT_CEL_C - decomp. of coarse woody debris C to cellulosic litter C gC/m^2/s F +CWD_C_TO_LIT_LIG_C - decomp. of coarse woody debris C to lignin litter C gC/m^2/s F +CWD_HR_L2 - Het. Resp. from coarse woody debris gC/m^2/s F +CWD_HR_L3 - Het. Resp. from coarse woody debris gC/m^2/s F +CWD_N - CWD N gN/m^2 T +CWD_N_1m - CWD N to 1 meter gN/m^2 F +CWD_N_TO_LIT_CEL_N - decomp. of coarse woody debris N to cellulosic litter N gN/m^2 F +CWD_N_TO_LIT_LIG_N - decomp. of coarse woody debris N to lignin litter N gN/m^2 F +C_ALLOMETRY - C allocation index none F +DAYL - daylength s F +DAYS_ACTIVE - number of days since last dormancy days F +DEADCROOTC - dead coarse root C gC/m^2 T +DEADCROOTC_STORAGE - dead coarse root C storage gC/m^2 F +DEADCROOTC_STORAGE_TO_XFER - dead coarse root C shift storage to transfer gC/m^2/s F +DEADCROOTC_XFER - dead coarse root C transfer gC/m^2 F +DEADCROOTC_XFER_TO_DEADCROOTC - dead coarse root C growth from storage gC/m^2/s F +DEADCROOTN - dead coarse root N gN/m^2 T +DEADCROOTN_STORAGE - dead coarse root N storage gN/m^2 F +DEADCROOTN_STORAGE_TO_XFER - dead coarse root N shift storage to transfer gN/m^2/s F +DEADCROOTN_XFER - dead coarse root N transfer gN/m^2 F +DEADCROOTN_XFER_TO_DEADCROOTN - dead coarse root N growth from storage gN/m^2/s F +DEADSTEMC - dead stem C gC/m^2 T +DEADSTEMC_STORAGE - dead stem C storage gC/m^2 F +DEADSTEMC_STORAGE_TO_XFER - dead stem C shift storage to transfer gC/m^2/s F +DEADSTEMC_XFER - dead stem C transfer gC/m^2 F +DEADSTEMC_XFER_TO_DEADSTEMC - dead stem C growth from storage gC/m^2/s F +DEADSTEMN - dead stem N gN/m^2 T +DEADSTEMN_STORAGE - dead stem N storage gN/m^2 F +DEADSTEMN_STORAGE_TO_XFER - dead stem N shift storage to transfer gN/m^2/s F +DEADSTEMN_XFER - dead stem N transfer gN/m^2 F +DEADSTEMN_XFER_TO_DEADSTEMN - dead stem N growth from storage gN/m^2/s F +DENIT - total rate of denitrification gN/m^2/s T +DGNETDT - derivative of net ground heat flux wrt soil temp W/m^2/K F +DISCOI - 2 m Discomfort Index C T +DISCOIS - 2 m Stull Discomfort Index C T +DISCOIS_R - Rural 2 m Stull Discomfort Index C T +DISCOIS_U - Urban 2 m Stull Discomfort Index C T +DISCOI_R - Rural 2 m Discomfort Index C T +DISCOI_U - Urban 2 m Discomfort Index C T +DISPLA - displacement height (vegetated landunits only) m F +DISPVEGC - displayed veg carbon, excluding storage and cpool gC/m^2 T +DISPVEGN - displayed vegetation nitrogen gN/m^2 T +DLRAD - downward longwave radiation below the canopy W/m^2 F +DORMANT_FLAG - dormancy flag none F +DOWNREG - fractional reduction in GPP due to N limitation proportion F +DPVLTRB1 - turbulent deposition velocity 1 m/s F +DPVLTRB2 - turbulent deposition velocity 2 m/s F +DPVLTRB3 - turbulent deposition velocity 3 m/s F +DPVLTRB4 - turbulent deposition velocity 4 m/s F +DSL - dry surface layer thickness mm T +DSTDEP - total dust deposition (dry+wet) from atmosphere kg/m^2/s T +DSTFLXT - total surface dust emission kg/m2/s T +DT_VEG - change in t_veg, last iteration K F +DWT_CONV_CFLUX - conversion C flux (immediate loss to atm) (0 at all times except first timestep of year) gC/m^2/s T +DWT_CONV_CFLUX_DRIBBLED - conversion C flux (immediate loss to atm), dribbled throughout the year gC/m^2/s T +DWT_CONV_CFLUX_PATCH - patch-level conversion C flux (immediate loss to atm) (0 at all times except first timestep of gC/m^2/s F +DWT_CONV_NFLUX - conversion N flux (immediate loss to atm) (0 at all times except first timestep of year) gN/m^2/s T +DWT_CONV_NFLUX_PATCH - patch-level conversion N flux (immediate loss to atm) (0 at all times except first timestep of gN/m^2/s F +DWT_CROPPROD1C_GAIN - landcover change-driven addition to 1-year crop product pool gC/m^2/s T +DWT_CROPPROD1N_GAIN - landcover change-driven addition to 1-year crop product pool gN/m^2/s T +DWT_PROD100C_GAIN - landcover change-driven addition to 100-yr wood product pool gC/m^2/s F +DWT_PROD100N_GAIN - landcover change-driven addition to 100-yr wood product pool gN/m^2/s F +DWT_PROD10C_GAIN - landcover change-driven addition to 10-yr wood product pool gC/m^2/s F +DWT_PROD10N_GAIN - landcover change-driven addition to 10-yr wood product pool gN/m^2/s F +DWT_SEEDC_TO_DEADSTEM - seed source to patch-level deadstem gC/m^2/s F +DWT_SEEDC_TO_DEADSTEM_PATCH - patch-level seed source to patch-level deadstem (per-area-gridcell; only makes sense with dov2 gC/m^2/s F +DWT_SEEDC_TO_LEAF - seed source to patch-level leaf gC/m^2/s F +DWT_SEEDC_TO_LEAF_PATCH - patch-level seed source to patch-level leaf (per-area-gridcell; only makes sense with dov2xy=. gC/m^2/s F +DWT_SEEDN_TO_DEADSTEM - seed source to patch-level deadstem gN/m^2/s T +DWT_SEEDN_TO_DEADSTEM_PATCH - patch-level seed source to patch-level deadstem (per-area-gridcell; only makes sense with dov2 gN/m^2/s F +DWT_SEEDN_TO_LEAF - seed source to patch-level leaf gN/m^2/s T +DWT_SEEDN_TO_LEAF_PATCH - patch-level seed source to patch-level leaf (per-area-gridcell; only makes sense with dov2xy=. gN/m^2/s F +DWT_SLASH_CFLUX - slash C flux (to litter diagnostic only) (0 at all times except first timestep of year) gC/m^2/s T +DWT_SLASH_CFLUX_PATCH - patch-level slash C flux (to litter diagnostic only) (0 at all times except first timestep of gC/m^2/s F +DWT_WOODPRODC_GAIN - landcover change-driven addition to wood product pools gC/m^2/s T +DWT_WOODPRODN_GAIN - landcover change-driven addition to wood product pools gN/m^2/s T +DWT_WOOD_PRODUCTC_GAIN_PATCH - patch-level landcover change-driven addition to wood product pools(0 at all times except first gC/m^2/s F +DYN_COL_ADJUSTMENTS_CH4 - Adjustments in ch4 due to dynamic column areas; only makes sense at the column level: should n gC/m^2 F +DYN_COL_SOIL_ADJUSTMENTS_C - Adjustments in soil carbon due to dynamic column areas; only makes sense at the column level: gC/m^2 F +DYN_COL_SOIL_ADJUSTMENTS_N - Adjustments in soil nitrogen due to dynamic column areas; only makes sense at the column level gN/m^2 F +DYN_COL_SOIL_ADJUSTMENTS_NH4 - Adjustments in soil NH4 due to dynamic column areas; only makes sense at the column level: sho gN/m^2 F +DYN_COL_SOIL_ADJUSTMENTS_NO3 - Adjustments in soil NO3 due to dynamic column areas; only makes sense at the column level: sho gN/m^2 F +EFLXBUILD - building heat flux from change in interior building air temperature W/m^2 T +EFLX_DYNBAL - dynamic land cover change conversion energy flux W/m^2 T +EFLX_GNET - net heat flux into ground W/m^2 F +EFLX_GRND_LAKE - net heat flux into lake/snow surface, excluding light transmission W/m^2 T +EFLX_LH_TOT - total latent heat flux [+ to atm] W/m^2 T +EFLX_LH_TOT_ICE - total latent heat flux [+ to atm] (ice landunits only) W/m^2 F +EFLX_LH_TOT_R - Rural total evaporation W/m^2 T +EFLX_LH_TOT_U - Urban total evaporation W/m^2 F +EFLX_SOIL_GRND - soil heat flux [+ into soil] W/m^2 F +ELAI - exposed one-sided leaf area index m^2/m^2 T +EMG - ground emissivity proportion F +EMV - vegetation emissivity proportion F +EOPT - Eopt coefficient for VOC calc non F +EPT - 2 m Equiv Pot Temp K T +EPT_R - Rural 2 m Equiv Pot Temp K T +EPT_U - Urban 2 m Equiv Pot Temp K T +ER - total ecosystem respiration, autotrophic + heterotrophic gC/m^2/s T +ERRH2O - total water conservation error mm T +ERRH2OSNO - imbalance in snow depth (liquid water) mm T +ERRSEB - surface energy conservation error W/m^2 T +ERRSOI - soil/lake energy conservation error W/m^2 T +ERRSOL - solar radiation conservation error W/m^2 T +ESAI - exposed one-sided stem area index m^2/m^2 T +EXCESSC_MR - excess C maintenance respiration gC/m^2/s F +EXCESS_CFLUX - C flux not allocated due to downregulation gC/m^2/s F +FAREA_BURNED - timestep fractional area burned s-1 T +FCANSNO - fraction of canopy that is wet proportion F +FCEV - canopy evaporation W/m^2 T +FCH4 - Gridcell surface CH4 flux to atmosphere (+ to atm) kgC/m2/s T +FCH4TOCO2 - Gridcell oxidation of CH4 to CO2 gC/m2/s T +FCH4_DFSAT - CH4 additional flux due to changing fsat, natural vegetated and crop landunits only kgC/m2/s T +FCO2 - CO2 flux to atmosphere (+ to atm) kgCO2/m2/s F +FCOV - fractional impermeable area unitless T +FCTR - canopy transpiration W/m^2 T +FDRY - fraction of foliage that is green and dry proportion F +FERTNITRO - Nitrogen fertilizer for each crop gN/m2/yr F +FERT_COUNTER - time left to fertilize seconds F +FERT_TO_SMINN - fertilizer to soil mineral N gN/m^2/s F +FFIX_TO_SMINN - free living N fixation to soil mineral N gN/m^2/s T +FGEV - ground evaporation W/m^2 T +FGR - heat flux into soil/snow including snow melt and lake / snow light transmission W/m^2 T +FGR12 - heat flux between soil layers 1 and 2 W/m^2 T +FGR_ICE - heat flux into soil/snow including snow melt and lake / snow light transmission (ice landunits W/m^2 F +FGR_R - Rural heat flux into soil/snow including snow melt and snow light transmission W/m^2 F +FGR_U - Urban heat flux into soil/snow including snow melt W/m^2 F +FH2OSFC - fraction of ground covered by surface water unitless T +FH2OSFC_NOSNOW - fraction of ground covered by surface water (if no snow present) unitless F +FINUNDATED - fractional inundated area of vegetated columns unitless T +FINUNDATED_LAG - time-lagged inundated fraction of vegetated columns unitless F +FIRA - net infrared (longwave) radiation W/m^2 T +FIRA_ICE - net infrared (longwave) radiation (ice landunits only) W/m^2 F +FIRA_R - Rural net infrared (longwave) radiation W/m^2 T +FIRA_U - Urban net infrared (longwave) radiation W/m^2 F +FIRE - emitted infrared (longwave) radiation W/m^2 T +FIRE_ICE - emitted infrared (longwave) radiation (ice landunits only) W/m^2 F +FIRE_R - Rural emitted infrared (longwave) radiation W/m^2 T +FIRE_U - Urban emitted infrared (longwave) radiation W/m^2 F +FLDS - atmospheric longwave radiation (downscaled to columns in glacier regions) W/m^2 T +FLDS_ICE - atmospheric longwave radiation (downscaled to columns in glacier regions) (ice landunits only) W/m^2 F +FPI - fraction of potential immobilization proportion T +FPSN - photosynthesis umol m-2 s-1 T +FPSN24 - 24 hour accumulative patch photosynthesis starting from mid-night umol CO2/m^2 ground/day F +FPSN_WC - Rubisco-limited photosynthesis umol m-2 s-1 F +FPSN_WJ - RuBP-limited photosynthesis umol m-2 s-1 F +FPSN_WP - Product-limited photosynthesis umol m-2 s-1 F +FREE_RETRANSN_TO_NPOOL - deployment of retranslocated N gN/m^2/s T +FROOTC - fine root C gC/m^2 T +FROOTC_ALLOC - fine root C allocation gC/m^2/s T +FROOTC_LOSS - fine root C loss gC/m^2/s T +FROOTC_STORAGE - fine root C storage gC/m^2 F +FROOTC_STORAGE_TO_XFER - fine root C shift storage to transfer gC/m^2/s F +FROOTC_TO_LITTER - fine root C litterfall gC/m^2/s F +FROOTC_XFER - fine root C transfer gC/m^2 F +FROOTC_XFER_TO_FROOTC - fine root C growth from storage gC/m^2/s F +FROOTN - fine root N gN/m^2 T +FROOTN_STORAGE - fine root N storage gN/m^2 F +FROOTN_STORAGE_TO_XFER - fine root N shift storage to transfer gN/m^2/s F +FROOTN_TO_LITTER - fine root N litterfall gN/m^2/s F +FROOTN_XFER - fine root N transfer gN/m^2 F +FROOTN_XFER_TO_FROOTN - fine root N growth from storage gN/m^2/s F +FROOT_MR - fine root maintenance respiration gC/m^2/s F +FROST_TABLE - frost table depth (natural vegetated and crop landunits only) m F +FSA - absorbed solar radiation W/m^2 T +FSAT - fractional area with water table at surface unitless T +FSA_ICE - absorbed solar radiation (ice landunits only) W/m^2 F +FSA_R - Rural absorbed solar radiation W/m^2 F +FSA_U - Urban absorbed solar radiation W/m^2 F +FSD24 - direct radiation (last 24hrs) K F +FSD240 - direct radiation (last 240hrs) K F +FSDS - atmospheric incident solar radiation W/m^2 T +FSDSND - direct nir incident solar radiation W/m^2 T +FSDSNDLN - direct nir incident solar radiation at local noon W/m^2 T +FSDSNI - diffuse nir incident solar radiation W/m^2 T +FSDSVD - direct vis incident solar radiation W/m^2 T +FSDSVDLN - direct vis incident solar radiation at local noon W/m^2 T +FSDSVI - diffuse vis incident solar radiation W/m^2 T +FSDSVILN - diffuse vis incident solar radiation at local noon W/m^2 T +FSH - sensible heat not including correction for land use change and rain/snow conversion W/m^2 T +FSH_G - sensible heat from ground W/m^2 T +FSH_ICE - sensible heat not including correction for land use change and rain/snow conversion (ice landu W/m^2 F +FSH_PRECIP_CONVERSION - Sensible heat flux from conversion of rain/snow atm forcing W/m^2 T +FSH_R - Rural sensible heat W/m^2 T +FSH_RUNOFF_ICE_TO_LIQ - sensible heat flux generated from conversion of ice runoff to liquid W/m^2 T +FSH_TO_COUPLER - sensible heat sent to coupler (includes corrections for land use change, rain/snow conversion W/m^2 T +FSH_U - Urban sensible heat W/m^2 F +FSH_V - sensible heat from veg W/m^2 T +FSI24 - indirect radiation (last 24hrs) K F +FSI240 - indirect radiation (last 240hrs) K F +FSM - snow melt heat flux W/m^2 T +FSM_ICE - snow melt heat flux (ice landunits only) W/m^2 F +FSM_R - Rural snow melt heat flux W/m^2 F +FSM_U - Urban snow melt heat flux W/m^2 F +FSNO - fraction of ground covered by snow unitless T +FSNO_EFF - effective fraction of ground covered by snow unitless T +FSNO_ICE - fraction of ground covered by snow (ice landunits only) unitless F +FSR - reflected solar radiation W/m^2 T +FSRND - direct nir reflected solar radiation W/m^2 T +FSRNDLN - direct nir reflected solar radiation at local noon W/m^2 T +FSRNI - diffuse nir reflected solar radiation W/m^2 T +FSRSF - reflected solar radiation W/m^2 T +FSRSFND - direct nir reflected solar radiation W/m^2 T +FSRSFNDLN - direct nir reflected solar radiation at local noon W/m^2 T +FSRSFNI - diffuse nir reflected solar radiation W/m^2 T +FSRSFVD - direct vis reflected solar radiation W/m^2 T +FSRSFVDLN - direct vis reflected solar radiation at local noon W/m^2 T +FSRSFVI - diffuse vis reflected solar radiation W/m^2 T +FSRVD - direct vis reflected solar radiation W/m^2 T +FSRVDLN - direct vis reflected solar radiation at local noon W/m^2 T +FSRVI - diffuse vis reflected solar radiation W/m^2 T +FSR_ICE - reflected solar radiation (ice landunits only) W/m^2 F +FSUN - sunlit fraction of canopy proportion F +FSUN24 - fraction sunlit (last 24hrs) K F +FSUN240 - fraction sunlit (last 240hrs) K F +FUELC - fuel load gC/m^2 T +FV - friction velocity m/s T +FWET - fraction of canopy that is wet proportion F +F_DENIT - denitrification flux gN/m^2/s T +F_N2O_DENIT - denitrification N2O flux gN/m^2/s T +F_N2O_NIT - nitrification N2O flux gN/m^2/s T +F_NIT - nitrification flux gN/m^2/s T +FireComp_BC - fire emissions flux of BC kg/m2/sec F +FireComp_OC - fire emissions flux of OC kg/m2/sec F +FireComp_SO2 - fire emissions flux of SO2 kg/m2/sec F +FireEmis_TOT - Total fire emissions flux gC/m2/sec F +FireEmis_ZTOP - Top of vertical fire emissions distribution m F +FireMech_SO2 - fire emissions flux of SO2 kg/m2/sec F +FireMech_bc_a1 - fire emissions flux of bc_a1 kg/m2/sec F +FireMech_pom_a1 - fire emissions flux of pom_a1 kg/m2/sec F +GAMMA - total gamma for VOC calc non F +GAMMAA - gamma A for VOC calc non F +GAMMAC - gamma C for VOC calc non F +GAMMAL - gamma L for VOC calc non F +GAMMAP - gamma P for VOC calc non F +GAMMAS - gamma S for VOC calc non F +GAMMAT - gamma T for VOC calc non F +GDD0 - Growing degree days base 0C from planting ddays F +GDD020 - Twenty year average of growing degree days base 0C from planting ddays F +GDD10 - Growing degree days base 10C from planting ddays F +GDD1020 - Twenty year average of growing degree days base 10C from planting ddays F +GDD8 - Growing degree days base 8C from planting ddays F +GDD820 - Twenty year average of growing degree days base 8C from planting ddays F +GDDACCUM - Accumulated growing degree days past planting date for crop ddays F +GDDHARV - Growing degree days (gdd) needed to harvest ddays F +GDDTSOI - Growing degree-days from planting (top two soil layers) ddays F +GPP - gross primary production gC/m^2/s T +GR - total growth respiration gC/m^2/s T +GRAINC - grain C (does not equal yield) gC/m^2 T +GRAINC_TO_FOOD - grain C to food gC/m^2/s T +GRAINC_TO_FOOD_ANN - grain C to food harvested per calendar year; should only be output annually gC/m^2 F +GRAINC_TO_SEED - grain C to seed gC/m^2/s T +GRAINC_TO_SEED_ANN - grain C to seed harvested per calendar year; should only be output annually gC/m^2 F +GRAINN - grain N gN/m^2 T +GRAINN_TO_FOOD - grain N to food (not scientifically supported) gN/m^2/s F +GRAINN_TO_FOOD_ANN - grain N to food harvested per calendar year; should only be output annually (not scientificall gN/m^2 F +GRAINN_TO_SEED - grain N to seed (not scientifically supported) gN/m^2/s F +GRAINN_TO_SEED_ANN - grain N to seed harvested per calendar year; should only be output annually (not scientificall gN/m^2 F +GRESP_STORAGE - growth respiration storage gC/m^2 F +GRESP_STORAGE_TO_XFER - growth respiration shift storage to transfer gC/m^2/s F +GRESP_XFER - growth respiration transfer gC/m^2 F +GROSS_NMIN - gross rate of N mineralization gN/m^2/s T +GRU_PROD100C_GAIN - gross unrepresented landcover change addition to 100-yr wood product pool gC/m^2/s F +GRU_PROD100N_GAIN - gross unrepresented landcover change addition to 100-yr wood product pool gN/m^2/s F +GRU_PROD10C_GAIN - gross unrepresented landcover change addition to 10-yr wood product pool gC/m^2/s F +GRU_PROD10N_GAIN - gross unrepresented landcover change addition to 10-yr wood product pool gN/m^2/s F +GSSHA - shaded leaf stomatal conductance umol H20/m2/s T +GSSHALN - shaded leaf stomatal conductance at local noon umol H20/m2/s T +GSSUN - sunlit leaf stomatal conductance umol H20/m2/s T +GSSUNLN - sunlit leaf stomatal conductance at local noon umol H20/m2/s T +H2OCAN - intercepted water mm T +H2OSFC - surface water depth mm T +H2OSNO - snow depth (liquid water) mm T +H2OSNO_ICE - snow depth (liquid water, ice landunits only) mm F +H2OSNO_TOP - mass of snow in top snow layer kg/m2 T +HBOT - canopy bottom m F +HEAT_CONTENT1 - initial gridcell total heat content J/m^2 T +HEAT_CONTENT1_VEG - initial gridcell total heat content - natural vegetated and crop landunits only J/m^2 F +HEAT_CONTENT2 - post land cover change total heat content J/m^2 F +HEAT_FROM_AC - sensible heat flux put into canyon due to heat removed from air conditioning W/m^2 T +HIA - 2 m NWS Heat Index C T +HIA_R - Rural 2 m NWS Heat Index C T +HIA_U - Urban 2 m NWS Heat Index C T +HR - total heterotrophic respiration gC/m^2/s T +HTOP - canopy top m T +HUI - Crop patch heat unit index ddays F +HUMIDEX - 2 m Humidex C T +HUMIDEX_R - Rural 2 m Humidex C T +HUMIDEX_U - Urban 2 m Humidex C T +ICE_CONTENT1 - initial gridcell total ice content mm T +ICE_CONTENT2 - post land cover change total ice content mm F +ICE_MODEL_FRACTION - Ice sheet model fractional coverage unitless F +INIT_GPP - GPP flux before downregulation gC/m^2/s F +INT_SNOW - accumulated swe (natural vegetated and crop landunits only) mm F +INT_SNOW_ICE - accumulated swe (ice landunits only) mm F +IWUELN - local noon intrinsic water use efficiency umolCO2/molH2O T +JMX25T - canopy profile of jmax umol/m2/s T +Jmx25Z - maximum rate of electron transport at 25 Celcius for canopy layers umol electrons/m2/s T +KBM1 - natural logarithm of Z0MG_P/Z0HG_P unitless F +LAI240 - 240hr average of leaf area index m^2/m^2 F +LAISHA - shaded projected leaf area index m^2/m^2 T +LAISUN - sunlit projected leaf area index m^2/m^2 T +LAKEICEFRAC_SURF - surface lake layer ice mass fraction unitless T +LAKEICETHICK - thickness of lake ice (including physical expansion on freezing) m T +LAND_USE_FLUX - total C emitted from land cover conversion (smoothed over the year) and wood and grain product gC/m^2/s T +LATBASET - latitude vary base temperature for hui degree C F +LEAFC - leaf C gC/m^2 T +LEAFCN - Leaf CN ratio used for flexible CN gC/gN T +LEAFCN_OFFSET - Leaf C:N used by FUN unitless F +LEAFCN_STORAGE - Storage Leaf CN ratio used for flexible CN gC/gN F +LEAFC_ALLOC - leaf C allocation gC/m^2/s T +LEAFC_CHANGE - C change in leaf gC/m^2/s T +LEAFC_LOSS - leaf C loss gC/m^2/s T +LEAFC_STORAGE - leaf C storage gC/m^2 F +LEAFC_STORAGE_TO_XFER - leaf C shift storage to transfer gC/m^2/s F +LEAFC_STORAGE_XFER_ACC - Accumulated leaf C transfer gC/m^2 F +LEAFC_TO_BIOFUELC - leaf C to biofuel C gC/m^2/s T +LEAFC_TO_LITTER - leaf C litterfall gC/m^2/s F +LEAFC_TO_LITTER_FUN - leaf C litterfall used by FUN gC/m^2/s T +LEAFC_XFER - leaf C transfer gC/m^2 F +LEAFC_XFER_TO_LEAFC - leaf C growth from storage gC/m^2/s F +LEAFN - leaf N gN/m^2 T +LEAFN_STORAGE - leaf N storage gN/m^2 F +LEAFN_STORAGE_TO_XFER - leaf N shift storage to transfer gN/m^2/s F +LEAFN_STORAGE_XFER_ACC - Accmulated leaf N transfer gN/m^2 F +LEAFN_TO_LITTER - leaf N litterfall gN/m^2/s T +LEAFN_TO_RETRANSN - leaf N to retranslocated N pool gN/m^2/s F +LEAFN_XFER - leaf N transfer gN/m^2 F +LEAFN_XFER_TO_LEAFN - leaf N growth from storage gN/m^2/s F +LEAF_MR - leaf maintenance respiration gC/m^2/s T +LFC2 - conversion area fraction of BET and BDT that burned per sec T +LGSF - long growing season factor proportion F +LIQCAN - intercepted liquid water mm T +LIQUID_CONTENT1 - initial gridcell total liq content mm T +LIQUID_CONTENT2 - post landuse change gridcell total liq content mm F +LIQUID_WATER_TEMP1 - initial gridcell weighted average liquid water temperature K F +LITFALL - litterfall (leaves and fine roots) gC/m^2/s T +LITFIRE - litter fire losses gC/m^2/s F +LITTERC_HR - litter C heterotrophic respiration gC/m^2/s T +LITTERC_LOSS - litter C loss gC/m^2/s T +LIT_CEL_C - LIT_CEL C gC/m^2 T +LIT_CEL_C_1m - LIT_CEL C to 1 meter gC/m^2 F +LIT_CEL_C_TO_SOM_ACT_C - decomp. of cellulosic litter C to active soil organic C gC/m^2/s F +LIT_CEL_HR - Het. Resp. from cellulosic litter gC/m^2/s F +LIT_CEL_N - LIT_CEL N gN/m^2 T +LIT_CEL_N_1m - LIT_CEL N to 1 meter gN/m^2 F +LIT_CEL_N_TO_SOM_ACT_N - decomp. of cellulosic litter N to active soil organic N gN/m^2 F +LIT_LIG_C - LIT_LIG C gC/m^2 T +LIT_LIG_C_1m - LIT_LIG C to 1 meter gC/m^2 F +LIT_LIG_C_TO_SOM_SLO_C - decomp. of lignin litter C to slow soil organic ma C gC/m^2/s F +LIT_LIG_HR - Het. Resp. from lignin litter gC/m^2/s F +LIT_LIG_N - LIT_LIG N gN/m^2 T +LIT_LIG_N_1m - LIT_LIG N to 1 meter gN/m^2 F +LIT_LIG_N_TO_SOM_SLO_N - decomp. of lignin litter N to slow soil organic ma N gN/m^2 F +LIT_MET_C - LIT_MET C gC/m^2 T +LIT_MET_C_1m - LIT_MET C to 1 meter gC/m^2 F +LIT_MET_C_TO_SOM_ACT_C - decomp. of metabolic litter C to active soil organic C gC/m^2/s F +LIT_MET_HR - Het. Resp. from metabolic litter gC/m^2/s F +LIT_MET_N - LIT_MET N gN/m^2 T +LIT_MET_N_1m - LIT_MET N to 1 meter gN/m^2 F +LIT_MET_N_TO_SOM_ACT_N - decomp. of metabolic litter N to active soil organic N gN/m^2 F +LIVECROOTC - live coarse root C gC/m^2 T +LIVECROOTC_STORAGE - live coarse root C storage gC/m^2 F +LIVECROOTC_STORAGE_TO_XFER - live coarse root C shift storage to transfer gC/m^2/s F +LIVECROOTC_TO_DEADCROOTC - live coarse root C turnover gC/m^2/s F +LIVECROOTC_XFER - live coarse root C transfer gC/m^2 F +LIVECROOTC_XFER_TO_LIVECROOTC - live coarse root C growth from storage gC/m^2/s F +LIVECROOTN - live coarse root N gN/m^2 T +LIVECROOTN_STORAGE - live coarse root N storage gN/m^2 F +LIVECROOTN_STORAGE_TO_XFER - live coarse root N shift storage to transfer gN/m^2/s F +LIVECROOTN_TO_DEADCROOTN - live coarse root N turnover gN/m^2/s F +LIVECROOTN_TO_RETRANSN - live coarse root N to retranslocated N pool gN/m^2/s F +LIVECROOTN_XFER - live coarse root N transfer gN/m^2 F +LIVECROOTN_XFER_TO_LIVECROOTN - live coarse root N growth from storage gN/m^2/s F +LIVECROOT_MR - live coarse root maintenance respiration gC/m^2/s F +LIVESTEMC - live stem C gC/m^2 T +LIVESTEMC_STORAGE - live stem C storage gC/m^2 F +LIVESTEMC_STORAGE_TO_XFER - live stem C shift storage to transfer gC/m^2/s F +LIVESTEMC_TO_BIOFUELC - livestem C to biofuel C gC/m^2/s T +LIVESTEMC_TO_DEADSTEMC - live stem C turnover gC/m^2/s F +LIVESTEMC_XFER - live stem C transfer gC/m^2 F +LIVESTEMC_XFER_TO_LIVESTEMC - live stem C growth from storage gC/m^2/s F +LIVESTEMN - live stem N gN/m^2 T +LIVESTEMN_STORAGE - live stem N storage gN/m^2 F +LIVESTEMN_STORAGE_TO_XFER - live stem N shift storage to transfer gN/m^2/s F +LIVESTEMN_TO_DEADSTEMN - live stem N turnover gN/m^2/s F +LIVESTEMN_TO_RETRANSN - live stem N to retranslocated N pool gN/m^2/s F +LIVESTEMN_XFER - live stem N transfer gN/m^2 F +LIVESTEMN_XFER_TO_LIVESTEMN - live stem N growth from storage gN/m^2/s F +LIVESTEM_MR - live stem maintenance respiration gC/m^2/s F +LNC - leaf N concentration gN leaf/m^2 T +LWdown - atmospheric longwave radiation (downscaled to columns in glacier regions) W/m^2 F +LWup - upwelling longwave radiation W/m^2 F +MEG_acetaldehyde - MEGAN flux kg/m2/sec T +MEG_acetic_acid - MEGAN flux kg/m2/sec T +MEG_acetone - MEGAN flux kg/m2/sec T +MEG_carene_3 - MEGAN flux kg/m2/sec T +MEG_ethanol - MEGAN flux kg/m2/sec T +MEG_formaldehyde - MEGAN flux kg/m2/sec T +MEG_isoprene - MEGAN flux kg/m2/sec T +MEG_methanol - MEGAN flux kg/m2/sec T +MEG_pinene_a - MEGAN flux kg/m2/sec T +MEG_thujene_a - MEGAN flux kg/m2/sec T +MR - maintenance respiration gC/m^2/s T +M_CWD_C_TO_FIRE - coarse woody debris C fire loss gC/m^2/s F +M_CWD_N_TO_FIRE - coarse woody debris N fire loss gN/m^2 F +M_DEADCROOTC_STORAGE_TO_LITTER - dead coarse root C storage mortality gC/m^2/s F +M_DEADCROOTC_STORAGE_TO_LITTER_FIRE - dead coarse root C storage fire mortality to litter gC/m^2/s F +M_DEADCROOTC_TO_LITTER - dead coarse root C mortality gC/m^2/s F +M_DEADCROOTC_XFER_TO_LITTER - dead coarse root C transfer mortality gC/m^2/s F +M_DEADCROOTN_STORAGE_TO_FIRE - dead coarse root N storage fire loss gN/m^2/s F +M_DEADCROOTN_STORAGE_TO_LITTER - dead coarse root N storage mortality gN/m^2/s F +M_DEADCROOTN_TO_FIRE - dead coarse root N fire loss gN/m^2/s F +M_DEADCROOTN_TO_LITTER - dead coarse root N mortality gN/m^2/s F +M_DEADCROOTN_TO_LITTER_FIRE - dead coarse root N fire mortality to litter gN/m^2/s F +M_DEADCROOTN_XFER_TO_FIRE - dead coarse root N transfer fire loss gN/m^2/s F +M_DEADCROOTN_XFER_TO_LITTER - dead coarse root N transfer mortality gN/m^2/s F +M_DEADROOTC_STORAGE_TO_FIRE - dead root C storage fire loss gC/m^2/s F +M_DEADROOTC_STORAGE_TO_LITTER_FIRE - dead root C storage fire mortality to litter gC/m^2/s F +M_DEADROOTC_TO_FIRE - dead root C fire loss gC/m^2/s F +M_DEADROOTC_TO_LITTER_FIRE - dead root C fire mortality to litter gC/m^2/s F +M_DEADROOTC_XFER_TO_FIRE - dead root C transfer fire loss gC/m^2/s F +M_DEADROOTC_XFER_TO_LITTER_FIRE - dead root C transfer fire mortality to litter gC/m^2/s F +M_DEADSTEMC_STORAGE_TO_FIRE - dead stem C storage fire loss gC/m^2/s F +M_DEADSTEMC_STORAGE_TO_LITTER - dead stem C storage mortality gC/m^2/s F +M_DEADSTEMC_STORAGE_TO_LITTER_FIRE - dead stem C storage fire mortality to litter gC/m^2/s F +M_DEADSTEMC_TO_FIRE - dead stem C fire loss gC/m^2/s F +M_DEADSTEMC_TO_LITTER - dead stem C mortality gC/m^2/s F +M_DEADSTEMC_TO_LITTER_FIRE - dead stem C fire mortality to litter gC/m^2/s F +M_DEADSTEMC_XFER_TO_FIRE - dead stem C transfer fire loss gC/m^2/s F +M_DEADSTEMC_XFER_TO_LITTER - dead stem C transfer mortality gC/m^2/s F +M_DEADSTEMC_XFER_TO_LITTER_FIRE - dead stem C transfer fire mortality to litter gC/m^2/s F +M_DEADSTEMN_STORAGE_TO_FIRE - dead stem N storage fire loss gN/m^2/s F +M_DEADSTEMN_STORAGE_TO_LITTER - dead stem N storage mortality gN/m^2/s F +M_DEADSTEMN_TO_FIRE - dead stem N fire loss gN/m^2/s F +M_DEADSTEMN_TO_LITTER - dead stem N mortality gN/m^2/s F +M_DEADSTEMN_TO_LITTER_FIRE - dead stem N fire mortality to litter gN/m^2/s F +M_DEADSTEMN_XFER_TO_FIRE - dead stem N transfer fire loss gN/m^2/s F +M_DEADSTEMN_XFER_TO_LITTER - dead stem N transfer mortality gN/m^2/s F +M_FROOTC_STORAGE_TO_FIRE - fine root C storage fire loss gC/m^2/s F +M_FROOTC_STORAGE_TO_LITTER - fine root C storage mortality gC/m^2/s F +M_FROOTC_STORAGE_TO_LITTER_FIRE - fine root C storage fire mortality to litter gC/m^2/s F +M_FROOTC_TO_FIRE - fine root C fire loss gC/m^2/s F +M_FROOTC_TO_LITTER - fine root C mortality gC/m^2/s F +M_FROOTC_TO_LITTER_FIRE - fine root C fire mortality to litter gC/m^2/s F +M_FROOTC_XFER_TO_FIRE - fine root C transfer fire loss gC/m^2/s F +M_FROOTC_XFER_TO_LITTER - fine root C transfer mortality gC/m^2/s F +M_FROOTC_XFER_TO_LITTER_FIRE - fine root C transfer fire mortality to litter gC/m^2/s F +M_FROOTN_STORAGE_TO_FIRE - fine root N storage fire loss gN/m^2/s F +M_FROOTN_STORAGE_TO_LITTER - fine root N storage mortality gN/m^2/s F +M_FROOTN_TO_FIRE - fine root N fire loss gN/m^2/s F +M_FROOTN_TO_LITTER - fine root N mortality gN/m^2/s F +M_FROOTN_XFER_TO_FIRE - fine root N transfer fire loss gN/m^2/s F +M_FROOTN_XFER_TO_LITTER - fine root N transfer mortality gN/m^2/s F +M_GRESP_STORAGE_TO_FIRE - growth respiration storage fire loss gC/m^2/s F +M_GRESP_STORAGE_TO_LITTER - growth respiration storage mortality gC/m^2/s F +M_GRESP_STORAGE_TO_LITTER_FIRE - growth respiration storage fire mortality to litter gC/m^2/s F +M_GRESP_XFER_TO_FIRE - growth respiration transfer fire loss gC/m^2/s F +M_GRESP_XFER_TO_LITTER - growth respiration transfer mortality gC/m^2/s F +M_GRESP_XFER_TO_LITTER_FIRE - growth respiration transfer fire mortality to litter gC/m^2/s F +M_LEAFC_STORAGE_TO_FIRE - leaf C storage fire loss gC/m^2/s F +M_LEAFC_STORAGE_TO_LITTER - leaf C storage mortality gC/m^2/s F +M_LEAFC_STORAGE_TO_LITTER_FIRE - leaf C fire mortality to litter gC/m^2/s F +M_LEAFC_TO_FIRE - leaf C fire loss gC/m^2/s F +M_LEAFC_TO_LITTER - leaf C mortality gC/m^2/s F +M_LEAFC_TO_LITTER_FIRE - leaf C fire mortality to litter gC/m^2/s F +M_LEAFC_XFER_TO_FIRE - leaf C transfer fire loss gC/m^2/s F +M_LEAFC_XFER_TO_LITTER - leaf C transfer mortality gC/m^2/s F +M_LEAFC_XFER_TO_LITTER_FIRE - leaf C transfer fire mortality to litter gC/m^2/s F +M_LEAFN_STORAGE_TO_FIRE - leaf N storage fire loss gN/m^2/s F +M_LEAFN_STORAGE_TO_LITTER - leaf N storage mortality gN/m^2/s F +M_LEAFN_TO_FIRE - leaf N fire loss gN/m^2/s F +M_LEAFN_TO_LITTER - leaf N mortality gN/m^2/s F +M_LEAFN_XFER_TO_FIRE - leaf N transfer fire loss gN/m^2/s F +M_LEAFN_XFER_TO_LITTER - leaf N transfer mortality gN/m^2/s F +M_LIT_CEL_C_TO_FIRE - cellulosic litter C fire loss gC/m^2/s F +M_LIT_CEL_C_TO_LEACHING - cellulosic litter C leaching loss gC/m^2/s F +M_LIT_CEL_N_TO_FIRE - cellulosic litter N fire loss gN/m^2 F +M_LIT_CEL_N_TO_LEACHING - cellulosic litter N leaching loss gN/m^2/s F +M_LIT_LIG_C_TO_FIRE - lignin litter C fire loss gC/m^2/s F +M_LIT_LIG_C_TO_LEACHING - lignin litter C leaching loss gC/m^2/s F +M_LIT_LIG_N_TO_FIRE - lignin litter N fire loss gN/m^2 F +M_LIT_LIG_N_TO_LEACHING - lignin litter N leaching loss gN/m^2/s F +M_LIT_MET_C_TO_FIRE - metabolic litter C fire loss gC/m^2/s F +M_LIT_MET_C_TO_LEACHING - metabolic litter C leaching loss gC/m^2/s F +M_LIT_MET_N_TO_FIRE - metabolic litter N fire loss gN/m^2 F +M_LIT_MET_N_TO_LEACHING - metabolic litter N leaching loss gN/m^2/s F +M_LIVECROOTC_STORAGE_TO_LITTER - live coarse root C storage mortality gC/m^2/s F +M_LIVECROOTC_STORAGE_TO_LITTER_FIRE - live coarse root C fire mortality to litter gC/m^2/s F +M_LIVECROOTC_TO_LITTER - live coarse root C mortality gC/m^2/s F +M_LIVECROOTC_XFER_TO_LITTER - live coarse root C transfer mortality gC/m^2/s F +M_LIVECROOTN_STORAGE_TO_FIRE - live coarse root N storage fire loss gN/m^2/s F +M_LIVECROOTN_STORAGE_TO_LITTER - live coarse root N storage mortality gN/m^2/s F +M_LIVECROOTN_TO_FIRE - live coarse root N fire loss gN/m^2/s F +M_LIVECROOTN_TO_LITTER - live coarse root N mortality gN/m^2/s F +M_LIVECROOTN_XFER_TO_FIRE - live coarse root N transfer fire loss gN/m^2/s F +M_LIVECROOTN_XFER_TO_LITTER - live coarse root N transfer mortality gN/m^2/s F +M_LIVEROOTC_STORAGE_TO_FIRE - live root C storage fire loss gC/m^2/s F +M_LIVEROOTC_STORAGE_TO_LITTER_FIRE - live root C storage fire mortality to litter gC/m^2/s F +M_LIVEROOTC_TO_DEADROOTC_FIRE - live root C fire mortality to dead root C gC/m^2/s F +M_LIVEROOTC_TO_FIRE - live root C fire loss gC/m^2/s F +M_LIVEROOTC_TO_LITTER_FIRE - live root C fire mortality to litter gC/m^2/s F +M_LIVEROOTC_XFER_TO_FIRE - live root C transfer fire loss gC/m^2/s F +M_LIVEROOTC_XFER_TO_LITTER_FIRE - live root C transfer fire mortality to litter gC/m^2/s F +M_LIVESTEMC_STORAGE_TO_FIRE - live stem C storage fire loss gC/m^2/s F +M_LIVESTEMC_STORAGE_TO_LITTER - live stem C storage mortality gC/m^2/s F +M_LIVESTEMC_STORAGE_TO_LITTER_FIRE - live stem C storage fire mortality to litter gC/m^2/s F +M_LIVESTEMC_TO_DEADSTEMC_FIRE - live stem C fire mortality to dead stem C gC/m^2/s F +M_LIVESTEMC_TO_FIRE - live stem C fire loss gC/m^2/s F +M_LIVESTEMC_TO_LITTER - live stem C mortality gC/m^2/s F +M_LIVESTEMC_TO_LITTER_FIRE - live stem C fire mortality to litter gC/m^2/s F +M_LIVESTEMC_XFER_TO_FIRE - live stem C transfer fire loss gC/m^2/s F +M_LIVESTEMC_XFER_TO_LITTER - live stem C transfer mortality gC/m^2/s F +M_LIVESTEMC_XFER_TO_LITTER_FIRE - live stem C transfer fire mortality to litter gC/m^2/s F +M_LIVESTEMN_STORAGE_TO_FIRE - live stem N storage fire loss gN/m^2/s F +M_LIVESTEMN_STORAGE_TO_LITTER - live stem N storage mortality gN/m^2/s F +M_LIVESTEMN_TO_FIRE - live stem N fire loss gN/m^2/s F +M_LIVESTEMN_TO_LITTER - live stem N mortality gN/m^2/s F +M_LIVESTEMN_XFER_TO_FIRE - live stem N transfer fire loss gN/m^2/s F +M_LIVESTEMN_XFER_TO_LITTER - live stem N transfer mortality gN/m^2/s F +M_RETRANSN_TO_FIRE - retranslocated N pool fire loss gN/m^2/s F +M_RETRANSN_TO_LITTER - retranslocated N pool mortality gN/m^2/s F +M_SOM_ACT_C_TO_LEACHING - active soil organic C leaching loss gC/m^2/s F +M_SOM_ACT_N_TO_LEACHING - active soil organic N leaching loss gN/m^2/s F +M_SOM_PAS_C_TO_LEACHING - passive soil organic C leaching loss gC/m^2/s F +M_SOM_PAS_N_TO_LEACHING - passive soil organic N leaching loss gN/m^2/s F +M_SOM_SLO_C_TO_LEACHING - slow soil organic ma C leaching loss gC/m^2/s F +M_SOM_SLO_N_TO_LEACHING - slow soil organic ma N leaching loss gN/m^2/s F +NACTIVE - Mycorrhizal N uptake flux gN/m^2/s T +NACTIVE_NH4 - Mycorrhizal N uptake flux gN/m^2/s T +NACTIVE_NO3 - Mycorrhizal N uptake flux gN/m^2/s T +NAM - AM-associated N uptake flux gN/m^2/s T +NAM_NH4 - AM-associated N uptake flux gN/m^2/s T +NAM_NO3 - AM-associated N uptake flux gN/m^2/s T +NBP - net biome production, includes fire, landuse, harvest and hrv_xsmrpool flux (latter smoothed o gC/m^2/s T +NDEPLOY - total N deployed in new growth gN/m^2/s T +NDEP_TO_SMINN - atmospheric N deposition to soil mineral N gN/m^2/s T +NECM - ECM-associated N uptake flux gN/m^2/s T +NECM_NH4 - ECM-associated N uptake flux gN/m^2/s T +NECM_NO3 - ECM-associated N uptake flux gN/m^2/s T +NEE - net ecosystem exchange of carbon, includes fire and hrv_xsmrpool (latter smoothed over the yea gC/m^2/s T +NEM - Gridcell net adjustment to net carbon exchange passed to atm. for methane production gC/m2/s T +NEP - net ecosystem production, excludes fire, landuse, and harvest flux, positive for sink gC/m^2/s T +NET_NMIN - net rate of N mineralization gN/m^2/s T +NFERTILIZATION - fertilizer added gN/m^2/s T +NFIRE - fire counts valid only in Reg.C counts/km2/sec T +NFIX - Symbiotic BNF uptake flux gN/m^2/s T +NFIX_TO_SMINN - symbiotic/asymbiotic N fixation to soil mineral N gN/m^2/s F +NNONMYC - Non-mycorrhizal N uptake flux gN/m^2/s T +NNONMYC_NH4 - Non-mycorrhizal N uptake flux gN/m^2/s T +NNONMYC_NO3 - Non-mycorrhizal N uptake flux gN/m^2/s T +NPASSIVE - Passive N uptake flux gN/m^2/s T +NPOOL - temporary plant N pool gN/m^2 T +NPOOL_TO_DEADCROOTN - allocation to dead coarse root N gN/m^2/s F +NPOOL_TO_DEADCROOTN_STORAGE - allocation to dead coarse root N storage gN/m^2/s F +NPOOL_TO_DEADSTEMN - allocation to dead stem N gN/m^2/s F +NPOOL_TO_DEADSTEMN_STORAGE - allocation to dead stem N storage gN/m^2/s F +NPOOL_TO_FROOTN - allocation to fine root N gN/m^2/s F +NPOOL_TO_FROOTN_STORAGE - allocation to fine root N storage gN/m^2/s F +NPOOL_TO_LEAFN - allocation to leaf N gN/m^2/s F +NPOOL_TO_LEAFN_STORAGE - allocation to leaf N storage gN/m^2/s F +NPOOL_TO_LIVECROOTN - allocation to live coarse root N gN/m^2/s F +NPOOL_TO_LIVECROOTN_STORAGE - allocation to live coarse root N storage gN/m^2/s F +NPOOL_TO_LIVESTEMN - allocation to live stem N gN/m^2/s F +NPOOL_TO_LIVESTEMN_STORAGE - allocation to live stem N storage gN/m^2/s F +NPP - net primary production gC/m^2/s T +NPP_BURNEDOFF - C that cannot be used for N uptake gC/m^2/s F +NPP_GROWTH - Total C used for growth in FUN gC/m^2/s T +NPP_NACTIVE - Mycorrhizal N uptake used C gC/m^2/s T +NPP_NACTIVE_NH4 - Mycorrhizal N uptake use C gC/m^2/s T +NPP_NACTIVE_NO3 - Mycorrhizal N uptake used C gC/m^2/s T +NPP_NAM - AM-associated N uptake used C gC/m^2/s T +NPP_NAM_NH4 - AM-associated N uptake use C gC/m^2/s T +NPP_NAM_NO3 - AM-associated N uptake use C gC/m^2/s T +NPP_NECM - ECM-associated N uptake used C gC/m^2/s T +NPP_NECM_NH4 - ECM-associated N uptake use C gC/m^2/s T +NPP_NECM_NO3 - ECM-associated N uptake used C gC/m^2/s T +NPP_NFIX - Symbiotic BNF uptake used C gC/m^2/s T +NPP_NNONMYC - Non-mycorrhizal N uptake used C gC/m^2/s T +NPP_NNONMYC_NH4 - Non-mycorrhizal N uptake use C gC/m^2/s T +NPP_NNONMYC_NO3 - Non-mycorrhizal N uptake use C gC/m^2/s T +NPP_NRETRANS - Retranslocated N uptake flux gC/m^2/s T +NPP_NUPTAKE - Total C used by N uptake in FUN gC/m^2/s T +NRETRANS - Retranslocated N uptake flux gN/m^2/s T +NRETRANS_REG - Retranslocated N uptake flux gN/m^2/s T +NRETRANS_SEASON - Retranslocated N uptake flux gN/m^2/s T +NRETRANS_STRESS - Retranslocated N uptake flux gN/m^2/s T +NSUBSTEPS - number of adaptive timesteps in CLM timestep unitless F +NUPTAKE - Total N uptake of FUN gN/m^2/s T +NUPTAKE_NPP_FRACTION - frac of NPP used in N uptake - T +N_ALLOMETRY - N allocation index none F +OBU - Monin-Obukhov length m F +OCDEP - total OC deposition (dry+wet) from atmosphere kg/m^2/s T +OFFSET_COUNTER - offset days counter days F +OFFSET_FDD - offset freezing degree days counter C degree-days F +OFFSET_FLAG - offset flag none F +OFFSET_SWI - offset soil water index none F +ONSET_COUNTER - onset days counter days F +ONSET_FDD - onset freezing degree days counter C degree-days F +ONSET_FLAG - onset flag none F +ONSET_GDD - onset growing degree days C degree-days F +ONSET_GDDFLAG - onset flag for growing degree day sum none F +ONSET_SWI - onset soil water index none F +PAR240DZ - 10-day running mean of daytime patch absorbed PAR for leaves for top canopy layer W/m^2 F +PAR240XZ - 10-day running mean of maximum patch absorbed PAR for leaves for top canopy layer W/m^2 F +PAR240_shade - shade PAR (240 hrs) umol/m2/s F +PAR240_sun - sunlit PAR (240 hrs) umol/m2/s F +PAR24_shade - shade PAR (24 hrs) umol/m2/s F +PAR24_sun - sunlit PAR (24 hrs) umol/m2/s F +PARVEGLN - absorbed par by vegetation at local noon W/m^2 T +PAR_shade - shade PAR umol/m2/s F +PAR_sun - sunlit PAR umol/m2/s F +PBOT - atmospheric pressure at surface (downscaled to columns in glacier regions) Pa T +PBOT_240 - 10 day running mean of air pressure Pa F +PCH4 - atmospheric partial pressure of CH4 Pa T +PCO2 - atmospheric partial pressure of CO2 Pa T +PCO2_240 - 10 day running mean of CO2 pressure Pa F +PFT_CTRUNC - patch-level sink for C truncation gC/m^2 F +PFT_FIRE_CLOSS - total patch-level fire C loss for non-peat fires outside land-type converted region gC/m^2/s T +PFT_FIRE_NLOSS - total patch-level fire N loss gN/m^2/s T +PFT_NTRUNC - patch-level sink for N truncation gN/m^2 F +PLANTCN - Plant C:N used by FUN unitless F +PLANT_CALLOC - total allocated C flux gC/m^2/s F +PLANT_NALLOC - total allocated N flux gN/m^2/s F +PLANT_NDEMAND - N flux required to support initial GPP gN/m^2/s T +PNLCZ - Proportion of nitrogen allocated for light capture unitless F +PO2_240 - 10 day running mean of O2 pressure Pa F +POTENTIAL_IMMOB - potential N immobilization gN/m^2/s T +POT_F_DENIT - potential denitrification flux gN/m^2/s T +POT_F_NIT - potential nitrification flux gN/m^2/s T +PREC10 - 10-day running mean of PREC MM H2O/S F +PREC60 - 60-day running mean of PREC MM H2O/S F +PREV_DAYL - daylength from previous timestep s F +PREV_FROOTC_TO_LITTER - previous timestep froot C litterfall flux gC/m^2/s F +PREV_LEAFC_TO_LITTER - previous timestep leaf C litterfall flux gC/m^2/s F +PROD100C - 100-yr wood product C gC/m^2 F +PROD100C_LOSS - loss from 100-yr wood product pool gC/m^2/s F +PROD100N - 100-yr wood product N gN/m^2 F +PROD100N_LOSS - loss from 100-yr wood product pool gN/m^2/s F +PROD10C - 10-yr wood product C gC/m^2 F +PROD10C_LOSS - loss from 10-yr wood product pool gC/m^2/s F +PROD10N - 10-yr wood product N gN/m^2 F +PROD10N_LOSS - loss from 10-yr wood product pool gN/m^2/s F +PSNSHA - shaded leaf photosynthesis umolCO2/m^2/s T +PSNSHADE_TO_CPOOL - C fixation from shaded canopy gC/m^2/s T +PSNSUN - sunlit leaf photosynthesis umolCO2/m^2/s T +PSNSUN_TO_CPOOL - C fixation from sunlit canopy gC/m^2/s T +PSurf - atmospheric pressure at surface (downscaled to columns in glacier regions) Pa F +Q2M - 2m specific humidity kg/kg T +QAF - canopy air humidity kg/kg F +QBOT - atmospheric specific humidity (downscaled to columns in glacier regions) kg/kg T +QDIRECT_THROUGHFALL - direct throughfall of liquid (rain + above-canopy irrigation) mm/s F +QDIRECT_THROUGHFALL_SNOW - direct throughfall of snow mm/s F +QDRAI - sub-surface drainage mm/s T +QDRAI_PERCH - perched wt drainage mm/s T +QDRAI_XS - saturation excess drainage mm/s T +QDRIP - rate of excess canopy liquid falling off canopy mm/s F +QDRIP_SNOW - rate of excess canopy snow falling off canopy mm/s F +QFLOOD - runoff from river flooding mm/s T +QFLX_EVAP_TOT - qflx_evap_soi + qflx_evap_can + qflx_tran_veg kg m-2 s-1 T +QFLX_EVAP_VEG - vegetation evaporation mm H2O/s F +QFLX_ICE_DYNBAL - ice dynamic land cover change conversion runoff flux mm/s T +QFLX_LIQDEW_TO_TOP_LAYER - rate of liquid water deposited on top soil or snow layer (dew) mm H2O/s T +QFLX_LIQEVAP_FROM_TOP_LAYER - rate of liquid water evaporated from top soil or snow layer mm H2O/s T +QFLX_LIQ_DYNBAL - liq dynamic land cover change conversion runoff flux mm/s T +QFLX_LIQ_GRND - liquid (rain+irrigation) on ground after interception mm H2O/s F +QFLX_SNOW_DRAIN - drainage from snow pack mm/s T +QFLX_SNOW_DRAIN_ICE - drainage from snow pack melt (ice landunits only) mm/s T +QFLX_SNOW_GRND - snow on ground after interception mm H2O/s F +QFLX_SOLIDDEW_TO_TOP_LAYER - rate of solid water deposited on top soil or snow layer (frost) mm H2O/s T +QFLX_SOLIDEVAP_FROM_TOP_LAYER - rate of ice evaporated from top soil or snow layer (sublimation) (also includes bare ice subli mm H2O/s T +QFLX_SOLIDEVAP_FROM_TOP_LAYER_ICE - rate of ice evaporated from top soil or snow layer (sublimation) (also includes bare ice subli mm H2O/s F +QH2OSFC - surface water runoff mm/s T +QH2OSFC_TO_ICE - surface water converted to ice mm/s F +QHR - hydraulic redistribution mm/s T +QICE - ice growth/melt mm/s T +QICE_FRZ - ice growth mm/s T +QICE_MELT - ice melt mm/s T +QINFL - infiltration mm/s T +QINTR - interception mm/s T +QIRRIG_DEMAND - irrigation demand mm/s F +QIRRIG_DRIP - water added via drip irrigation mm/s F +QIRRIG_FROM_GW_CONFINED - water added through confined groundwater irrigation mm/s T +QIRRIG_FROM_GW_UNCONFINED - water added through unconfined groundwater irrigation mm/s T +QIRRIG_FROM_SURFACE - water added through surface water irrigation mm/s T +QIRRIG_SPRINKLER - water added via sprinkler irrigation mm/s F +QOVER - total surface runoff (includes QH2OSFC) mm/s T +QOVER_LAG - time-lagged surface runoff for soil columns mm/s F +QPHSNEG - net negative hydraulic redistribution flux mm/s F +QRGWL - surface runoff at glaciers (liquid only), wetlands, lakes; also includes melted ice runoff fro mm/s T +QRUNOFF - total liquid runoff not including correction for land use change mm/s T +QRUNOFF_ICE - total liquid runoff not incl corret for LULCC (ice landunits only) mm/s T +QRUNOFF_ICE_TO_COUPLER - total ice runoff sent to coupler (includes corrections for land use change) mm/s T +QRUNOFF_ICE_TO_LIQ - liquid runoff from converted ice runoff mm/s F +QRUNOFF_R - Rural total runoff mm/s F +QRUNOFF_TO_COUPLER - total liquid runoff sent to coupler (includes corrections for land use change) mm/s T +QRUNOFF_U - Urban total runoff mm/s F +QSNOCPLIQ - excess liquid h2o due to snow capping not including correction for land use change mm H2O/s T +QSNOEVAP - evaporation from snow (only when snl<0, otherwise it is equal to qflx_ev_soil) mm/s T +QSNOFRZ - column-integrated snow freezing rate kg/m2/s T +QSNOFRZ_ICE - column-integrated snow freezing rate (ice landunits only) mm/s T +QSNOMELT - snow melt rate mm/s T +QSNOMELT_ICE - snow melt (ice landunits only) mm/s T +QSNOUNLOAD - canopy snow unloading mm/s T +QSNO_TEMPUNLOAD - canopy snow temp unloading mm/s T +QSNO_WINDUNLOAD - canopy snow wind unloading mm/s T +QSNWCPICE - excess solid h2o due to snow capping not including correction for land use change mm H2O/s T +QSOIL - Ground evaporation (soil/snow evaporation + soil/snow sublimation - dew) mm/s T +QSOIL_ICE - Ground evaporation (ice landunits only) mm/s T +QTOPSOIL - water input to surface mm/s F +QVEGE - canopy evaporation mm/s T +QVEGT - canopy transpiration mm/s T +Qair - atmospheric specific humidity (downscaled to columns in glacier regions) kg/kg F +Qh - sensible heat W/m^2 F +Qle - total evaporation W/m^2 F +Qstor - storage heat flux (includes snowmelt) W/m^2 F +Qtau - momentum flux kg/m/s^2 F +RAH1 - aerodynamical resistance s/m F +RAH2 - aerodynamical resistance s/m F +RAIN - atmospheric rain, after rain/snow repartitioning based on temperature mm/s T +RAIN_FROM_ATM - atmospheric rain received from atmosphere (pre-repartitioning) mm/s T +RAIN_ICE - atmospheric rain, after rain/snow repartitioning based on temperature (ice landunits only) mm/s F +RAM1 - aerodynamical resistance s/m F +RAM_LAKE - aerodynamic resistance for momentum (lakes only) s/m F +RAW1 - aerodynamical resistance s/m F +RAW2 - aerodynamical resistance s/m F +RB - leaf boundary resistance s/m F +RB10 - 10 day running mean boundary layer resistance s/m F +RETRANSN - plant pool of retranslocated N gN/m^2 T +RETRANSN_TO_NPOOL - deployment of retranslocated N gN/m^2/s T +RH - atmospheric relative humidity % F +RH2M - 2m relative humidity % T +RH2M_R - Rural 2m specific humidity % F +RH2M_U - Urban 2m relative humidity % F +RH30 - 30-day running mean of relative humidity % F +RHAF - fractional humidity of canopy air fraction F +RHAF10 - 10 day running mean of fractional humidity of canopy air fraction F +RH_LEAF - fractional humidity at leaf surface fraction F +RR - root respiration (fine root MR + total root GR) gC/m^2/s T +RSSHA - shaded leaf stomatal resistance s/m T +RSSUN - sunlit leaf stomatal resistance s/m T +Rainf - atmospheric rain, after rain/snow repartitioning based on temperature mm/s F +Rnet - net radiation W/m^2 F +SABG - solar rad absorbed by ground W/m^2 T +SABG_PEN - Rural solar rad penetrating top soil or snow layer watt/m^2 T +SABV - solar rad absorbed by veg W/m^2 T +SEEDC - pool for seeding new PFTs via dynamic landcover gC/m^2 T +SEEDN - pool for seeding new PFTs via dynamic landcover gN/m^2 T +SLASH_HARVESTC - slash harvest carbon (to litter) gC/m^2/s T +SMINN - soil mineral N gN/m^2 T +SMINN_TO_NPOOL - deployment of soil mineral N uptake gN/m^2/s T +SMINN_TO_PLANT - plant uptake of soil mineral N gN/m^2/s T +SMINN_TO_PLANT_FUN - Total soil N uptake of FUN gN/m^2/s T +SMINN_TO_S1N_L1 - mineral N flux for decomp. of LIT_METto SOM_ACT gN/m^2 F +SMINN_TO_S1N_L2 - mineral N flux for decomp. of LIT_CELto SOM_ACT gN/m^2 F +SMINN_TO_S1N_S2 - mineral N flux for decomp. of SOM_SLOto SOM_ACT gN/m^2 F +SMINN_TO_S1N_S3 - mineral N flux for decomp. of SOM_PASto SOM_ACT gN/m^2 F +SMINN_TO_S2N_L3 - mineral N flux for decomp. of LIT_LIGto SOM_SLO gN/m^2 F +SMINN_TO_S2N_S1 - mineral N flux for decomp. of SOM_ACTto SOM_SLO gN/m^2 F +SMINN_TO_S3N_S1 - mineral N flux for decomp. of SOM_ACTto SOM_PAS gN/m^2 F +SMINN_TO_S3N_S2 - mineral N flux for decomp. of SOM_SLOto SOM_PAS gN/m^2 F +SMIN_NH4 - soil mineral NH4 gN/m^2 T +SMIN_NO3 - soil mineral NO3 gN/m^2 T +SMIN_NO3_LEACHED - soil NO3 pool loss to leaching gN/m^2/s T +SMIN_NO3_RUNOFF - soil NO3 pool loss to runoff gN/m^2/s T +SNOBCMCL - mass of BC in snow column kg/m2 T +SNOBCMSL - mass of BC in top snow layer kg/m2 T +SNOCAN - intercepted snow mm T +SNODSTMCL - mass of dust in snow column kg/m2 T +SNODSTMSL - mass of dust in top snow layer kg/m2 T +SNOFSDSND - direct nir incident solar radiation on snow W/m^2 F +SNOFSDSNI - diffuse nir incident solar radiation on snow W/m^2 F +SNOFSDSVD - direct vis incident solar radiation on snow W/m^2 F +SNOFSDSVI - diffuse vis incident solar radiation on snow W/m^2 F +SNOFSRND - direct nir reflected solar radiation from snow W/m^2 T +SNOFSRNI - diffuse nir reflected solar radiation from snow W/m^2 T +SNOFSRVD - direct vis reflected solar radiation from snow W/m^2 T +SNOFSRVI - diffuse vis reflected solar radiation from snow W/m^2 T +SNOINTABS - Fraction of incoming solar absorbed by lower snow layers - T +SNOLIQFL - top snow layer liquid water fraction (land) fraction F +SNOMELT_ACCUM - accumulated snow melt for z0 m T +SNOOCMCL - mass of OC in snow column kg/m2 T +SNOOCMSL - mass of OC in top snow layer kg/m2 T +SNORDSL - top snow layer effective grain radius m^-6 F +SNOTTOPL - snow temperature (top layer) K F +SNOTTOPL_ICE - snow temperature (top layer, ice landunits only) K F +SNOTXMASS - snow temperature times layer mass, layer sum; to get mass-weighted temperature, divide by (SNO K kg/m2 T +SNOTXMASS_ICE - snow temperature times layer mass, layer sum (ice landunits only); to get mass-weighted temper K kg/m2 F +SNOW - atmospheric snow, after rain/snow repartitioning based on temperature mm/s T +SNOWDP - gridcell mean snow height m T +SNOWICE - snow ice kg/m2 T +SNOWICE_ICE - snow ice (ice landunits only) kg/m2 F +SNOWLIQ - snow liquid water kg/m2 T +SNOWLIQ_ICE - snow liquid water (ice landunits only) kg/m2 F +SNOW_5D - 5day snow avg m F +SNOW_DEPTH - snow height of snow covered area m T +SNOW_DEPTH_ICE - snow height of snow covered area (ice landunits only) m F +SNOW_FROM_ATM - atmospheric snow received from atmosphere (pre-repartitioning) mm/s T +SNOW_ICE - atmospheric snow, after rain/snow repartitioning based on temperature (ice landunits only) mm/s F +SNOW_PERSISTENCE - Length of time of continuous snow cover (nat. veg. landunits only) seconds T +SNOW_SINKS - snow sinks (liquid water) mm/s T +SNOW_SOURCES - snow sources (liquid water) mm/s T +SNOdTdzL - top snow layer temperature gradient (land) K/m F +SOIL10 - 10-day running mean of 12cm layer soil K F +SOILC_CHANGE - C change in soil gC/m^2/s T +SOILC_HR - soil C heterotrophic respiration gC/m^2/s T +SOILRESIS - soil resistance to evaporation s/m T +SOILWATER_10CM - soil liquid water + ice in top 10cm of soil (veg landunits only) kg/m2 T +SOMC_FIRE - C loss due to peat burning gC/m^2/s T +SOMFIRE - soil organic matter fire losses gC/m^2/s F +SOM_ACT_C - SOM_ACT C gC/m^2 T +SOM_ACT_C_1m - SOM_ACT C to 1 meter gC/m^2 F +SOM_ACT_C_TO_SOM_PAS_C - decomp. of active soil organic C to passive soil organic C gC/m^2/s F +SOM_ACT_C_TO_SOM_SLO_C - decomp. of active soil organic C to slow soil organic ma C gC/m^2/s F +SOM_ACT_HR_S2 - Het. Resp. from active soil organic gC/m^2/s F +SOM_ACT_HR_S3 - Het. Resp. from active soil organic gC/m^2/s F +SOM_ACT_N - SOM_ACT N gN/m^2 T +SOM_ACT_N_1m - SOM_ACT N to 1 meter gN/m^2 F +SOM_ACT_N_TO_SOM_PAS_N - decomp. of active soil organic N to passive soil organic N gN/m^2 F +SOM_ACT_N_TO_SOM_SLO_N - decomp. of active soil organic N to slow soil organic ma N gN/m^2 F +SOM_C_LEACHED - total flux of C from SOM pools due to leaching gC/m^2/s T +SOM_N_LEACHED - total flux of N from SOM pools due to leaching gN/m^2/s F +SOM_PAS_C - SOM_PAS C gC/m^2 T +SOM_PAS_C_1m - SOM_PAS C to 1 meter gC/m^2 F +SOM_PAS_C_TO_SOM_ACT_C - decomp. of passive soil organic C to active soil organic C gC/m^2/s F +SOM_PAS_HR - Het. Resp. from passive soil organic gC/m^2/s F +SOM_PAS_N - SOM_PAS N gN/m^2 T +SOM_PAS_N_1m - SOM_PAS N to 1 meter gN/m^2 F +SOM_PAS_N_TO_SOM_ACT_N - decomp. of passive soil organic N to active soil organic N gN/m^2 F +SOM_SLO_C - SOM_SLO C gC/m^2 T +SOM_SLO_C_1m - SOM_SLO C to 1 meter gC/m^2 F +SOM_SLO_C_TO_SOM_ACT_C - decomp. of slow soil organic ma C to active soil organic C gC/m^2/s F +SOM_SLO_C_TO_SOM_PAS_C - decomp. of slow soil organic ma C to passive soil organic C gC/m^2/s F +SOM_SLO_HR_S1 - Het. Resp. from slow soil organic ma gC/m^2/s F +SOM_SLO_HR_S3 - Het. Resp. from slow soil organic ma gC/m^2/s F +SOM_SLO_N - SOM_SLO N gN/m^2 T +SOM_SLO_N_1m - SOM_SLO N to 1 meter gN/m^2 F +SOM_SLO_N_TO_SOM_ACT_N - decomp. of slow soil organic ma N to active soil organic N gN/m^2 F +SOM_SLO_N_TO_SOM_PAS_N - decomp. of slow soil organic ma N to passive soil organic N gN/m^2 F +SR - total soil respiration (HR + root resp) gC/m^2/s T +SSRE_FSR - surface snow effect on reflected solar radiation W/m^2 T +SSRE_FSRND - surface snow effect on direct nir reflected solar radiation W/m^2 T +SSRE_FSRNDLN - surface snow effect on direct nir reflected solar radiation at local noon W/m^2 T +SSRE_FSRNI - surface snow effect on diffuse nir reflected solar radiation W/m^2 T +SSRE_FSRVD - surface snow radiatve effect on direct vis reflected solar radiation W/m^2 T +SSRE_FSRVDLN - surface snow radiatve effect on direct vis reflected solar radiation at local noon W/m^2 T +SSRE_FSRVI - surface snow radiatve effect on diffuse vis reflected solar radiation W/m^2 T +STORAGE_CDEMAND - C use from the C storage pool gC/m^2 F +STORAGE_GR - growth resp for growth sent to storage for later display gC/m^2/s F +STORAGE_NDEMAND - N demand during the offset period gN/m^2 F +STORVEGC - stored vegetation carbon, excluding cpool gC/m^2 T +STORVEGN - stored vegetation nitrogen gN/m^2 T +SUPPLEMENT_TO_SMINN - supplemental N supply gN/m^2/s T +SWBGT - 2 m Simplified Wetbulb Globe Temp C T +SWBGT_R - Rural 2 m Simplified Wetbulb Globe Temp C T +SWBGT_U - Urban 2 m Simplified Wetbulb Globe Temp C T +SWMP65 - 2 m Swamp Cooler Temp 65% Eff C T +SWMP65_R - Rural 2 m Swamp Cooler Temp 65% Eff C T +SWMP65_U - Urban 2 m Swamp Cooler Temp 65% Eff C T +SWMP80 - 2 m Swamp Cooler Temp 80% Eff C T +SWMP80_R - Rural 2 m Swamp Cooler Temp 80% Eff C T +SWMP80_U - Urban 2 m Swamp Cooler Temp 80% Eff C T +SWdown - atmospheric incident solar radiation W/m^2 F +SWup - upwelling shortwave radiation W/m^2 F +SoilAlpha - factor limiting ground evap unitless F +SoilAlpha_U - urban factor limiting ground evap unitless F +T10 - 10-day running mean of 2-m temperature K F +TAF - canopy air temperature K F +TAUX - zonal surface stress kg/m/s^2 T +TAUY - meridional surface stress kg/m/s^2 T +TBOT - atmospheric air temperature (downscaled to columns in glacier regions) K T +TBUILD - internal urban building air temperature K T +TBUILD_MAX - prescribed maximum interior building temperature K F +TEMPAVG_T2M - temporary average 2m air temperature K F +TEMPMAX_RETRANSN - temporary annual max of retranslocated N pool gN/m^2 F +TEMPSUM_POTENTIAL_GPP - temporary annual sum of potential GPP gC/m^2/yr F +TEQ - 2 m Equiv Temp K T +TEQ_R - Rural 2 m Equiv Temp K T +TEQ_U - Urban 2 m Equiv Temp K T +TFLOOR - floor temperature K F +TG - ground temperature K T +TG_ICE - ground temperature (ice landunits only) K F +TG_R - Rural ground temperature K F +TG_U - Urban ground temperature K F +TH2OSFC - surface water temperature K T +THBOT - atmospheric air potential temperature (downscaled to columns in glacier regions) K T +THIC - 2 m Temp Hum Index Comfort C T +THIC_R - Rural 2 m Temp Hum Index Comfort C T +THIC_U - Urban 2 m Temp Hum Index Comfort C T +THIP - 2 m Temp Hum Index Physiology C T +THIP_R - Rural 2 m Temp Hum Index Physiology C T +THIP_U - Urban 2 m Temp Hum Index Physiology C T +TKE1 - top lake level eddy thermal conductivity W/(mK) T +TLAI - total projected leaf area index m^2/m^2 T +TOPO_COL - column-level topographic height m F +TOPO_COL_ICE - column-level topographic height (ice landunits only) m F +TOPT - topt coefficient for VOC calc non F +TOTCOLC - total column carbon, incl veg and cpool but excl product pools gC/m^2 T +TOTCOLCH4 - total belowground CH4 (0 for non-lake special landunits in the absence of dynamic landunits) gC/m2 T +TOTCOLN - total column-level N, excluding product pools gN/m^2 T +TOTECOSYSC - total ecosystem carbon, incl veg but excl cpool and product pools gC/m^2 T +TOTECOSYSN - total ecosystem N, excluding product pools gN/m^2 T +TOTFIRE - total ecosystem fire losses gC/m^2/s F +TOTLITC - total litter carbon gC/m^2 T +TOTLITC_1m - total litter carbon to 1 meter depth gC/m^2 T +TOTLITN - total litter N gN/m^2 T +TOTLITN_1m - total litter N to 1 meter gN/m^2 T +TOTPFTC - total patch-level carbon, including cpool gC/m^2 T +TOTPFTN - total patch-level nitrogen gN/m^2 T +TOTSOILICE - vertically summed soil ice (veg landunits only) kg/m2 T +TOTSOILLIQ - vertically summed soil liquid water (veg landunits only) kg/m2 T +TOTSOMC - total soil organic matter carbon gC/m^2 T +TOTSOMC_1m - total soil organic matter carbon to 1 meter depth gC/m^2 T +TOTSOMN - total soil organic matter N gN/m^2 T +TOTSOMN_1m - total soil organic matter N to 1 meter gN/m^2 T +TOTVEGC - total vegetation carbon, excluding cpool gC/m^2 T +TOTVEGN - total vegetation nitrogen gN/m^2 T +TOT_WOODPRODC - total wood product C gC/m^2 T +TOT_WOODPRODC_LOSS - total loss from wood product pools gC/m^2/s T +TOT_WOODPRODN - total wood product N gN/m^2 T +TOT_WOODPRODN_LOSS - total loss from wood product pools gN/m^2/s T +TPU25T - canopy profile of tpu umol/m2/s T +TRAFFICFLUX - sensible heat flux from urban traffic W/m^2 F +TRANSFER_DEADCROOT_GR - dead coarse root growth respiration from storage gC/m^2/s F +TRANSFER_DEADSTEM_GR - dead stem growth respiration from storage gC/m^2/s F +TRANSFER_FROOT_GR - fine root growth respiration from storage gC/m^2/s F +TRANSFER_GR - growth resp for transfer growth displayed in this timestep gC/m^2/s F +TRANSFER_LEAF_GR - leaf growth respiration from storage gC/m^2/s F +TRANSFER_LIVECROOT_GR - live coarse root growth respiration from storage gC/m^2/s F +TRANSFER_LIVESTEM_GR - live stem growth respiration from storage gC/m^2/s F +TREFMNAV - daily minimum of average 2-m temperature K T +TREFMNAV_R - Rural daily minimum of average 2-m temperature K F +TREFMNAV_U - Urban daily minimum of average 2-m temperature K F +TREFMXAV - daily maximum of average 2-m temperature K T +TREFMXAV_R - Rural daily maximum of average 2-m temperature K F +TREFMXAV_U - Urban daily maximum of average 2-m temperature K F +TROOF_INNER - roof inside surface temperature K F +TSA - 2m air temperature K T +TSAI - total projected stem area index m^2/m^2 T +TSA_ICE - 2m air temperature (ice landunits only) K F +TSA_R - Rural 2m air temperature K F +TSA_U - Urban 2m air temperature K F +TSHDW_INNER - shadewall inside surface temperature K F +TSKIN - skin temperature K T +TSL - temperature of near-surface soil layer (natural vegetated and crop landunits only) K T +TSOI_10CM - soil temperature in top 10cm of soil K T +TSUNW_INNER - sunwall inside surface temperature K F +TV - vegetation temperature K T +TV24 - vegetation temperature (last 24hrs) K F +TV240 - vegetation temperature (last 240hrs) K F +TVEGD10 - 10 day running mean of patch daytime vegetation temperature Kelvin F +TVEGN10 - 10 day running mean of patch night-time vegetation temperature Kelvin F +TWS - total water storage mm T +Tair - atmospheric air temperature (downscaled to columns in glacier regions) K F +Tair_from_atm - atmospheric air temperature received from atmosphere (pre-downscaling) K F +U10 - 10-m wind m/s T +U10_DUST - 10-m wind for dust model m/s T +U10_ICE - 10-m wind (ice landunits only) m/s F +UAF - canopy air speed m/s F +ULRAD - upward longwave radiation above the canopy W/m^2 F +UM - wind speed plus stability effect m/s F +URBAN_AC - urban air conditioning flux W/m^2 T +URBAN_HEAT - urban heating flux W/m^2 T +USTAR - aerodynamical resistance s/m F +UST_LAKE - friction velocity (lakes only) m/s F +VA - atmospheric wind speed plus convective velocity m/s F +VCMX25T - canopy profile of vcmax25 umol/m2/s T +VENTILATION - sensible heat flux from building ventilation W/m^2 T +VOCFLXT - total VOC flux into atmosphere moles/m2/sec F +VOLR - river channel total water storage m3 T +VOLRMCH - river channel main channel water storage m3 T +VPD - vpd Pa F +VPD2M - 2m vapor pressure deficit Pa T +VPD_CAN - canopy vapor pressure deficit kPa T +Vcmx25Z - canopy profile of vcmax25 predicted by LUNA model umol/m2/s T +WASTEHEAT - sensible heat flux from heating/cooling sources of urban waste heat W/m^2 T +WBA - 2 m Wet Bulb C T +WBA_R - Rural 2 m Wet Bulb C T +WBA_U - Urban 2 m Wet Bulb C T +WBT - 2 m Stull Wet Bulb C T +WBT_R - Rural 2 m Stull Wet Bulb C T +WBT_U - Urban 2 m Stull Wet Bulb C T +WF - soil water as frac. of whc for top 0.05 m proportion F +WIND - atmospheric wind velocity magnitude m/s T +WOODC - wood C gC/m^2 T +WOODC_ALLOC - wood C eallocation gC/m^2/s T +WOODC_LOSS - wood C loss gC/m^2/s T +WOOD_HARVESTC - wood harvest carbon (to product pools) gC/m^2/s T +WOOD_HARVESTN - wood harvest N (to product pools) gN/m^2/s T +WTGQ - surface tracer conductance m/s T +Wind - atmospheric wind velocity magnitude m/s F +XSMRPOOL - temporary photosynthate C pool gC/m^2 T +XSMRPOOL_LOSS - temporary photosynthate C pool loss gC/m^2 F +XSMRPOOL_RECOVER - C flux assigned to recovery of negative xsmrpool gC/m^2/s T +Z0HG - roughness length over ground, sensible heat (vegetated landunits only) m F +Z0HG_P - patch roughness length over ground, sensible heat m F +Z0HV - roughness length over vegetation, sensible heat m F +Z0MG - roughness length over ground, momentum (vegetated landunits only) m F +Z0MG_P - patch roughness length over ground, momentum m F +Z0MV - roughness length over vegetation, momentum m F +Z0MV_DENSE - roughness length over vegetation, momentum, for dense canopy m F +Z0M_TO_COUPLER - roughness length, momentum: gridcell average sent to coupler m F +Z0QG - roughness length over ground, latent heat (vegetated landunits only) m F +Z0QG_P - patch roughness length over ground, latent heat m F +Z0QV - roughness length over vegetation, latent heat m F +ZBOT - atmospheric reference height m T +ZETA - dimensionless stability parameter unitless F +ZII - convective boundary height m F +ZWT - water table depth (natural vegetated and crop landunits only) m T +ZWT_CH4_UNSAT - depth of water table for methane production used in non-inundated area m T +ZWT_PERCH - perched water table depth (natural vegetated and crop landunits only) m T +currentPatch - currentPatch coefficient for VOC calc non F +num_iter - number of iterations unitless F +QICE_FORC elevclas qice forcing sent to GLC mm/s F +TOPO_FORC elevclas topograephic height sent to GLC m F +TSRF_FORC elevclas surface temperature sent to GLC K F +ACTUAL_IMMOB_NH4 levdcmp immobilization of NH4 gN/m^3/s F +ACTUAL_IMMOB_NO3 levdcmp immobilization of NO3 gN/m^3/s F +ACTUAL_IMMOB_vr levdcmp actual N immobilization gN/m^3/s F +CROOT_PROF levdcmp profile for litter C and N inputs from coarse roots 1/m F +CWD_C_TO_LIT_CEL_C_vr levdcmp decomp. of coarse woody debris C to cellulosic litter C gC/m^3/s F +CWD_C_TO_LIT_LIG_C_vr levdcmp decomp. of coarse woody debris C to lignin litter C gC/m^3/s F +CWD_HR_L2_vr levdcmp Het. Resp. from coarse woody debris gC/m^3/s F +CWD_HR_L3_vr levdcmp Het. Resp. from coarse woody debris gC/m^3/s F +CWD_N_TO_LIT_CEL_N_vr levdcmp decomp. of coarse woody debris N to cellulosic litter N gN/m^3 F +CWD_N_TO_LIT_LIG_N_vr levdcmp decomp. of coarse woody debris N to lignin litter N gN/m^3 F +CWD_N_vr levdcmp CWD N (vertically resolved) gN/m^3 T +CWD_PATHFRAC_L2_vr levdcmp PATHFRAC from coarse woody debris to cellulosic litter fraction F +CWD_PATHFRAC_L3_vr levdcmp PATHFRAC from coarse woody debris to lignin litter fraction F +CWD_RESP_FRAC_L2_vr levdcmp respired from coarse woody debris to cellulosic litter fraction F +CWD_RESP_FRAC_L3_vr levdcmp respired from coarse woody debris to lignin litter fraction F +DWT_DEADCROOTC_TO_CWDC levdcmp dead coarse root to CWD due to landcover change gC/m^2/s F +DWT_DEADCROOTN_TO_CWDN levdcmp dead coarse root to CWD due to landcover change gN/m^2/s F +DWT_FROOTC_TO_LIT_CEL_C levdcmp fine root to cellulosic litter due to landcover change gC/m^2/s F +DWT_FROOTC_TO_LIT_LIG_C levdcmp fine root to lignin litter due to landcover change gC/m^2/s F +DWT_FROOTC_TO_LIT_MET_C levdcmp fine root to metabolic litter due to landcover change gC/m^2/s F +DWT_FROOTN_TO_LIT_CEL_N levdcmp fine root N to cellulosic litter due to landcover change gN/m^2/s F +DWT_FROOTN_TO_LIT_LIG_N levdcmp fine root N to lignin litter due to landcover change gN/m^2/s F +DWT_FROOTN_TO_LIT_MET_N levdcmp fine root N to metabolic litter due to landcover change gN/m^2/s F +DWT_LIVECROOTC_TO_CWDC levdcmp live coarse root to CWD due to landcover change gC/m^2/s F +DWT_LIVECROOTN_TO_CWDN levdcmp live coarse root to CWD due to landcover change gN/m^2/s F +FMAX_DENIT_CARBONSUBSTRATE levdcmp FMAX_DENIT_CARBONSUBSTRATE gN/m^3/s F +FMAX_DENIT_NITRATE levdcmp FMAX_DENIT_NITRATE gN/m^3/s F +FPI_vr levdcmp fraction of potential immobilization proportion F +FROOT_PROF levdcmp profile for litter C and N inputs from fine roots 1/m F +F_DENIT_BASE levdcmp F_DENIT_BASE gN/m^3/s F +F_DENIT_vr levdcmp denitrification flux gN/m^3/s F +F_NIT_vr levdcmp nitrification flux gN/m^3/s F +GROSS_NMIN_vr levdcmp gross rate of N mineralization gN/m^3/s F +K_CWD levdcmp coarse woody debris potential loss coefficient 1/s F +K_LIT_CEL levdcmp cellulosic litter potential loss coefficient 1/s F +K_LIT_LIG levdcmp lignin litter potential loss coefficient 1/s F +K_LIT_MET levdcmp metabolic litter potential loss coefficient 1/s F +K_NITR levdcmp K_NITR 1/s F +K_NITR_H2O levdcmp K_NITR_H2O unitless F +K_NITR_PH levdcmp K_NITR_PH unitless F +K_NITR_T levdcmp K_NITR_T unitless F +K_SOM_ACT levdcmp active soil organic potential loss coefficient 1/s F +K_SOM_PAS levdcmp passive soil organic potential loss coefficient 1/s F +K_SOM_SLO levdcmp slow soil organic ma potential loss coefficient 1/s F +L1_PATHFRAC_S1_vr levdcmp PATHFRAC from metabolic litter to active soil organic fraction F +L1_RESP_FRAC_S1_vr levdcmp respired from metabolic litter to active soil organic fraction F +L2_PATHFRAC_S1_vr levdcmp PATHFRAC from cellulosic litter to active soil organic fraction F +L2_RESP_FRAC_S1_vr levdcmp respired from cellulosic litter to active soil organic fraction F +L3_PATHFRAC_S2_vr levdcmp PATHFRAC from lignin litter to slow soil organic ma fraction F +L3_RESP_FRAC_S2_vr levdcmp respired from lignin litter to slow soil organic ma fraction F +LEAF_PROF levdcmp profile for litter C and N inputs from leaves 1/m F +LIT_CEL_C_TNDNCY_VERT_TR levdcmp cellulosic litter C tendency due to vertical transport gC/m^3/s F +LIT_CEL_C_TO_SOM_ACT_C_v levdcmp decomp. of cellulosic litter C to active soil organic C gC/m^3/s F +LIT_CEL_HR_vr levdcmp Het. Resp. from cellulosic litter gC/m^3/s F +LIT_CEL_N_TNDNCY_VERT_TR levdcmp cellulosic litter N tendency due to vertical transport gN/m^3/s F +LIT_CEL_N_TO_SOM_ACT_N_v levdcmp decomp. of cellulosic litter N to active soil organic N gN/m^3 F +LIT_CEL_N_vr levdcmp LIT_CEL N (vertically resolved) gN/m^3 T +LIT_LIG_C_TNDNCY_VERT_TR levdcmp lignin litter C tendency due to vertical transport gC/m^3/s F +LIT_LIG_C_TO_SOM_SLO_C_v levdcmp decomp. of lignin litter C to slow soil organic ma C gC/m^3/s F +LIT_LIG_HR_vr levdcmp Het. Resp. from lignin litter gC/m^3/s F +LIT_LIG_N_TNDNCY_VERT_TR levdcmp lignin litter N tendency due to vertical transport gN/m^3/s F +LIT_LIG_N_TO_SOM_SLO_N_v levdcmp decomp. of lignin litter N to slow soil organic ma N gN/m^3 F +LIT_LIG_N_vr levdcmp LIT_LIG N (vertically resolved) gN/m^3 T +LIT_MET_C_TNDNCY_VERT_TR levdcmp metabolic litter C tendency due to vertical transport gC/m^3/s F +LIT_MET_C_TO_SOM_ACT_C_v levdcmp decomp. of metabolic litter C to active soil organic C gC/m^3/s F +LIT_MET_HR_vr levdcmp Het. Resp. from metabolic litter gC/m^3/s F +LIT_MET_N_TNDNCY_VERT_TR levdcmp metabolic litter N tendency due to vertical transport gN/m^3/s F +LIT_MET_N_TO_SOM_ACT_N_v levdcmp decomp. of metabolic litter N to active soil organic N gN/m^3 F +LIT_MET_N_vr levdcmp LIT_MET N (vertically resolved) gN/m^3 T +M_CWD_C_TO_FIRE_vr levdcmp coarse woody debris C fire loss gC/m^3/s F +M_CWD_N_TO_FIRE_vr levdcmp coarse woody debris N fire loss gN/m^3 F +M_LIT_CEL_C_TO_FIRE_vr levdcmp cellulosic litter C fire loss gC/m^3/s F +M_LIT_CEL_N_TO_FIRE_vr levdcmp cellulosic litter N fire loss gN/m^3 F +M_LIT_LIG_C_TO_FIRE_vr levdcmp lignin litter C fire loss gC/m^3/s F +M_LIT_LIG_N_TO_FIRE_vr levdcmp lignin litter N fire loss gN/m^3 F +M_LIT_MET_C_TO_FIRE_vr levdcmp metabolic litter C fire loss gC/m^3/s F +M_LIT_MET_N_TO_FIRE_vr levdcmp metabolic litter N fire loss gN/m^3 F +NDEP_PROF levdcmp profile for atmospheric N deposition 1/m F +NET_NMIN_vr levdcmp net rate of N mineralization gN/m^3/s F +NFIXATION_PROF levdcmp profile for biological N fixation 1/m F +POTENTIAL_IMMOB_vr levdcmp potential N immobilization gN/m^3/s F +POT_F_DENIT_vr levdcmp potential denitrification flux gN/m^3/s F +POT_F_NIT_vr levdcmp potential nitrification flux gN/m^3/s F +S1_PATHFRAC_S2_vr levdcmp PATHFRAC from active soil organic to slow soil organic ma fraction F +S1_PATHFRAC_S3_vr levdcmp PATHFRAC from active soil organic to passive soil organic fraction F +S1_RESP_FRAC_S2_vr levdcmp respired from active soil organic to slow soil organic ma fraction F +S1_RESP_FRAC_S3_vr levdcmp respired from active soil organic to passive soil organic fraction F +S2_PATHFRAC_S1_vr levdcmp PATHFRAC from slow soil organic ma to active soil organic fraction F +S2_PATHFRAC_S3_vr levdcmp PATHFRAC from slow soil organic ma to passive soil organic fraction F +S2_RESP_FRAC_S1_vr levdcmp respired from slow soil organic ma to active soil organic fraction F +S2_RESP_FRAC_S3_vr levdcmp respired from slow soil organic ma to passive soil organic fraction F +S3_PATHFRAC_S1_vr levdcmp PATHFRAC from passive soil organic to active soil organic fraction F +S3_RESP_FRAC_S1_vr levdcmp respired from passive soil organic to active soil organic fraction F +SMINN_TO_PLANT_vr levdcmp plant uptake of soil mineral N gN/m^3/s F +SMINN_TO_S1N_L1_vr levdcmp mineral N flux for decomp. of LIT_METto SOM_ACT gN/m^3 F +SMINN_TO_S1N_L2_vr levdcmp mineral N flux for decomp. of LIT_CELto SOM_ACT gN/m^3 F +SMINN_TO_S1N_S2_vr levdcmp mineral N flux for decomp. of SOM_SLOto SOM_ACT gN/m^3 F +SMINN_TO_S1N_S3_vr levdcmp mineral N flux for decomp. of SOM_PASto SOM_ACT gN/m^3 F +SMINN_TO_S2N_L3_vr levdcmp mineral N flux for decomp. of LIT_LIGto SOM_SLO gN/m^3 F +SMINN_TO_S2N_S1_vr levdcmp mineral N flux for decomp. of SOM_ACTto SOM_SLO gN/m^3 F +SMINN_TO_S3N_S1_vr levdcmp mineral N flux for decomp. of SOM_ACTto SOM_PAS gN/m^3 F +SMINN_TO_S3N_S2_vr levdcmp mineral N flux for decomp. of SOM_SLOto SOM_PAS gN/m^3 F +SMIN_NH4_TO_PLANT levdcmp plant uptake of NH4 gN/m^3/s F +SMIN_NO3_LEACHED_vr levdcmp soil NO3 pool loss to leaching gN/m^3/s F +SMIN_NO3_MASSDENS levdcmp SMIN_NO3_MASSDENS ugN/cm^3 soil F +SMIN_NO3_RUNOFF_vr levdcmp soil NO3 pool loss to runoff gN/m^3/s F +SMIN_NO3_TO_PLANT levdcmp plant uptake of NO3 gN/m^3/s F +SOILN_vr levdcmp SOIL N (vertically resolved) gN/m^3 T +SOM_ACT_C_TNDNCY_VERT_TR levdcmp active soil organic C tendency due to vertical transport gC/m^3/s F +SOM_ACT_C_TO_SOM_PAS_C_v levdcmp decomp. of active soil organic C to passive soil organic C gC/m^3/s F +SOM_ACT_C_TO_SOM_SLO_C_v levdcmp decomp. of active soil organic C to slow soil organic ma C gC/m^3/s F +SOM_ACT_HR_S2_vr levdcmp Het. Resp. from active soil organic gC/m^3/s F +SOM_ACT_HR_S3_vr levdcmp Het. Resp. from active soil organic gC/m^3/s F +SOM_ACT_N_TNDNCY_VERT_TR levdcmp active soil organic N tendency due to vertical transport gN/m^3/s F +SOM_ACT_N_TO_SOM_PAS_N_v levdcmp decomp. of active soil organic N to passive soil organic N gN/m^3 F +SOM_ACT_N_TO_SOM_SLO_N_v levdcmp decomp. of active soil organic N to slow soil organic ma N gN/m^3 F +SOM_ACT_N_vr levdcmp SOM_ACT N (vertically resolved) gN/m^3 T +SOM_ADV_COEF levdcmp advection term for vertical SOM translocation m/s F +SOM_DIFFUS_COEF levdcmp diffusion coefficient for vertical SOM translocation m^2/s F +SOM_PAS_C_TNDNCY_VERT_TR levdcmp passive soil organic C tendency due to vertical transport gC/m^3/s F +SOM_PAS_C_TO_SOM_ACT_C_v levdcmp decomp. of passive soil organic C to active soil organic C gC/m^3/s F +SOM_PAS_HR_vr levdcmp Het. Resp. from passive soil organic gC/m^3/s F +SOM_PAS_N_TNDNCY_VERT_TR levdcmp passive soil organic N tendency due to vertical transport gN/m^3/s F +SOM_PAS_N_TO_SOM_ACT_N_v levdcmp decomp. of passive soil organic N to active soil organic N gN/m^3 F +SOM_PAS_N_vr levdcmp SOM_PAS N (vertically resolved) gN/m^3 T +SOM_SLO_C_TNDNCY_VERT_TR levdcmp slow soil organic ma C tendency due to vertical transport gC/m^3/s F +SOM_SLO_C_TO_SOM_ACT_C_v levdcmp decomp. of slow soil organic ma C to active soil organic C gC/m^3/s F +SOM_SLO_C_TO_SOM_PAS_C_v levdcmp decomp. of slow soil organic ma C to passive soil organic C gC/m^3/s F +SOM_SLO_HR_S1_vr levdcmp Het. Resp. from slow soil organic ma gC/m^3/s F +SOM_SLO_HR_S3_vr levdcmp Het. Resp. from slow soil organic ma gC/m^3/s F +SOM_SLO_N_TNDNCY_VERT_TR levdcmp slow soil organic ma N tendency due to vertical transport gN/m^3/s F +SOM_SLO_N_TO_SOM_ACT_N_v levdcmp decomp. of slow soil organic ma N to active soil organic N gN/m^3 F +SOM_SLO_N_TO_SOM_PAS_N_v levdcmp decomp. of slow soil organic ma N to passive soil organic N gN/m^3 F +SOM_SLO_N_vr levdcmp SOM_SLO N (vertically resolved) gN/m^3 T +STEM_PROF levdcmp profile for litter C and N inputs from stems 1/m F +SUPPLEMENT_TO_SMINN_vr levdcmp supplemental N supply gN/m^3/s F +WFPS levdcmp WFPS percent F +anaerobic_frac levdcmp anaerobic_frac m3/m3 F +diffus levdcmp diffusivity (from nitrification-denitrification) m^2/s F +fr_WFPS levdcmp fr_WFPS fraction F +n2_n2o_ratio_denit levdcmp n2_n2o_ratio_denit gN/gN F +r_psi levdcmp r_psi m F +ratio_k1 levdcmp ratio_k1 none F +ratio_no3_co2 levdcmp ratio_no3_co2 ratio F +soil_bulkdensity levdcmp soil_bulkdensity kg/m3 F +soil_co2_prod levdcmp soil_co2_prod ug C / g soil / day F +CONC_CH4_SAT levgrnd CH4 soil Concentration for inundated / lake area mol/m3 F +CONC_CH4_UNSAT levgrnd CH4 soil Concentration for non-inundated area mol/m3 F +EFF_POROSITY levgrnd effective porosity = porosity - vol_ice proportion F +FGR_SOIL_R levgrnd Rural downward heat flux at interface below each soil layer watt/m^2 F +FRAC_ICEOLD levgrnd fraction of ice relative to the tot water proportion F +HK levgrnd hydraulic conductivity (natural vegetated and crop landunits only) mm/s F +O2_DECOMP_DEPTH_UNSAT levgrnd O2 consumption from HR and AR for non-inundated area mol/m3/s F +ROOTR levgrnd effective fraction of roots in each soil layer (SMS method) proportion F +RRESIS levgrnd root resistance in each soil layer proportion F +SMP levgrnd soil matric potential (natural vegetated and crop landunits only) mm T +SOILPSI levgrnd soil water potential in each soil layer MPa F +TSOI levgrnd soil temperature (natural vegetated and crop landunits only) K T +TSOI_ICE levgrnd soil temperature (ice landunits only) K T +bsw levgrnd clap and hornberger B unitless F +watfc levgrnd water field capacity m^3/m^3 F +watsat levgrnd water saturated m^3/m^3 F +LAKEICEFRAC levlak lake layer ice mass fraction unitless F +TLAKE levlak lake temperature K T +SNO_ABS levsno Absorbed solar radiation in each snow layer W/m^2 F +SNO_ABS_ICE levsno Absorbed solar radiation in each snow layer (ice landunits only) W/m^2 F +SNO_BW levsno Partial density of water in the snow pack (ice + liquid) kg/m3 F +SNO_BW_ICE levsno Partial density of water in the snow pack (ice + liquid, ice landunits only) kg/m3 F +SNO_EXISTENCE levsno Fraction of averaging period for which each snow layer existed unitless F +SNO_FRZ levsno snow freezing rate in each snow layer kg/m2/s F +SNO_FRZ_ICE levsno snow freezing rate in each snow layer (ice landunits only) mm/s F +SNO_GS levsno Mean snow grain size Microns F +SNO_GS_ICE levsno Mean snow grain size (ice landunits only) Microns F +SNO_ICE levsno Snow ice content kg/m2 F +SNO_LIQH2O levsno Snow liquid water content kg/m2 F +SNO_MELT levsno snow melt rate in each snow layer mm/s F +SNO_MELT_ICE levsno snow melt rate in each snow layer (ice landunits only) mm/s F +SNO_T levsno Snow temperatures K F +SNO_TK levsno Thermal conductivity W/m-K F +SNO_TK_ICE levsno Thermal conductivity (ice landunits only) W/m-K F +SNO_T_ICE levsno Snow temperatures (ice landunits only) K F +SNO_Z levsno Snow layer thicknesses m F +SNO_Z_ICE levsno Snow layer thicknesses (ice landunits only) m F +CONC_O2_SAT levsoi O2 soil Concentration for inundated / lake area mol/m3 T +CONC_O2_UNSAT levsoi O2 soil Concentration for non-inundated area mol/m3 T +CWD_C_vr levsoi CWD C (vertically resolved) gC/m^3 T +H2OSOI levsoi volumetric soil water (natural vegetated and crop landunits only) mm3/mm3 T +HR_vr levsoi total vertically resolved heterotrophic respiration gC/m^3/s T +KROOT levsoi root conductance each soil layer 1/s F +KSOIL levsoi soil conductance in each soil layer 1/s F +LIT_CEL_C_vr levsoi LIT_CEL C (vertically resolved) gC/m^3 T +LIT_LIG_C_vr levsoi LIT_LIG C (vertically resolved) gC/m^3 T +LIT_MET_C_vr levsoi LIT_MET C (vertically resolved) gC/m^3 T +O_SCALAR levsoi fraction by which decomposition is reduced due to anoxia unitless T +QROOTSINK levsoi water flux from soil to root in each soil-layer mm/s F +SMINN_vr levsoi soil mineral N gN/m^3 T +SMIN_NH4_vr levsoi soil mineral NH4 (vert. res.) gN/m^3 T +SMIN_NO3_vr levsoi soil mineral NO3 (vert. res.) gN/m^3 T +SOILC_vr levsoi SOIL C (vertically resolved) gC/m^3 T +SOILICE levsoi soil ice (natural vegetated and crop landunits only) kg/m2 T +SOILLIQ levsoi soil liquid water (natural vegetated and crop landunits only) kg/m2 T +SOM_ACT_C_vr levsoi SOM_ACT C (vertically resolved) gC/m^3 T +SOM_PAS_C_vr levsoi SOM_PAS C (vertically resolved) gC/m^3 T +SOM_SLO_C_vr levsoi SOM_SLO C (vertically resolved) gC/m^3 T +T_SCALAR levsoi temperature inhibition of decomposition unitless T +W_SCALAR levsoi Moisture (dryness) inhibition of decomposition unitless T +GDDACCUM_PERHARV mxharvests At-harvest accumulated growing degree days past planting date for crop; should only be output ddays F +GDDHARV_PERHARV mxharvests Growing degree days (gdd) needed to harvest; should only be output annually ddays F +GRAINC_TO_FOOD_PERHARV mxharvests grain C to food per harvest; should only be output annually gC/m^2 F +GRAINC_TO_SEED_PERHARV mxharvests grain C to seed per harvest; should only be output annually gC/m^2 F +GRAINN_TO_FOOD_PERHARV mxharvests grain N to food per harvest; should only be output annually (not scientifically supported) gN/m^2 F +GRAINN_TO_SEED_PERHARV mxharvests grain N to seed per harvest; should only be output annually (not scientifically supported) gN/m^2 F +HARVEST_REASON_PERHARV mxharvests Reason for each crop harvest; should only be output annually 1 = mature; 2 = max season length; 3 = incorrect Dec. 31 sowing; F +HDATES mxharvests actual crop harvest dates; should only be output annually day of year F +HUI_PERHARV mxharvests At-harvest accumulated heat unit index for crop; should only be output annually ddays F +SDATES_PERHARV mxharvests actual sowing dates for crops harvested this year; should only be output annually day of year F +SOWING_REASON_PERHARV mxharvests Reason for sowing of each crop harvested this year; should only be output annually unitless F +SYEARS_PERHARV mxharvests actual sowing years for crops harvested this year; should only be output annually year F +SDATES mxsowings actual crop sowing dates; should only be output annually day of year F +SOWING_REASON mxsowings Reason for each crop sowing; should only be output annually unitless F +SWINDOW_ENDS mxsowings crop sowing window end dates; should only be output annually day of year F +SWINDOW_STARTS mxsowings crop sowing window start dates; should only be output annually day of year F +ALBD numrad surface albedo (direct) proportion T +ALBDSF numrad diagnostic snow-free surface albedo (direct) proportion T +ALBGRD numrad ground albedo (direct) proportion F +ALBGRI numrad ground albedo (indirect) proportion F +ALBI numrad surface albedo (indirect) proportion T +ALBISF numrad diagnostic snow-free surface albedo (indirect) proportion T +VEGWP nvegwcs vegetation water matric potential for sun/sha canopy,xyl,root segments mm T +VEGWPLN nvegwcs vegetation water matric potential for sun/sha canopy,xyl,root at local noon mm T +VEGWPPD nvegwcs predawn vegetation water matric potential for sun/sha canopy,xyl,root mm T +=================================== ================ ============================================================================================== ================================================================= ======= diff --git a/doc/source/users_guide/setting-up-and-running-a-case/index.rst b/doc/source/users_guide/setting-up-and-running-a-case/index.rst index f882df277b..40988c303e 100644 --- a/doc/source/users_guide/setting-up-and-running-a-case/index.rst +++ b/doc/source/users_guide/setting-up-and-running-a-case/index.rst @@ -3,10 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. _customizing_section: - .. include:: ../substitutions.rst +.. _customizing_section: + ##################################### Setting Up and Running a Case ##################################### @@ -18,6 +18,6 @@ Setting Up and Running a Case customizing-the-clm-configuration.rst customizing-the-clm-namelist.rst customizing-the-datm-namelist.rst - master_list_nofates - master_list_fates + history_fields_nofates + history_fields_fates diff --git a/doc/source/users_guide/setting-up-and-running-a-case/master_list_fates.rst b/doc/source/users_guide/setting-up-and-running-a-case/master_list_fates.rst deleted file mode 100644 index 57edbfd3ca..0000000000 --- a/doc/source/users_guide/setting-up-and-running-a-case/master_list_fates.rst +++ /dev/null @@ -1,955 +0,0 @@ -============================= -CTSM History Fields (fates) -============================= - -CAUTION: Not all variables are relevant / present for all CTSM cases. -Key flags used in this CTSM case: -use_cn = F -use_crop = F -use_fates = T - -==== =================================== ============================================================================================== ================================================================= ======= -CTSM History Fields ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - # Variable Name Long Description Units Active? -==== =================================== ============================================================================================== ================================================================= ======= - 1 A5TMIN 5-day running mean of min 2-m temperature K F - 2 ACTUAL_IMMOB actual N immobilization gN/m^2/s T - 3 ACT_SOMC ACT_SOM C gC/m^2 T - 4 ACT_SOMC_1m ACT_SOM C to 1 meter gC/m^2 F - 5 ACT_SOMC_TNDNCY_VERT_TRA active soil organic C tendency due to vertical transport gC/m^3/s F - 6 ACT_SOMC_TO_PAS_SOMC decomp. of active soil organic C to passive soil organic C gC/m^2/s F - 7 ACT_SOMC_TO_PAS_SOMC_vr decomp. of active soil organic C to passive soil organic C gC/m^3/s F - 8 ACT_SOMC_TO_SLO_SOMC decomp. of active soil organic C to slow soil organic ma C gC/m^2/s F - 9 ACT_SOMC_TO_SLO_SOMC_vr decomp. of active soil organic C to slow soil organic ma C gC/m^3/s F - 10 ACT_SOMC_vr ACT_SOM C (vertically resolved) gC/m^3 T - 11 ACT_SOMN ACT_SOM N gN/m^2 T - 12 ACT_SOMN_1m ACT_SOM N to 1 meter gN/m^2 F - 13 ACT_SOMN_TNDNCY_VERT_TRA active soil organic N tendency due to vertical transport gN/m^3/s F - 14 ACT_SOMN_TO_PAS_SOMN decomp. of active soil organic N to passive soil organic N gN/m^2 F - 15 ACT_SOMN_TO_PAS_SOMN_vr decomp. of active soil organic N to passive soil organic N gN/m^3 F - 16 ACT_SOMN_TO_SLO_SOMN decomp. of active soil organic N to slow soil organic ma N gN/m^2 F - 17 ACT_SOMN_TO_SLO_SOMN_vr decomp. of active soil organic N to slow soil organic ma N gN/m^3 F - 18 ACT_SOMN_vr ACT_SOM N (vertically resolved) gN/m^3 T - 19 ACT_SOM_HR_S2 Het. Resp. from active soil organic gC/m^2/s F - 20 ACT_SOM_HR_S2_vr Het. Resp. from active soil organic gC/m^3/s F - 21 ACT_SOM_HR_S3 Het. Resp. from active soil organic gC/m^2/s F - 22 ACT_SOM_HR_S3_vr Het. Resp. from active soil organic gC/m^3/s F - 23 AGB Aboveground biomass gC m-2 T - 24 AGB_SCLS Aboveground biomass by size class kgC/m2 T - 25 AGB_SCPF Aboveground biomass by pft/size kgC/m2 F - 26 AGLB Aboveground leaf biomass kg/m^2 F - 27 AGSB Aboveground stem biomass kg/m^2 F - 28 ALBD surface albedo (direct) proportion F - 29 ALBGRD ground albedo (direct) proportion F - 30 ALBGRI ground albedo (indirect) proportion F - 31 ALBI surface albedo (indirect) proportion F - 32 ALT current active layer thickness m F - 33 ALTMAX maximum annual active layer thickness m F - 34 ALTMAX_LASTYEAR maximum prior year active layer thickness m F - 35 AR autotrophic respiration gC/m^2/s T - 36 AREA_BURNT_BY_PATCH_AGE spitfire area burnt by patch age (divide by patch_area_by_age to get burnt fraction by age) m2/m2/day T - 37 AREA_PLANT area occupied by all plants m2/m2 T - 38 AREA_TREES area occupied by woody plants m2/m2 T - 39 AR_AGSAPM_SCPF above-ground sapwood maintenance autotrophic respiration per m2 per year by pft/size kgC/m2/yr F - 40 AR_CANOPY autotrophic respiration of canopy plants gC/m^2/s T - 41 AR_CANOPY_SCPF autotrophic respiration of canopy plants by pft/size kgC/m2/yr F - 42 AR_CROOTM_SCPF below-ground sapwood maintenance autotrophic respiration per m2 per year by pft/size kgC/m2/yr F - 43 AR_DARKM_SCPF dark portion of maintenance autotrophic respiration per m2 per year by pft/size kgC/m2/yr F - 44 AR_FROOTM_SCPF fine root maintenance autotrophic respiration per m2 per year by pft/size kgC/m2/yr F - 45 AR_GROW_SCPF growth autotrophic respiration per m2 per year by pft/size kgC/m2/yr F - 46 AR_MAINT_SCPF maintenance autotrophic respiration per m2 per year by pft/size kgC/m2/yr F - 47 AR_SCPF total autotrophic respiration per m2 per year by pft/size kgC/m2/yr F - 48 AR_UNDERSTORY autotrophic respiration of understory plants gC/m^2/s T - 49 AR_UNDERSTORY_SCPF autotrophic respiration of understory plants by pft/size kgC/m2/yr F - 50 ATM_TOPO atmospheric surface height m T - 51 AnnET Annual ET mm/s F - 52 BA_SCLS basal area by size class m2/ha T - 53 BA_SCPF basal area by pft/size m2/ha F - 54 BCDEP total BC deposition (dry+wet) from atmosphere kg/m^2/s T - 55 BDEAD_MD_CANOPY_SCLS BDEAD_MD for canopy plants by size class kg C / ha / yr F - 56 BDEAD_MD_UNDERSTORY_SCLS BDEAD_MD for understory plants by size class kg C / ha / yr F - 57 BIOMASS_AGEPFT biomass per PFT in each age bin kg C / m2 F - 58 BIOMASS_BY_AGE Total Biomass within a given patch age bin kgC/m2 F - 59 BIOMASS_CANOPY Biomass of canopy plants gC m-2 T - 60 BIOMASS_SCLS Total biomass by size class kgC/m2 F - 61 BIOMASS_UNDERSTORY Biomass of understory plants gC m-2 T - 62 BLEAF_CANOPY_SCPF biomass carbon in leaf of canopy plants by pft/size kgC/ha F - 63 BLEAF_UNDERSTORY_SCPF biomass carbon in leaf of understory plants by pft/size kgC/ha F - 64 BSTORE_MD_CANOPY_SCLS BSTORE_MD for canopy plants by size class kg C / ha / yr F - 65 BSTORE_MD_UNDERSTORY_SCLS BSTORE_MD for understory plants by size class kg C / ha / yr F - 66 BSTOR_CANOPY_SCPF biomass carbon in storage pools of canopy plants by pft/size kgC/ha F - 67 BSTOR_UNDERSTORY_SCPF biomass carbon in storage pools of understory plants by pft/size kgC/ha F - 68 BSW_MD_CANOPY_SCLS BSW_MD for canopy plants by size class kg C / ha / yr F - 69 BSW_MD_UNDERSTORY_SCLS BSW_MD for understory plants by size class kg C / ha / yr F - 70 BTRAN transpiration beta factor unitless T - 71 BTRANMN daily minimum of transpiration beta factor unitless T - 72 BURNT_LITTER_FRAC_AREA_PRODUCT product of fraction of fuel burnt and burned area (divide by FIRE_AREA to get burned-area-weig fraction T - 73 C13disc_SCPF C13 discrimination by pft/size per mil F - 74 CAMBIALFIREMORT_SCPF cambial fire mortality by pft/size N/ha/yr F - 75 CANOPY_AREA_BY_AGE canopy area by age bin m2/m2 T - 76 CANOPY_HEIGHT_DIST canopy height distribution m2/m2 T - 77 CANOPY_SPREAD Scaling factor between tree basal area and canopy area 0-1 T - 78 CARBON_BALANCE_CANOPY_SCLS CARBON_BALANCE for canopy plants by size class kg C / ha / yr F - 79 CARBON_BALANCE_UNDERSTORY_SCLS CARBON_BALANCE for understory plants by size class kg C / ha / yr F - 80 CBALANCE_ERROR_FATES total carbon error, FATES mgC/day T - 81 CEFFLUX carbon efflux, root to soil kgC/ha/day T - 82 CEFFLUX_SCPF carbon efflux, root to soil, by size-class x pft kg/ha/day F - 83 CEL_LITC CEL_LIT C gC/m^2 T - 84 CEL_LITC_1m CEL_LIT C to 1 meter gC/m^2 F - 85 CEL_LITC_TNDNCY_VERT_TRA cellulosic litter C tendency due to vertical transport gC/m^3/s F - 86 CEL_LITC_TO_ACT_SOMC decomp. of cellulosic litter C to active soil organic C gC/m^2/s F - 87 CEL_LITC_TO_ACT_SOMC_vr decomp. of cellulosic litter C to active soil organic C gC/m^3/s F - 88 CEL_LITC_vr CEL_LIT C (vertically resolved) gC/m^3 T - 89 CEL_LITN CEL_LIT N gN/m^2 T - 90 CEL_LITN_1m CEL_LIT N to 1 meter gN/m^2 F - 91 CEL_LITN_TNDNCY_VERT_TRA cellulosic litter N tendency due to vertical transport gN/m^3/s F - 92 CEL_LITN_TO_ACT_SOMN decomp. of cellulosic litter N to active soil organic N gN/m^2 F - 93 CEL_LITN_TO_ACT_SOMN_vr decomp. of cellulosic litter N to active soil organic N gN/m^3 F - 94 CEL_LITN_vr CEL_LIT N (vertically resolved) gN/m^3 T - 95 CEL_LIT_HR Het. Resp. from cellulosic litter gC/m^2/s F - 96 CEL_LIT_HR_vr Het. Resp. from cellulosic litter gC/m^3/s F - 97 CH4PROD Gridcell total production of CH4 gC/m2/s T - 98 CH4_EBUL_TOTAL_SAT ebullition surface CH4 flux; (+ to atm) mol/m2/s F - 99 CH4_EBUL_TOTAL_UNSAT ebullition surface CH4 flux; (+ to atm) mol/m2/s F - 100 CH4_SURF_AERE_SAT aerenchyma surface CH4 flux for inundated area; (+ to atm) mol/m2/s T - 101 CH4_SURF_AERE_UNSAT aerenchyma surface CH4 flux for non-inundated area; (+ to atm) mol/m2/s T - 102 CH4_SURF_DIFF_SAT diffusive surface CH4 flux for inundated / lake area; (+ to atm) mol/m2/s T - 103 CH4_SURF_DIFF_UNSAT diffusive surface CH4 flux for non-inundated area; (+ to atm) mol/m2/s T - 104 CH4_SURF_EBUL_SAT ebullition surface CH4 flux for inundated / lake area; (+ to atm) mol/m2/s T - 105 CH4_SURF_EBUL_UNSAT ebullition surface CH4 flux for non-inundated area; (+ to atm) mol/m2/s T - 106 COL_CTRUNC column-level sink for C truncation gC/m^2 F - 107 COL_NTRUNC column-level sink for N truncation gN/m^2 F - 108 CONC_CH4_SAT CH4 soil Concentration for inundated / lake area mol/m3 F - 109 CONC_CH4_UNSAT CH4 soil Concentration for non-inundated area mol/m3 F - 110 CONC_O2_SAT O2 soil Concentration for inundated / lake area mol/m3 T - 111 CONC_O2_UNSAT O2 soil Concentration for non-inundated area mol/m3 T - 112 COSZEN cosine of solar zenith angle none F - 113 CROWNAREA_CAN total crown area in each canopy layer m2/m2 T - 114 CROWNAREA_CNLF total crown area that is occupied by leaves in each canopy and leaf layer m2/m2 F - 115 CROWNFIREMORT_SCPF crown fire mortality by pft/size N/ha/yr F - 116 CROWN_AREA_CANOPY_SCLS total crown area of canopy plants by size class m2/ha F - 117 CROWN_AREA_UNDERSTORY_SCLS total crown area of understory plants by size class m2/ha F - 118 CWDC_HR cwd C heterotrophic respiration gC/m^2/s F - 119 CWD_AG_CWDSC size-resolved AG CWD stocks gC/m^2 F - 120 CWD_AG_IN_CWDSC size-resolved AG CWD input gC/m^2/y F - 121 CWD_AG_OUT_CWDSC size-resolved AG CWD output gC/m^2/y F - 122 CWD_BG_CWDSC size-resolved BG CWD stocks gC/m^2 F - 123 CWD_BG_IN_CWDSC size-resolved BG CWD input gC/m^2/y F - 124 CWD_BG_OUT_CWDSC size-resolved BG CWD output gC/m^2/y F - 125 C_LBLAYER mean leaf boundary layer conductance umol m-2 s-1 T - 126 C_LBLAYER_BY_AGE mean leaf boundary layer conductance - by patch age umol m-2 s-1 F - 127 C_STOMATA mean stomatal conductance umol m-2 s-1 T - 128 C_STOMATA_BY_AGE mean stomatal conductance - by patch age umol m-2 s-1 F - 129 DDBH_CANOPY_SCAG growth rate of canopy plantsnumber of plants per hectare in canopy in each size x age class cm/yr/ha F - 130 DDBH_CANOPY_SCLS diameter growth increment by pft/size cm/yr/ha T - 131 DDBH_CANOPY_SCPF diameter growth increment by pft/size cm/yr/ha F - 132 DDBH_SCPF diameter growth increment by pft/size cm/yr/ha F - 133 DDBH_UNDERSTORY_SCAG growth rate of understory plants in each size x age class cm/yr/ha F - 134 DDBH_UNDERSTORY_SCLS diameter growth increment by pft/size cm/yr/ha T - 135 DDBH_UNDERSTORY_SCPF diameter growth increment by pft/size cm/yr/ha F - 136 DEMOTION_CARBONFLUX demotion-associated biomass carbon flux from canopy to understory gC/m2/s T - 137 DEMOTION_RATE_SCLS demotion rate from canopy to understory by size class indiv/ha/yr F - 138 DENIT total rate of denitrification gN/m^2/s T - 139 DGNETDT derivative of net ground heat flux wrt soil temp W/m^2/K F - 140 DISPLA displacement height m F - 141 DISTURBANCE_RATE_FIRE Disturbance rate from fire m2 m-2 d-1 T - 142 DISTURBANCE_RATE_LOGGING Disturbance rate from logging m2 m-2 d-1 T - 143 DISTURBANCE_RATE_P2P Disturbance rate from primary to primary lands m2 m-2 d-1 T - 144 DISTURBANCE_RATE_P2S Disturbance rate from primary to secondary lands m2 m-2 d-1 T - 145 DISTURBANCE_RATE_POTENTIAL Potential (i.e., including unresolved) disturbance rate m2 m-2 d-1 T - 146 DISTURBANCE_RATE_S2S Disturbance rate from secondary to secondary lands m2 m-2 d-1 T - 147 DISTURBANCE_RATE_TREEFALL Disturbance rate from treefall m2 m-2 d-1 T - 148 DPVLTRB1 turbulent deposition velocity 1 m/s F - 149 DPVLTRB2 turbulent deposition velocity 2 m/s F - 150 DPVLTRB3 turbulent deposition velocity 3 m/s F - 151 DPVLTRB4 turbulent deposition velocity 4 m/s F - 152 DSL dry surface layer thickness mm T - 153 DSTDEP total dust deposition (dry+wet) from atmosphere kg/m^2/s T - 154 DSTFLXT total surface dust emission kg/m2/s T - 155 DYN_COL_ADJUSTMENTS_CH4 Adjustments in ch4 due to dynamic column areas; only makes sense at the column level: should n gC/m^2 F - 156 DYN_COL_SOIL_ADJUSTMENTS_C Adjustments in soil carbon due to dynamic column areas; only makes sense at the column level: gC/m^2 F - 157 DYN_COL_SOIL_ADJUSTMENTS_N Adjustments in soil nitrogen due to dynamic column areas; only makes sense at the column level gN/m^2 F - 158 ED_NCOHORTS Total number of ED cohorts per site none T - 159 ED_NPATCHES Total number of ED patches per site none T - 160 ED_balive Live biomass gC m-2 T - 161 ED_bdead Dead (structural) biomass (live trees, not CWD) gC m-2 T - 162 ED_bfineroot Fine root biomass gC m-2 T - 163 ED_biomass Total biomass gC m-2 T - 164 ED_bleaf Leaf biomass gC m-2 T - 165 ED_bsapwood Sapwood biomass gC m-2 T - 166 ED_bstore Storage biomass gC m-2 T - 167 EFFECT_WSPEED effective windspeed for fire spread none T - 168 EFLXBUILD building heat flux from change in interior building air temperature W/m^2 T - 169 EFLX_DYNBAL dynamic land cover change conversion energy flux W/m^2 T - 170 EFLX_GNET net heat flux into ground W/m^2 F - 171 EFLX_GRND_LAKE net heat flux into lake/snow surface, excluding light transmission W/m^2 T - 172 EFLX_LH_TOT total latent heat flux [+ to atm] W/m^2 T - 173 EFLX_LH_TOT_ICE total latent heat flux [+ to atm] (ice landunits only) W/m^2 F - 174 EFLX_LH_TOT_R Rural total evaporation W/m^2 T - 175 EFLX_LH_TOT_U Urban total evaporation W/m^2 F - 176 EFLX_SOIL_GRND soil heat flux [+ into soil] W/m^2 F - 177 ELAI exposed one-sided leaf area index m^2/m^2 T - 178 ERRH2O total water conservation error mm T - 179 ERRH2OSNO imbalance in snow depth (liquid water) mm T - 180 ERROR_FATES total error, FATES mass-balance mg/day T - 181 ERRSEB surface energy conservation error W/m^2 T - 182 ERRSOI soil/lake energy conservation error W/m^2 T - 183 ERRSOL solar radiation conservation error W/m^2 T - 184 ESAI exposed one-sided stem area index m^2/m^2 T - 185 FABD_SHA_CNLF shade fraction of direct light absorbed by each canopy and leaf layer fraction F - 186 FABD_SHA_CNLFPFT shade fraction of direct light absorbed by each canopy, leaf, and PFT fraction F - 187 FABD_SHA_TOPLF_BYCANLAYER shade fraction of direct light absorbed by the top leaf layer of each canopy layer fraction F - 188 FABD_SUN_CNLF sun fraction of direct light absorbed by each canopy and leaf layer fraction F - 189 FABD_SUN_CNLFPFT sun fraction of direct light absorbed by each canopy, leaf, and PFT fraction F - 190 FABD_SUN_TOPLF_BYCANLAYER sun fraction of direct light absorbed by the top leaf layer of each canopy layer fraction F - 191 FABI_SHA_CNLF shade fraction of indirect light absorbed by each canopy and leaf layer fraction F - 192 FABI_SHA_CNLFPFT shade fraction of indirect light absorbed by each canopy, leaf, and PFT fraction F - 193 FABI_SHA_TOPLF_BYCANLAYER shade fraction of indirect light absorbed by the top leaf layer of each canopy layer fraction F - 194 FABI_SUN_CNLF sun fraction of indirect light absorbed by each canopy and leaf layer fraction F - 195 FABI_SUN_CNLFPFT sun fraction of indirect light absorbed by each canopy, leaf, and PFT fraction F - 196 FABI_SUN_TOPLF_BYCANLAYER sun fraction of indirect light absorbed by the top leaf layer of each canopy layer fraction F - 197 FATES_HR heterotrophic respiration gC/m^2/s T - 198 FATES_c_to_litr_cel_c litter celluluse carbon flux from FATES to BGC gC/m^3/s T - 199 FATES_c_to_litr_lab_c litter labile carbon flux from FATES to BGC gC/m^3/s T - 200 FATES_c_to_litr_lig_c litter lignin carbon flux from FATES to BGC gC/m^3/s T - 201 FCEV canopy evaporation W/m^2 T - 202 FCH4 Gridcell surface CH4 flux to atmosphere (+ to atm) kgC/m2/s T - 203 FCH4TOCO2 Gridcell oxidation of CH4 to CO2 gC/m2/s T - 204 FCH4_DFSAT CH4 additional flux due to changing fsat, natural vegetated and crop landunits only kgC/m2/s T - 205 FCO2 CO2 flux to atmosphere (+ to atm) kgCO2/m2/s F - 206 FCOV fractional impermeable area unitless T - 207 FCTR canopy transpiration W/m^2 T - 208 FGEV ground evaporation W/m^2 T - 209 FGR heat flux into soil/snow including snow melt and lake / snow light transmission W/m^2 T - 210 FGR12 heat flux between soil layers 1 and 2 W/m^2 T - 211 FGR_ICE heat flux into soil/snow including snow melt and lake / snow light transmission (ice landunits W/m^2 F - 212 FGR_R Rural heat flux into soil/snow including snow melt and snow light transmission W/m^2 F - 213 FGR_SOIL_R Rural downward heat flux at interface below each soil layer watt/m^2 F - 214 FGR_U Urban heat flux into soil/snow including snow melt W/m^2 F - 215 FH2OSFC fraction of ground covered by surface water unitless T - 216 FH2OSFC_NOSNOW fraction of ground covered by surface water (if no snow present) unitless F - 217 FINUNDATED fractional inundated area of vegetated columns unitless T - 218 FINUNDATED_LAG time-lagged inundated fraction of vegetated columns unitless F - 219 FIRA net infrared (longwave) radiation W/m^2 T - 220 FIRA_ICE net infrared (longwave) radiation (ice landunits only) W/m^2 F - 221 FIRA_R Rural net infrared (longwave) radiation W/m^2 T - 222 FIRA_U Urban net infrared (longwave) radiation W/m^2 F - 223 FIRE emitted infrared (longwave) radiation W/m^2 T - 224 FIRE_AREA spitfire fire area burn fraction fraction/day T - 225 FIRE_FDI probability that an ignition will lead to a fire none T - 226 FIRE_FLUX ED-spitfire loss to atmosphere of elements g/m^2/s T - 227 FIRE_FUEL_BULKD spitfire fuel bulk density kg biomass/m3 T - 228 FIRE_FUEL_EFF_MOIST spitfire fuel moisture m T - 229 FIRE_FUEL_MEF spitfire fuel moisture m T - 230 FIRE_FUEL_SAV spitfire fuel surface/volume per m T - 231 FIRE_ICE emitted infrared (longwave) radiation (ice landunits only) W/m^2 F - 232 FIRE_IGNITIONS number of successful ignitions number/km2/day T - 233 FIRE_INTENSITY spitfire fire intensity: kJ/m/s kJ/m/s T - 234 FIRE_INTENSITY_AREA_PRODUCT spitfire product of fire intensity and burned area (divide by FIRE_AREA to get area-weighted m kJ/m/s T - 235 FIRE_INTENSITY_BY_PATCH_AGE product of fire intensity and burned area, resolved by patch age (so divide by AREA_BURNT_BY_P kJ/m/2 T - 236 FIRE_NESTEROV_INDEX nesterov_fire_danger index none T - 237 FIRE_R Rural emitted infrared (longwave) radiation W/m^2 T - 238 FIRE_ROS fire rate of spread m/min m/min T - 239 FIRE_ROS_AREA_PRODUCT product of fire rate of spread (m/min) and burned area (fraction)--divide by FIRE_AREA to get m/min T - 240 FIRE_TFC_ROS total fuel consumed kgC/m2 T - 241 FIRE_TFC_ROS_AREA_PRODUCT product of total fuel consumed and burned area--divide by FIRE_AREA to get burned-area-weighte kgC/m2 T - 242 FIRE_U Urban emitted infrared (longwave) radiation W/m^2 F - 243 FLDS atmospheric longwave radiation (downscaled to columns in glacier regions) W/m^2 T - 244 FLDS_ICE atmospheric longwave radiation (downscaled to columns in glacier regions) (ice landunits only) W/m^2 F - 245 FNRTC Total carbon in live plant fine-roots kgC ha-1 T - 246 FNRTC_SCPF fine-root carbon mass by size-class x pft kgC/ha F - 247 FRAGMENTATION_SCALER_SL factor by which litter/cwd fragmentation proceeds relative to max rate by soil layer unitless (0-1) T - 248 FROOT_MR fine root maintenance respiration) kg C / m2 / yr T - 249 FROOT_MR_CANOPY_SCLS FROOT_MR for canopy plants by size class kg C / ha / yr F - 250 FROOT_MR_UNDERSTORY_SCLS FROOT_MR for understory plants by size class kg C / ha / yr F - 251 FROST_TABLE frost table depth (natural vegetated and crop landunits only) m F - 252 FSA absorbed solar radiation W/m^2 T - 253 FSAT fractional area with water table at surface unitless T - 254 FSA_ICE absorbed solar radiation (ice landunits only) W/m^2 F - 255 FSA_R Rural absorbed solar radiation W/m^2 F - 256 FSA_U Urban absorbed solar radiation W/m^2 F - 257 FSD24 direct radiation (last 24hrs) K F - 258 FSD240 direct radiation (last 240hrs) K F - 259 FSDS atmospheric incident solar radiation W/m^2 T - 260 FSDSND direct nir incident solar radiation W/m^2 T - 261 FSDSNDLN direct nir incident solar radiation at local noon W/m^2 T - 262 FSDSNI diffuse nir incident solar radiation W/m^2 T - 263 FSDSVD direct vis incident solar radiation W/m^2 T - 264 FSDSVDLN direct vis incident solar radiation at local noon W/m^2 T - 265 FSDSVI diffuse vis incident solar radiation W/m^2 T - 266 FSDSVILN diffuse vis incident solar radiation at local noon W/m^2 T - 267 FSH sensible heat not including correction for land use change and rain/snow conversion W/m^2 T - 268 FSH_G sensible heat from ground W/m^2 T - 269 FSH_ICE sensible heat not including correction for land use change and rain/snow conversion (ice landu W/m^2 F - 270 FSH_PRECIP_CONVERSION Sensible heat flux from conversion of rain/snow atm forcing W/m^2 T - 271 FSH_R Rural sensible heat W/m^2 T - 272 FSH_RUNOFF_ICE_TO_LIQ sensible heat flux generated from conversion of ice runoff to liquid W/m^2 T - 273 FSH_TO_COUPLER sensible heat sent to coupler (includes corrections for land use change, rain/snow conversion W/m^2 T - 274 FSH_U Urban sensible heat W/m^2 F - 275 FSH_V sensible heat from veg W/m^2 T - 276 FSI24 indirect radiation (last 24hrs) K F - 277 FSI240 indirect radiation (last 240hrs) K F - 278 FSM snow melt heat flux W/m^2 T - 279 FSM_ICE snow melt heat flux (ice landunits only) W/m^2 F - 280 FSM_R Rural snow melt heat flux W/m^2 F - 281 FSM_U Urban snow melt heat flux W/m^2 F - 282 FSNO fraction of ground covered by snow unitless T - 283 FSNO_EFF effective fraction of ground covered by snow unitless T - 284 FSNO_ICE fraction of ground covered by snow (ice landunits only) unitless F - 285 FSR reflected solar radiation W/m^2 T - 286 FSRND direct nir reflected solar radiation W/m^2 T - 287 FSRNDLN direct nir reflected solar radiation at local noon W/m^2 T - 288 FSRNI diffuse nir reflected solar radiation W/m^2 T - 289 FSRVD direct vis reflected solar radiation W/m^2 T - 290 FSRVDLN direct vis reflected solar radiation at local noon W/m^2 T - 291 FSRVI diffuse vis reflected solar radiation W/m^2 T - 292 FSR_ICE reflected solar radiation (ice landunits only) W/m^2 F - 293 FSUN sunlit fraction of canopy proportion F - 294 FSUN24 fraction sunlit (last 24hrs) K F - 295 FSUN240 fraction sunlit (last 240hrs) K F - 296 FUEL_AMOUNT_AGEFUEL spitfire fuel quantity in each age x fuel class kg C / m2 T - 297 FUEL_AMOUNT_BY_NFSC spitfire size-resolved fuel quantity kg C / m2 T - 298 FUEL_MOISTURE_NFSC spitfire size-resolved fuel moisture - T - 299 Fire_Closs ED/SPitfire Carbon loss to atmosphere gC/m^2/s T - 300 GPP gross primary production gC/m^2/s T - 301 GPP_BY_AGE gross primary productivity by age bin gC/m^2/s F - 302 GPP_CANOPY gross primary production of canopy plants gC/m^2/s T - 303 GPP_CANOPY_SCPF gross primary production of canopy plants by pft/size kgC/m2/yr F - 304 GPP_SCPF gross primary production by pft/size kgC/m2/yr F - 305 GPP_UNDERSTORY gross primary production of understory plants gC/m^2/s T - 306 GPP_UNDERSTORY_SCPF gross primary production of understory plants by pft/size kgC/m2/yr F - 307 GROSS_NMIN gross rate of N mineralization gN/m^2/s T - 308 GROWTHFLUX_FUSION_SCPF flux of individuals into a given size class bin via fusion n/yr/ha F - 309 GROWTHFLUX_SCPF flux of individuals into a given size class bin via growth and recruitment n/yr/ha F - 310 GROWTH_RESP growth respiration gC/m^2/s T - 311 GSSHA shaded leaf stomatal conductance umol H20/m2/s T - 312 GSSHALN shaded leaf stomatal conductance at local noon umol H20/m2/s T - 313 GSSUN sunlit leaf stomatal conductance umol H20/m2/s T - 314 GSSUNLN sunlit leaf stomatal conductance at local noon umol H20/m2/s T - 315 H2OCAN intercepted water mm T - 316 H2OSFC surface water depth mm T - 317 H2OSNO snow depth (liquid water) mm T - 318 H2OSNO_ICE snow depth (liquid water, ice landunits only) mm F - 319 H2OSNO_TOP mass of snow in top snow layer kg/m2 T - 320 H2OSOI volumetric soil water (natural vegetated and crop landunits only) mm3/mm3 T - 321 HARVEST_CARBON_FLUX Harvest carbon flux kg C m-2 d-1 T - 322 HBOT canopy bottom m F - 323 HEAT_CONTENT1 initial gridcell total heat content J/m^2 T - 324 HEAT_CONTENT1_VEG initial gridcell total heat content - natural vegetated and crop landunits only J/m^2 F - 325 HEAT_CONTENT2 post land cover change total heat content J/m^2 F - 326 HEAT_FROM_AC sensible heat flux put into canyon due to heat removed from air conditioning W/m^2 T - 327 HIA 2 m NWS Heat Index C T - 328 HIA_R Rural 2 m NWS Heat Index C T - 329 HIA_U Urban 2 m NWS Heat Index C T - 330 HK hydraulic conductivity (natural vegetated and crop landunits only) mm/s F - 331 HR total heterotrophic respiration gC/m^2/s T - 332 HR_vr total vertically resolved heterotrophic respiration gC/m^3/s T - 333 HTOP canopy top m T - 334 HUMIDEX 2 m Humidex C T - 335 HUMIDEX_R Rural 2 m Humidex C T - 336 HUMIDEX_U Urban 2 m Humidex C T - 337 ICE_CONTENT1 initial gridcell total ice content mm T - 338 ICE_CONTENT2 post land cover change total ice content mm F - 339 ICE_MODEL_FRACTION Ice sheet model fractional coverage unitless F - 340 INT_SNOW accumulated swe (natural vegetated and crop landunits only) mm F - 341 INT_SNOW_ICE accumulated swe (ice landunits only) mm F - 342 IWUELN local noon intrinsic water use efficiency umolCO2/molH2O T - 343 KROOT root conductance each soil layer 1/s F - 344 KSOIL soil conductance in each soil layer 1/s F - 345 K_ACT_SOM active soil organic potential loss coefficient 1/s F - 346 K_CEL_LIT cellulosic litter potential loss coefficient 1/s F - 347 K_LIG_LIT lignin litter potential loss coefficient 1/s F - 348 K_MET_LIT metabolic litter potential loss coefficient 1/s F - 349 K_PAS_SOM passive soil organic potential loss coefficient 1/s F - 350 K_SLO_SOM slow soil organic ma potential loss coefficient 1/s F - 351 LAI240 240hr average of leaf area index m^2/m^2 F - 352 LAISHA shaded projected leaf area index m^2/m^2 T - 353 LAISHA_TOP_CAN LAI in the shade by the top leaf layer of each canopy layer m2/m2 F - 354 LAISHA_Z_CNLF LAI in the shade by each canopy and leaf layer m2/m2 F - 355 LAISHA_Z_CNLFPFT LAI in the shade by each canopy, leaf, and PFT m2/m2 F - 356 LAISUN sunlit projected leaf area index m^2/m^2 T - 357 LAISUN_TOP_CAN LAI in the sun by the top leaf layer of each canopy layer m2/m2 F - 358 LAISUN_Z_CNLF LAI in the sun by each canopy and leaf layer m2/m2 F - 359 LAISUN_Z_CNLFPFT LAI in the sun by each canopy, leaf, and PFT m2/m2 F - 360 LAI_BY_AGE leaf area index by age bin m2/m2 T - 361 LAI_CANOPY_SCLS Leaf are index (LAI) by size class m2/m2 T - 362 LAI_UNDERSTORY_SCLS number of understory plants by size class indiv/ha T - 363 LAKEICEFRAC lake layer ice mass fraction unitless F - 364 LAKEICEFRAC_SURF surface lake layer ice mass fraction unitless T - 365 LAKEICETHICK thickness of lake ice (including physical expansion on freezing) m T - 366 LEAFC Total carbon in live plant leaves kgC ha-1 T - 367 LEAFC_SCPF leaf carbon mass by size-class x pft kgC/ha F - 368 LEAF_HEIGHT_DIST leaf height distribution m2/m2 T - 369 LEAF_MD_CANOPY_SCLS LEAF_MD for canopy plants by size class kg C / ha / yr F - 370 LEAF_MD_UNDERSTORY_SCLS LEAF_MD for understory plants by size class kg C / ha / yr F - 371 LEAF_MR RDARK (leaf maintenance respiration) kg C / m2 / yr T - 372 LIG_LITC LIG_LIT C gC/m^2 T - 373 LIG_LITC_1m LIG_LIT C to 1 meter gC/m^2 F - 374 LIG_LITC_TNDNCY_VERT_TRA lignin litter C tendency due to vertical transport gC/m^3/s F - 375 LIG_LITC_TO_SLO_SOMC decomp. of lignin litter C to slow soil organic ma C gC/m^2/s F - 376 LIG_LITC_TO_SLO_SOMC_vr decomp. of lignin litter C to slow soil organic ma C gC/m^3/s F - 377 LIG_LITC_vr LIG_LIT C (vertically resolved) gC/m^3 T - 378 LIG_LITN LIG_LIT N gN/m^2 T - 379 LIG_LITN_1m LIG_LIT N to 1 meter gN/m^2 F - 380 LIG_LITN_TNDNCY_VERT_TRA lignin litter N tendency due to vertical transport gN/m^3/s F - 381 LIG_LITN_TO_SLO_SOMN decomp. of lignin litter N to slow soil organic ma N gN/m^2 F - 382 LIG_LITN_TO_SLO_SOMN_vr decomp. of lignin litter N to slow soil organic ma N gN/m^3 F - 383 LIG_LITN_vr LIG_LIT N (vertically resolved) gN/m^3 T - 384 LIG_LIT_HR Het. Resp. from lignin litter gC/m^2/s F - 385 LIG_LIT_HR_vr Het. Resp. from lignin litter gC/m^3/s F - 386 LIQCAN intercepted liquid water mm T - 387 LIQUID_CONTENT1 initial gridcell total liq content mm T - 388 LIQUID_CONTENT2 post landuse change gridcell total liq content mm F - 389 LIQUID_WATER_TEMP1 initial gridcell weighted average liquid water temperature K F - 390 LITTERC_HR litter C heterotrophic respiration gC/m^2/s T - 391 LITTER_CWD total mass of litter in CWD kg ha-1 T - 392 LITTER_CWD_AG_ELEM mass of above ground litter in CWD (trunks/branches/twigs) kg ha-1 T - 393 LITTER_CWD_BG_ELEM mass of below ground litter in CWD (coarse roots) kg ha-1 T - 394 LITTER_FINES_AG_ELEM mass of above ground litter in fines (leaves,nonviable seed) kg ha-1 T - 395 LITTER_FINES_BG_ELEM mass of below ground litter in fines (fineroots) kg ha-1 T - 396 LITTER_IN FATES litter flux in gC m-2 s-1 T - 397 LITTER_IN_ELEM FATES litter flux in kg ha-1 d-1 T - 398 LITTER_OUT FATES litter flux out gC m-2 s-1 T - 399 LITTER_OUT_ELEM FATES litter flux out (fragmentation only) kg ha-1 d-1 T - 400 LIVECROOT_MR live coarse root maintenance respiration) kg C / m2 / yr T - 401 LIVECROOT_MR_CANOPY_SCLS LIVECROOT_MR for canopy plants by size class kg C / ha / yr F - 402 LIVECROOT_MR_UNDERSTORY_SCLS LIVECROOT_MR for understory plants by size class kg C / ha / yr F - 403 LIVESTEM_MR live stem maintenance respiration) kg C / m2 / yr T - 404 LIVESTEM_MR_CANOPY_SCLS LIVESTEM_MR for canopy plants by size class kg C / ha / yr F - 405 LIVESTEM_MR_UNDERSTORY_SCLS LIVESTEM_MR for understory plants by size class kg C / ha / yr F - 406 LNC leaf N concentration gN leaf/m^2 T - 407 LWdown atmospheric longwave radiation (downscaled to columns in glacier regions) W/m^2 F - 408 LWup upwelling longwave radiation W/m^2 F - 409 M10_CACLS age senescence mortality by cohort age N/ha/yr T - 410 M10_CAPF age senescence mortality by pft/cohort age N/ha/yr F - 411 M10_SCLS age senescence mortality by size N/ha/yr T - 412 M10_SCPF age senescence mortality by pft/size N/ha/yr F - 413 M1_SCLS background mortality by size N/ha/yr T - 414 M1_SCPF background mortality by pft/size N/ha/yr F - 415 M2_SCLS hydraulic mortality by size N/ha/yr T - 416 M2_SCPF hydraulic mortality by pft/size N/ha/yr F - 417 M3_SCLS carbon starvation mortality by size N/ha/yr T - 418 M3_SCPF carbon starvation mortality by pft/size N/ha/yr F - 419 M4_SCLS impact mortality by size N/ha/yr T - 420 M4_SCPF impact mortality by pft/size N/ha/yr F - 421 M5_SCLS fire mortality by size N/ha/yr T - 422 M5_SCPF fire mortality by pft/size N/ha/yr F - 423 M6_SCLS termination mortality by size N/ha/yr T - 424 M6_SCPF termination mortality by pft/size N/ha/yr F - 425 M7_SCLS logging mortality by size N/ha/event T - 426 M7_SCPF logging mortality by pft/size N/ha/event F - 427 M8_SCLS freezing mortality by size N/ha/event T - 428 M8_SCPF freezing mortality by pft/size N/ha/yr F - 429 M9_SCLS senescence mortality by size N/ha/yr T - 430 M9_SCPF senescence mortality by pft/size N/ha/yr F - 431 MAINT_RESP maintenance respiration gC/m^2/s T - 432 MET_LITC MET_LIT C gC/m^2 T - 433 MET_LITC_1m MET_LIT C to 1 meter gC/m^2 F - 434 MET_LITC_TNDNCY_VERT_TRA metabolic litter C tendency due to vertical transport gC/m^3/s F - 435 MET_LITC_TO_ACT_SOMC decomp. of metabolic litter C to active soil organic C gC/m^2/s F - 436 MET_LITC_TO_ACT_SOMC_vr decomp. of metabolic litter C to active soil organic C gC/m^3/s F - 437 MET_LITC_vr MET_LIT C (vertically resolved) gC/m^3 T - 438 MET_LITN MET_LIT N gN/m^2 T - 439 MET_LITN_1m MET_LIT N to 1 meter gN/m^2 F - 440 MET_LITN_TNDNCY_VERT_TRA metabolic litter N tendency due to vertical transport gN/m^3/s F - 441 MET_LITN_TO_ACT_SOMN decomp. of metabolic litter N to active soil organic N gN/m^2 F - 442 MET_LITN_TO_ACT_SOMN_vr decomp. of metabolic litter N to active soil organic N gN/m^3 F - 443 MET_LITN_vr MET_LIT N (vertically resolved) gN/m^3 T - 444 MET_LIT_HR Het. Resp. from metabolic litter gC/m^2/s F - 445 MET_LIT_HR_vr Het. Resp. from metabolic litter gC/m^3/s F - 446 MORTALITY Rate of total mortality by PFT indiv/ha/yr T - 447 MORTALITY_CANOPY_SCAG mortality rate of canopy plants in each size x age class plants/ha/yr F - 448 MORTALITY_CANOPY_SCLS total mortality of canopy trees by size class indiv/ha/yr T - 449 MORTALITY_CANOPY_SCPF total mortality of canopy plants by pft/size N/ha/yr F - 450 MORTALITY_CARBONFLUX_CANOPY flux of biomass carbon from live to dead pools from mortality of canopy plants gC/m2/s T - 451 MORTALITY_CARBONFLUX_UNDERSTORY flux of biomass carbon from live to dead pools from mortality of understory plants gC/m2/s T - 452 MORTALITY_UNDERSTORY_SCAG mortality rate of understory plantsin each size x age class plants/ha/yr F - 453 MORTALITY_UNDERSTORY_SCLS total mortality of understory trees by size class indiv/ha/yr T - 454 MORTALITY_UNDERSTORY_SCPF total mortality of understory plants by pft/size N/ha/yr F - 455 M_ACT_SOMC_TO_LEACHING active soil organic C leaching loss gC/m^2/s F - 456 M_ACT_SOMN_TO_LEACHING active soil organic N leaching loss gN/m^2/s F - 457 M_CEL_LITC_TO_LEACHING cellulosic litter C leaching loss gC/m^2/s F - 458 M_CEL_LITN_TO_LEACHING cellulosic litter N leaching loss gN/m^2/s F - 459 M_LIG_LITC_TO_LEACHING lignin litter C leaching loss gC/m^2/s F - 460 M_LIG_LITN_TO_LEACHING lignin litter N leaching loss gN/m^2/s F - 461 M_MET_LITC_TO_LEACHING metabolic litter C leaching loss gC/m^2/s F - 462 M_MET_LITN_TO_LEACHING metabolic litter N leaching loss gN/m^2/s F - 463 M_PAS_SOMC_TO_LEACHING passive soil organic C leaching loss gC/m^2/s F - 464 M_PAS_SOMN_TO_LEACHING passive soil organic N leaching loss gN/m^2/s F - 465 M_SLO_SOMC_TO_LEACHING slow soil organic ma C leaching loss gC/m^2/s F - 466 M_SLO_SOMN_TO_LEACHING slow soil organic ma N leaching loss gN/m^2/s F - 467 NCL_BY_AGE number of canopy levels by age bin -- F - 468 NDEP_TO_SMINN atmospheric N deposition to soil mineral N gN/m^2/s T - 469 NEM Gridcell net adjustment to net carbon exchange passed to atm. for methane production gC/m2/s T - 470 NEP net ecosystem production gC/m^2/s T - 471 NET_C_UPTAKE_CNLF net carbon uptake by each canopy and leaf layer per unit ground area (i.e. divide by CROWNAREA gC/m2/s F - 472 NET_NMIN net rate of N mineralization gN/m^2/s T - 473 NFIX_TO_SMINN symbiotic/asymbiotic N fixation to soil mineral N gN/m^2/s T - 474 NPATCH_BY_AGE number of patches by age bin -- F - 475 NPLANT_CACLS number of plants by coage class indiv/ha T - 476 NPLANT_CANOPY_SCAG number of plants per hectare in canopy in each size x age class plants/ha F - 477 NPLANT_CANOPY_SCLS number of canopy plants by size class indiv/ha T - 478 NPLANT_CANOPY_SCPF stem number of canopy plants density by pft/size N/ha F - 479 NPLANT_CAPF stem number density by pft/coage N/ha F - 480 NPLANT_SCAG number of plants per hectare in each size x age class plants/ha T - 481 NPLANT_SCAGPFT number of plants per hectare in each size x age x pft class plants/ha F - 482 NPLANT_SCLS number of plants by size class indiv/ha T - 483 NPLANT_SCPF stem number density by pft/size N/ha F - 484 NPLANT_UNDERSTORY_SCAG number of plants per hectare in understory in each size x age class plants/ha F - 485 NPLANT_UNDERSTORY_SCLS number of understory plants by size class indiv/ha T - 486 NPLANT_UNDERSTORY_SCPF stem number of understory plants density by pft/size N/ha F - 487 NPP net primary production gC/m^2/s T - 488 NPP_AGDW_SCPF NPP flux into above-ground deadwood by pft/size kgC/m2/yr F - 489 NPP_AGEPFT NPP per PFT in each age bin kgC/m2/yr F - 490 NPP_AGSW_SCPF NPP flux into above-ground sapwood by pft/size kgC/m2/yr F - 491 NPP_BDEAD_CANOPY_SCLS NPP_BDEAD for canopy plants by size class kg C / ha / yr F - 492 NPP_BDEAD_UNDERSTORY_SCLS NPP_BDEAD for understory plants by size class kg C / ha / yr F - 493 NPP_BGDW_SCPF NPP flux into below-ground deadwood by pft/size kgC/m2/yr F - 494 NPP_BGSW_SCPF NPP flux into below-ground sapwood by pft/size kgC/m2/yr F - 495 NPP_BSEED_CANOPY_SCLS NPP_BSEED for canopy plants by size class kg C / ha / yr F - 496 NPP_BSEED_UNDERSTORY_SCLS NPP_BSEED for understory plants by size class kg C / ha / yr F - 497 NPP_BSW_CANOPY_SCLS NPP_BSW for canopy plants by size class kg C / ha / yr F - 498 NPP_BSW_UNDERSTORY_SCLS NPP_BSW for understory plants by size class kg C / ha / yr F - 499 NPP_BY_AGE net primary productivity by age bin gC/m^2/s F - 500 NPP_CROOT NPP flux into coarse roots kgC/m2/yr T - 501 NPP_FNRT_SCPF NPP flux into fine roots by pft/size kgC/m2/yr F - 502 NPP_FROOT NPP flux into fine roots kgC/m2/yr T - 503 NPP_FROOT_CANOPY_SCLS NPP_FROOT for canopy plants by size class kg C / ha / yr F - 504 NPP_FROOT_UNDERSTORY_SCLS NPP_FROOT for understory plants by size class kg C / ha / yr F - 505 NPP_LEAF NPP flux into leaves kgC/m2/yr T - 506 NPP_LEAF_CANOPY_SCLS NPP_LEAF for canopy plants by size class kg C / ha / yr F - 507 NPP_LEAF_SCPF NPP flux into leaves by pft/size kgC/m2/yr F - 508 NPP_LEAF_UNDERSTORY_SCLS NPP_LEAF for understory plants by size class kg C / ha / yr F - 509 NPP_SCPF total net primary production by pft/size kgC/m2/yr F - 510 NPP_SEED NPP flux into seeds kgC/m2/yr T - 511 NPP_SEED_SCPF NPP flux into seeds by pft/size kgC/m2/yr F - 512 NPP_STEM NPP flux into stem kgC/m2/yr T - 513 NPP_STOR NPP flux into storage tissues kgC/m2/yr T - 514 NPP_STORE_CANOPY_SCLS NPP_STORE for canopy plants by size class kg C / ha / yr F - 515 NPP_STORE_UNDERSTORY_SCLS NPP_STORE for understory plants by size class kg C / ha / yr F - 516 NPP_STOR_SCPF NPP flux into storage by pft/size kgC/m2/yr F - 517 NSUBSTEPS number of adaptive timesteps in CLM timestep unitless F - 518 O2_DECOMP_DEPTH_UNSAT O2 consumption from HR and AR for non-inundated area mol/m3/s F - 519 OBU Monin-Obukhov length m F - 520 OCDEP total OC deposition (dry+wet) from atmosphere kg/m^2/s T - 521 O_SCALAR fraction by which decomposition is reduced due to anoxia unitless T - 522 PARPROF_DIF_CNLF Radiative profile of diffuse PAR through each canopy and leaf layer (averaged across PFTs) W/m2 F - 523 PARPROF_DIF_CNLFPFT Radiative profile of diffuse PAR through each canopy, leaf, and PFT W/m2 F - 524 PARPROF_DIR_CNLF Radiative profile of direct PAR through each canopy and leaf layer (averaged across PFTs) W/m2 F - 525 PARPROF_DIR_CNLFPFT Radiative profile of direct PAR through each canopy, leaf, and PFT W/m2 F - 526 PARSHA_Z_CAN PAR absorbed in the shade by top leaf layer in each canopy layer W/m2 F - 527 PARSHA_Z_CNLF PAR absorbed in the shade by each canopy and leaf layer W/m2 F - 528 PARSHA_Z_CNLFPFT PAR absorbed in the shade by each canopy, leaf, and PFT W/m2 F - 529 PARSUN_Z_CAN PAR absorbed in the sun by top leaf layer in each canopy layer W/m2 F - 530 PARSUN_Z_CNLF PAR absorbed in the sun by each canopy and leaf layer W/m2 F - 531 PARSUN_Z_CNLFPFT PAR absorbed in the sun by each canopy, leaf, and PFT W/m2 F - 532 PARVEGLN absorbed par by vegetation at local noon W/m^2 T - 533 PAS_SOMC PAS_SOM C gC/m^2 T - 534 PAS_SOMC_1m PAS_SOM C to 1 meter gC/m^2 F - 535 PAS_SOMC_TNDNCY_VERT_TRA passive soil organic C tendency due to vertical transport gC/m^3/s F - 536 PAS_SOMC_TO_ACT_SOMC decomp. of passive soil organic C to active soil organic C gC/m^2/s F - 537 PAS_SOMC_TO_ACT_SOMC_vr decomp. of passive soil organic C to active soil organic C gC/m^3/s F - 538 PAS_SOMC_vr PAS_SOM C (vertically resolved) gC/m^3 T - 539 PAS_SOMN PAS_SOM N gN/m^2 T - 540 PAS_SOMN_1m PAS_SOM N to 1 meter gN/m^2 F - 541 PAS_SOMN_TNDNCY_VERT_TRA passive soil organic N tendency due to vertical transport gN/m^3/s F - 542 PAS_SOMN_TO_ACT_SOMN decomp. of passive soil organic N to active soil organic N gN/m^2 F - 543 PAS_SOMN_TO_ACT_SOMN_vr decomp. of passive soil organic N to active soil organic N gN/m^3 F - 544 PAS_SOMN_vr PAS_SOM N (vertically resolved) gN/m^3 T - 545 PAS_SOM_HR Het. Resp. from passive soil organic gC/m^2/s F - 546 PAS_SOM_HR_vr Het. Resp. from passive soil organic gC/m^3/s F - 547 PATCH_AREA_BY_AGE patch area by age bin m2/m2 T - 548 PBOT atmospheric pressure at surface (downscaled to columns in glacier regions) Pa T - 549 PCH4 atmospheric partial pressure of CH4 Pa T - 550 PCO2 atmospheric partial pressure of CO2 Pa T - 551 PFTbiomass total PFT level biomass gC/m2 T - 552 PFTcanopycrownarea total PFT-level canopy-layer crown area m2/m2 F - 553 PFTcrownarea total PFT level crown area m2/m2 F - 554 PFTgpp total PFT-level GPP kg C m-2 y-1 T - 555 PFTleafbiomass total PFT level leaf biomass gC/m2 T - 556 PFTnindivs total PFT level number of individuals indiv / m2 T - 557 PFTnpp total PFT-level NPP kg C m-2 y-1 T - 558 PFTstorebiomass total PFT level stored biomass gC/m2 T - 559 POTENTIAL_IMMOB potential N immobilization gN/m^2/s T - 560 PRIMARYLAND_PATCHFUSION_ERROR Error in total primary lands associated with patch fusion m2 m-2 d-1 T - 561 PROMOTION_CARBONFLUX promotion-associated biomass carbon flux from understory to canopy gC/m2/s T - 562 PROMOTION_RATE_SCLS promotion rate from understory to canopy by size class indiv/ha/yr F - 563 PSurf atmospheric pressure at surface (downscaled to columns in glacier regions) Pa F - 564 Q2M 2m specific humidity kg/kg T - 565 QAF canopy air humidity kg/kg F - 566 QBOT atmospheric specific humidity (downscaled to columns in glacier regions) kg/kg T - 567 QDIRECT_THROUGHFALL direct throughfall of liquid (rain + above-canopy irrigation) mm/s F - 568 QDIRECT_THROUGHFALL_SNOW direct throughfall of snow mm/s F - 569 QDRAI sub-surface drainage mm/s T - 570 QDRAI_PERCH perched wt drainage mm/s T - 571 QDRAI_XS saturation excess drainage mm/s T - 572 QDRIP rate of excess canopy liquid falling off canopy mm/s F - 573 QDRIP_SNOW rate of excess canopy snow falling off canopy mm/s F - 574 QFLOOD runoff from river flooding mm/s T - 575 QFLX_EVAP_TOT qflx_evap_soi + qflx_evap_can + qflx_tran_veg kg m-2 s-1 T - 576 QFLX_EVAP_VEG vegetation evaporation mm H2O/s F - 577 QFLX_ICE_DYNBAL ice dynamic land cover change conversion runoff flux mm/s T - 578 QFLX_LIQDEW_TO_TOP_LAYER rate of liquid water deposited on top soil or snow layer (dew) mm H2O/s T - 579 QFLX_LIQEVAP_FROM_TOP_LAYER rate of liquid water evaporated from top soil or snow layer mm H2O/s T - 580 QFLX_LIQ_DYNBAL liq dynamic land cover change conversion runoff flux mm/s T - 581 QFLX_LIQ_GRND liquid (rain+irrigation) on ground after interception mm H2O/s F - 582 QFLX_SNOW_DRAIN drainage from snow pack mm/s T - 583 QFLX_SNOW_DRAIN_ICE drainage from snow pack melt (ice landunits only) mm/s T - 584 QFLX_SNOW_GRND snow on ground after interception mm H2O/s F - 585 QFLX_SOLIDDEW_TO_TOP_LAYER rate of solid water deposited on top soil or snow layer (frost) mm H2O/s T - 586 QFLX_SOLIDEVAP_FROM_TOP_LAYER rate of ice evaporated from top soil or snow layer (sublimation) (also includes bare ice subli mm H2O/s T - 587 QFLX_SOLIDEVAP_FROM_TOP_LAYER_ICE rate of ice evaporated from top soil or snow layer (sublimation) (also includes bare ice subli mm H2O/s F - 588 QH2OSFC surface water runoff mm/s T - 589 QH2OSFC_TO_ICE surface water converted to ice mm/s F - 590 QHR hydraulic redistribution mm/s T - 591 QICE ice growth/melt mm/s T - 592 QICE_FORC qice forcing sent to GLC mm/s F - 593 QICE_FRZ ice growth mm/s T - 594 QICE_MELT ice melt mm/s T - 595 QINFL infiltration mm/s T - 596 QINTR interception mm/s T - 597 QIRRIG_DEMAND irrigation demand mm/s F - 598 QIRRIG_DRIP water added via drip irrigation mm/s F - 599 QIRRIG_FROM_GW_CONFINED water added through confined groundwater irrigation mm/s T - 600 QIRRIG_FROM_GW_UNCONFINED water added through unconfined groundwater irrigation mm/s T - 601 QIRRIG_FROM_SURFACE water added through surface water irrigation mm/s T - 602 QIRRIG_SPRINKLER water added via sprinkler irrigation mm/s F - 603 QOVER total surface runoff (includes QH2OSFC) mm/s T - 604 QOVER_LAG time-lagged surface runoff for soil columns mm/s F - 605 QPHSNEG net negative hydraulic redistribution flux mm/s F - 606 QRGWL surface runoff at glaciers (liquid only), wetlands, lakes; also includes melted ice runoff fro mm/s T - 607 QROOTSINK water flux from soil to root in each soil-layer mm/s F - 608 QRUNOFF total liquid runoff not including correction for land use change mm/s T - 609 QRUNOFF_ICE total liquid runoff not incl corret for LULCC (ice landunits only) mm/s T - 610 QRUNOFF_ICE_TO_COUPLER total ice runoff sent to coupler (includes corrections for land use change) mm/s T - 611 QRUNOFF_ICE_TO_LIQ liquid runoff from converted ice runoff mm/s F - 612 QRUNOFF_R Rural total runoff mm/s F - 613 QRUNOFF_TO_COUPLER total liquid runoff sent to coupler (includes corrections for land use change) mm/s T - 614 QRUNOFF_U Urban total runoff mm/s F - 615 QSNOCPLIQ excess liquid h2o due to snow capping not including correction for land use change mm H2O/s T - 616 QSNOEVAP evaporation from snow (only when snl<0, otherwise it is equal to qflx_ev_soil) mm/s T - 617 QSNOFRZ column-integrated snow freezing rate kg/m2/s T - 618 QSNOFRZ_ICE column-integrated snow freezing rate (ice landunits only) mm/s T - 619 QSNOMELT snow melt rate mm/s T - 620 QSNOMELT_ICE snow melt (ice landunits only) mm/s T - 621 QSNOUNLOAD canopy snow unloading mm/s T - 622 QSNO_TEMPUNLOAD canopy snow temp unloading mm/s T - 623 QSNO_WINDUNLOAD canopy snow wind unloading mm/s T - 624 QSNWCPICE excess solid h2o due to snow capping not including correction for land use change mm H2O/s T - 625 QSOIL Ground evaporation (soil/snow evaporation + soil/snow sublimation - dew) mm/s T - 626 QSOIL_ICE Ground evaporation (ice landunits only) mm/s T - 627 QTOPSOIL water input to surface mm/s F - 628 QVEGE canopy evaporation mm/s T - 629 QVEGT canopy transpiration mm/s T - 630 Qair atmospheric specific humidity (downscaled to columns in glacier regions) kg/kg F - 631 Qh sensible heat W/m^2 F - 632 Qle total evaporation W/m^2 F - 633 Qstor storage heat flux (includes snowmelt) W/m^2 F - 634 Qtau momentum flux kg/m/s^2 F - 635 RAH1 aerodynamical resistance s/m F - 636 RAH2 aerodynamical resistance s/m F - 637 RAIN atmospheric rain, after rain/snow repartitioning based on temperature mm/s T - 638 RAIN_FROM_ATM atmospheric rain received from atmosphere (pre-repartitioning) mm/s T - 639 RAIN_ICE atmospheric rain, after rain/snow repartitioning based on temperature (ice landunits only) mm/s F - 640 RAM_LAKE aerodynamic resistance for momentum (lakes only) s/m F - 641 RAW1 aerodynamical resistance s/m F - 642 RAW2 aerodynamical resistance s/m F - 643 RB leaf boundary resistance s/m F - 644 RDARK_CANOPY_SCLS RDARK for canopy plants by size class kg C / ha / yr F - 645 RDARK_UNDERSTORY_SCLS RDARK for understory plants by size class kg C / ha / yr F - 646 RECRUITMENT Rate of recruitment by PFT indiv/ha/yr T - 647 REPROC Total carbon in live plant reproductive tissues kgC ha-1 T - 648 REPROC_SCPF reproductive carbon mass (on plant) by size-class x pft kgC/ha F - 649 RESP_G_CANOPY_SCLS RESP_G for canopy plants by size class kg C / ha / yr F - 650 RESP_G_UNDERSTORY_SCLS RESP_G for understory plants by size class kg C / ha / yr F - 651 RESP_M_CANOPY_SCLS RESP_M for canopy plants by size class kg C / ha / yr F - 652 RESP_M_UNDERSTORY_SCLS RESP_M for understory plants by size class kg C / ha / yr F - 653 RH atmospheric relative humidity % F - 654 RH2M 2m relative humidity % T - 655 RH2M_R Rural 2m specific humidity % F - 656 RH2M_U Urban 2m relative humidity % F - 657 RHAF fractional humidity of canopy air fraction F - 658 RH_LEAF fractional humidity at leaf surface fraction F - 659 ROOT_MD_CANOPY_SCLS ROOT_MD for canopy plants by size class kg C / ha / yr F - 660 ROOT_MD_UNDERSTORY_SCLS ROOT_MD for understory plants by size class kg C / ha / yr F - 661 RSCANOPY canopy resistance s m-1 T - 662 RSSHA shaded leaf stomatal resistance s/m T - 663 RSSUN sunlit leaf stomatal resistance s/m T - 664 Rainf atmospheric rain, after rain/snow repartitioning based on temperature mm/s F - 665 Rnet net radiation W/m^2 F - 666 SABG solar rad absorbed by ground W/m^2 T - 667 SABG_PEN Rural solar rad penetrating top soil or snow layer watt/m^2 T - 668 SABV solar rad absorbed by veg W/m^2 T - 669 SAI_CANOPY_SCLS stem area index(SAI) by size class m2/m2 F - 670 SAI_UNDERSTORY_SCLS number of understory plants by size class indiv/ha F - 671 SAPWC Total carbon in live plant sapwood kgC ha-1 T - 672 SAPWC_SCPF sapwood carbon mass by size-class x pft kgC/ha F - 673 SCORCH_HEIGHT SPITFIRE Flame Scorch Height (calculated per PFT in each patch age bin) m T - 674 SECONDARY_AREA_AGE_ANTHRO_DIST Secondary forest patch area age distribution since anthropgenic disturbance m2/m2 F - 675 SECONDARY_AREA_PATCH_AGE_DIST Secondary forest patch area age distribution since any kind of disturbance m2/m2 F - 676 SECONDARY_FOREST_BIOMASS Biomass on secondary lands (per total site area, mult by SECONDARY_FOREST_FRACTION to get per kgC/m2 F - 677 SECONDARY_FOREST_FRACTION Secondary forest fraction m2/m2 F - 678 SEEDS_IN Seed Production Rate gC m-2 s-1 T - 679 SEEDS_IN_EXTERN_ELEM External Seed Influx Rate kg ha-1 d-1 T - 680 SEEDS_IN_LOCAL_ELEM Within Site Seed Production Rate kg ha-1 d-1 T - 681 SEED_BANK Total Seed Mass of all PFTs gC m-2 T - 682 SEED_BANK_ELEM Total Seed Mass of all PFTs kg ha-1 T - 683 SEED_DECAY_ELEM Seed mass decay (germinated and un-germinated) kg ha-1 d-1 T - 684 SEED_GERM_ELEM Seed mass converted into new cohorts kg ha-1 d-1 T - 685 SEED_PROD_CANOPY_SCLS SEED_PROD for canopy plants by size class kg C / ha / yr F - 686 SEED_PROD_UNDERSTORY_SCLS SEED_PROD for understory plants by size class kg C / ha / yr F - 687 SITE_COLD_STATUS Site level cold status, 0=not cold-dec, 1=too cold for leaves, 2=not-too cold 0,1,2 T - 688 SITE_DAYSINCE_COLDLEAFOFF site level days elapsed since cold leaf drop days T - 689 SITE_DAYSINCE_COLDLEAFON site level days elapsed since cold leaf flush days T - 690 SITE_DAYSINCE_DROUGHTLEAFOFF site level days elapsed since drought leaf drop days T - 691 SITE_DAYSINCE_DROUGHTLEAFON site level days elapsed since drought leaf flush days T - 692 SITE_DROUGHT_STATUS Site level drought status, <2 too dry for leaves, >=2 not-too dry 0,1,2,3 T - 693 SITE_GDD site level growing degree days degC T - 694 SITE_MEANLIQVOL_DROUGHTPHEN site level mean liquid water volume for drought phen m3/m3 T - 695 SITE_NCHILLDAYS site level number of chill days days T - 696 SITE_NCOLDDAYS site level number of cold days days T - 697 SLO_SOMC SLO_SOM C gC/m^2 T - 698 SLO_SOMC_1m SLO_SOM C to 1 meter gC/m^2 F - 699 SLO_SOMC_TNDNCY_VERT_TRA slow soil organic ma C tendency due to vertical transport gC/m^3/s F - 700 SLO_SOMC_TO_ACT_SOMC decomp. of slow soil organic ma C to active soil organic C gC/m^2/s F - 701 SLO_SOMC_TO_ACT_SOMC_vr decomp. of slow soil organic ma C to active soil organic C gC/m^3/s F - 702 SLO_SOMC_TO_PAS_SOMC decomp. of slow soil organic ma C to passive soil organic C gC/m^2/s F - 703 SLO_SOMC_TO_PAS_SOMC_vr decomp. of slow soil organic ma C to passive soil organic C gC/m^3/s F - 704 SLO_SOMC_vr SLO_SOM C (vertically resolved) gC/m^3 T - 705 SLO_SOMN SLO_SOM N gN/m^2 T - 706 SLO_SOMN_1m SLO_SOM N to 1 meter gN/m^2 F - 707 SLO_SOMN_TNDNCY_VERT_TRA slow soil organic ma N tendency due to vertical transport gN/m^3/s F - 708 SLO_SOMN_TO_ACT_SOMN decomp. of slow soil organic ma N to active soil organic N gN/m^2 F - 709 SLO_SOMN_TO_ACT_SOMN_vr decomp. of slow soil organic ma N to active soil organic N gN/m^3 F - 710 SLO_SOMN_TO_PAS_SOMN decomp. of slow soil organic ma N to passive soil organic N gN/m^2 F - 711 SLO_SOMN_TO_PAS_SOMN_vr decomp. of slow soil organic ma N to passive soil organic N gN/m^3 F - 712 SLO_SOMN_vr SLO_SOM N (vertically resolved) gN/m^3 T - 713 SLO_SOM_HR_S1 Het. Resp. from slow soil organic ma gC/m^2/s F - 714 SLO_SOM_HR_S1_vr Het. Resp. from slow soil organic ma gC/m^3/s F - 715 SLO_SOM_HR_S3 Het. Resp. from slow soil organic ma gC/m^2/s F - 716 SLO_SOM_HR_S3_vr Het. Resp. from slow soil organic ma gC/m^3/s F - 717 SMINN soil mineral N gN/m^2 T - 718 SMINN_LEACHED soil mineral N pool loss to leaching gN/m^2/s T - 719 SMINN_LEACHED_vr soil mineral N pool loss to leaching gN/m^3/s F - 720 SMINN_TO_DENIT_EXCESS denitrification from excess mineral N pool gN/m^2/s F - 721 SMINN_TO_DENIT_EXCESS_vr denitrification from excess mineral N pool gN/m^3/s F - 722 SMINN_TO_DENIT_L1S1 denitrification for decomp. of metabolic litterto ACT_SOM gN/m^2 F - 723 SMINN_TO_DENIT_L1S1_vr denitrification for decomp. of metabolic litterto ACT_SOM gN/m^3 F - 724 SMINN_TO_DENIT_L2S1 denitrification for decomp. of cellulosic litterto ACT_SOM gN/m^2 F - 725 SMINN_TO_DENIT_L2S1_vr denitrification for decomp. of cellulosic litterto ACT_SOM gN/m^3 F - 726 SMINN_TO_DENIT_L3S2 denitrification for decomp. of lignin litterto SLO_SOM gN/m^2 F - 727 SMINN_TO_DENIT_L3S2_vr denitrification for decomp. of lignin litterto SLO_SOM gN/m^3 F - 728 SMINN_TO_DENIT_S1S2 denitrification for decomp. of active soil organicto SLO_SOM gN/m^2 F - 729 SMINN_TO_DENIT_S1S2_vr denitrification for decomp. of active soil organicto SLO_SOM gN/m^3 F - 730 SMINN_TO_DENIT_S1S3 denitrification for decomp. of active soil organicto PAS_SOM gN/m^2 F - 731 SMINN_TO_DENIT_S1S3_vr denitrification for decomp. of active soil organicto PAS_SOM gN/m^3 F - 732 SMINN_TO_DENIT_S2S1 denitrification for decomp. of slow soil organic mato ACT_SOM gN/m^2 F - 733 SMINN_TO_DENIT_S2S1_vr denitrification for decomp. of slow soil organic mato ACT_SOM gN/m^3 F - 734 SMINN_TO_DENIT_S2S3 denitrification for decomp. of slow soil organic mato PAS_SOM gN/m^2 F - 735 SMINN_TO_DENIT_S2S3_vr denitrification for decomp. of slow soil organic mato PAS_SOM gN/m^3 F - 736 SMINN_TO_DENIT_S3S1 denitrification for decomp. of passive soil organicto ACT_SOM gN/m^2 F - 737 SMINN_TO_DENIT_S3S1_vr denitrification for decomp. of passive soil organicto ACT_SOM gN/m^3 F - 738 SMINN_TO_PLANT plant uptake of soil mineral N gN/m^2/s T - 739 SMINN_TO_S1N_L1 mineral N flux for decomp. of MET_LITto ACT_SOM gN/m^2 F - 740 SMINN_TO_S1N_L1_vr mineral N flux for decomp. of MET_LITto ACT_SOM gN/m^3 F - 741 SMINN_TO_S1N_L2 mineral N flux for decomp. of CEL_LITto ACT_SOM gN/m^2 F - 742 SMINN_TO_S1N_L2_vr mineral N flux for decomp. of CEL_LITto ACT_SOM gN/m^3 F - 743 SMINN_TO_S1N_S2 mineral N flux for decomp. of SLO_SOMto ACT_SOM gN/m^2 F - 744 SMINN_TO_S1N_S2_vr mineral N flux for decomp. of SLO_SOMto ACT_SOM gN/m^3 F - 745 SMINN_TO_S1N_S3 mineral N flux for decomp. of PAS_SOMto ACT_SOM gN/m^2 F - 746 SMINN_TO_S1N_S3_vr mineral N flux for decomp. of PAS_SOMto ACT_SOM gN/m^3 F - 747 SMINN_TO_S2N_L3 mineral N flux for decomp. of LIG_LITto SLO_SOM gN/m^2 F - 748 SMINN_TO_S2N_L3_vr mineral N flux for decomp. of LIG_LITto SLO_SOM gN/m^3 F - 749 SMINN_TO_S2N_S1 mineral N flux for decomp. of ACT_SOMto SLO_SOM gN/m^2 F - 750 SMINN_TO_S2N_S1_vr mineral N flux for decomp. of ACT_SOMto SLO_SOM gN/m^3 F - 751 SMINN_TO_S3N_S1 mineral N flux for decomp. of ACT_SOMto PAS_SOM gN/m^2 F - 752 SMINN_TO_S3N_S1_vr mineral N flux for decomp. of ACT_SOMto PAS_SOM gN/m^3 F - 753 SMINN_TO_S3N_S2 mineral N flux for decomp. of SLO_SOMto PAS_SOM gN/m^2 F - 754 SMINN_TO_S3N_S2_vr mineral N flux for decomp. of SLO_SOMto PAS_SOM gN/m^3 F - 755 SMINN_vr soil mineral N gN/m^3 T - 756 SMP soil matric potential (natural vegetated and crop landunits only) mm T - 757 SNOBCMCL mass of BC in snow column kg/m2 T - 758 SNOBCMSL mass of BC in top snow layer kg/m2 T - 759 SNOCAN intercepted snow mm T - 760 SNODSTMCL mass of dust in snow column kg/m2 T - 761 SNODSTMSL mass of dust in top snow layer kg/m2 T - 762 SNOFSDSND direct nir incident solar radiation on snow W/m^2 F - 763 SNOFSDSNI diffuse nir incident solar radiation on snow W/m^2 F - 764 SNOFSDSVD direct vis incident solar radiation on snow W/m^2 F - 765 SNOFSDSVI diffuse vis incident solar radiation on snow W/m^2 F - 766 SNOFSRND direct nir reflected solar radiation from snow W/m^2 T - 767 SNOFSRNI diffuse nir reflected solar radiation from snow W/m^2 T - 768 SNOFSRVD direct vis reflected solar radiation from snow W/m^2 T - 769 SNOFSRVI diffuse vis reflected solar radiation from snow W/m^2 T - 770 SNOINTABS Fraction of incoming solar absorbed by lower snow layers - T - 771 SNOLIQFL top snow layer liquid water fraction (land) fraction F - 772 SNOOCMCL mass of OC in snow column kg/m2 T - 773 SNOOCMSL mass of OC in top snow layer kg/m2 T - 774 SNORDSL top snow layer effective grain radius m^-6 F - 775 SNOTTOPL snow temperature (top layer) K F - 776 SNOTTOPL_ICE snow temperature (top layer, ice landunits only) K F - 777 SNOTXMASS snow temperature times layer mass, layer sum; to get mass-weighted temperature, divide by (SNO K kg/m2 T - 778 SNOTXMASS_ICE snow temperature times layer mass, layer sum (ice landunits only); to get mass-weighted temper K kg/m2 F - 779 SNOW atmospheric snow, after rain/snow repartitioning based on temperature mm/s T - 780 SNOWDP gridcell mean snow height m T - 781 SNOWICE snow ice kg/m2 T - 782 SNOWICE_ICE snow ice (ice landunits only) kg/m2 F - 783 SNOWLIQ snow liquid water kg/m2 T - 784 SNOWLIQ_ICE snow liquid water (ice landunits only) kg/m2 F - 785 SNOW_5D 5day snow avg m F - 786 SNOW_DEPTH snow height of snow covered area m T - 787 SNOW_DEPTH_ICE snow height of snow covered area (ice landunits only) m F - 788 SNOW_FROM_ATM atmospheric snow received from atmosphere (pre-repartitioning) mm/s T - 789 SNOW_ICE atmospheric snow, after rain/snow repartitioning based on temperature (ice landunits only) mm/s F - 790 SNOW_PERSISTENCE Length of time of continuous snow cover (nat. veg. landunits only) seconds T - 791 SNOW_SINKS snow sinks (liquid water) mm/s T - 792 SNOW_SOURCES snow sources (liquid water) mm/s T - 793 SNO_ABS Absorbed solar radiation in each snow layer W/m^2 F - 794 SNO_ABS_ICE Absorbed solar radiation in each snow layer (ice landunits only) W/m^2 F - 795 SNO_BW Partial density of water in the snow pack (ice + liquid) kg/m3 F - 796 SNO_BW_ICE Partial density of water in the snow pack (ice + liquid, ice landunits only) kg/m3 F - 797 SNO_EXISTENCE Fraction of averaging period for which each snow layer existed unitless F - 798 SNO_FRZ snow freezing rate in each snow layer kg/m2/s F - 799 SNO_FRZ_ICE snow freezing rate in each snow layer (ice landunits only) mm/s F - 800 SNO_GS Mean snow grain size Microns F - 801 SNO_GS_ICE Mean snow grain size (ice landunits only) Microns F - 802 SNO_ICE Snow ice content kg/m2 F - 803 SNO_LIQH2O Snow liquid water content kg/m2 F - 804 SNO_MELT snow melt rate in each snow layer mm/s F - 805 SNO_MELT_ICE snow melt rate in each snow layer (ice landunits only) mm/s F - 806 SNO_T Snow temperatures K F - 807 SNO_TK Thermal conductivity W/m-K F - 808 SNO_TK_ICE Thermal conductivity (ice landunits only) W/m-K F - 809 SNO_T_ICE Snow temperatures (ice landunits only) K F - 810 SNO_Z Snow layer thicknesses m F - 811 SNO_Z_ICE Snow layer thicknesses (ice landunits only) m F - 812 SNOdTdzL top snow layer temperature gradient (land) K/m F - 813 SOIL10 10-day running mean of 12cm layer soil K F - 814 SOILC_HR soil C heterotrophic respiration gC/m^2/s T - 815 SOILC_vr SOIL C (vertically resolved) gC/m^3 T - 816 SOILICE soil ice (natural vegetated and crop landunits only) kg/m2 T - 817 SOILLIQ soil liquid water (natural vegetated and crop landunits only) kg/m2 T - 818 SOILN_vr SOIL N (vertically resolved) gN/m^3 T - 819 SOILPSI soil water potential in each soil layer MPa F - 820 SOILRESIS soil resistance to evaporation s/m T - 821 SOILWATER_10CM soil liquid water + ice in top 10cm of soil (veg landunits only) kg/m2 T - 822 SOMC_FIRE C loss due to peat burning gC/m^2/s T - 823 SOM_C_LEACHED total flux of C from SOM pools due to leaching gC/m^2/s T - 824 SOM_N_LEACHED total flux of N from SOM pools due to leaching gN/m^2/s F - 825 STOREC Total carbon in live plant storage kgC ha-1 T - 826 STOREC_SCPF storage carbon mass by size-class x pft kgC/ha F - 827 SUM_FUEL total ground fuel related to ros (omits 1000hr fuels) gC m-2 T - 828 SUM_FUEL_BY_PATCH_AGE spitfire ground fuel related to ros (omits 1000hr fuels) within each patch age bin (divide by gC / m2 of site area T - 829 SUPPLEMENT_TO_SMINN supplemental N supply gN/m^2/s T - 830 SWBGT 2 m Simplified Wetbulb Globe Temp C T - 831 SWBGT_R Rural 2 m Simplified Wetbulb Globe Temp C T - 832 SWBGT_U Urban 2 m Simplified Wetbulb Globe Temp C T - 833 SWdown atmospheric incident solar radiation W/m^2 F - 834 SWup upwelling shortwave radiation W/m^2 F - 835 SoilAlpha factor limiting ground evap unitless F - 836 SoilAlpha_U urban factor limiting ground evap unitless F - 837 T10 10-day running mean of 2-m temperature K F - 838 TAF canopy air temperature K F - 839 TAUX zonal surface stress kg/m/s^2 T - 840 TAUY meridional surface stress kg/m/s^2 T - 841 TBOT atmospheric air temperature (downscaled to columns in glacier regions) K T - 842 TBUILD internal urban building air temperature K T - 843 TBUILD_MAX prescribed maximum interior building temperature K F - 844 TFLOOR floor temperature K F - 845 TG ground temperature K T - 846 TG_ICE ground temperature (ice landunits only) K F - 847 TG_R Rural ground temperature K F - 848 TG_U Urban ground temperature K F - 849 TH2OSFC surface water temperature K T - 850 THBOT atmospheric air potential temperature (downscaled to columns in glacier regions) K T - 851 TKE1 top lake level eddy thermal conductivity W/(mK) T - 852 TLAI total projected leaf area index m^2/m^2 T - 853 TLAKE lake temperature K T - 854 TOPO_COL column-level topographic height m F - 855 TOPO_COL_ICE column-level topographic height (ice landunits only) m F - 856 TOPO_FORC topograephic height sent to GLC m F - 857 TOTCOLCH4 total belowground CH4 (0 for non-lake special landunits in the absence of dynamic landunits) gC/m2 T - 858 TOTLITC total litter carbon gC/m^2 T - 859 TOTLITC_1m total litter carbon to 1 meter depth gC/m^2 T - 860 TOTLITN total litter N gN/m^2 T - 861 TOTLITN_1m total litter N to 1 meter gN/m^2 T - 862 TOTSOILICE vertically summed soil cie (veg landunits only) kg/m2 T - 863 TOTSOILLIQ vertically summed soil liquid water (veg landunits only) kg/m2 T - 864 TOTSOMC total soil organic matter carbon gC/m^2 T - 865 TOTSOMC_1m total soil organic matter carbon to 1 meter depth gC/m^2 T - 866 TOTSOMN total soil organic matter N gN/m^2 T - 867 TOTSOMN_1m total soil organic matter N to 1 meter gN/m^2 T - 868 TOTVEGC Total carbon in live plants kgC ha-1 T - 869 TOTVEGC_SCPF total vegetation carbon mass in live plants by size-class x pft kgC/ha F - 870 TRAFFICFLUX sensible heat flux from urban traffic W/m^2 F - 871 TREFMNAV daily minimum of average 2-m temperature K T - 872 TREFMNAV_R Rural daily minimum of average 2-m temperature K F - 873 TREFMNAV_U Urban daily minimum of average 2-m temperature K F - 874 TREFMXAV daily maximum of average 2-m temperature K T - 875 TREFMXAV_R Rural daily maximum of average 2-m temperature K F - 876 TREFMXAV_U Urban daily maximum of average 2-m temperature K F - 877 TRIMMING Degree to which canopy expansion is limited by leaf economics none T - 878 TRIMMING_CANOPY_SCLS trimming term of canopy plants by size class indiv/ha F - 879 TRIMMING_UNDERSTORY_SCLS trimming term of understory plants by size class indiv/ha F - 880 TROOF_INNER roof inside surface temperature K F - 881 TSA 2m air temperature K T - 882 TSAI total projected stem area index m^2/m^2 T - 883 TSA_ICE 2m air temperature (ice landunits only) K F - 884 TSA_R Rural 2m air temperature K F - 885 TSA_U Urban 2m air temperature K F - 886 TSHDW_INNER shadewall inside surface temperature K F - 887 TSKIN skin temperature K T - 888 TSL temperature of near-surface soil layer (natural vegetated and crop landunits only) K T - 889 TSOI soil temperature (natural vegetated and crop landunits only) K T - 890 TSOI_10CM soil temperature in top 10cm of soil K T - 891 TSOI_ICE soil temperature (ice landunits only) K T - 892 TSRF_FORC surface temperature sent to GLC K F - 893 TSUNW_INNER sunwall inside surface temperature K F - 894 TV vegetation temperature K T - 895 TV24 vegetation temperature (last 24hrs) K F - 896 TV240 vegetation temperature (last 240hrs) K F - 897 TWS total water storage mm T - 898 T_SCALAR temperature inhibition of decomposition unitless T - 899 Tair atmospheric air temperature (downscaled to columns in glacier regions) K F - 900 Tair_from_atm atmospheric air temperature received from atmosphere (pre-downscaling) K F - 901 U10 10-m wind m/s T - 902 U10_DUST 10-m wind for dust model m/s T - 903 U10_ICE 10-m wind (ice landunits only) m/s F - 904 UAF canopy air speed m/s F - 905 UM wind speed plus stability effect m/s F - 906 URBAN_AC urban air conditioning flux W/m^2 T - 907 URBAN_HEAT urban heating flux W/m^2 T - 908 USTAR aerodynamical resistance s/m F - 909 UST_LAKE friction velocity (lakes only) m/s F - 910 VA atmospheric wind speed plus convective velocity m/s F - 911 VOLR river channel total water storage m3 T - 912 VOLRMCH river channel main channel water storage m3 T - 913 VPD vpd Pa F - 914 VPD2M 2m vapor pressure deficit Pa T - 915 VPD_CAN canopy vapor pressure deficit kPa T - 916 WASTEHEAT sensible heat flux from heating/cooling sources of urban waste heat W/m^2 T - 917 WBT 2 m Stull Wet Bulb C T - 918 WBT_R Rural 2 m Stull Wet Bulb C T - 919 WBT_U Urban 2 m Stull Wet Bulb C T - 920 WIND atmospheric wind velocity magnitude m/s T - 921 WOOD_PRODUCT Total wood product from logging gC/m2 F - 922 WTGQ surface tracer conductance m/s T - 923 W_SCALAR Moisture (dryness) inhibition of decomposition unitless T - 924 Wind atmospheric wind velocity magnitude m/s F - 925 YESTERDAYCANLEV_CANOPY_SCLS Yesterdays canopy level for canopy plants by size class indiv/ha F - 926 YESTERDAYCANLEV_UNDERSTORY_SCLS Yesterdays canopy level for understory plants by size class indiv/ha F - 927 Z0HG roughness length over ground, sensible heat m F - 928 Z0M momentum roughness length m F - 929 Z0MG roughness length over ground, momentum m F - 930 Z0M_TO_COUPLER roughness length, momentum: gridcell average sent to coupler m F - 931 Z0QG roughness length over ground, latent heat m F - 932 ZBOT atmospheric reference height m T - 933 ZETA dimensionless stability parameter unitless F - 934 ZII convective boundary height m F - 935 ZSTAR_BY_AGE product of zstar and patch area by age bin (divide by PATCH_AREA_BY_AGE to get mean zstar) m F - 936 ZWT water table depth (natural vegetated and crop landunits only) m T - 937 ZWT_CH4_UNSAT depth of water table for methane production used in non-inundated area m T - 938 ZWT_PERCH perched water table depth (natural vegetated and crop landunits only) m T - 939 num_iter number of iterations unitless F -==== =================================== ============================================================================================== ================================================================= ======= diff --git a/doc/source/users_guide/setting-up-and-running-a-case/master_list_nofates.rst b/doc/source/users_guide/setting-up-and-running-a-case/master_list_nofates.rst deleted file mode 100644 index 776acc9833..0000000000 --- a/doc/source/users_guide/setting-up-and-running-a-case/master_list_nofates.rst +++ /dev/null @@ -1,1303 +0,0 @@ -============================= -CTSM History Fields (nofates) -============================= - -CAUTION: Not all variables are relevant / present for all CTSM cases. -Key flags used in this CTSM case: -use_cn = T -use_crop = T -use_fates = F - -==== =================================== ============================================================================================== ================================================================= ======= -CTSM History Fields ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - # Variable Name Long Description Units Active? -==== =================================== ============================================================================================== ================================================================= ======= - 1 A10TMIN 10-day running mean of min 2-m temperature K F - 2 A5TMIN 5-day running mean of min 2-m temperature K F - 3 ACTUAL_IMMOB actual N immobilization gN/m^2/s T - 4 ACTUAL_IMMOB_NH4 immobilization of NH4 gN/m^3/s F - 5 ACTUAL_IMMOB_NO3 immobilization of NO3 gN/m^3/s F - 6 ACTUAL_IMMOB_vr actual N immobilization gN/m^3/s F - 7 ACT_SOMC ACT_SOM C gC/m^2 T - 8 ACT_SOMC_1m ACT_SOM C to 1 meter gC/m^2 F - 9 ACT_SOMC_TNDNCY_VERT_TRA active soil organic C tendency due to vertical transport gC/m^3/s F - 10 ACT_SOMC_TO_PAS_SOMC decomp. of active soil organic C to passive soil organic C gC/m^2/s F - 11 ACT_SOMC_TO_PAS_SOMC_vr decomp. of active soil organic C to passive soil organic C gC/m^3/s F - 12 ACT_SOMC_TO_SLO_SOMC decomp. of active soil organic C to slow soil organic ma C gC/m^2/s F - 13 ACT_SOMC_TO_SLO_SOMC_vr decomp. of active soil organic C to slow soil organic ma C gC/m^3/s F - 14 ACT_SOMC_vr ACT_SOM C (vertically resolved) gC/m^3 T - 15 ACT_SOMN ACT_SOM N gN/m^2 T - 16 ACT_SOMN_1m ACT_SOM N to 1 meter gN/m^2 F - 17 ACT_SOMN_TNDNCY_VERT_TRA active soil organic N tendency due to vertical transport gN/m^3/s F - 18 ACT_SOMN_TO_PAS_SOMN decomp. of active soil organic N to passive soil organic N gN/m^2 F - 19 ACT_SOMN_TO_PAS_SOMN_vr decomp. of active soil organic N to passive soil organic N gN/m^3 F - 20 ACT_SOMN_TO_SLO_SOMN decomp. of active soil organic N to slow soil organic ma N gN/m^2 F - 21 ACT_SOMN_TO_SLO_SOMN_vr decomp. of active soil organic N to slow soil organic ma N gN/m^3 F - 22 ACT_SOMN_vr ACT_SOM N (vertically resolved) gN/m^3 T - 23 ACT_SOM_HR_S2 Het. Resp. from active soil organic gC/m^2/s F - 24 ACT_SOM_HR_S2_vr Het. Resp. from active soil organic gC/m^3/s F - 25 ACT_SOM_HR_S3 Het. Resp. from active soil organic gC/m^2/s F - 26 ACT_SOM_HR_S3_vr Het. Resp. from active soil organic gC/m^3/s F - 27 AGLB Aboveground leaf biomass kg/m^2 F - 28 AGNPP aboveground NPP gC/m^2/s T - 29 AGSB Aboveground stem biomass kg/m^2 F - 30 ALBD surface albedo (direct) proportion T - 31 ALBDSF diagnostic snow-free surface albedo (direct) proportion T - 32 ALBGRD ground albedo (direct) proportion F - 33 ALBGRI ground albedo (indirect) proportion F - 34 ALBI surface albedo (indirect) proportion T - 35 ALBISF diagnostic snow-free surface albedo (indirect) proportion T - 36 ALPHA alpha coefficient for VOC calc non F - 37 ALT current active layer thickness m T - 38 ALTMAX maximum annual active layer thickness m T - 39 ALTMAX_LASTYEAR maximum prior year active layer thickness m F - 40 ANNAVG_T2M annual average 2m air temperature K F - 41 ANNMAX_RETRANSN annual max of retranslocated N pool gN/m^2 F - 42 ANNSUM_COUNTER seconds since last annual accumulator turnover s F - 43 ANNSUM_NPP annual sum of NPP gC/m^2/yr F - 44 ANNSUM_POTENTIAL_GPP annual sum of potential GPP gN/m^2/yr F - 45 APPAR_TEMP 2 m apparent temperature C T - 46 APPAR_TEMP_R Rural 2 m apparent temperature C T - 47 APPAR_TEMP_U Urban 2 m apparent temperature C T - 48 AR autotrophic respiration (MR + GR) gC/m^2/s T - 49 ATM_TOPO atmospheric surface height m T - 50 AVAILC C flux available for allocation gC/m^2/s F - 51 AVAIL_RETRANSN N flux available from retranslocation pool gN/m^2/s F - 52 AnnET Annual ET mm/s F - 53 BAF_CROP fractional area burned for crop s-1 T - 54 BAF_PEATF fractional area burned in peatland s-1 T - 55 BCDEP total BC deposition (dry+wet) from atmosphere kg/m^2/s T - 56 BETA coefficient of convective velocity none F - 57 BGLFR background litterfall rate 1/s F - 58 BGNPP belowground NPP gC/m^2/s T - 59 BGTR background transfer growth rate 1/s F - 60 BTRANMN daily minimum of transpiration beta factor unitless T - 61 CANNAVG_T2M annual average of 2m air temperature K F - 62 CANNSUM_NPP annual sum of column-level NPP gC/m^2/s F - 63 CEL_LITC CEL_LIT C gC/m^2 T - 64 CEL_LITC_1m CEL_LIT C to 1 meter gC/m^2 F - 65 CEL_LITC_TNDNCY_VERT_TRA cellulosic litter C tendency due to vertical transport gC/m^3/s F - 66 CEL_LITC_TO_ACT_SOMC decomp. of cellulosic litter C to active soil organic C gC/m^2/s F - 67 CEL_LITC_TO_ACT_SOMC_vr decomp. of cellulosic litter C to active soil organic C gC/m^3/s F - 68 CEL_LITC_vr CEL_LIT C (vertically resolved) gC/m^3 T - 69 CEL_LITN CEL_LIT N gN/m^2 T - 70 CEL_LITN_1m CEL_LIT N to 1 meter gN/m^2 F - 71 CEL_LITN_TNDNCY_VERT_TRA cellulosic litter N tendency due to vertical transport gN/m^3/s F - 72 CEL_LITN_TO_ACT_SOMN decomp. of cellulosic litter N to active soil organic N gN/m^2 F - 73 CEL_LITN_TO_ACT_SOMN_vr decomp. of cellulosic litter N to active soil organic N gN/m^3 F - 74 CEL_LITN_vr CEL_LIT N (vertically resolved) gN/m^3 T - 75 CEL_LIT_HR Het. Resp. from cellulosic litter gC/m^2/s F - 76 CEL_LIT_HR_vr Het. Resp. from cellulosic litter gC/m^3/s F - 77 CGRND deriv. of soil energy flux wrt to soil temp W/m^2/K F - 78 CGRNDL deriv. of soil latent heat flux wrt soil temp W/m^2/K F - 79 CGRNDS deriv. of soil sensible heat flux wrt soil temp W/m^2/K F - 80 CH4PROD Gridcell total production of CH4 gC/m2/s T - 81 CH4_EBUL_TOTAL_SAT ebullition surface CH4 flux; (+ to atm) mol/m2/s F - 82 CH4_EBUL_TOTAL_UNSAT ebullition surface CH4 flux; (+ to atm) mol/m2/s F - 83 CH4_SURF_AERE_SAT aerenchyma surface CH4 flux for inundated area; (+ to atm) mol/m2/s T - 84 CH4_SURF_AERE_UNSAT aerenchyma surface CH4 flux for non-inundated area; (+ to atm) mol/m2/s T - 85 CH4_SURF_DIFF_SAT diffusive surface CH4 flux for inundated / lake area; (+ to atm) mol/m2/s T - 86 CH4_SURF_DIFF_UNSAT diffusive surface CH4 flux for non-inundated area; (+ to atm) mol/m2/s T - 87 CH4_SURF_EBUL_SAT ebullition surface CH4 flux for inundated / lake area; (+ to atm) mol/m2/s T - 88 CH4_SURF_EBUL_UNSAT ebullition surface CH4 flux for non-inundated area; (+ to atm) mol/m2/s T - 89 COL_CTRUNC column-level sink for C truncation gC/m^2 F - 90 COL_FIRE_CLOSS total column-level fire C loss for non-peat fires outside land-type converted region gC/m^2/s T - 91 COL_FIRE_NLOSS total column-level fire N loss gN/m^2/s T - 92 COL_NTRUNC column-level sink for N truncation gN/m^2 F - 93 CONC_CH4_SAT CH4 soil Concentration for inundated / lake area mol/m3 F - 94 CONC_CH4_UNSAT CH4 soil Concentration for non-inundated area mol/m3 F - 95 CONC_O2_SAT O2 soil Concentration for inundated / lake area mol/m3 T - 96 CONC_O2_UNSAT O2 soil Concentration for non-inundated area mol/m3 T - 97 COST_NACTIVE Cost of active uptake gN/gC T - 98 COST_NFIX Cost of fixation gN/gC T - 99 COST_NRETRANS Cost of retranslocation gN/gC T - 100 COSZEN cosine of solar zenith angle none F - 101 CPHASE crop phenology phase 0-not planted, 1-planted, 2-leaf emerge, 3-grain fill, 4-harvest T - 102 CPOOL temporary photosynthate C pool gC/m^2 T - 103 CPOOL_DEADCROOT_GR dead coarse root growth respiration gC/m^2/s F - 104 CPOOL_DEADCROOT_STORAGE_GR dead coarse root growth respiration to storage gC/m^2/s F - 105 CPOOL_DEADSTEM_GR dead stem growth respiration gC/m^2/s F - 106 CPOOL_DEADSTEM_STORAGE_GR dead stem growth respiration to storage gC/m^2/s F - 107 CPOOL_FROOT_GR fine root growth respiration gC/m^2/s F - 108 CPOOL_FROOT_STORAGE_GR fine root growth respiration to storage gC/m^2/s F - 109 CPOOL_LEAF_GR leaf growth respiration gC/m^2/s F - 110 CPOOL_LEAF_STORAGE_GR leaf growth respiration to storage gC/m^2/s F - 111 CPOOL_LIVECROOT_GR live coarse root growth respiration gC/m^2/s F - 112 CPOOL_LIVECROOT_STORAGE_GR live coarse root growth respiration to storage gC/m^2/s F - 113 CPOOL_LIVESTEM_GR live stem growth respiration gC/m^2/s F - 114 CPOOL_LIVESTEM_STORAGE_GR live stem growth respiration to storage gC/m^2/s F - 115 CPOOL_TO_DEADCROOTC allocation to dead coarse root C gC/m^2/s F - 116 CPOOL_TO_DEADCROOTC_STORAGE allocation to dead coarse root C storage gC/m^2/s F - 117 CPOOL_TO_DEADSTEMC allocation to dead stem C gC/m^2/s F - 118 CPOOL_TO_DEADSTEMC_STORAGE allocation to dead stem C storage gC/m^2/s F - 119 CPOOL_TO_FROOTC allocation to fine root C gC/m^2/s F - 120 CPOOL_TO_FROOTC_STORAGE allocation to fine root C storage gC/m^2/s F - 121 CPOOL_TO_GRESP_STORAGE allocation to growth respiration storage gC/m^2/s F - 122 CPOOL_TO_LEAFC allocation to leaf C gC/m^2/s F - 123 CPOOL_TO_LEAFC_STORAGE allocation to leaf C storage gC/m^2/s F - 124 CPOOL_TO_LIVECROOTC allocation to live coarse root C gC/m^2/s F - 125 CPOOL_TO_LIVECROOTC_STORAGE allocation to live coarse root C storage gC/m^2/s F - 126 CPOOL_TO_LIVESTEMC allocation to live stem C gC/m^2/s F - 127 CPOOL_TO_LIVESTEMC_STORAGE allocation to live stem C storage gC/m^2/s F - 128 CROOT_PROF profile for litter C and N inputs from coarse roots 1/m F - 129 CROPPROD1C 1-yr crop product (grain+biofuel) C gC/m^2 T - 130 CROPPROD1C_LOSS loss from 1-yr crop product pool gC/m^2/s T - 131 CROPPROD1N 1-yr crop product (grain+biofuel) N gN/m^2 T - 132 CROPPROD1N_LOSS loss from 1-yr crop product pool gN/m^2/s T - 133 CROPSEEDC_DEFICIT C used for crop seed that needs to be repaid gC/m^2 T - 134 CROPSEEDN_DEFICIT N used for crop seed that needs to be repaid gN/m^2 F - 135 CROP_SEEDC_TO_LEAF crop seed source to leaf gC/m^2/s F - 136 CROP_SEEDN_TO_LEAF crop seed source to leaf gN/m^2/s F - 137 CURRENT_GR growth resp for new growth displayed in this timestep gC/m^2/s F - 138 CWDC CWD C gC/m^2 T - 139 CWDC_1m CWD C to 1 meter gC/m^2 F - 140 CWDC_HR cwd C heterotrophic respiration gC/m^2/s F - 141 CWDC_LOSS coarse woody debris C loss gC/m^2/s T - 142 CWDC_TO_CEL_LITC decomp. of coarse woody debris C to cellulosic litter C gC/m^2/s F - 143 CWDC_TO_CEL_LITC_vr decomp. of coarse woody debris C to cellulosic litter C gC/m^3/s F - 144 CWDC_TO_LIG_LITC decomp. of coarse woody debris C to lignin litter C gC/m^2/s F - 145 CWDC_TO_LIG_LITC_vr decomp. of coarse woody debris C to lignin litter C gC/m^3/s F - 146 CWDC_vr CWD C (vertically resolved) gC/m^3 T - 147 CWDN CWD N gN/m^2 T - 148 CWDN_1m CWD N to 1 meter gN/m^2 F - 149 CWDN_TO_CEL_LITN decomp. of coarse woody debris N to cellulosic litter N gN/m^2 F - 150 CWDN_TO_CEL_LITN_vr decomp. of coarse woody debris N to cellulosic litter N gN/m^3 F - 151 CWDN_TO_LIG_LITN decomp. of coarse woody debris N to lignin litter N gN/m^2 F - 152 CWDN_TO_LIG_LITN_vr decomp. of coarse woody debris N to lignin litter N gN/m^3 F - 153 CWDN_vr CWD N (vertically resolved) gN/m^3 T - 154 CWD_HR_L2 Het. Resp. from coarse woody debris gC/m^2/s F - 155 CWD_HR_L2_vr Het. Resp. from coarse woody debris gC/m^3/s F - 156 CWD_HR_L3 Het. Resp. from coarse woody debris gC/m^2/s F - 157 CWD_HR_L3_vr Het. Resp. from coarse woody debris gC/m^3/s F - 158 C_ALLOMETRY C allocation index none F - 159 DAYL daylength s F - 160 DAYS_ACTIVE number of days since last dormancy days F - 161 DEADCROOTC dead coarse root C gC/m^2 T - 162 DEADCROOTC_STORAGE dead coarse root C storage gC/m^2 F - 163 DEADCROOTC_STORAGE_TO_XFER dead coarse root C shift storage to transfer gC/m^2/s F - 164 DEADCROOTC_XFER dead coarse root C transfer gC/m^2 F - 165 DEADCROOTC_XFER_TO_DEADCROOTC dead coarse root C growth from storage gC/m^2/s F - 166 DEADCROOTN dead coarse root N gN/m^2 T - 167 DEADCROOTN_STORAGE dead coarse root N storage gN/m^2 F - 168 DEADCROOTN_STORAGE_TO_XFER dead coarse root N shift storage to transfer gN/m^2/s F - 169 DEADCROOTN_XFER dead coarse root N transfer gN/m^2 F - 170 DEADCROOTN_XFER_TO_DEADCROOTN dead coarse root N growth from storage gN/m^2/s F - 171 DEADSTEMC dead stem C gC/m^2 T - 172 DEADSTEMC_STORAGE dead stem C storage gC/m^2 F - 173 DEADSTEMC_STORAGE_TO_XFER dead stem C shift storage to transfer gC/m^2/s F - 174 DEADSTEMC_XFER dead stem C transfer gC/m^2 F - 175 DEADSTEMC_XFER_TO_DEADSTEMC dead stem C growth from storage gC/m^2/s F - 176 DEADSTEMN dead stem N gN/m^2 T - 177 DEADSTEMN_STORAGE dead stem N storage gN/m^2 F - 178 DEADSTEMN_STORAGE_TO_XFER dead stem N shift storage to transfer gN/m^2/s F - 179 DEADSTEMN_XFER dead stem N transfer gN/m^2 F - 180 DEADSTEMN_XFER_TO_DEADSTEMN dead stem N growth from storage gN/m^2/s F - 181 DENIT total rate of denitrification gN/m^2/s T - 182 DGNETDT derivative of net ground heat flux wrt soil temp W/m^2/K F - 183 DISCOI 2 m Discomfort Index C T - 184 DISCOIS 2 m Stull Discomfort Index C T - 185 DISCOIS_R Rural 2 m Stull Discomfort Index C T - 186 DISCOIS_U Urban 2 m Stull Discomfort Index C T - 187 DISCOI_R Rural 2 m Discomfort Index C T - 188 DISCOI_U Urban 2 m Discomfort Index C T - 189 DISPLA displacement height m F - 190 DISPVEGC displayed veg carbon, excluding storage and cpool gC/m^2 T - 191 DISPVEGN displayed vegetation nitrogen gN/m^2 T - 192 DLRAD downward longwave radiation below the canopy W/m^2 F - 193 DORMANT_FLAG dormancy flag none F - 194 DOWNREG fractional reduction in GPP due to N limitation proportion F - 195 DPVLTRB1 turbulent deposition velocity 1 m/s F - 196 DPVLTRB2 turbulent deposition velocity 2 m/s F - 197 DPVLTRB3 turbulent deposition velocity 3 m/s F - 198 DPVLTRB4 turbulent deposition velocity 4 m/s F - 199 DSL dry surface layer thickness mm T - 200 DSTDEP total dust deposition (dry+wet) from atmosphere kg/m^2/s T - 201 DSTFLXT total surface dust emission kg/m2/s T - 202 DT_VEG change in t_veg, last iteration K F - 203 DWT_CONV_CFLUX conversion C flux (immediate loss to atm) (0 at all times except first timestep of year) gC/m^2/s T - 204 DWT_CONV_CFLUX_DRIBBLED conversion C flux (immediate loss to atm), dribbled throughout the year gC/m^2/s T - 205 DWT_CONV_CFLUX_PATCH patch-level conversion C flux (immediate loss to atm) (0 at all times except first timestep of gC/m^2/s F - 206 DWT_CONV_NFLUX conversion N flux (immediate loss to atm) (0 at all times except first timestep of year) gN/m^2/s T - 207 DWT_CONV_NFLUX_PATCH patch-level conversion N flux (immediate loss to atm) (0 at all times except first timestep of gN/m^2/s F - 208 DWT_CROPPROD1C_GAIN landcover change-driven addition to 1-year crop product pool gC/m^2/s T - 209 DWT_CROPPROD1N_GAIN landcover change-driven addition to 1-year crop product pool gN/m^2/s T - 210 DWT_DEADCROOTC_TO_CWDC dead coarse root to CWD due to landcover change gC/m^2/s F - 211 DWT_DEADCROOTN_TO_CWDN dead coarse root to CWD due to landcover change gN/m^2/s F - 212 DWT_FROOTC_TO_CEL_LIT_C fine root to cellulosic litter due to landcover change gC/m^2/s F - 213 DWT_FROOTC_TO_LIG_LIT_C fine root to lignin litter due to landcover change gC/m^2/s F - 214 DWT_FROOTC_TO_MET_LIT_C fine root to metabolic litter due to landcover change gC/m^2/s F - 215 DWT_FROOTN_TO_CEL_LIT_N fine root N to cellulosic litter due to landcover change gN/m^2/s F - 216 DWT_FROOTN_TO_LIG_LIT_N fine root N to lignin litter due to landcover change gN/m^2/s F - 217 DWT_FROOTN_TO_MET_LIT_N fine root N to metabolic litter due to landcover change gN/m^2/s F - 218 DWT_LIVECROOTC_TO_CWDC live coarse root to CWD due to landcover change gC/m^2/s F - 219 DWT_LIVECROOTN_TO_CWDN live coarse root to CWD due to landcover change gN/m^2/s F - 220 DWT_PROD100C_GAIN landcover change-driven addition to 100-yr wood product pool gC/m^2/s F - 221 DWT_PROD100N_GAIN landcover change-driven addition to 100-yr wood product pool gN/m^2/s F - 222 DWT_PROD10C_GAIN landcover change-driven addition to 10-yr wood product pool gC/m^2/s F - 223 DWT_PROD10N_GAIN landcover change-driven addition to 10-yr wood product pool gN/m^2/s F - 224 DWT_SEEDC_TO_DEADSTEM seed source to patch-level deadstem gC/m^2/s F - 225 DWT_SEEDC_TO_DEADSTEM_PATCH patch-level seed source to patch-level deadstem (per-area-gridcell; only makes sense with dov2 gC/m^2/s F - 226 DWT_SEEDC_TO_LEAF seed source to patch-level leaf gC/m^2/s F - 227 DWT_SEEDC_TO_LEAF_PATCH patch-level seed source to patch-level leaf (per-area-gridcell; only makes sense with dov2xy=. gC/m^2/s F - 228 DWT_SEEDN_TO_DEADSTEM seed source to patch-level deadstem gN/m^2/s T - 229 DWT_SEEDN_TO_DEADSTEM_PATCH patch-level seed source to patch-level deadstem (per-area-gridcell; only makes sense with dov2 gN/m^2/s F - 230 DWT_SEEDN_TO_LEAF seed source to patch-level leaf gN/m^2/s T - 231 DWT_SEEDN_TO_LEAF_PATCH patch-level seed source to patch-level leaf (per-area-gridcell; only makes sense with dov2xy=. gN/m^2/s F - 232 DWT_SLASH_CFLUX slash C flux (to litter diagnostic only) (0 at all times except first timestep of year) gC/m^2/s T - 233 DWT_SLASH_CFLUX_PATCH patch-level slash C flux (to litter diagnostic only) (0 at all times except first timestep of gC/m^2/s F - 234 DWT_WOODPRODC_GAIN landcover change-driven addition to wood product pools gC/m^2/s T - 235 DWT_WOODPRODN_GAIN landcover change-driven addition to wood product pools gN/m^2/s T - 236 DWT_WOOD_PRODUCTC_GAIN_PATCH patch-level landcover change-driven addition to wood product pools(0 at all times except first gC/m^2/s F - 237 DYN_COL_ADJUSTMENTS_CH4 Adjustments in ch4 due to dynamic column areas; only makes sense at the column level: should n gC/m^2 F - 238 DYN_COL_SOIL_ADJUSTMENTS_C Adjustments in soil carbon due to dynamic column areas; only makes sense at the column level: gC/m^2 F - 239 DYN_COL_SOIL_ADJUSTMENTS_N Adjustments in soil nitrogen due to dynamic column areas; only makes sense at the column level gN/m^2 F - 240 DYN_COL_SOIL_ADJUSTMENTS_NH4 Adjustments in soil NH4 due to dynamic column areas; only makes sense at the column level: sho gN/m^2 F - 241 DYN_COL_SOIL_ADJUSTMENTS_NO3 Adjustments in soil NO3 due to dynamic column areas; only makes sense at the column level: sho gN/m^2 F - 242 EFF_POROSITY effective porosity = porosity - vol_ice proportion F - 243 EFLXBUILD building heat flux from change in interior building air temperature W/m^2 T - 244 EFLX_DYNBAL dynamic land cover change conversion energy flux W/m^2 T - 245 EFLX_GNET net heat flux into ground W/m^2 F - 246 EFLX_GRND_LAKE net heat flux into lake/snow surface, excluding light transmission W/m^2 T - 247 EFLX_LH_TOT total latent heat flux [+ to atm] W/m^2 T - 248 EFLX_LH_TOT_ICE total latent heat flux [+ to atm] (ice landunits only) W/m^2 F - 249 EFLX_LH_TOT_R Rural total evaporation W/m^2 T - 250 EFLX_LH_TOT_U Urban total evaporation W/m^2 F - 251 EFLX_SOIL_GRND soil heat flux [+ into soil] W/m^2 F - 252 ELAI exposed one-sided leaf area index m^2/m^2 T - 253 EMG ground emissivity proportion F - 254 EMV vegetation emissivity proportion F - 255 EOPT Eopt coefficient for VOC calc non F - 256 EPT 2 m Equiv Pot Temp K T - 257 EPT_R Rural 2 m Equiv Pot Temp K T - 258 EPT_U Urban 2 m Equiv Pot Temp K T - 259 ER total ecosystem respiration, autotrophic + heterotrophic gC/m^2/s T - 260 ERRH2O total water conservation error mm T - 261 ERRH2OSNO imbalance in snow depth (liquid water) mm T - 262 ERRSEB surface energy conservation error W/m^2 T - 263 ERRSOI soil/lake energy conservation error W/m^2 T - 264 ERRSOL solar radiation conservation error W/m^2 T - 265 ESAI exposed one-sided stem area index m^2/m^2 T - 266 EXCESSC_MR excess C maintenance respiration gC/m^2/s F - 267 EXCESS_CFLUX C flux not allocated due to downregulation gC/m^2/s F - 268 FAREA_BURNED timestep fractional area burned s-1 T - 269 FCANSNO fraction of canopy that is wet proportion F - 270 FCEV canopy evaporation W/m^2 T - 271 FCH4 Gridcell surface CH4 flux to atmosphere (+ to atm) kgC/m2/s T - 272 FCH4TOCO2 Gridcell oxidation of CH4 to CO2 gC/m2/s T - 273 FCH4_DFSAT CH4 additional flux due to changing fsat, natural vegetated and crop landunits only kgC/m2/s T - 274 FCO2 CO2 flux to atmosphere (+ to atm) kgCO2/m2/s F - 275 FCOV fractional impermeable area unitless T - 276 FCTR canopy transpiration W/m^2 T - 277 FDRY fraction of foliage that is green and dry proportion F - 278 FERTNITRO Nitrogen fertilizer for each crop gN/m2/yr F - 279 FERT_COUNTER time left to fertilize seconds F - 280 FERT_TO_SMINN fertilizer to soil mineral N gN/m^2/s F - 281 FFIX_TO_SMINN free living N fixation to soil mineral N gN/m^2/s T - 282 FGEV ground evaporation W/m^2 T - 283 FGR heat flux into soil/snow including snow melt and lake / snow light transmission W/m^2 T - 284 FGR12 heat flux between soil layers 1 and 2 W/m^2 T - 285 FGR_ICE heat flux into soil/snow including snow melt and lake / snow light transmission (ice landunits W/m^2 F - 286 FGR_R Rural heat flux into soil/snow including snow melt and snow light transmission W/m^2 F - 287 FGR_SOIL_R Rural downward heat flux at interface below each soil layer watt/m^2 F - 288 FGR_U Urban heat flux into soil/snow including snow melt W/m^2 F - 289 FH2OSFC fraction of ground covered by surface water unitless T - 290 FH2OSFC_NOSNOW fraction of ground covered by surface water (if no snow present) unitless F - 291 FINUNDATED fractional inundated area of vegetated columns unitless T - 292 FINUNDATED_LAG time-lagged inundated fraction of vegetated columns unitless F - 293 FIRA net infrared (longwave) radiation W/m^2 T - 294 FIRA_ICE net infrared (longwave) radiation (ice landunits only) W/m^2 F - 295 FIRA_R Rural net infrared (longwave) radiation W/m^2 T - 296 FIRA_U Urban net infrared (longwave) radiation W/m^2 F - 297 FIRE emitted infrared (longwave) radiation W/m^2 T - 298 FIRE_ICE emitted infrared (longwave) radiation (ice landunits only) W/m^2 F - 299 FIRE_R Rural emitted infrared (longwave) radiation W/m^2 T - 300 FIRE_U Urban emitted infrared (longwave) radiation W/m^2 F - 301 FLDS atmospheric longwave radiation (downscaled to columns in glacier regions) W/m^2 T - 302 FLDS_ICE atmospheric longwave radiation (downscaled to columns in glacier regions) (ice landunits only) W/m^2 F - 303 FMAX_DENIT_CARBONSUBSTRATE FMAX_DENIT_CARBONSUBSTRATE gN/m^3/s F - 304 FMAX_DENIT_NITRATE FMAX_DENIT_NITRATE gN/m^3/s F - 305 FPI fraction of potential immobilization proportion T - 306 FPI_vr fraction of potential immobilization proportion F - 307 FPSN photosynthesis umol m-2 s-1 T - 308 FPSN24 24 hour accumulative patch photosynthesis starting from mid-night umol CO2/m^2 ground/day F - 309 FPSN_WC Rubisco-limited photosynthesis umol m-2 s-1 F - 310 FPSN_WJ RuBP-limited photosynthesis umol m-2 s-1 F - 311 FPSN_WP Product-limited photosynthesis umol m-2 s-1 F - 312 FRAC_ICEOLD fraction of ice relative to the tot water proportion F - 313 FREE_RETRANSN_TO_NPOOL deployment of retranslocated N gN/m^2/s T - 314 FROOTC fine root C gC/m^2 T - 315 FROOTC_ALLOC fine root C allocation gC/m^2/s T - 316 FROOTC_LOSS fine root C loss gC/m^2/s T - 317 FROOTC_STORAGE fine root C storage gC/m^2 F - 318 FROOTC_STORAGE_TO_XFER fine root C shift storage to transfer gC/m^2/s F - 319 FROOTC_TO_LITTER fine root C litterfall gC/m^2/s F - 320 FROOTC_XFER fine root C transfer gC/m^2 F - 321 FROOTC_XFER_TO_FROOTC fine root C growth from storage gC/m^2/s F - 322 FROOTN fine root N gN/m^2 T - 323 FROOTN_STORAGE fine root N storage gN/m^2 F - 324 FROOTN_STORAGE_TO_XFER fine root N shift storage to transfer gN/m^2/s F - 325 FROOTN_TO_LITTER fine root N litterfall gN/m^2/s F - 326 FROOTN_XFER fine root N transfer gN/m^2 F - 327 FROOTN_XFER_TO_FROOTN fine root N growth from storage gN/m^2/s F - 328 FROOT_MR fine root maintenance respiration gC/m^2/s F - 329 FROOT_PROF profile for litter C and N inputs from fine roots 1/m F - 330 FROST_TABLE frost table depth (natural vegetated and crop landunits only) m F - 331 FSA absorbed solar radiation W/m^2 T - 332 FSAT fractional area with water table at surface unitless T - 333 FSA_ICE absorbed solar radiation (ice landunits only) W/m^2 F - 334 FSA_R Rural absorbed solar radiation W/m^2 F - 335 FSA_U Urban absorbed solar radiation W/m^2 F - 336 FSD24 direct radiation (last 24hrs) K F - 337 FSD240 direct radiation (last 240hrs) K F - 338 FSDS atmospheric incident solar radiation W/m^2 T - 339 FSDSND direct nir incident solar radiation W/m^2 T - 340 FSDSNDLN direct nir incident solar radiation at local noon W/m^2 T - 341 FSDSNI diffuse nir incident solar radiation W/m^2 T - 342 FSDSVD direct vis incident solar radiation W/m^2 T - 343 FSDSVDLN direct vis incident solar radiation at local noon W/m^2 T - 344 FSDSVI diffuse vis incident solar radiation W/m^2 T - 345 FSDSVILN diffuse vis incident solar radiation at local noon W/m^2 T - 346 FSH sensible heat not including correction for land use change and rain/snow conversion W/m^2 T - 347 FSH_G sensible heat from ground W/m^2 T - 348 FSH_ICE sensible heat not including correction for land use change and rain/snow conversion (ice landu W/m^2 F - 349 FSH_PRECIP_CONVERSION Sensible heat flux from conversion of rain/snow atm forcing W/m^2 T - 350 FSH_R Rural sensible heat W/m^2 T - 351 FSH_RUNOFF_ICE_TO_LIQ sensible heat flux generated from conversion of ice runoff to liquid W/m^2 T - 352 FSH_TO_COUPLER sensible heat sent to coupler (includes corrections for land use change, rain/snow conversion W/m^2 T - 353 FSH_U Urban sensible heat W/m^2 F - 354 FSH_V sensible heat from veg W/m^2 T - 355 FSI24 indirect radiation (last 24hrs) K F - 356 FSI240 indirect radiation (last 240hrs) K F - 357 FSM snow melt heat flux W/m^2 T - 358 FSM_ICE snow melt heat flux (ice landunits only) W/m^2 F - 359 FSM_R Rural snow melt heat flux W/m^2 F - 360 FSM_U Urban snow melt heat flux W/m^2 F - 361 FSNO fraction of ground covered by snow unitless T - 362 FSNO_EFF effective fraction of ground covered by snow unitless T - 363 FSNO_ICE fraction of ground covered by snow (ice landunits only) unitless F - 364 FSR reflected solar radiation W/m^2 T - 365 FSRND direct nir reflected solar radiation W/m^2 T - 366 FSRNDLN direct nir reflected solar radiation at local noon W/m^2 T - 367 FSRNI diffuse nir reflected solar radiation W/m^2 T - 368 FSRSF reflected solar radiation W/m^2 T - 369 FSRSFND direct nir reflected solar radiation W/m^2 T - 370 FSRSFNDLN direct nir reflected solar radiation at local noon W/m^2 T - 371 FSRSFNI diffuse nir reflected solar radiation W/m^2 T - 372 FSRSFVD direct vis reflected solar radiation W/m^2 T - 373 FSRSFVDLN direct vis reflected solar radiation at local noon W/m^2 T - 374 FSRSFVI diffuse vis reflected solar radiation W/m^2 T - 375 FSRVD direct vis reflected solar radiation W/m^2 T - 376 FSRVDLN direct vis reflected solar radiation at local noon W/m^2 T - 377 FSRVI diffuse vis reflected solar radiation W/m^2 T - 378 FSR_ICE reflected solar radiation (ice landunits only) W/m^2 F - 379 FSUN sunlit fraction of canopy proportion F - 380 FSUN24 fraction sunlit (last 24hrs) K F - 381 FSUN240 fraction sunlit (last 240hrs) K F - 382 FUELC fuel load gC/m^2 T - 383 FV friction velocity m/s T - 384 FWET fraction of canopy that is wet proportion F - 385 F_DENIT denitrification flux gN/m^2/s T - 386 F_DENIT_BASE F_DENIT_BASE gN/m^3/s F - 387 F_DENIT_vr denitrification flux gN/m^3/s F - 388 F_N2O_DENIT denitrification N2O flux gN/m^2/s T - 389 F_N2O_NIT nitrification N2O flux gN/m^2/s T - 390 F_NIT nitrification flux gN/m^2/s T - 391 F_NIT_vr nitrification flux gN/m^3/s F - 392 FireComp_BC fire emissions flux of BC kg/m2/sec F - 393 FireComp_OC fire emissions flux of OC kg/m2/sec F - 394 FireComp_SO2 fire emissions flux of SO2 kg/m2/sec F - 395 FireEmis_TOT Total fire emissions flux gC/m2/sec F - 396 FireEmis_ZTOP Top of vertical fire emissions distribution m F - 397 FireMech_SO2 fire emissions flux of SO2 kg/m2/sec F - 398 FireMech_bc_a1 fire emissions flux of bc_a1 kg/m2/sec F - 399 FireMech_pom_a1 fire emissions flux of pom_a1 kg/m2/sec F - 400 GAMMA total gamma for VOC calc non F - 401 GAMMAA gamma A for VOC calc non F - 402 GAMMAC gamma C for VOC calc non F - 403 GAMMAL gamma L for VOC calc non F - 404 GAMMAP gamma P for VOC calc non F - 405 GAMMAS gamma S for VOC calc non F - 406 GAMMAT gamma T for VOC calc non F - 407 GDD0 Growing degree days base 0C from planting ddays F - 408 GDD020 Twenty year average of growing degree days base 0C from planting ddays F - 409 GDD10 Growing degree days base 10C from planting ddays F - 410 GDD1020 Twenty year average of growing degree days base 10C from planting ddays F - 411 GDD8 Growing degree days base 8C from planting ddays F - 412 GDD820 Twenty year average of growing degree days base 8C from planting ddays F - 413 GDDHARV Growing degree days (gdd) needed to harvest ddays F - 414 GDDPLANT Accumulated growing degree days past planting date for crop ddays F - 415 GDDTSOI Growing degree-days from planting (top two soil layers) ddays F - 416 GPP gross primary production gC/m^2/s T - 417 GR total growth respiration gC/m^2/s T - 418 GRAINC grain C (does not equal yield) gC/m^2 T - 419 GRAINC_TO_FOOD grain C to food gC/m^2/s T - 420 GRAINC_TO_SEED grain C to seed gC/m^2/s T - 421 GRAINN grain N gN/m^2 T - 422 GRESP_STORAGE growth respiration storage gC/m^2 F - 423 GRESP_STORAGE_TO_XFER growth respiration shift storage to transfer gC/m^2/s F - 424 GRESP_XFER growth respiration transfer gC/m^2 F - 425 GROSS_NMIN gross rate of N mineralization gN/m^2/s T - 426 GROSS_NMIN_vr gross rate of N mineralization gN/m^3/s F - 427 GSSHA shaded leaf stomatal conductance umol H20/m2/s T - 428 GSSHALN shaded leaf stomatal conductance at local noon umol H20/m2/s T - 429 GSSUN sunlit leaf stomatal conductance umol H20/m2/s T - 430 GSSUNLN sunlit leaf stomatal conductance at local noon umol H20/m2/s T - 431 H2OCAN intercepted water mm T - 432 H2OSFC surface water depth mm T - 433 H2OSNO snow depth (liquid water) mm T - 434 H2OSNO_ICE snow depth (liquid water, ice landunits only) mm F - 435 H2OSNO_TOP mass of snow in top snow layer kg/m2 T - 436 H2OSOI volumetric soil water (natural vegetated and crop landunits only) mm3/mm3 T - 437 HBOT canopy bottom m F - 438 HEAT_CONTENT1 initial gridcell total heat content J/m^2 T - 439 HEAT_CONTENT1_VEG initial gridcell total heat content - natural vegetated and crop landunits only J/m^2 F - 440 HEAT_CONTENT2 post land cover change total heat content J/m^2 F - 441 HEAT_FROM_AC sensible heat flux put into canyon due to heat removed from air conditioning W/m^2 T - 442 HIA 2 m NWS Heat Index C T - 443 HIA_R Rural 2 m NWS Heat Index C T - 444 HIA_U Urban 2 m NWS Heat Index C T - 445 HK hydraulic conductivity (natural vegetated and crop landunits only) mm/s F - 446 HR total heterotrophic respiration gC/m^2/s T - 447 HR_vr total vertically resolved heterotrophic respiration gC/m^3/s T - 448 HTOP canopy top m T - 449 HUMIDEX 2 m Humidex C T - 450 HUMIDEX_R Rural 2 m Humidex C T - 451 HUMIDEX_U Urban 2 m Humidex C T - 452 ICE_CONTENT1 initial gridcell total ice content mm T - 453 ICE_CONTENT2 post land cover change total ice content mm F - 454 ICE_MODEL_FRACTION Ice sheet model fractional coverage unitless F - 455 INIT_GPP GPP flux before downregulation gC/m^2/s F - 456 INT_SNOW accumulated swe (natural vegetated and crop landunits only) mm F - 457 INT_SNOW_ICE accumulated swe (ice landunits only) mm F - 458 IWUELN local noon intrinsic water use efficiency umolCO2/molH2O T - 459 JMX25T canopy profile of jmax umol/m2/s T - 460 Jmx25Z maximum rate of electron transport at 25 Celcius for canopy layers umol electrons/m2/s T - 461 KROOT root conductance each soil layer 1/s F - 462 KSOIL soil conductance in each soil layer 1/s F - 463 K_ACT_SOM active soil organic potential loss coefficient 1/s F - 464 K_CEL_LIT cellulosic litter potential loss coefficient 1/s F - 465 K_CWD coarse woody debris potential loss coefficient 1/s F - 466 K_LIG_LIT lignin litter potential loss coefficient 1/s F - 467 K_MET_LIT metabolic litter potential loss coefficient 1/s F - 468 K_NITR K_NITR 1/s F - 469 K_NITR_H2O K_NITR_H2O unitless F - 470 K_NITR_PH K_NITR_PH unitless F - 471 K_NITR_T K_NITR_T unitless F - 472 K_PAS_SOM passive soil organic potential loss coefficient 1/s F - 473 K_SLO_SOM slow soil organic ma potential loss coefficient 1/s F - 474 LAI240 240hr average of leaf area index m^2/m^2 F - 475 LAISHA shaded projected leaf area index m^2/m^2 T - 476 LAISUN sunlit projected leaf area index m^2/m^2 T - 477 LAKEICEFRAC lake layer ice mass fraction unitless F - 478 LAKEICEFRAC_SURF surface lake layer ice mass fraction unitless T - 479 LAKEICETHICK thickness of lake ice (including physical expansion on freezing) m T - 480 LAND_USE_FLUX total C emitted from land cover conversion (smoothed over the year) and wood and grain product gC/m^2/s T - 481 LATBASET latitude vary base temperature for gddplant degree C F - 482 LEAFC leaf C gC/m^2 T - 483 LEAFCN Leaf CN ratio used for flexible CN gC/gN T - 484 LEAFCN_OFFSET Leaf C:N used by FUN unitless F - 485 LEAFCN_STORAGE Storage Leaf CN ratio used for flexible CN gC/gN F - 486 LEAFC_ALLOC leaf C allocation gC/m^2/s T - 487 LEAFC_CHANGE C change in leaf gC/m^2/s T - 488 LEAFC_LOSS leaf C loss gC/m^2/s T - 489 LEAFC_STORAGE leaf C storage gC/m^2 F - 490 LEAFC_STORAGE_TO_XFER leaf C shift storage to transfer gC/m^2/s F - 491 LEAFC_STORAGE_XFER_ACC Accumulated leaf C transfer gC/m^2 F - 492 LEAFC_TO_BIOFUELC leaf C to biofuel C gC/m^2/s T - 493 LEAFC_TO_LITTER leaf C litterfall gC/m^2/s F - 494 LEAFC_TO_LITTER_FUN leaf C litterfall used by FUN gC/m^2/s T - 495 LEAFC_XFER leaf C transfer gC/m^2 F - 496 LEAFC_XFER_TO_LEAFC leaf C growth from storage gC/m^2/s F - 497 LEAFN leaf N gN/m^2 T - 498 LEAFN_STORAGE leaf N storage gN/m^2 F - 499 LEAFN_STORAGE_TO_XFER leaf N shift storage to transfer gN/m^2/s F - 500 LEAFN_STORAGE_XFER_ACC Accmulated leaf N transfer gN/m^2 F - 501 LEAFN_TO_LITTER leaf N litterfall gN/m^2/s T - 502 LEAFN_TO_RETRANSN leaf N to retranslocated N pool gN/m^2/s F - 503 LEAFN_XFER leaf N transfer gN/m^2 F - 504 LEAFN_XFER_TO_LEAFN leaf N growth from storage gN/m^2/s F - 505 LEAF_MR leaf maintenance respiration gC/m^2/s T - 506 LEAF_PROF profile for litter C and N inputs from leaves 1/m F - 507 LFC2 conversion area fraction of BET and BDT that burned per sec T - 508 LGSF long growing season factor proportion F - 509 LIG_LITC LIG_LIT C gC/m^2 T - 510 LIG_LITC_1m LIG_LIT C to 1 meter gC/m^2 F - 511 LIG_LITC_TNDNCY_VERT_TRA lignin litter C tendency due to vertical transport gC/m^3/s F - 512 LIG_LITC_TO_SLO_SOMC decomp. of lignin litter C to slow soil organic ma C gC/m^2/s F - 513 LIG_LITC_TO_SLO_SOMC_vr decomp. of lignin litter C to slow soil organic ma C gC/m^3/s F - 514 LIG_LITC_vr LIG_LIT C (vertically resolved) gC/m^3 T - 515 LIG_LITN LIG_LIT N gN/m^2 T - 516 LIG_LITN_1m LIG_LIT N to 1 meter gN/m^2 F - 517 LIG_LITN_TNDNCY_VERT_TRA lignin litter N tendency due to vertical transport gN/m^3/s F - 518 LIG_LITN_TO_SLO_SOMN decomp. of lignin litter N to slow soil organic ma N gN/m^2 F - 519 LIG_LITN_TO_SLO_SOMN_vr decomp. of lignin litter N to slow soil organic ma N gN/m^3 F - 520 LIG_LITN_vr LIG_LIT N (vertically resolved) gN/m^3 T - 521 LIG_LIT_HR Het. Resp. from lignin litter gC/m^2/s F - 522 LIG_LIT_HR_vr Het. Resp. from lignin litter gC/m^3/s F - 523 LIQCAN intercepted liquid water mm T - 524 LIQUID_CONTENT1 initial gridcell total liq content mm T - 525 LIQUID_CONTENT2 post landuse change gridcell total liq content mm F - 526 LIQUID_WATER_TEMP1 initial gridcell weighted average liquid water temperature K F - 527 LITFALL litterfall (leaves and fine roots) gC/m^2/s T - 528 LITFIRE litter fire losses gC/m^2/s F - 529 LITTERC_HR litter C heterotrophic respiration gC/m^2/s T - 530 LITTERC_LOSS litter C loss gC/m^2/s T - 531 LIVECROOTC live coarse root C gC/m^2 T - 532 LIVECROOTC_STORAGE live coarse root C storage gC/m^2 F - 533 LIVECROOTC_STORAGE_TO_XFER live coarse root C shift storage to transfer gC/m^2/s F - 534 LIVECROOTC_TO_DEADCROOTC live coarse root C turnover gC/m^2/s F - 535 LIVECROOTC_XFER live coarse root C transfer gC/m^2 F - 536 LIVECROOTC_XFER_TO_LIVECROOTC live coarse root C growth from storage gC/m^2/s F - 537 LIVECROOTN live coarse root N gN/m^2 T - 538 LIVECROOTN_STORAGE live coarse root N storage gN/m^2 F - 539 LIVECROOTN_STORAGE_TO_XFER live coarse root N shift storage to transfer gN/m^2/s F - 540 LIVECROOTN_TO_DEADCROOTN live coarse root N turnover gN/m^2/s F - 541 LIVECROOTN_TO_RETRANSN live coarse root N to retranslocated N pool gN/m^2/s F - 542 LIVECROOTN_XFER live coarse root N transfer gN/m^2 F - 543 LIVECROOTN_XFER_TO_LIVECROOTN live coarse root N growth from storage gN/m^2/s F - 544 LIVECROOT_MR live coarse root maintenance respiration gC/m^2/s F - 545 LIVESTEMC live stem C gC/m^2 T - 546 LIVESTEMC_STORAGE live stem C storage gC/m^2 F - 547 LIVESTEMC_STORAGE_TO_XFER live stem C shift storage to transfer gC/m^2/s F - 548 LIVESTEMC_TO_BIOFUELC livestem C to biofuel C gC/m^2/s T - 549 LIVESTEMC_TO_DEADSTEMC live stem C turnover gC/m^2/s F - 550 LIVESTEMC_XFER live stem C transfer gC/m^2 F - 551 LIVESTEMC_XFER_TO_LIVESTEMC live stem C growth from storage gC/m^2/s F - 552 LIVESTEMN live stem N gN/m^2 T - 553 LIVESTEMN_STORAGE live stem N storage gN/m^2 F - 554 LIVESTEMN_STORAGE_TO_XFER live stem N shift storage to transfer gN/m^2/s F - 555 LIVESTEMN_TO_DEADSTEMN live stem N turnover gN/m^2/s F - 556 LIVESTEMN_TO_RETRANSN live stem N to retranslocated N pool gN/m^2/s F - 557 LIVESTEMN_XFER live stem N transfer gN/m^2 F - 558 LIVESTEMN_XFER_TO_LIVESTEMN live stem N growth from storage gN/m^2/s F - 559 LIVESTEM_MR live stem maintenance respiration gC/m^2/s F - 560 LNC leaf N concentration gN leaf/m^2 T - 561 LWdown atmospheric longwave radiation (downscaled to columns in glacier regions) W/m^2 F - 562 LWup upwelling longwave radiation W/m^2 F - 563 MEG_acetaldehyde MEGAN flux kg/m2/sec T - 564 MEG_acetic_acid MEGAN flux kg/m2/sec T - 565 MEG_acetone MEGAN flux kg/m2/sec T - 566 MEG_carene_3 MEGAN flux kg/m2/sec T - 567 MEG_ethanol MEGAN flux kg/m2/sec T - 568 MEG_formaldehyde MEGAN flux kg/m2/sec T - 569 MEG_isoprene MEGAN flux kg/m2/sec T - 570 MEG_methanol MEGAN flux kg/m2/sec T - 571 MEG_pinene_a MEGAN flux kg/m2/sec T - 572 MEG_thujene_a MEGAN flux kg/m2/sec T - 573 MET_LITC MET_LIT C gC/m^2 T - 574 MET_LITC_1m MET_LIT C to 1 meter gC/m^2 F - 575 MET_LITC_TNDNCY_VERT_TRA metabolic litter C tendency due to vertical transport gC/m^3/s F - 576 MET_LITC_TO_ACT_SOMC decomp. of metabolic litter C to active soil organic C gC/m^2/s F - 577 MET_LITC_TO_ACT_SOMC_vr decomp. of metabolic litter C to active soil organic C gC/m^3/s F - 578 MET_LITC_vr MET_LIT C (vertically resolved) gC/m^3 T - 579 MET_LITN MET_LIT N gN/m^2 T - 580 MET_LITN_1m MET_LIT N to 1 meter gN/m^2 F - 581 MET_LITN_TNDNCY_VERT_TRA metabolic litter N tendency due to vertical transport gN/m^3/s F - 582 MET_LITN_TO_ACT_SOMN decomp. of metabolic litter N to active soil organic N gN/m^2 F - 583 MET_LITN_TO_ACT_SOMN_vr decomp. of metabolic litter N to active soil organic N gN/m^3 F - 584 MET_LITN_vr MET_LIT N (vertically resolved) gN/m^3 T - 585 MET_LIT_HR Het. Resp. from metabolic litter gC/m^2/s F - 586 MET_LIT_HR_vr Het. Resp. from metabolic litter gC/m^3/s F - 587 MR maintenance respiration gC/m^2/s T - 588 M_ACT_SOMC_TO_LEACHING active soil organic C leaching loss gC/m^2/s F - 589 M_ACT_SOMN_TO_LEACHING active soil organic N leaching loss gN/m^2/s F - 590 M_CEL_LITC_TO_FIRE cellulosic litter C fire loss gC/m^2/s F - 591 M_CEL_LITC_TO_FIRE_vr cellulosic litter C fire loss gC/m^3/s F - 592 M_CEL_LITC_TO_LEACHING cellulosic litter C leaching loss gC/m^2/s F - 593 M_CEL_LITN_TO_FIRE cellulosic litter N fire loss gN/m^2 F - 594 M_CEL_LITN_TO_FIRE_vr cellulosic litter N fire loss gN/m^3 F - 595 M_CEL_LITN_TO_LEACHING cellulosic litter N leaching loss gN/m^2/s F - 596 M_CWDC_TO_FIRE coarse woody debris C fire loss gC/m^2/s F - 597 M_CWDC_TO_FIRE_vr coarse woody debris C fire loss gC/m^3/s F - 598 M_CWDN_TO_FIRE coarse woody debris N fire loss gN/m^2 F - 599 M_CWDN_TO_FIRE_vr coarse woody debris N fire loss gN/m^3 F - 600 M_DEADCROOTC_STORAGE_TO_LITTER dead coarse root C storage mortality gC/m^2/s F - 601 M_DEADCROOTC_STORAGE_TO_LITTER_FIRE dead coarse root C storage fire mortality to litter gC/m^2/s F - 602 M_DEADCROOTC_TO_LITTER dead coarse root C mortality gC/m^2/s F - 603 M_DEADCROOTC_XFER_TO_LITTER dead coarse root C transfer mortality gC/m^2/s F - 604 M_DEADCROOTN_STORAGE_TO_FIRE dead coarse root N storage fire loss gN/m^2/s F - 605 M_DEADCROOTN_STORAGE_TO_LITTER dead coarse root N storage mortality gN/m^2/s F - 606 M_DEADCROOTN_TO_FIRE dead coarse root N fire loss gN/m^2/s F - 607 M_DEADCROOTN_TO_LITTER dead coarse root N mortality gN/m^2/s F - 608 M_DEADCROOTN_TO_LITTER_FIRE dead coarse root N fire mortality to litter gN/m^2/s F - 609 M_DEADCROOTN_XFER_TO_FIRE dead coarse root N transfer fire loss gN/m^2/s F - 610 M_DEADCROOTN_XFER_TO_LITTER dead coarse root N transfer mortality gN/m^2/s F - 611 M_DEADROOTC_STORAGE_TO_FIRE dead root C storage fire loss gC/m^2/s F - 612 M_DEADROOTC_STORAGE_TO_LITTER_FIRE dead root C storage fire mortality to litter gC/m^2/s F - 613 M_DEADROOTC_TO_FIRE dead root C fire loss gC/m^2/s F - 614 M_DEADROOTC_TO_LITTER_FIRE dead root C fire mortality to litter gC/m^2/s F - 615 M_DEADROOTC_XFER_TO_FIRE dead root C transfer fire loss gC/m^2/s F - 616 M_DEADROOTC_XFER_TO_LITTER_FIRE dead root C transfer fire mortality to litter gC/m^2/s F - 617 M_DEADSTEMC_STORAGE_TO_FIRE dead stem C storage fire loss gC/m^2/s F - 618 M_DEADSTEMC_STORAGE_TO_LITTER dead stem C storage mortality gC/m^2/s F - 619 M_DEADSTEMC_STORAGE_TO_LITTER_FIRE dead stem C storage fire mortality to litter gC/m^2/s F - 620 M_DEADSTEMC_TO_FIRE dead stem C fire loss gC/m^2/s F - 621 M_DEADSTEMC_TO_LITTER dead stem C mortality gC/m^2/s F - 622 M_DEADSTEMC_TO_LITTER_FIRE dead stem C fire mortality to litter gC/m^2/s F - 623 M_DEADSTEMC_XFER_TO_FIRE dead stem C transfer fire loss gC/m^2/s F - 624 M_DEADSTEMC_XFER_TO_LITTER dead stem C transfer mortality gC/m^2/s F - 625 M_DEADSTEMC_XFER_TO_LITTER_FIRE dead stem C transfer fire mortality to litter gC/m^2/s F - 626 M_DEADSTEMN_STORAGE_TO_FIRE dead stem N storage fire loss gN/m^2/s F - 627 M_DEADSTEMN_STORAGE_TO_LITTER dead stem N storage mortality gN/m^2/s F - 628 M_DEADSTEMN_TO_FIRE dead stem N fire loss gN/m^2/s F - 629 M_DEADSTEMN_TO_LITTER dead stem N mortality gN/m^2/s F - 630 M_DEADSTEMN_TO_LITTER_FIRE dead stem N fire mortality to litter gN/m^2/s F - 631 M_DEADSTEMN_XFER_TO_FIRE dead stem N transfer fire loss gN/m^2/s F - 632 M_DEADSTEMN_XFER_TO_LITTER dead stem N transfer mortality gN/m^2/s F - 633 M_FROOTC_STORAGE_TO_FIRE fine root C storage fire loss gC/m^2/s F - 634 M_FROOTC_STORAGE_TO_LITTER fine root C storage mortality gC/m^2/s F - 635 M_FROOTC_STORAGE_TO_LITTER_FIRE fine root C storage fire mortality to litter gC/m^2/s F - 636 M_FROOTC_TO_FIRE fine root C fire loss gC/m^2/s F - 637 M_FROOTC_TO_LITTER fine root C mortality gC/m^2/s F - 638 M_FROOTC_TO_LITTER_FIRE fine root C fire mortality to litter gC/m^2/s F - 639 M_FROOTC_XFER_TO_FIRE fine root C transfer fire loss gC/m^2/s F - 640 M_FROOTC_XFER_TO_LITTER fine root C transfer mortality gC/m^2/s F - 641 M_FROOTC_XFER_TO_LITTER_FIRE fine root C transfer fire mortality to litter gC/m^2/s F - 642 M_FROOTN_STORAGE_TO_FIRE fine root N storage fire loss gN/m^2/s F - 643 M_FROOTN_STORAGE_TO_LITTER fine root N storage mortality gN/m^2/s F - 644 M_FROOTN_TO_FIRE fine root N fire loss gN/m^2/s F - 645 M_FROOTN_TO_LITTER fine root N mortality gN/m^2/s F - 646 M_FROOTN_XFER_TO_FIRE fine root N transfer fire loss gN/m^2/s F - 647 M_FROOTN_XFER_TO_LITTER fine root N transfer mortality gN/m^2/s F - 648 M_GRESP_STORAGE_TO_FIRE growth respiration storage fire loss gC/m^2/s F - 649 M_GRESP_STORAGE_TO_LITTER growth respiration storage mortality gC/m^2/s F - 650 M_GRESP_STORAGE_TO_LITTER_FIRE growth respiration storage fire mortality to litter gC/m^2/s F - 651 M_GRESP_XFER_TO_FIRE growth respiration transfer fire loss gC/m^2/s F - 652 M_GRESP_XFER_TO_LITTER growth respiration transfer mortality gC/m^2/s F - 653 M_GRESP_XFER_TO_LITTER_FIRE growth respiration transfer fire mortality to litter gC/m^2/s F - 654 M_LEAFC_STORAGE_TO_FIRE leaf C storage fire loss gC/m^2/s F - 655 M_LEAFC_STORAGE_TO_LITTER leaf C storage mortality gC/m^2/s F - 656 M_LEAFC_STORAGE_TO_LITTER_FIRE leaf C fire mortality to litter gC/m^2/s F - 657 M_LEAFC_TO_FIRE leaf C fire loss gC/m^2/s F - 658 M_LEAFC_TO_LITTER leaf C mortality gC/m^2/s F - 659 M_LEAFC_TO_LITTER_FIRE leaf C fire mortality to litter gC/m^2/s F - 660 M_LEAFC_XFER_TO_FIRE leaf C transfer fire loss gC/m^2/s F - 661 M_LEAFC_XFER_TO_LITTER leaf C transfer mortality gC/m^2/s F - 662 M_LEAFC_XFER_TO_LITTER_FIRE leaf C transfer fire mortality to litter gC/m^2/s F - 663 M_LEAFN_STORAGE_TO_FIRE leaf N storage fire loss gN/m^2/s F - 664 M_LEAFN_STORAGE_TO_LITTER leaf N storage mortality gN/m^2/s F - 665 M_LEAFN_TO_FIRE leaf N fire loss gN/m^2/s F - 666 M_LEAFN_TO_LITTER leaf N mortality gN/m^2/s F - 667 M_LEAFN_XFER_TO_FIRE leaf N transfer fire loss gN/m^2/s F - 668 M_LEAFN_XFER_TO_LITTER leaf N transfer mortality gN/m^2/s F - 669 M_LIG_LITC_TO_FIRE lignin litter C fire loss gC/m^2/s F - 670 M_LIG_LITC_TO_FIRE_vr lignin litter C fire loss gC/m^3/s F - 671 M_LIG_LITC_TO_LEACHING lignin litter C leaching loss gC/m^2/s F - 672 M_LIG_LITN_TO_FIRE lignin litter N fire loss gN/m^2 F - 673 M_LIG_LITN_TO_FIRE_vr lignin litter N fire loss gN/m^3 F - 674 M_LIG_LITN_TO_LEACHING lignin litter N leaching loss gN/m^2/s F - 675 M_LIVECROOTC_STORAGE_TO_LITTER live coarse root C storage mortality gC/m^2/s F - 676 M_LIVECROOTC_STORAGE_TO_LITTER_FIRE live coarse root C fire mortality to litter gC/m^2/s F - 677 M_LIVECROOTC_TO_LITTER live coarse root C mortality gC/m^2/s F - 678 M_LIVECROOTC_XFER_TO_LITTER live coarse root C transfer mortality gC/m^2/s F - 679 M_LIVECROOTN_STORAGE_TO_FIRE live coarse root N storage fire loss gN/m^2/s F - 680 M_LIVECROOTN_STORAGE_TO_LITTER live coarse root N storage mortality gN/m^2/s F - 681 M_LIVECROOTN_TO_FIRE live coarse root N fire loss gN/m^2/s F - 682 M_LIVECROOTN_TO_LITTER live coarse root N mortality gN/m^2/s F - 683 M_LIVECROOTN_XFER_TO_FIRE live coarse root N transfer fire loss gN/m^2/s F - 684 M_LIVECROOTN_XFER_TO_LITTER live coarse root N transfer mortality gN/m^2/s F - 685 M_LIVEROOTC_STORAGE_TO_FIRE live root C storage fire loss gC/m^2/s F - 686 M_LIVEROOTC_STORAGE_TO_LITTER_FIRE live root C storage fire mortality to litter gC/m^2/s F - 687 M_LIVEROOTC_TO_DEADROOTC_FIRE live root C fire mortality to dead root C gC/m^2/s F - 688 M_LIVEROOTC_TO_FIRE live root C fire loss gC/m^2/s F - 689 M_LIVEROOTC_TO_LITTER_FIRE live root C fire mortality to litter gC/m^2/s F - 690 M_LIVEROOTC_XFER_TO_FIRE live root C transfer fire loss gC/m^2/s F - 691 M_LIVEROOTC_XFER_TO_LITTER_FIRE live root C transfer fire mortality to litter gC/m^2/s F - 692 M_LIVESTEMC_STORAGE_TO_FIRE live stem C storage fire loss gC/m^2/s F - 693 M_LIVESTEMC_STORAGE_TO_LITTER live stem C storage mortality gC/m^2/s F - 694 M_LIVESTEMC_STORAGE_TO_LITTER_FIRE live stem C storage fire mortality to litter gC/m^2/s F - 695 M_LIVESTEMC_TO_DEADSTEMC_FIRE live stem C fire mortality to dead stem C gC/m^2/s F - 696 M_LIVESTEMC_TO_FIRE live stem C fire loss gC/m^2/s F - 697 M_LIVESTEMC_TO_LITTER live stem C mortality gC/m^2/s F - 698 M_LIVESTEMC_TO_LITTER_FIRE live stem C fire mortality to litter gC/m^2/s F - 699 M_LIVESTEMC_XFER_TO_FIRE live stem C transfer fire loss gC/m^2/s F - 700 M_LIVESTEMC_XFER_TO_LITTER live stem C transfer mortality gC/m^2/s F - 701 M_LIVESTEMC_XFER_TO_LITTER_FIRE live stem C transfer fire mortality to litter gC/m^2/s F - 702 M_LIVESTEMN_STORAGE_TO_FIRE live stem N storage fire loss gN/m^2/s F - 703 M_LIVESTEMN_STORAGE_TO_LITTER live stem N storage mortality gN/m^2/s F - 704 M_LIVESTEMN_TO_FIRE live stem N fire loss gN/m^2/s F - 705 M_LIVESTEMN_TO_LITTER live stem N mortality gN/m^2/s F - 706 M_LIVESTEMN_XFER_TO_FIRE live stem N transfer fire loss gN/m^2/s F - 707 M_LIVESTEMN_XFER_TO_LITTER live stem N transfer mortality gN/m^2/s F - 708 M_MET_LITC_TO_FIRE metabolic litter C fire loss gC/m^2/s F - 709 M_MET_LITC_TO_FIRE_vr metabolic litter C fire loss gC/m^3/s F - 710 M_MET_LITC_TO_LEACHING metabolic litter C leaching loss gC/m^2/s F - 711 M_MET_LITN_TO_FIRE metabolic litter N fire loss gN/m^2 F - 712 M_MET_LITN_TO_FIRE_vr metabolic litter N fire loss gN/m^3 F - 713 M_MET_LITN_TO_LEACHING metabolic litter N leaching loss gN/m^2/s F - 714 M_PAS_SOMC_TO_LEACHING passive soil organic C leaching loss gC/m^2/s F - 715 M_PAS_SOMN_TO_LEACHING passive soil organic N leaching loss gN/m^2/s F - 716 M_RETRANSN_TO_FIRE retranslocated N pool fire loss gN/m^2/s F - 717 M_RETRANSN_TO_LITTER retranslocated N pool mortality gN/m^2/s F - 718 M_SLO_SOMC_TO_LEACHING slow soil organic ma C leaching loss gC/m^2/s F - 719 M_SLO_SOMN_TO_LEACHING slow soil organic ma N leaching loss gN/m^2/s F - 720 NACTIVE Mycorrhizal N uptake flux gN/m^2/s T - 721 NACTIVE_NH4 Mycorrhizal N uptake flux gN/m^2/s T - 722 NACTIVE_NO3 Mycorrhizal N uptake flux gN/m^2/s T - 723 NAM AM-associated N uptake flux gN/m^2/s T - 724 NAM_NH4 AM-associated N uptake flux gN/m^2/s T - 725 NAM_NO3 AM-associated N uptake flux gN/m^2/s T - 726 NBP net biome production, includes fire, landuse, harvest and hrv_xsmrpool flux (latter smoothed o gC/m^2/s T - 727 NDEPLOY total N deployed in new growth gN/m^2/s T - 728 NDEP_PROF profile for atmospheric N deposition 1/m F - 729 NDEP_TO_SMINN atmospheric N deposition to soil mineral N gN/m^2/s T - 730 NECM ECM-associated N uptake flux gN/m^2/s T - 731 NECM_NH4 ECM-associated N uptake flux gN/m^2/s T - 732 NECM_NO3 ECM-associated N uptake flux gN/m^2/s T - 733 NEE net ecosystem exchange of carbon, includes fire and hrv_xsmrpool (latter smoothed over the yea gC/m^2/s T - 734 NEM Gridcell net adjustment to net carbon exchange passed to atm. for methane production gC/m2/s T - 735 NEP net ecosystem production, excludes fire, landuse, and harvest flux, positive for sink gC/m^2/s T - 736 NET_NMIN net rate of N mineralization gN/m^2/s T - 737 NET_NMIN_vr net rate of N mineralization gN/m^3/s F - 738 NFERTILIZATION fertilizer added gN/m^2/s T - 739 NFIRE fire counts valid only in Reg.C counts/km2/sec T - 740 NFIX Symbiotic BNF uptake flux gN/m^2/s T - 741 NFIXATION_PROF profile for biological N fixation 1/m F - 742 NFIX_TO_SMINN symbiotic/asymbiotic N fixation to soil mineral N gN/m^2/s F - 743 NNONMYC Non-mycorrhizal N uptake flux gN/m^2/s T - 744 NNONMYC_NH4 Non-mycorrhizal N uptake flux gN/m^2/s T - 745 NNONMYC_NO3 Non-mycorrhizal N uptake flux gN/m^2/s T - 746 NPASSIVE Passive N uptake flux gN/m^2/s T - 747 NPOOL temporary plant N pool gN/m^2 T - 748 NPOOL_TO_DEADCROOTN allocation to dead coarse root N gN/m^2/s F - 749 NPOOL_TO_DEADCROOTN_STORAGE allocation to dead coarse root N storage gN/m^2/s F - 750 NPOOL_TO_DEADSTEMN allocation to dead stem N gN/m^2/s F - 751 NPOOL_TO_DEADSTEMN_STORAGE allocation to dead stem N storage gN/m^2/s F - 752 NPOOL_TO_FROOTN allocation to fine root N gN/m^2/s F - 753 NPOOL_TO_FROOTN_STORAGE allocation to fine root N storage gN/m^2/s F - 754 NPOOL_TO_LEAFN allocation to leaf N gN/m^2/s F - 755 NPOOL_TO_LEAFN_STORAGE allocation to leaf N storage gN/m^2/s F - 756 NPOOL_TO_LIVECROOTN allocation to live coarse root N gN/m^2/s F - 757 NPOOL_TO_LIVECROOTN_STORAGE allocation to live coarse root N storage gN/m^2/s F - 758 NPOOL_TO_LIVESTEMN allocation to live stem N gN/m^2/s F - 759 NPOOL_TO_LIVESTEMN_STORAGE allocation to live stem N storage gN/m^2/s F - 760 NPP net primary production gC/m^2/s T - 761 NPP_BURNEDOFF C that cannot be used for N uptake gC/m^2/s F - 762 NPP_GROWTH Total C used for growth in FUN gC/m^2/s T - 763 NPP_NACTIVE Mycorrhizal N uptake used C gC/m^2/s T - 764 NPP_NACTIVE_NH4 Mycorrhizal N uptake use C gC/m^2/s T - 765 NPP_NACTIVE_NO3 Mycorrhizal N uptake used C gC/m^2/s T - 766 NPP_NAM AM-associated N uptake used C gC/m^2/s T - 767 NPP_NAM_NH4 AM-associated N uptake use C gC/m^2/s T - 768 NPP_NAM_NO3 AM-associated N uptake use C gC/m^2/s T - 769 NPP_NECM ECM-associated N uptake used C gC/m^2/s T - 770 NPP_NECM_NH4 ECM-associated N uptake use C gC/m^2/s T - 771 NPP_NECM_NO3 ECM-associated N uptake used C gC/m^2/s T - 772 NPP_NFIX Symbiotic BNF uptake used C gC/m^2/s T - 773 NPP_NNONMYC Non-mycorrhizal N uptake used C gC/m^2/s T - 774 NPP_NNONMYC_NH4 Non-mycorrhizal N uptake use C gC/m^2/s T - 775 NPP_NNONMYC_NO3 Non-mycorrhizal N uptake use C gC/m^2/s T - 776 NPP_NRETRANS Retranslocated N uptake flux gC/m^2/s T - 777 NPP_NUPTAKE Total C used by N uptake in FUN gC/m^2/s T - 778 NRETRANS Retranslocated N uptake flux gN/m^2/s T - 779 NRETRANS_REG Retranslocated N uptake flux gN/m^2/s T - 780 NRETRANS_SEASON Retranslocated N uptake flux gN/m^2/s T - 781 NRETRANS_STRESS Retranslocated N uptake flux gN/m^2/s T - 782 NSUBSTEPS number of adaptive timesteps in CLM timestep unitless F - 783 NUPTAKE Total N uptake of FUN gN/m^2/s T - 784 NUPTAKE_NPP_FRACTION frac of NPP used in N uptake - T - 785 N_ALLOMETRY N allocation index none F - 786 O2_DECOMP_DEPTH_UNSAT O2 consumption from HR and AR for non-inundated area mol/m3/s F - 787 OBU Monin-Obukhov length m F - 788 OCDEP total OC deposition (dry+wet) from atmosphere kg/m^2/s T - 789 OFFSET_COUNTER offset days counter days F - 790 OFFSET_FDD offset freezing degree days counter C degree-days F - 791 OFFSET_FLAG offset flag none F - 792 OFFSET_SWI offset soil water index none F - 793 ONSET_COUNTER onset days counter days F - 794 ONSET_FDD onset freezing degree days counter C degree-days F - 795 ONSET_FLAG onset flag none F - 796 ONSET_GDD onset growing degree days C degree-days F - 797 ONSET_GDDFLAG onset flag for growing degree day sum none F - 798 ONSET_SWI onset soil water index none F - 799 O_SCALAR fraction by which decomposition is reduced due to anoxia unitless T - 800 PAR240DZ 10-day running mean of daytime patch absorbed PAR for leaves for top canopy layer W/m^2 F - 801 PAR240XZ 10-day running mean of maximum patch absorbed PAR for leaves for top canopy layer W/m^2 F - 802 PAR240_shade shade PAR (240 hrs) umol/m2/s F - 803 PAR240_sun sunlit PAR (240 hrs) umol/m2/s F - 804 PAR24_shade shade PAR (24 hrs) umol/m2/s F - 805 PAR24_sun sunlit PAR (24 hrs) umol/m2/s F - 806 PARVEGLN absorbed par by vegetation at local noon W/m^2 T - 807 PAR_shade shade PAR umol/m2/s F - 808 PAR_sun sunlit PAR umol/m2/s F - 809 PAS_SOMC PAS_SOM C gC/m^2 T - 810 PAS_SOMC_1m PAS_SOM C to 1 meter gC/m^2 F - 811 PAS_SOMC_TNDNCY_VERT_TRA passive soil organic C tendency due to vertical transport gC/m^3/s F - 812 PAS_SOMC_TO_ACT_SOMC decomp. of passive soil organic C to active soil organic C gC/m^2/s F - 813 PAS_SOMC_TO_ACT_SOMC_vr decomp. of passive soil organic C to active soil organic C gC/m^3/s F - 814 PAS_SOMC_vr PAS_SOM C (vertically resolved) gC/m^3 T - 815 PAS_SOMN PAS_SOM N gN/m^2 T - 816 PAS_SOMN_1m PAS_SOM N to 1 meter gN/m^2 F - 817 PAS_SOMN_TNDNCY_VERT_TRA passive soil organic N tendency due to vertical transport gN/m^3/s F - 818 PAS_SOMN_TO_ACT_SOMN decomp. of passive soil organic N to active soil organic N gN/m^2 F - 819 PAS_SOMN_TO_ACT_SOMN_vr decomp. of passive soil organic N to active soil organic N gN/m^3 F - 820 PAS_SOMN_vr PAS_SOM N (vertically resolved) gN/m^3 T - 821 PAS_SOM_HR Het. Resp. from passive soil organic gC/m^2/s F - 822 PAS_SOM_HR_vr Het. Resp. from passive soil organic gC/m^3/s F - 823 PBOT atmospheric pressure at surface (downscaled to columns in glacier regions) Pa T - 824 PBOT_240 10 day running mean of air pressure Pa F - 825 PCH4 atmospheric partial pressure of CH4 Pa T - 826 PCO2 atmospheric partial pressure of CO2 Pa T - 827 PCO2_240 10 day running mean of CO2 pressure Pa F - 828 PFT_CTRUNC patch-level sink for C truncation gC/m^2 F - 829 PFT_FIRE_CLOSS total patch-level fire C loss for non-peat fires outside land-type converted region gC/m^2/s T - 830 PFT_FIRE_NLOSS total patch-level fire N loss gN/m^2/s T - 831 PFT_NTRUNC patch-level sink for N truncation gN/m^2 F - 832 PLANTCN Plant C:N used by FUN unitless F - 833 PLANT_CALLOC total allocated C flux gC/m^2/s F - 834 PLANT_NALLOC total allocated N flux gN/m^2/s F - 835 PLANT_NDEMAND N flux required to support initial GPP gN/m^2/s T - 836 PNLCZ Proportion of nitrogen allocated for light capture unitless F - 837 PO2_240 10 day running mean of O2 pressure Pa F - 838 POTENTIAL_IMMOB potential N immobilization gN/m^2/s T - 839 POTENTIAL_IMMOB_vr potential N immobilization gN/m^3/s F - 840 POT_F_DENIT potential denitrification flux gN/m^2/s T - 841 POT_F_DENIT_vr potential denitrification flux gN/m^3/s F - 842 POT_F_NIT potential nitrification flux gN/m^2/s T - 843 POT_F_NIT_vr potential nitrification flux gN/m^3/s F - 844 PREC10 10-day running mean of PREC MM H2O/S F - 845 PREC60 60-day running mean of PREC MM H2O/S F - 846 PREV_DAYL daylength from previous timestep s F - 847 PREV_FROOTC_TO_LITTER previous timestep froot C litterfall flux gC/m^2/s F - 848 PREV_LEAFC_TO_LITTER previous timestep leaf C litterfall flux gC/m^2/s F - 849 PROD100C 100-yr wood product C gC/m^2 F - 850 PROD100C_LOSS loss from 100-yr wood product pool gC/m^2/s F - 851 PROD100N 100-yr wood product N gN/m^2 F - 852 PROD100N_LOSS loss from 100-yr wood product pool gN/m^2/s F - 853 PROD10C 10-yr wood product C gC/m^2 F - 854 PROD10C_LOSS loss from 10-yr wood product pool gC/m^2/s F - 855 PROD10N 10-yr wood product N gN/m^2 F - 856 PROD10N_LOSS loss from 10-yr wood product pool gN/m^2/s F - 857 PSNSHA shaded leaf photosynthesis umolCO2/m^2/s T - 858 PSNSHADE_TO_CPOOL C fixation from shaded canopy gC/m^2/s T - 859 PSNSUN sunlit leaf photosynthesis umolCO2/m^2/s T - 860 PSNSUN_TO_CPOOL C fixation from sunlit canopy gC/m^2/s T - 861 PSurf atmospheric pressure at surface (downscaled to columns in glacier regions) Pa F - 862 Q2M 2m specific humidity kg/kg T - 863 QAF canopy air humidity kg/kg F - 864 QBOT atmospheric specific humidity (downscaled to columns in glacier regions) kg/kg T - 865 QDIRECT_THROUGHFALL direct throughfall of liquid (rain + above-canopy irrigation) mm/s F - 866 QDIRECT_THROUGHFALL_SNOW direct throughfall of snow mm/s F - 867 QDRAI sub-surface drainage mm/s T - 868 QDRAI_PERCH perched wt drainage mm/s T - 869 QDRAI_XS saturation excess drainage mm/s T - 870 QDRIP rate of excess canopy liquid falling off canopy mm/s F - 871 QDRIP_SNOW rate of excess canopy snow falling off canopy mm/s F - 872 QFLOOD runoff from river flooding mm/s T - 873 QFLX_EVAP_TOT qflx_evap_soi + qflx_evap_can + qflx_tran_veg kg m-2 s-1 T - 874 QFLX_EVAP_VEG vegetation evaporation mm H2O/s F - 875 QFLX_ICE_DYNBAL ice dynamic land cover change conversion runoff flux mm/s T - 876 QFLX_LIQDEW_TO_TOP_LAYER rate of liquid water deposited on top soil or snow layer (dew) mm H2O/s T - 877 QFLX_LIQEVAP_FROM_TOP_LAYER rate of liquid water evaporated from top soil or snow layer mm H2O/s T - 878 QFLX_LIQ_DYNBAL liq dynamic land cover change conversion runoff flux mm/s T - 879 QFLX_LIQ_GRND liquid (rain+irrigation) on ground after interception mm H2O/s F - 880 QFLX_SNOW_DRAIN drainage from snow pack mm/s T - 881 QFLX_SNOW_DRAIN_ICE drainage from snow pack melt (ice landunits only) mm/s T - 882 QFLX_SNOW_GRND snow on ground after interception mm H2O/s F - 883 QFLX_SOLIDDEW_TO_TOP_LAYER rate of solid water deposited on top soil or snow layer (frost) mm H2O/s T - 884 QFLX_SOLIDEVAP_FROM_TOP_LAYER rate of ice evaporated from top soil or snow layer (sublimation) (also includes bare ice subli mm H2O/s T - 885 QFLX_SOLIDEVAP_FROM_TOP_LAYER_ICE rate of ice evaporated from top soil or snow layer (sublimation) (also includes bare ice subli mm H2O/s F - 886 QH2OSFC surface water runoff mm/s T - 887 QH2OSFC_TO_ICE surface water converted to ice mm/s F - 888 QHR hydraulic redistribution mm/s T - 889 QICE ice growth/melt mm/s T - 890 QICE_FORC qice forcing sent to GLC mm/s F - 891 QICE_FRZ ice growth mm/s T - 892 QICE_MELT ice melt mm/s T - 893 QINFL infiltration mm/s T - 894 QINTR interception mm/s T - 895 QIRRIG_DEMAND irrigation demand mm/s F - 896 QIRRIG_DRIP water added via drip irrigation mm/s F - 897 QIRRIG_FROM_GW_CONFINED water added through confined groundwater irrigation mm/s T - 898 QIRRIG_FROM_GW_UNCONFINED water added through unconfined groundwater irrigation mm/s T - 899 QIRRIG_FROM_SURFACE water added through surface water irrigation mm/s T - 900 QIRRIG_SPRINKLER water added via sprinkler irrigation mm/s F - 901 QOVER total surface runoff (includes QH2OSFC) mm/s T - 902 QOVER_LAG time-lagged surface runoff for soil columns mm/s F - 903 QPHSNEG net negative hydraulic redistribution flux mm/s F - 904 QRGWL surface runoff at glaciers (liquid only), wetlands, lakes; also includes melted ice runoff fro mm/s T - 905 QROOTSINK water flux from soil to root in each soil-layer mm/s F - 906 QRUNOFF total liquid runoff not including correction for land use change mm/s T - 907 QRUNOFF_ICE total liquid runoff not incl corret for LULCC (ice landunits only) mm/s T - 908 QRUNOFF_ICE_TO_COUPLER total ice runoff sent to coupler (includes corrections for land use change) mm/s T - 909 QRUNOFF_ICE_TO_LIQ liquid runoff from converted ice runoff mm/s F - 910 QRUNOFF_R Rural total runoff mm/s F - 911 QRUNOFF_TO_COUPLER total liquid runoff sent to coupler (includes corrections for land use change) mm/s T - 912 QRUNOFF_U Urban total runoff mm/s F - 913 QSNOCPLIQ excess liquid h2o due to snow capping not including correction for land use change mm H2O/s T - 914 QSNOEVAP evaporation from snow (only when snl<0, otherwise it is equal to qflx_ev_soil) mm/s T - 915 QSNOFRZ column-integrated snow freezing rate kg/m2/s T - 916 QSNOFRZ_ICE column-integrated snow freezing rate (ice landunits only) mm/s T - 917 QSNOMELT snow melt rate mm/s T - 918 QSNOMELT_ICE snow melt (ice landunits only) mm/s T - 919 QSNOUNLOAD canopy snow unloading mm/s T - 920 QSNO_TEMPUNLOAD canopy snow temp unloading mm/s T - 921 QSNO_WINDUNLOAD canopy snow wind unloading mm/s T - 922 QSNWCPICE excess solid h2o due to snow capping not including correction for land use change mm H2O/s T - 923 QSOIL Ground evaporation (soil/snow evaporation + soil/snow sublimation - dew) mm/s T - 924 QSOIL_ICE Ground evaporation (ice landunits only) mm/s T - 925 QTOPSOIL water input to surface mm/s F - 926 QVEGE canopy evaporation mm/s T - 927 QVEGT canopy transpiration mm/s T - 928 Qair atmospheric specific humidity (downscaled to columns in glacier regions) kg/kg F - 929 Qh sensible heat W/m^2 F - 930 Qle total evaporation W/m^2 F - 931 Qstor storage heat flux (includes snowmelt) W/m^2 F - 932 Qtau momentum flux kg/m/s^2 F - 933 RAH1 aerodynamical resistance s/m F - 934 RAH2 aerodynamical resistance s/m F - 935 RAIN atmospheric rain, after rain/snow repartitioning based on temperature mm/s T - 936 RAIN_FROM_ATM atmospheric rain received from atmosphere (pre-repartitioning) mm/s T - 937 RAIN_ICE atmospheric rain, after rain/snow repartitioning based on temperature (ice landunits only) mm/s F - 938 RAM1 aerodynamical resistance s/m F - 939 RAM_LAKE aerodynamic resistance for momentum (lakes only) s/m F - 940 RAW1 aerodynamical resistance s/m F - 941 RAW2 aerodynamical resistance s/m F - 942 RB leaf boundary resistance s/m F - 943 RB10 10 day running mean boundary layer resistance s/m F - 944 RETRANSN plant pool of retranslocated N gN/m^2 T - 945 RETRANSN_TO_NPOOL deployment of retranslocated N gN/m^2/s T - 946 RH atmospheric relative humidity % F - 947 RH2M 2m relative humidity % T - 948 RH2M_R Rural 2m specific humidity % F - 949 RH2M_U Urban 2m relative humidity % F - 950 RH30 30-day running mean of relative humidity % F - 951 RHAF fractional humidity of canopy air fraction F - 952 RHAF10 10 day running mean of fractional humidity of canopy air fraction F - 953 RH_LEAF fractional humidity at leaf surface fraction F - 954 ROOTR effective fraction of roots in each soil layer (SMS method) proportion F - 955 RR root respiration (fine root MR + total root GR) gC/m^2/s T - 956 RRESIS root resistance in each soil layer proportion F - 957 RSSHA shaded leaf stomatal resistance s/m T - 958 RSSUN sunlit leaf stomatal resistance s/m T - 959 Rainf atmospheric rain, after rain/snow repartitioning based on temperature mm/s F - 960 Rnet net radiation W/m^2 F - 961 SABG solar rad absorbed by ground W/m^2 T - 962 SABG_PEN Rural solar rad penetrating top soil or snow layer watt/m^2 T - 963 SABV solar rad absorbed by veg W/m^2 T - 964 SEEDC pool for seeding new PFTs via dynamic landcover gC/m^2 T - 965 SEEDN pool for seeding new PFTs via dynamic landcover gN/m^2 T - 966 SLASH_HARVESTC slash harvest carbon (to litter) gC/m^2/s T - 967 SLO_SOMC SLO_SOM C gC/m^2 T - 968 SLO_SOMC_1m SLO_SOM C to 1 meter gC/m^2 F - 969 SLO_SOMC_TNDNCY_VERT_TRA slow soil organic ma C tendency due to vertical transport gC/m^3/s F - 970 SLO_SOMC_TO_ACT_SOMC decomp. of slow soil organic ma C to active soil organic C gC/m^2/s F - 971 SLO_SOMC_TO_ACT_SOMC_vr decomp. of slow soil organic ma C to active soil organic C gC/m^3/s F - 972 SLO_SOMC_TO_PAS_SOMC decomp. of slow soil organic ma C to passive soil organic C gC/m^2/s F - 973 SLO_SOMC_TO_PAS_SOMC_vr decomp. of slow soil organic ma C to passive soil organic C gC/m^3/s F - 974 SLO_SOMC_vr SLO_SOM C (vertically resolved) gC/m^3 T - 975 SLO_SOMN SLO_SOM N gN/m^2 T - 976 SLO_SOMN_1m SLO_SOM N to 1 meter gN/m^2 F - 977 SLO_SOMN_TNDNCY_VERT_TRA slow soil organic ma N tendency due to vertical transport gN/m^3/s F - 978 SLO_SOMN_TO_ACT_SOMN decomp. of slow soil organic ma N to active soil organic N gN/m^2 F - 979 SLO_SOMN_TO_ACT_SOMN_vr decomp. of slow soil organic ma N to active soil organic N gN/m^3 F - 980 SLO_SOMN_TO_PAS_SOMN decomp. of slow soil organic ma N to passive soil organic N gN/m^2 F - 981 SLO_SOMN_TO_PAS_SOMN_vr decomp. of slow soil organic ma N to passive soil organic N gN/m^3 F - 982 SLO_SOMN_vr SLO_SOM N (vertically resolved) gN/m^3 T - 983 SLO_SOM_HR_S1 Het. Resp. from slow soil organic ma gC/m^2/s F - 984 SLO_SOM_HR_S1_vr Het. Resp. from slow soil organic ma gC/m^3/s F - 985 SLO_SOM_HR_S3 Het. Resp. from slow soil organic ma gC/m^2/s F - 986 SLO_SOM_HR_S3_vr Het. Resp. from slow soil organic ma gC/m^3/s F - 987 SMINN soil mineral N gN/m^2 T - 988 SMINN_TO_NPOOL deployment of soil mineral N uptake gN/m^2/s T - 989 SMINN_TO_PLANT plant uptake of soil mineral N gN/m^2/s T - 990 SMINN_TO_PLANT_FUN Total soil N uptake of FUN gN/m^2/s T - 991 SMINN_TO_PLANT_vr plant uptake of soil mineral N gN/m^3/s F - 992 SMINN_TO_S1N_L1 mineral N flux for decomp. of MET_LITto ACT_SOM gN/m^2 F - 993 SMINN_TO_S1N_L1_vr mineral N flux for decomp. of MET_LITto ACT_SOM gN/m^3 F - 994 SMINN_TO_S1N_L2 mineral N flux for decomp. of CEL_LITto ACT_SOM gN/m^2 F - 995 SMINN_TO_S1N_L2_vr mineral N flux for decomp. of CEL_LITto ACT_SOM gN/m^3 F - 996 SMINN_TO_S1N_S2 mineral N flux for decomp. of SLO_SOMto ACT_SOM gN/m^2 F - 997 SMINN_TO_S1N_S2_vr mineral N flux for decomp. of SLO_SOMto ACT_SOM gN/m^3 F - 998 SMINN_TO_S1N_S3 mineral N flux for decomp. of PAS_SOMto ACT_SOM gN/m^2 F - 999 SMINN_TO_S1N_S3_vr mineral N flux for decomp. of PAS_SOMto ACT_SOM gN/m^3 F -1000 SMINN_TO_S2N_L3 mineral N flux for decomp. of LIG_LITto SLO_SOM gN/m^2 F -1001 SMINN_TO_S2N_L3_vr mineral N flux for decomp. of LIG_LITto SLO_SOM gN/m^3 F -1002 SMINN_TO_S2N_S1 mineral N flux for decomp. of ACT_SOMto SLO_SOM gN/m^2 F -1003 SMINN_TO_S2N_S1_vr mineral N flux for decomp. of ACT_SOMto SLO_SOM gN/m^3 F -1004 SMINN_TO_S3N_S1 mineral N flux for decomp. of ACT_SOMto PAS_SOM gN/m^2 F -1005 SMINN_TO_S3N_S1_vr mineral N flux for decomp. of ACT_SOMto PAS_SOM gN/m^3 F -1006 SMINN_TO_S3N_S2 mineral N flux for decomp. of SLO_SOMto PAS_SOM gN/m^2 F -1007 SMINN_TO_S3N_S2_vr mineral N flux for decomp. of SLO_SOMto PAS_SOM gN/m^3 F -1008 SMINN_vr soil mineral N gN/m^3 T -1009 SMIN_NH4 soil mineral NH4 gN/m^2 T -1010 SMIN_NH4_TO_PLANT plant uptake of NH4 gN/m^3/s F -1011 SMIN_NH4_vr soil mineral NH4 (vert. res.) gN/m^3 T -1012 SMIN_NO3 soil mineral NO3 gN/m^2 T -1013 SMIN_NO3_LEACHED soil NO3 pool loss to leaching gN/m^2/s T -1014 SMIN_NO3_LEACHED_vr soil NO3 pool loss to leaching gN/m^3/s F -1015 SMIN_NO3_MASSDENS SMIN_NO3_MASSDENS ugN/cm^3 soil F -1016 SMIN_NO3_RUNOFF soil NO3 pool loss to runoff gN/m^2/s T -1017 SMIN_NO3_RUNOFF_vr soil NO3 pool loss to runoff gN/m^3/s F -1018 SMIN_NO3_TO_PLANT plant uptake of NO3 gN/m^3/s F -1019 SMIN_NO3_vr soil mineral NO3 (vert. res.) gN/m^3 T -1020 SMP soil matric potential (natural vegetated and crop landunits only) mm T -1021 SNOBCMCL mass of BC in snow column kg/m2 T -1022 SNOBCMSL mass of BC in top snow layer kg/m2 T -1023 SNOCAN intercepted snow mm T -1024 SNODSTMCL mass of dust in snow column kg/m2 T -1025 SNODSTMSL mass of dust in top snow layer kg/m2 T -1026 SNOFSDSND direct nir incident solar radiation on snow W/m^2 F -1027 SNOFSDSNI diffuse nir incident solar radiation on snow W/m^2 F -1028 SNOFSDSVD direct vis incident solar radiation on snow W/m^2 F -1029 SNOFSDSVI diffuse vis incident solar radiation on snow W/m^2 F -1030 SNOFSRND direct nir reflected solar radiation from snow W/m^2 T -1031 SNOFSRNI diffuse nir reflected solar radiation from snow W/m^2 T -1032 SNOFSRVD direct vis reflected solar radiation from snow W/m^2 T -1033 SNOFSRVI diffuse vis reflected solar radiation from snow W/m^2 T -1034 SNOINTABS Fraction of incoming solar absorbed by lower snow layers - T -1035 SNOLIQFL top snow layer liquid water fraction (land) fraction F -1036 SNOOCMCL mass of OC in snow column kg/m2 T -1037 SNOOCMSL mass of OC in top snow layer kg/m2 T -1038 SNORDSL top snow layer effective grain radius m^-6 F -1039 SNOTTOPL snow temperature (top layer) K F -1040 SNOTTOPL_ICE snow temperature (top layer, ice landunits only) K F -1041 SNOTXMASS snow temperature times layer mass, layer sum; to get mass-weighted temperature, divide by (SNO K kg/m2 T -1042 SNOTXMASS_ICE snow temperature times layer mass, layer sum (ice landunits only); to get mass-weighted temper K kg/m2 F -1043 SNOW atmospheric snow, after rain/snow repartitioning based on temperature mm/s T -1044 SNOWDP gridcell mean snow height m T -1045 SNOWICE snow ice kg/m2 T -1046 SNOWICE_ICE snow ice (ice landunits only) kg/m2 F -1047 SNOWLIQ snow liquid water kg/m2 T -1048 SNOWLIQ_ICE snow liquid water (ice landunits only) kg/m2 F -1049 SNOW_5D 5day snow avg m F -1050 SNOW_DEPTH snow height of snow covered area m T -1051 SNOW_DEPTH_ICE snow height of snow covered area (ice landunits only) m F -1052 SNOW_FROM_ATM atmospheric snow received from atmosphere (pre-repartitioning) mm/s T -1053 SNOW_ICE atmospheric snow, after rain/snow repartitioning based on temperature (ice landunits only) mm/s F -1054 SNOW_PERSISTENCE Length of time of continuous snow cover (nat. veg. landunits only) seconds T -1055 SNOW_SINKS snow sinks (liquid water) mm/s T -1056 SNOW_SOURCES snow sources (liquid water) mm/s T -1057 SNO_ABS Absorbed solar radiation in each snow layer W/m^2 F -1058 SNO_ABS_ICE Absorbed solar radiation in each snow layer (ice landunits only) W/m^2 F -1059 SNO_BW Partial density of water in the snow pack (ice + liquid) kg/m3 F -1060 SNO_BW_ICE Partial density of water in the snow pack (ice + liquid, ice landunits only) kg/m3 F -1061 SNO_EXISTENCE Fraction of averaging period for which each snow layer existed unitless F -1062 SNO_FRZ snow freezing rate in each snow layer kg/m2/s F -1063 SNO_FRZ_ICE snow freezing rate in each snow layer (ice landunits only) mm/s F -1064 SNO_GS Mean snow grain size Microns F -1065 SNO_GS_ICE Mean snow grain size (ice landunits only) Microns F -1066 SNO_ICE Snow ice content kg/m2 F -1067 SNO_LIQH2O Snow liquid water content kg/m2 F -1068 SNO_MELT snow melt rate in each snow layer mm/s F -1069 SNO_MELT_ICE snow melt rate in each snow layer (ice landunits only) mm/s F -1070 SNO_T Snow temperatures K F -1071 SNO_TK Thermal conductivity W/m-K F -1072 SNO_TK_ICE Thermal conductivity (ice landunits only) W/m-K F -1073 SNO_T_ICE Snow temperatures (ice landunits only) K F -1074 SNO_Z Snow layer thicknesses m F -1075 SNO_Z_ICE Snow layer thicknesses (ice landunits only) m F -1076 SNOdTdzL top snow layer temperature gradient (land) K/m F -1077 SOIL10 10-day running mean of 12cm layer soil K F -1078 SOILC_CHANGE C change in soil gC/m^2/s T -1079 SOILC_HR soil C heterotrophic respiration gC/m^2/s T -1080 SOILC_vr SOIL C (vertically resolved) gC/m^3 T -1081 SOILICE soil ice (natural vegetated and crop landunits only) kg/m2 T -1082 SOILLIQ soil liquid water (natural vegetated and crop landunits only) kg/m2 T -1083 SOILN_vr SOIL N (vertically resolved) gN/m^3 T -1084 SOILPSI soil water potential in each soil layer MPa F -1085 SOILRESIS soil resistance to evaporation s/m T -1086 SOILWATER_10CM soil liquid water + ice in top 10cm of soil (veg landunits only) kg/m2 T -1087 SOMC_FIRE C loss due to peat burning gC/m^2/s T -1088 SOMFIRE soil organic matter fire losses gC/m^2/s F -1089 SOM_ADV_COEF advection term for vertical SOM translocation m/s F -1090 SOM_C_LEACHED total flux of C from SOM pools due to leaching gC/m^2/s T -1091 SOM_DIFFUS_COEF diffusion coefficient for vertical SOM translocation m^2/s F -1092 SOM_N_LEACHED total flux of N from SOM pools due to leaching gN/m^2/s F -1093 SR total soil respiration (HR + root resp) gC/m^2/s T -1094 SSRE_FSR surface snow effect on reflected solar radiation W/m^2 T -1095 SSRE_FSRND surface snow effect on direct nir reflected solar radiation W/m^2 T -1096 SSRE_FSRNDLN surface snow effect on direct nir reflected solar radiation at local noon W/m^2 T -1097 SSRE_FSRNI surface snow effect on diffuse nir reflected solar radiation W/m^2 T -1098 SSRE_FSRVD surface snow radiatve effect on direct vis reflected solar radiation W/m^2 T -1099 SSRE_FSRVDLN surface snow radiatve effect on direct vis reflected solar radiation at local noon W/m^2 T -1100 SSRE_FSRVI surface snow radiatve effect on diffuse vis reflected solar radiation W/m^2 T -1101 STEM_PROF profile for litter C and N inputs from stems 1/m F -1102 STORAGE_CDEMAND C use from the C storage pool gC/m^2 F -1103 STORAGE_GR growth resp for growth sent to storage for later display gC/m^2/s F -1104 STORAGE_NDEMAND N demand during the offset period gN/m^2 F -1105 STORVEGC stored vegetation carbon, excluding cpool gC/m^2 T -1106 STORVEGN stored vegetation nitrogen gN/m^2 T -1107 SUPPLEMENT_TO_SMINN supplemental N supply gN/m^2/s T -1108 SUPPLEMENT_TO_SMINN_vr supplemental N supply gN/m^3/s F -1109 SWBGT 2 m Simplified Wetbulb Globe Temp C T -1110 SWBGT_R Rural 2 m Simplified Wetbulb Globe Temp C T -1111 SWBGT_U Urban 2 m Simplified Wetbulb Globe Temp C T -1112 SWMP65 2 m Swamp Cooler Temp 65% Eff C T -1113 SWMP65_R Rural 2 m Swamp Cooler Temp 65% Eff C T -1114 SWMP65_U Urban 2 m Swamp Cooler Temp 65% Eff C T -1115 SWMP80 2 m Swamp Cooler Temp 80% Eff C T -1116 SWMP80_R Rural 2 m Swamp Cooler Temp 80% Eff C T -1117 SWMP80_U Urban 2 m Swamp Cooler Temp 80% Eff C T -1118 SWdown atmospheric incident solar radiation W/m^2 F -1119 SWup upwelling shortwave radiation W/m^2 F -1120 SoilAlpha factor limiting ground evap unitless F -1121 SoilAlpha_U urban factor limiting ground evap unitless F -1122 T10 10-day running mean of 2-m temperature K F -1123 TAF canopy air temperature K F -1124 TAUX zonal surface stress kg/m/s^2 T -1125 TAUY meridional surface stress kg/m/s^2 T -1126 TBOT atmospheric air temperature (downscaled to columns in glacier regions) K T -1127 TBUILD internal urban building air temperature K T -1128 TBUILD_MAX prescribed maximum interior building temperature K F -1129 TEMPAVG_T2M temporary average 2m air temperature K F -1130 TEMPMAX_RETRANSN temporary annual max of retranslocated N pool gN/m^2 F -1131 TEMPSUM_POTENTIAL_GPP temporary annual sum of potential GPP gC/m^2/yr F -1132 TEQ 2 m Equiv Temp K T -1133 TEQ_R Rural 2 m Equiv Temp K T -1134 TEQ_U Urban 2 m Equiv Temp K T -1135 TFLOOR floor temperature K F -1136 TG ground temperature K T -1137 TG_ICE ground temperature (ice landunits only) K F -1138 TG_R Rural ground temperature K F -1139 TG_U Urban ground temperature K F -1140 TH2OSFC surface water temperature K T -1141 THBOT atmospheric air potential temperature (downscaled to columns in glacier regions) K T -1142 THIC 2 m Temp Hum Index Comfort C T -1143 THIC_R Rural 2 m Temp Hum Index Comfort C T -1144 THIC_U Urban 2 m Temp Hum Index Comfort C T -1145 THIP 2 m Temp Hum Index Physiology C T -1146 THIP_R Rural 2 m Temp Hum Index Physiology C T -1147 THIP_U Urban 2 m Temp Hum Index Physiology C T -1148 TKE1 top lake level eddy thermal conductivity W/(mK) T -1149 TLAI total projected leaf area index m^2/m^2 T -1150 TLAKE lake temperature K T -1151 TOPO_COL column-level topographic height m F -1152 TOPO_COL_ICE column-level topographic height (ice landunits only) m F -1153 TOPO_FORC topograephic height sent to GLC m F -1154 TOPT topt coefficient for VOC calc non F -1155 TOTCOLC total column carbon, incl veg and cpool but excl product pools gC/m^2 T -1156 TOTCOLCH4 total belowground CH4 (0 for non-lake special landunits in the absence of dynamic landunits) gC/m2 T -1157 TOTCOLN total column-level N, excluding product pools gN/m^2 T -1158 TOTECOSYSC total ecosystem carbon, incl veg but excl cpool and product pools gC/m^2 T -1159 TOTECOSYSN total ecosystem N, excluding product pools gN/m^2 T -1160 TOTFIRE total ecosystem fire losses gC/m^2/s F -1161 TOTLITC total litter carbon gC/m^2 T -1162 TOTLITC_1m total litter carbon to 1 meter depth gC/m^2 T -1163 TOTLITN total litter N gN/m^2 T -1164 TOTLITN_1m total litter N to 1 meter gN/m^2 T -1165 TOTPFTC total patch-level carbon, including cpool gC/m^2 T -1166 TOTPFTN total patch-level nitrogen gN/m^2 T -1167 TOTSOILICE vertically summed soil cie (veg landunits only) kg/m2 T -1168 TOTSOILLIQ vertically summed soil liquid water (veg landunits only) kg/m2 T -1169 TOTSOMC total soil organic matter carbon gC/m^2 T -1170 TOTSOMC_1m total soil organic matter carbon to 1 meter depth gC/m^2 T -1171 TOTSOMN total soil organic matter N gN/m^2 T -1172 TOTSOMN_1m total soil organic matter N to 1 meter gN/m^2 T -1173 TOTVEGC total vegetation carbon, excluding cpool gC/m^2 T -1174 TOTVEGN total vegetation nitrogen gN/m^2 T -1175 TOT_WOODPRODC total wood product C gC/m^2 T -1176 TOT_WOODPRODC_LOSS total loss from wood product pools gC/m^2/s T -1177 TOT_WOODPRODN total wood product N gN/m^2 T -1178 TOT_WOODPRODN_LOSS total loss from wood product pools gN/m^2/s T -1179 TPU25T canopy profile of tpu umol/m2/s T -1180 TRAFFICFLUX sensible heat flux from urban traffic W/m^2 F -1181 TRANSFER_DEADCROOT_GR dead coarse root growth respiration from storage gC/m^2/s F -1182 TRANSFER_DEADSTEM_GR dead stem growth respiration from storage gC/m^2/s F -1183 TRANSFER_FROOT_GR fine root growth respiration from storage gC/m^2/s F -1184 TRANSFER_GR growth resp for transfer growth displayed in this timestep gC/m^2/s F -1185 TRANSFER_LEAF_GR leaf growth respiration from storage gC/m^2/s F -1186 TRANSFER_LIVECROOT_GR live coarse root growth respiration from storage gC/m^2/s F -1187 TRANSFER_LIVESTEM_GR live stem growth respiration from storage gC/m^2/s F -1188 TREFMNAV daily minimum of average 2-m temperature K T -1189 TREFMNAV_R Rural daily minimum of average 2-m temperature K F -1190 TREFMNAV_U Urban daily minimum of average 2-m temperature K F -1191 TREFMXAV daily maximum of average 2-m temperature K T -1192 TREFMXAV_R Rural daily maximum of average 2-m temperature K F -1193 TREFMXAV_U Urban daily maximum of average 2-m temperature K F -1194 TROOF_INNER roof inside surface temperature K F -1195 TSA 2m air temperature K T -1196 TSAI total projected stem area index m^2/m^2 T -1197 TSA_ICE 2m air temperature (ice landunits only) K F -1198 TSA_R Rural 2m air temperature K F -1199 TSA_U Urban 2m air temperature K F -1200 TSHDW_INNER shadewall inside surface temperature K F -1201 TSKIN skin temperature K T -1202 TSL temperature of near-surface soil layer (natural vegetated and crop landunits only) K T -1203 TSOI soil temperature (natural vegetated and crop landunits only) K T -1204 TSOI_10CM soil temperature in top 10cm of soil K T -1205 TSOI_ICE soil temperature (ice landunits only) K T -1206 TSRF_FORC surface temperature sent to GLC K F -1207 TSUNW_INNER sunwall inside surface temperature K F -1208 TV vegetation temperature K T -1209 TV24 vegetation temperature (last 24hrs) K F -1210 TV240 vegetation temperature (last 240hrs) K F -1211 TVEGD10 10 day running mean of patch daytime vegetation temperature Kelvin F -1212 TVEGN10 10 day running mean of patch night-time vegetation temperature Kelvin F -1213 TWS total water storage mm T -1214 T_SCALAR temperature inhibition of decomposition unitless T -1215 Tair atmospheric air temperature (downscaled to columns in glacier regions) K F -1216 Tair_from_atm atmospheric air temperature received from atmosphere (pre-downscaling) K F -1217 U10 10-m wind m/s T -1218 U10_DUST 10-m wind for dust model m/s T -1219 U10_ICE 10-m wind (ice landunits only) m/s F -1220 UAF canopy air speed m/s F -1221 ULRAD upward longwave radiation above the canopy W/m^2 F -1222 UM wind speed plus stability effect m/s F -1223 URBAN_AC urban air conditioning flux W/m^2 T -1224 URBAN_HEAT urban heating flux W/m^2 T -1225 USTAR aerodynamical resistance s/m F -1226 UST_LAKE friction velocity (lakes only) m/s F -1227 VA atmospheric wind speed plus convective velocity m/s F -1228 VCMX25T canopy profile of vcmax25 umol/m2/s T -1229 VEGWP vegetation water matric potential for sun/sha canopy,xyl,root segments mm T -1230 VEGWPLN vegetation water matric potential for sun/sha canopy,xyl,root at local noon mm T -1231 VEGWPPD predawn vegetation water matric potential for sun/sha canopy,xyl,root mm T -1232 VOCFLXT total VOC flux into atmosphere moles/m2/sec F -1233 VOLR river channel total water storage m3 T -1234 VOLRMCH river channel main channel water storage m3 T -1235 VPD vpd Pa F -1236 VPD2M 2m vapor pressure deficit Pa T -1237 VPD_CAN canopy vapor pressure deficit kPa T -1238 Vcmx25Z canopy profile of vcmax25 predicted by LUNA model umol/m2/s T -1239 WASTEHEAT sensible heat flux from heating/cooling sources of urban waste heat W/m^2 T -1240 WBA 2 m Wet Bulb C T -1241 WBA_R Rural 2 m Wet Bulb C T -1242 WBA_U Urban 2 m Wet Bulb C T -1243 WBT 2 m Stull Wet Bulb C T -1244 WBT_R Rural 2 m Stull Wet Bulb C T -1245 WBT_U Urban 2 m Stull Wet Bulb C T -1246 WF soil water as frac. of whc for top 0.05 m proportion F -1247 WFPS WFPS percent F -1248 WIND atmospheric wind velocity magnitude m/s T -1249 WOODC wood C gC/m^2 T -1250 WOODC_ALLOC wood C eallocation gC/m^2/s T -1251 WOODC_LOSS wood C loss gC/m^2/s T -1252 WOOD_HARVESTC wood harvest carbon (to product pools) gC/m^2/s T -1253 WOOD_HARVESTN wood harvest N (to product pools) gN/m^2/s T -1254 WTGQ surface tracer conductance m/s T -1255 W_SCALAR Moisture (dryness) inhibition of decomposition unitless T -1256 Wind atmospheric wind velocity magnitude m/s F -1257 XSMRPOOL temporary photosynthate C pool gC/m^2 T -1258 XSMRPOOL_LOSS temporary photosynthate C pool loss gC/m^2 F -1259 XSMRPOOL_RECOVER C flux assigned to recovery of negative xsmrpool gC/m^2/s T -1260 Z0HG roughness length over ground, sensible heat m F -1261 Z0HV roughness length over vegetation, sensible heat m F -1262 Z0M momentum roughness length m F -1263 Z0MG roughness length over ground, momentum m F -1264 Z0MV roughness length over vegetation, momentum m F -1265 Z0M_TO_COUPLER roughness length, momentum: gridcell average sent to coupler m F -1266 Z0QG roughness length over ground, latent heat m F -1267 Z0QV roughness length over vegetation, latent heat m F -1268 ZBOT atmospheric reference height m T -1269 ZETA dimensionless stability parameter unitless F -1270 ZII convective boundary height m F -1271 ZWT water table depth (natural vegetated and crop landunits only) m T -1272 ZWT_CH4_UNSAT depth of water table for methane production used in non-inundated area m T -1273 ZWT_PERCH perched water table depth (natural vegetated and crop landunits only) m T -1274 anaerobic_frac anaerobic_frac m3/m3 F -1275 bsw clap and hornberger B unitless F -1276 currentPatch currentPatch coefficient for VOC calc non F -1277 diffus diffusivity m^2/s F -1278 fr_WFPS fr_WFPS fraction F -1279 n2_n2o_ratio_denit n2_n2o_ratio_denit gN/gN F -1280 num_iter number of iterations unitless F -1281 r_psi r_psi m F -1282 ratio_k1 ratio_k1 none F -1283 ratio_no3_co2 ratio_no3_co2 ratio F -1284 soil_bulkdensity soil_bulkdensity kg/m3 F -1285 soil_co2_prod soil_co2_prod ug C / g soil / day F -1286 watfc water field capacity m^3/m^3 F -1287 watsat water saturated m^3/m^3 F -==== =================================== ============================================================================================== ================================================================= ======= diff --git a/doc/source/users_guide/testing/index.rst b/doc/source/users_guide/testing/index.rst index b9e99506e1..b7baedc04f 100644 --- a/doc/source/users_guide/testing/index.rst +++ b/doc/source/users_guide/testing/index.rst @@ -3,10 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. _testing_section: - .. include:: ../substitutions.rst +.. _testing_section: + ##################################### Testing ##################################### @@ -16,4 +16,3 @@ Testing testing.rst - diff --git a/doc/source/users_guide/testing/testing.rst b/doc/source/users_guide/testing/testing.rst index a01630c293..bad1183fff 100644 --- a/doc/source/users_guide/testing/testing.rst +++ b/doc/source/users_guide/testing/testing.rst @@ -1,30 +1,17 @@ -.. _testing: - .. include:: ../substitutions.rst +.. _testing: + ******* Testing ******* -Technically, you could use the customization we gave in `Chapter 1 `_ to test various configuration and namelist options for CLM. -Sometimes, it's also useful to have automated tests though to test that restarts give exactly the same results as without a restart. -It's also useful to have automated tests to run over a wide variety of configurations, resolutions, and namelist options. -To do that we have several different types of scripts set up to make running comprehensive testing of CLM easy. -There are two types of testing scripts for CLM. -The first are the CESM test scripts, which utilize the **create_newcase** scripts that we shown how to use in this User's Guide. -The second are a set of stand-alone scripts that use the CLM **configure** and **build-namelist** scripts to build and test the model as well as testing the CLM tools as well. -Below we will go into further details of how to use both methods. - +Technically, you could use the customization we gave in :ref:`customizing_section` to test various configuration and namelist options for CLM. Sometimes, it's also useful to have automated tests though to test that restarts give exactly the same results as without a restart. It's also useful to have automated tests to run over a wide variety of configurations, resolutions, and namelist options. To do that we have several different types of scripts set up to make running comprehensive testing of CLM easy. There are two types of testing scripts for CLM. The first are the CESM test scripts, which utilize the **create_newcase** scripts that we shown how to use in this User's Guide. The second are a set of stand-alone scripts that use the CLM **configure** and **build-namelist** scripts to build and test the model as well as testing the CLM tools as well. Below we will go into further details of how to use both methods. CIME Testing scripts ==================== -We first introduce the test scripts that work for all CESM components. -The CIME script **create_test** runs a specific type of test, at a given resolution, for a given compset using a given machine. -See `CIME Chapter on Testing `_ for how to use it to run single -tests as well as lists of tests. The standard testname for CLM is "aux_clm" for cheyenne with intel and gnu compilers as -well as the CGD machine hobart for intel, nag, and pgi compilers. There's also a shorter test list called "clm_short". Also -see the `CTSM Wiki on Testing `_. +We first introduce the test scripts that work for all CESM components. The CIME script **create_test** runs a specific type of test, at a given resolution, for a given compset using a given machine. See `CIME Chapter on Testing `_ for how to use it to run single tests as well as lists of tests. The standard testname for CLM is "aux_clm" for cheyenne with intel and gnu compilers as well as the CGD machine hobart for intel, nag, and pgi compilers. There's also a shorter test list called "clm_short". Also see the `CTSM Wiki on Testing `_. CTSM Tools Testing ================== @@ -41,12 +28,20 @@ CTSM Fortran Unit Tests CTSM Build-namelist Tests ========================= -Run the following perl tester that +Test the namelist build script by running the following: :: + > cd bld/unit_testers - > ./build-namelist_test.pl + > ./build-namelist_test.pl 1>namelist_test.log 2>&1 +When that's complete, inspect ``namelist_test.log`` (e.g., with ``less namelist_test.log``). If you see ``Successfully ran all testing for build-namelist`` but nothing like ``# Looks like you failed 4 tests of 1999.``, then everything went fine. + +If something went wrong, you can find the failing tests like so: + +:: + + > grep -E "^[0-9]+/[0-9]+ < [a-zA-Z]+" namelist_test.log | grep -v "PASS" Testing PTCLM ============= @@ -56,6 +51,5 @@ Testing PTCLM To run on cheyenne, you do the following: - .. include:: ../../../../tools/PTCLM/test/README.run_cheyenne :literal: diff --git a/doc/source/users_guide/trouble-shooting/index.rst b/doc/source/users_guide/trouble-shooting/index.rst index 64b0cecee3..de6b0c053a 100644 --- a/doc/source/users_guide/trouble-shooting/index.rst +++ b/doc/source/users_guide/trouble-shooting/index.rst @@ -3,10 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. _troubleshooting: - .. include:: ../substitutions.rst +.. _troubleshooting-index: + ##################################### Troubleshooting ##################################### diff --git a/doc/source/users_guide/trouble-shooting/trouble-shooting.rst b/doc/source/users_guide/trouble-shooting/trouble-shooting.rst index 2228caa740..e139796f71 100644 --- a/doc/source/users_guide/trouble-shooting/trouble-shooting.rst +++ b/doc/source/users_guide/trouble-shooting/trouble-shooting.rst @@ -1,7 +1,7 @@ -.. _trouble-shooting: - .. include:: ../substitutions.rst +.. _trouble-shooting: + *************** Troubleshooting *************** @@ -37,7 +37,7 @@ It is important to examine all of the component log files in the run directory f 398: iam = 362: gridcell latitude = -70.0000000 398: iam = 362: pft type = 10 398: iam = 362: column type = 1 - 398: iam = 362: landunit type = 1 + 398: iam = 362: landunit type = 1 398: ENDRUN: 398: ERROR: 398: ERROR: carbon or nitrogen state critically negative ERROR in CNPrecisionControl @@ -79,7 +79,7 @@ If you are writing your own ``endrun`` call, you can get this additional informa You can get this same information without aborting the run via a call to ``write_point_context``, which is also defined in the ``abortutils`` module; e.g.:: - if (abs(carbon_patch(p)) < ccrit) then + if (abs(carbon_patch(p)) < ccrit) then call write_point_context(subgrid_index=p, subgrid_level=subgrid_level_patch) end if @@ -110,17 +110,14 @@ The second method is to use the local index along with the processor number:: write(iulog,*)'CNCStateUpdate1Mod +leafc_xfer_to_leafc: ',cf_veg%leafc_xfer_to_leafc_patch(p)*dt end if -By placing these write statements in the code, one can get a sense of how leafc is evolving toward a negative state and why. -This is a very complex example of troubleshooting. To make a long story short, as described `here `_, the error turned out to be caused by a few lines in the phenology code that weren't handling a 20 minute time step properly, thus an actual bug in the code. This was also a good example of where a much less computationally expensive land-only simulation was able to be used for debugging instead of the orginal expensive fully-coupled simulation. +By placing these write statements in the code, one can get a sense of how leafc is evolving toward a negative state and why. This is a very complex example of troubleshooting. To make a long story short, as described `here `_, the error turned out to be caused by a few lines in the phenology code that weren't handling a 20 minute time step properly, thus an actual bug in the code. This was also a good example of where a much less computationally expensive land-only simulation was able to be used for debugging instead of the orginal expensive fully-coupled simulation. Another method of troubleshooting is to use the **point_of_interest** module. Use the point_of_interest module -------------------------------- -It is common, when debugging, to want to print the values of various variables for all patches or columns of certain landunit types within a certain grid cell of interest. For example, one might be able to identify a certain grid cell with an erroneous value for a particular history field variable (e.g., GPP) using for example ncview. Once the latitude and longitude of this grid cell has been determined, the point_of_interest module (**src/utils/point_of_interest.F90**) helps create the logical functions needed to do this. -This module is compiled into every CTSM build, but is not invoked by default. -To use it +It is common, when debugging, to want to print the values of various variables for all patches or columns of certain landunit types within a certain grid cell of interest. For example, one might be able to identify a certain grid cell with an erroneous value for a particular history field variable (e.g., GPP) using for example ncview. Once the latitude and longitude of this grid cell has been determined, the point_of_interest module (**src/utils/point_of_interest.F90**) helps create the logical functions needed to do this. This module is compiled into every CTSM build, but is not invoked by default. To use it (1) Enter in the latitude/longitude of the point of interest in the function **at_poi** in **point_of_interest.F90** by setting the variables **poi_lat** and **poi_lon**. @@ -174,12 +171,12 @@ Simplifying to one processor removes all multi-processing problems and makes the # Set tasks and threads for each component to 1 # You could also set threads to something > 1 for speed, but still # run interactively if threading isn't an issue. - + > ./xmlchange NTASKS_ATM=1,NTHRDS_ATM=1,NTASKS_LND=1,NTHRDS_LND=1,NTASKS_ICE=1,NTHRDS_ICE=1 > ./xmlchange NTASKS_OCN=1,NTHRDS_OCN=1,NTASKS_CPL=1,NTHRDS_CPL=1,NTASKS_GLC=1,NTHRDS_GLC=1 # set MPILIB to mpi-serial so that you can run interactively > ./xmlchange MPILIB=mpi-serial - > ./case.setup + > ./case.setup # Then build your case # And finally run, by running the *.run script interactively @@ -196,9 +193,5 @@ Along the same lines, you might try running a simpler case, trying another comps Run with a debugger ------------------- -Another suggestion is to run the model with a debugger such as: **ddt**, **dbx**, **gdb**, or **totalview**. -Often to run with a debugger you will need to reduce the number of processors as outlined above. -Some debuggers such as **dbx** will only work with one processor, while more advanced debuggers such as **totalview** can work with both MPI tasks and OMP threads. -Even simple debuggers though can be used to query core files, to see where the code was at when it died (for example using the **where** in **dbx** for a core file can be very helpful. -For help in running with a debugger you will need to contact your system administrators for the machine you are running on. +Another suggestion is to run the model with a debugger such as: **ddt**, **dbx**, **gdb**, or **totalview**. Often to run with a debugger you will need to reduce the number of processors as outlined above. Some debuggers such as **dbx** will only work with one processor, while more advanced debuggers such as **totalview** can work with both MPI tasks and OMP threads. Even simple debuggers though can be used to query core files, to see where the code was at when it died (for example using the **where** in **dbx** for a core file can be very helpful. For help in running with a debugger you will need to contact your system administrators for the machine you are running on. diff --git a/doc/source/users_guide/using-clm-tools/building-the-clm-tools.rst b/doc/source/users_guide/using-clm-tools/building-the-clm-tools.rst index 14c3a75207..09725c8afc 100644 --- a/doc/source/users_guide/using-clm-tools/building-the-clm-tools.rst +++ b/doc/source/users_guide/using-clm-tools/building-the-clm-tools.rst @@ -4,16 +4,9 @@ .. include:: ../substitutions.rst -The CLM FORTRAN tools all have similar makefiles, and similar options for building. The tools -**cprnc** and **gen_domain** use the CIME configure/build system which is described in the next section. +The CLM FORTRAN tools all have similar makefiles, and similar options for building. The tools **cprnc** and **gen_domain** use the CIME configure/build system which is described in the next section. -The Makefiles (for **mksurfdata_map** and **mkprocdata_map**) use GNU Make extensions and thus require that you use GNU make to use them. -They also auto detect the type of platform you are on, using "uname -s" and set the compiler, compiler flags and such accordingly. -There are also environment variables that can be set to set things that must be customized. -All the tools use NetCDF and hence require the path to the NetCDF libraries and include files. -On some platforms (such as Linux) multiple compilers can be used, and hence there are env variables that can be set to change the FORTRAN and/or "C" compilers used. -The tools also allow finer control, by also allowing the user to add compiler flags they choose, for both FORTRAN and "C", as well as picking the compiler, linker and and add linker options. -Finally the tools allow you to turn optimization on (which is off by default but on for **mksurfdata_map**) with the OPT flag so that the tool will run faster. +The Makefiles (for **mksurfdata_map** and **mkprocdata_map**) use GNU Make extensions and thus require that you use GNU make to use them. They also auto detect the type of platform you are on, using "uname -s" and set the compiler, compiler flags and such accordingly. There are also environment variables that can be set to set things that must be customized. All the tools use NetCDF and hence require the path to the NetCDF libraries and include files. On some platforms (such as Linux) multiple compilers can be used, and hence there are env variables that can be set to change the FORTRAN and/or "C" compilers used. The tools also allow finer control, by also allowing the user to add compiler flags they choose, for both FORTRAN and "C", as well as picking the compiler, linker and and add linker options. Finally the tools allow you to turn optimization on (which is off by default but on for **mksurfdata_map**) with the OPT flag so that the tool will run faster. Options used by all: **mksurfdata_map** @@ -108,7 +101,5 @@ The *README.filecopies* (which can be found in ``$CTSMROOT/tools``) is repeated **cprnc** and *gen_domain** both use the CIME configure/build system rather than the CLM specific version described above. -See `CIME documentation on adding grids `_ for -more information on adding grids, creating mapping files, and running **gen_domain**. Also see the CIME file: -``$CTSMROOT/tools/mapping/gen_domain_files/INSTALL`` for how to build **gen_domain**. +See `CIME documentation on adding grids `_ for more information on adding grids, creating mapping files, and running **gen_domain**. Also see the CIME file: ``$CTSMROOT/tools/mapping/gen_domain_files/INSTALL`` for how to build **gen_domain**. diff --git a/doc/source/users_guide/using-clm-tools/cprnc.rst b/doc/source/users_guide/using-clm-tools/cprnc.rst index 050e059296..05a2ca8279 100644 --- a/doc/source/users_guide/using-clm-tools/cprnc.rst +++ b/doc/source/users_guide/using-clm-tools/cprnc.rst @@ -1,18 +1,12 @@ -.. comparing-history-files: - .. include:: ../substitutions.rst +.. _comparing-history-files: + ========================= Comparing History Files ========================= -**cprnc** is a tool shared by |cesmrelease| to compare two NetCDF history files. -It differences every field that is shared on both files, and reports a summary of the difference. -The summary includes the three largest differences, as well as the root mean square (RMS) difference. -It also gives some summary information on the field as well. -You have to enter at least one file, and up to two files. -With one file it gives you summary information on the file, and with two it gives you information on the differences between the two. -At the end it will give you a summary of the fields compared and how many fields were different and how many were identical. +**cprnc** is a tool shared by |cesmrelease| to compare two NetCDF history files. It differences every field that is shared on both files, and reports a summary of the difference. The summary includes the three largest differences, as well as the root mean square (RMS) difference. It also gives some summary information on the field as well. You have to enter at least one file, and up to two files. With one file it gives you summary information on the file, and with two it gives you information on the differences between the two. At the end it will give you a summary of the fields compared and how many fields were different and how many were identical. Options: @@ -26,5 +20,5 @@ Options: -kpr -See the **cprnc** `README `_ file for more details. +See the ``cprnc`` `README `_ file for more details. diff --git a/doc/source/users_guide/using-clm-tools/creating-domain-files.rst b/doc/source/users_guide/using-clm-tools/creating-domain-files.rst index 972967da0c..d4ebd6c4e7 100644 --- a/doc/source/users_guide/using-clm-tools/creating-domain-files.rst +++ b/doc/source/users_guide/using-clm-tools/creating-domain-files.rst @@ -1,13 +1,12 @@ -.. _creating-domain-files: - .. include:: ../substitutions.rst +.. _creating-domain-files: + ***************************** Creating CLM domain files ***************************** -*gen_domain* to create a domain file for datm from a mapping file. **gen_domain** is a tool that is a part of CIME. The domain file is then used by BOTH DATM AND CLM to define the grid and land-mask. The general data flow is shown in two figures. :numref:`Figure mkmapdata.sh` shows the general flow for a general global case (or for a regional grid that DOES include ocean). :numref:`Figure mknoocnmap.pl` shows the use of **mknoocnmap.pl** (see `the Section called Using mknocnmap.pl to create grid and maps for single-point regional grids `_) to create a regional or single-point map file that is then run through **gen_domain** to create the domain file for it. As stated before :numref:`Figure Data_Flow_Legend` is the legend for both of these figures. See `the -$CIMEROOT/tools/mapping/gen_domain_files/README `_ file for more help on **gen_domain**. +*gen_domain* to create a domain file for datm from a mapping file. ``gen_domain`` is a tool that is a part of CIME. The domain file is then used by BOTH DATM AND CLM to define the grid and land-mask. The general data flow is shown in two figures. :numref:`Figure mkmapdata.sh` shows the general flow for a general global case (or for a regional grid that DOES include ocean). :numref:`Figure mknoocnmap.pl` shows the use of ``mknoocnmap.pl`` (see :ref:`using-mkocnmap`) to create a regional or single-point map file that is then run through ``gen_domain`` to create the domain file for it. As stated before :numref:`Figure Data_Flow_Legend` is the legend for both of these figures. See `the $CIMEROOT/tools/mapping/gen_domain_files/README `_ file for more help on ``gen_domain``. Here we create domain files for a regular global domain. @@ -22,7 +21,7 @@ Global Domain file creation Starting from SCRIP grid files for both your atmosphere and ocean, you use **$CIMEROOT/tools/mapping/gen_mapping_files/gen_cesm_maps.sh** to create a mapping file between the atmosphere and ocean. That mapping file is then used as input to **gen_domain** to create output domain files for both atmosphere and ocean. The atmosphere domain file is then used by both CLM and DATM for I compsets, while the ocean domain file is ignored. For this process you have to define your SCRIP grid files on your own. For a regional or single-point case that doesn't include ocean see :numref:`Figure mknoocnmap.pl`. (See :numref:`Figure Global-Domain` for the legend for this figure.) -Note, that the SCRIP grid file used to start this process, is also used in **mkmapdata.sh** (see `the Section called Creating mapping files that mksurfdata_map will use `_). Next we create domain files for a single-point or regional domain. +Note that the SCRIP grid file used to start this process is also used in ``mkmapdata.sh`` (see :ref:`using-mkocnmap`). Next we create domain files for a single-point or regional domain. Domain file creation using mknoocnmap.pl ======================================== diff --git a/doc/source/users_guide/using-clm-tools/creating-input-for-surface-dataset-generation.rst b/doc/source/users_guide/using-clm-tools/creating-input-for-surface-dataset-generation.rst index 6048baa98c..276394e2b9 100644 --- a/doc/source/users_guide/using-clm-tools/creating-input-for-surface-dataset-generation.rst +++ b/doc/source/users_guide/using-clm-tools/creating-input-for-surface-dataset-generation.rst @@ -1,19 +1,20 @@ -.. _creating-maps-for-mksurfdata: - .. include:: ../substitutions.rst +.. _creating-maps-for-mksurfdata: + ********************************************* Creating input for surface dataset generation ********************************************* -1. Generating SCRIP grid files +Generating SCRIP grid files ================================== -The utility ``mkmapdata.sh`` requires SCRIP format input files to describe the input and output grids that maps are generated for. CLM provides a utility, ``mkmapgrids`` that generates those files. -The program converts old formats of CAM or CLM grid files to SCRIP grid format. There is also a NCL script (``mkscripgrid.ncl``) to create regular latitude longitude regional or single-point grids at the resolution the user desires. +The utility ``mkmapdata.sh`` requires SCRIP format input files to describe the input and output grids that maps are generated for. CLM provides a utility, ``mkmapgrids`` that generates those files. The program converts old formats of CAM or CLM grid files to SCRIP grid format. There is also a NCL script (``mkscripgrid.ncl``) to create regular latitude longitude regional or single-point grids at the resolution the user desires. SCRIP grid files for all the standard model resolutions and the raw surface datasets have already been done and the files are in the XML database. Hence, this step doesn't need to be done -- EXCEPT WHEN YOU ARE CREATING YOUR OWN GRIDS. +.. _using-mkocnmap: + Using mknocnmap.pl to create grid and maps for single-point regional grids -------------------------------------------------------------------------- @@ -27,9 +28,9 @@ If you want to create a regular latitude/longitude single-point or regional grid -name [-or -n] Name to use to describe point OPTIONS - -dx Size of total grid in degrees in longitude direction + -dx Size of total grid in degrees in longitude direction (default is 0.1) - -dy Size of total grid in degrees in latitude direction + -dy Size of total grid in degrees in latitude direction (default is 0.1) -silent [or -s] Make output silent -help [or -h] Print usage to STDOUT. @@ -39,34 +40,27 @@ If you want to create a regular latitude/longitude single-point or regional grid See :numref:`Figure mknoocnmap.pl` for a visual representation of this process. - -2. Creating mapping files for mksurfdata_map +Creating mapping files for mksurfdata_map ============================================== -``mkmapdata.sh`` uses the above SCRIP grid input files to create SCRIP mapping data files (uses ESMF). +``mkmapdata.sh`` uses the above SCRIP grid input files to create SCRIP mapping data files (uses ESMF). -The bash shell script ``$CTSMROOT/tools/mkmapgrids/mkmapdata.sh`` uses **ESMF_RegridWeightGen** to create a list of maps from the raw datasets that are input to **mksurfdata_map**. -Each dataset that has a different grid, or land-mask needs a different mapping file for it, but many different raw datasets share the same grid/land-mask as other files. -Hence, there doesn't need to be a different mapping file for EACH raw dataset -- just for each raw dataset that has a DIFFERENT grid or land-mask.. -See :numref:`Figure mkmapdata.sh` for a visual representation of how this works. -The bash script figures out which mapping files it needs to create and then runs **ESMF_RegridWeightGen** for each one. -You can then either enter the datasets into the XML database (see `Chapter 3 `_ or leave the files in place, and use the "-res usrspec -usr_gname -usr_gdate" options to **mksurfdata_map** (see `the Section called Running mksurfdata.pl `_ below). -mkmapdata.sh has a help option with the following +The bash shell script ``$CTSMROOT/tools/mkmapgrids/mkmapdata.sh`` uses ``ESMF_RegridWeightGen`` to create a list of maps from the raw datasets that are input to ``mksurfdata_map``. Each dataset that has a different grid, or land-mask needs a different mapping file for it, but many different raw datasets share the same grid/land-mask as other files. Hence, there doesn't need to be a different mapping file for EACH raw dataset---just for each raw dataset that has a DIFFERENT grid or land-mask. See :numref:`Figure mkmapdata.sh` for a visual representation of how this works. The bash script figures out which mapping files it needs to create and then runs ``ESMF_RegridWeightGen`` for each one. You can then either enter the datasets into the XML database (see Chapter :numref:`adding-new-resolutions-section`), or leave the files in place and use the ``-res usrspec -usr_gname -usr_gdate`` options to ``mksurfdata_map``. ``mkmapdata.sh`` has a help option with the following :: ../../tools/mkmapdata/mkmapdata.sh ********************** - usage on cheyenne: + usage on cheyenne:Figure mkmapdata.sh ./mkmapdata.sh - valid arguments: - [-f|--gridfile ] - Full pathname of model SCRIP grid file to use + valid arguments: + [-f|--gridfile ] + Full pathname of model SCRIP grid file to use This variable should be set if this is not a supported grid This variable will override the automatic generation of the - filename generated from the -res argument - the filename is generated ASSUMING that this is a supported + filename generated from the -res argument + the filename is generated ASSUMING that this is a supported grid that has entries in the file namelist_defaults_clm.xml the -r|--res argument MUST be specied if this argument is specified [-r|--res ] @@ -75,7 +69,7 @@ mkmapdata.sh has a help option with the following Model output grid type supported values are [regional,global], (default is global) [-b|--batch] - Toggles batch mode usage. + Toggles batch mode usage. If you want to run in batch mode you need to have a separate batch script for a supported machine that calls this script interactively - you cannot submit this @@ -85,13 +79,13 @@ mkmapdata.sh has a help option with the following also writes data to clm.input_data_list [-d|--debug] Toggles debug-only (don't actually run mkmapdata just echo what would happen) - [-h|--help] + [-h|--help] Displays this help message [-v|--verbose] - Toggle verbose usage -- log more information on what is happening + Toggle verbose usage -- log more information on what is happening You can also set the following env variables: - ESMFBIN_PATH - Path to ESMF binaries + ESMFBIN_PATH - Path to ESMF binaries (default is /contrib/esmf-5.3.0-64-O/bin) CSMDATA ------ Path to CESM input data (default is /glade/p/cesm/cseg/inputdata) @@ -100,15 +94,14 @@ mkmapdata.sh has a help option with the following REGRID_PROC -- Number of MPI processors to use (default is 8) - **pass environment variables by preceding above commands + **pass environment variables by preceding above commands with 'env var1=setting var2=setting ' ********************** - .. _Figure mkmapdata.sh: .. figure:: mkmapdata_details.jpeg Details of running mkmapdata.sh -Each of the raw datasets for **mksurfdata_map** needs a mapping file to map from the output grid you are running on to the grid and land-mask for that dataset. This is what **mkmapdata.sh** does. To create the mapping files you need a SCRIP grid file to correspond with each resolution and land mask that you have a raw data file in **mksurfdata_map**. Some raw datasets share the same grid and land mask -- hence they can share the same SCRIP grid file. The output maps created here go into **mksurfdata_map** see :numref:`Figure mksurfdatamap`. +Each of the raw datasets for ``mksurfdata_map`` needs a mapping file to map from the output grid you are running on to the grid and land-mask for that dataset. This is what ``mkmapdata.sh`` does. To create the mapping files you need a SCRIP grid file to correspond with each resolution and land mask that you have a raw data file in ``mksurfdata_map``. Some raw datasets share the same grid and land mask -- hence they can share the same SCRIP grid file. The output maps created here go into ``mksurfdata_map`` see :numref:`Figure Workflow of CLM5 Land Use Data Tool and Mksurfdata_map Tool`. diff --git a/doc/source/users_guide/using-clm-tools/creating-surface-datasets.rst b/doc/source/users_guide/using-clm-tools/creating-surface-datasets.rst index c974b9c886..cfaa8527cd 100644 --- a/doc/source/users_guide/using-clm-tools/creating-surface-datasets.rst +++ b/doc/source/users_guide/using-clm-tools/creating-surface-datasets.rst @@ -1,12 +1,12 @@ -.. _creating-surface-datasets: - .. include:: ../substitutions.rst +.. _creating-surface-datasets: + =========================== Creating Surface Datasets =========================== -When just creating a replacement file for an existing one, the relevant tool should be used directly to create the file. When you are creating a set of files for a new resolution there are some dependencies between the tools that you need to keep in mind when creating them. The main dependency is that you MUST create a SCRIP grid file first as the SCRIP grid dataset is then input into the other tools. Also look at `Table 3-1 `_ which gives information on the files required and when. :numref:`Figure Data_Flow` shows an overview of the general data-flow for creation of the fsurdat datasets. +When just creating a replacement file for an existing one, the relevant tool should be used directly to create the file. When you are creating a set of files for a new resolution there are some dependencies between the tools that you need to keep in mind when creating them. The main dependency is that you MUST create a SCRIP grid file first as the SCRIP grid dataset is then input into the other tools. Also look at Table :numref:`reqd-files-table` which gives information on the files required and when. :numref:`Figure Data_Flow` shows an overview of the general data-flow for creation of the fsurdat datasets. .. _Figure Data_Flow: @@ -14,9 +14,9 @@ When just creating a replacement file for an existing one, the relevant tool sho Data Flow for Creation of Surface Datasets from Raw SCRIP Grid Files -Starting from a SCRIP grid file that describes the grid you will run the model on, you first run **mkmapdata.sh** to create a list of mapping files. See :numref:`Figure mkmapdata.sh` for a more detailed view of how **mkmapdata.sh** works. The mapping files tell **mksurfdata_map** how to map between the output grid and the raw datasets that it uses as input. The output of **mksurfdata_map** is a surface dataset that you then use for running the model. See `Figure :numref:`Figure mksurfdatamap` for a more detailed view of how **mksurfdata_map** works. +Starting from a SCRIP grid file that describes the grid you will run the model on, you first run ```mkmapdata.sh`` to create a list of mapping files. See :numref:`Figure mkmapdata.sh` for a more detailed view of how ``mkmapdata.sh`` works. The mapping files tell ``mksurfdata_map`` how to map between the output grid and the raw datasets that it uses as input. The output of ``mksurfdata_map`` is a surface dataset that you then use for running the model. See :numref:`Figure Workflow of CLM5 Land Use Data Tool and Mksurfdata_map Tool` for a more detailed view of how ``mksurfdata_map`` works. -:numref:`Figure Data_Flow_Legend` is the legend for this figure (:numref:`Figure Data_Flow`) and other figures in this chapter (:numref:`Figure Global_Domain`, :numref:`Figure mknoocnmap.pl` and :numref:`Figure mksurfdatamap`). +:numref:`Figure Data_Flow_Legend` is the legend for this figure (:numref:`Figure Data_Flow`) and other figures in this chapter (:numref:`Figure Global-Domain` and :numref:`Figure mknoocnmap.pl`). .. _Figure Data_Flow_Legend: diff --git a/doc/source/users_guide/using-clm-tools/datasts-for-observational-sites.rst b/doc/source/users_guide/using-clm-tools/datasts-for-observational-sites.rst deleted file mode 100644 index 7fb915a6ed..0000000000 --- a/doc/source/users_guide/using-clm-tools/datasts-for-observational-sites.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. include:: ../substitutions.rst - -================================== - Datasets for Observational Sites -================================== - -There are two ways to customize datasets for a particular observational site. -The first is to customize the input to the tools that create the dataset, and the second is to over-write the default data after you've created a given dataset. -Depending on the tool it might be easier to do it one way or the other. -In `Table 3-1 `_ we list the files that are most likely to be customized and the way they might be customized. -Of those files, the ones you are most likely to customize are: fatmlndfrc, fsurdat, faerdep (for DATM), and stream_fldfilename_ndep. -Note **mksurfdata_map** as documented previously has options to overwrite the vegetation and soil types. -For more information on this also see `the Section called Creating your own single-point/regional surface datasets in Chapter 5 `_. -And PTCLM uses these methods to customize datasets see `Chapter 6 `_. - - -Another aspect of customizing your input datasets is customizing the input atmospheric forcing datasets. -See `the Section called Running with your own atmosphere forcing in Chapter 5 `_ for more information on this. -Also the chapter on PTCLM in `the Section called Converting AmeriFlux Data for use by PTCLM in Chapter 6 `_ has information on using the AmeriFlux tower site data as atmospheric forcing. diff --git a/doc/source/users_guide/using-clm-tools/index.rst b/doc/source/users_guide/using-clm-tools/index.rst index 58435f92aa..e9a1dd5238 100644 --- a/doc/source/users_guide/using-clm-tools/index.rst +++ b/doc/source/users_guide/using-clm-tools/index.rst @@ -3,10 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. _using-clm-tools-section: - .. include:: ../substitutions.rst +.. _using-clm-tools-section: + ##################################### Using CLM tools ##################################### @@ -18,7 +18,6 @@ Using CLM tools building-the-clm-tools.rst creating-input-for-surface-dataset-generation.rst creating-surface-datasets.rst - datasts-for-observational-sites.rst creating-domain-files.rst observational-sites-datasets.rst cprnc.rst diff --git a/doc/source/users_guide/using-clm-tools/observational-sites-datasets.rst b/doc/source/users_guide/using-clm-tools/observational-sites-datasets.rst index b5cc2efad9..385ec159aa 100644 --- a/doc/source/users_guide/using-clm-tools/observational-sites-datasets.rst +++ b/doc/source/users_guide/using-clm-tools/observational-sites-datasets.rst @@ -1,20 +1,11 @@ -.. _observational-sites-datasets: - .. include:: ../substitutions.rst +.. _observational-sites-datasets: + ******************************* Observational Sites Datasets ******************************* -There are two ways to customize datasets for a particular observational site. -The first is to customize the input to the tools that create the dataset, and the second is to over-write the default data after you've created a given dataset. -Depending on the tool it might be easier to do it one way or the other. -In `Table 3-1 `_ we list the files that are most likely to be customized and the way they might be customized. -Of those files, the ones you are most likely to customize are: fatmlndfrc, fsurdat, faerdep (for DATM), and stream_fldfilename_ndep. -Note **mksurfdata_map** as documented previously has options to overwrite the vegetation and soil types. -For more information on this also see `the Section called Creating your own single-point/regional surface datasets in Chapter 5 `_. -And PTCLM uses these methods to customize datasets see `Chapter 6 `_. +There are two ways to customize datasets for a particular observational site. The first is to customize the input to the tools that create the dataset, and the second is to overwrite the default data after you've created a given dataset. Depending on the tool it might be easier to do it one way or the other. In Table :numref:`reqd-files-table` we list the files that are most likely to be customized and the way they might be customized. Of those files, the ones you are most likely to customize are: ``fatmlndfrc``, ``fsurdat``, ``faerdep`` (for DATM), and ``stream_fldfilename_ndep``. Note ``mksurfdata_map`` as documented previously has options to overwrite the vegetation and soil types. For more information on this also see :ref:`creating-your-own-singlepoint-dataset`. ``PTCLM`` uses these methods to customize datasets; see Chapter :numref:`running-PTCLM`. -Another aspect of customizing your input datasets is customizing the input atmospheric forcing datasets. -See `the Section called Running with your own atmosphere forcing in Chapter 5 `_ for more information on this. -Also the chapter on PTCLM in `the Section called Converting AmeriFlux Data for use by PTCLM in Chapter 6 `_ has information on using the AmeriFlux tower site data as atmospheric forcing. +Another aspect of customizing your input datasets is customizing the input atmospheric forcing datasets; see :ref:`creating-your-own-singlepoint-dataset` for more information on this. :ref:`converting-ameriflux-for-ptclmmkdata` has information on using the AmeriFlux tower site data as atmospheric forcing. diff --git a/doc/source/users_guide/using-clm-tools/what-are-the-clm-tools.rst b/doc/source/users_guide/using-clm-tools/what-are-the-clm-tools.rst index a8663176e1..6921e4dafd 100644 --- a/doc/source/users_guide/using-clm-tools/what-are-the-clm-tools.rst +++ b/doc/source/users_guide/using-clm-tools/what-are-the-clm-tools.rst @@ -1,19 +1,14 @@ -.. _what-are-the-clm-tools: - .. include:: ../substitutions.rst +.. _what-are-the-clm-tools: + ======================== What are the CLM tools ======================== -There are several tools provided with CLM that allow you to create your own input datasets at resolutions you choose, or to interpolate initial conditions to a different resolution, or used to compare CLM history files between different cases. -The tools are all available in the ``$CTSMROOT/tools`` directory. -Most of the tools are FORTRAN stand-alone programs in their own directory, but there is also a suite of NCL scripts in the ``$CTSMROOT/tools//ncl_scripts`` directory, and some of the tools are scripts that may also call the ESMF regridding program. -Some of the NCL scripts are very specialized and not meant for general use, and we won't document them here. -They still contain documentation in the script itself and the README file in the tools directory. +There are several tools provided with CLM that allow you to create your own input datasets at resolutions you choose, or to interpolate initial conditions to a different resolution, or used to compare CLM history files between different cases. The tools are all available in the ``$CTSMROOT/tools`` directory. Most of the tools are FORTRAN stand-alone programs in their own directory, but there is also a suite of NCL scripts in the ``$CTSMROOT/tools//ncl_scripts`` directory, and some of the tools are scripts that may also call the ESMF regridding program. Some of the NCL scripts are very specialized and not meant for general use, and we won't document them here. They still contain documentation in the script itself and the README file in the tools directory. -The tools produce files that can be used for CLM4.5 and |version|. They do **NOT** produce files that can be used for CLM4.0. -If you need files for CLM4.0, you'll need to use a previous version of CLM. +The tools produce files that can be used for CLM4.5 and |version|. They do **NOT** produce files that can be used for CLM4.0. If you need files for CLM4.0, you'll need to use a previous version of CLM. The list of generally important scripts and programs are as follows. @@ -29,60 +24,44 @@ The list of generally important scripts and programs are as follows. #. *$CIMEROOT/tools/cprnc* to compare two NetCDF files. -In the sections to come we will go into detailed description of how to use each of these tools in turn. -First, however we will discuss the common environment variables and options that are used by all of the FORTRAN tools. -Second, we go over the outline of the entire file creation process for all input files needed by CLM for a new resolution, then we turn to each tool. -In the last section we will discuss how to customize files for particular observational sites. +In the sections to come we will go into detailed description of how to use each of these tools in turn. First, however we will discuss the common environment variables and options that are used by all of the FORTRAN tools. Second, we go over the outline of the entire file creation process for all input files needed by CLM for a new resolution, then we turn to each tool. In the last section we will discuss how to customize files for particular observational sites. The FORTRAN tools (mksurfdata_map and mkprocdata_map) run, with a namelist (mksurfdata_map) to provide options, or with command line arguments (mkprocdata_map). -In the following sections, we will outline how to make these files available for build-namelist so that you can easily create simulations that include them. -In the chapter on single-point and regional datasets we also give an alternative way to enter new datasets without having to edit files. +In the following sections, we will outline how to make these files available for build-namelist so that you can easily create simulations that include them. In the chapter on single-point and regional datasets we also give an alternative way to enter new datasets without having to edit files. ------------------------------------ Running FORTRAN tools with namelists ------------------------------------ -**mksurfdata_map** runs with a namelist that is read from standard input. -Hence, you create a namelist and then run them by redirecting the namelist file into standard input as follows: +**mksurfdata_map** runs with a namelist that is read from standard input. Hence, you create a namelist and then run them by redirecting the namelist file into standard input as follows: :: ./program < namelist -There is a sample namelist called ``$CTSMROOT/tools/mksurfdata_map/mksurfdata_map.namleist`` that shows you what the -namelist should look like. **mksurfdata_map** also has a script that creates the namelist and runs the program for you. -Namelists that you create should be similar to the example namelist. -The namelist values are also documented along with the other namelists in the: +There is a sample namelist called ``$CTSMROOT/tools/mksurfdata_map/mksurfdata_map.namleist`` that shows you what the namelist should look like. **mksurfdata_map** also has a script that creates the namelist and runs the program for you. Namelists that you create should be similar to the example namelist. The namelist values are also documented along with the other namelists in the: :: - $CTSMROOT/bld/namelist_files/namelist_definition.xml`` file - and default values in the: + $CTSMROOT/bld/namelist_files/namelist_definition.xml`` file + and default values in the: $CTSMROOT/bld/namelist_files/namelist_defaults_clm_tools.xml`` file. ----------------------------------------------- Running FORTRAN tools with command line options ----------------------------------------------- -**gen_domain**, mkprocdata_map, and **cprnc** run with command line arguments. -The detailed sections below will give you more information on the command line arguments specific to each tool. -Also running the tool without any arguments will give you a general synopsis on how to run the tool. +**gen_domain**, mkprocdata_map, and **cprnc** run with command line arguments. The detailed sections below will give you more information on the command line arguments specific to each tool. Also running the tool without any arguments will give you a general synopsis on how to run the tool. ----------------------------------------- Running FORTRAN tools built with SMP=TRUE ----------------------------------------- -When you enable ``SMP=TRUE`` on your build of one of the tools that make use of it, you are using OpenMP for shared memory parallelism (SMP). -In SMP loops are run in parallel with different threads run on different processors all of which access the same memory (called on-node). -Thus you can only usefully run up to the number of processors that are available on a single-node of the machine you are running on. -For example, on the NCAR machine cheyenne there are 36 processors per node, so you can use up to 36 processors. +When you enable ``SMP=TRUE`` on your build of one of the tools that make use of it, you are using OpenMP for shared memory parallelism (SMP). In SMP loops are run in parallel with different threads run on different processors all of which access the same memory (called on-node). Thus you can only usefully run up to the number of processors that are available on a single-node of the machine you are running on. For example, on the NCAR machine cheyenne there are 36 processors per node, so you can use up to 36 processors. +.. _using-ncl: --------- Using NCL --------- -In the tools directory ``$CTSMROOT/tools/ncl_scripts`` and in a few other locations there are scripts that use NCAR Command Language (NCL). -Unlike the FORTRAN tools, you will need to get a copy of NCL in order to use them. -You also won't have to build an executable in order to use them, hence no Makefile is provided. -NCL is provided for free download as either binaries or source code from: `http://www.ncl.ucar.edu/ `_. -The NCL web-site also contains documentation on NCL and it's use. These scripts are stand-alone and at most use environment variables to control how to use them. In some cases there are perl scripts with command line arguments that call the NCL scripts to control what they do. +In the tools directory ``$CTSMROOT/tools/ncl_scripts`` and in a few other locations there are scripts that use NCAR Command Language (NCL). Unlike the FORTRAN tools, you will need to get a copy of NCL in order to use them. You also won't have to build an executable in order to use them, hence no Makefile is provided. NCL is provided for free download as either binaries or source code from: `http://www.ncl.ucar.edu/ `_. The NCL web-site also contains documentation on NCL and it's use. These scripts are stand-alone and at most use environment variables to control how to use them. In some cases there are perl scripts with command line arguments that call the NCL scripts to control what they do. diff --git a/manage_externals/.github/workflows/bumpversion.yml b/manage_externals/.github/workflows/bumpversion.yml new file mode 100644 index 0000000000..f4dc9b7ca5 --- /dev/null +++ b/manage_externals/.github/workflows/bumpversion.yml @@ -0,0 +1,19 @@ +name: Bump version +on: + push: + branches: + - main +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Bump version and push tag + id: tag_version + uses: mathieudutour/github-tag-action@v5.5 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + create_annotated_tag: true + default_bump: patch + dry_run: false + tag_prefix: manic- diff --git a/manage_externals/.github/workflows/tests.yml b/manage_externals/.github/workflows/tests.yml new file mode 100644 index 0000000000..dd75b91b49 --- /dev/null +++ b/manage_externals/.github/workflows/tests.yml @@ -0,0 +1,30 @@ +# This is a workflow to compile the cmeps source without cime +name: Test Manic + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + test-manic: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Test Manic + run: | + pushd test + git config --global user.email "devnull@example.com" + git config --global user.name "GITHUB tester" + git config --global protocol.file.allow always + make utest + make stest + popd + + - name: Setup tmate session + if: ${{ failure() }} + uses: mxschmitt/action-tmate@v3 diff --git a/manage_externals/README.md b/manage_externals/README.md index c931c8e213..9475301b5d 100644 --- a/manage_externals/README.md +++ b/manage_externals/README.md @@ -201,6 +201,11 @@ The root of the source tree will be referred to as `${SRC_ROOT}` below. externals description file pointed 'useful_library/sub-xternals.cfg', Then the main 'externals' field in the top level repo should point to 'sub-externals.cfg'. + Note that by default, `checkout_externals` will clone an external's + submodules. As a special case, the entry, `externals = None`, will + prevent this behavior. For more control over which externals are + checked out, create an externals file (and see the `from_submodule` + configuration entry below). * from_submodule (True / False) : used to pull the repo_url, local_path, and hash properties for this external from the .gitmodules file in diff --git a/manage_externals/manic/checkout.py b/manage_externals/manic/checkout.py index 2223b1f0d8..3f5537adce 100755 --- a/manage_externals/manic/checkout.py +++ b/manage_externals/manic/checkout.py @@ -227,6 +227,12 @@ def commandline_arguments(args=None): Now, %(prog)s will process Externals.cfg and also process Externals_LIBX.cfg as if it was a sub-external. + Note that by default, checkout_externals will clone an external's + submodules. As a special case, the entry, "externals = None", will + prevent this behavior. For more control over which externals are + checked out, create an externals file (and see the from_submodule + configuration entry below). + * from_submodule (True / False) : used to pull the repo_url, local_path, and hash properties for this external from the .gitmodules file in this repository. Note that the section name (the entry in square @@ -332,7 +338,34 @@ def commandline_arguments(args=None): options = parser.parse_args() return options - +def _dirty_local_repo_msg(program_name, config_file): + return """The external repositories labeled with 'M' above are not in a clean state. +The following are four options for how to proceed: +(1) Go into each external that is not in a clean state and issue either a 'git status' or + an 'svn status' command (depending on whether the external is managed by git or + svn). Either revert or commit your changes so that all externals are in a clean + state. (To revert changes in git, follow the instructions given when you run 'git + status'.) (Note, though, that it is okay to have untracked files in your working + directory.) Then rerun {program_name}. +(2) Alternatively, you do not have to rely on {program_name}. Instead, you can manually + update out-of-sync externals (labeled with 's' above) as described in the + configuration file {config_file}. (For example, run 'git fetch' and 'git checkout' + commands to checkout the appropriate tags for each external, as given in + {config_file}.) +(3) You can also use {program_name} to manage most, but not all externals: You can specify + one or more externals to ignore using the '-x' or '--exclude' argument to + {program_name}. Excluding externals labeled with 'M' will allow {program_name} to + update the other, non-excluded externals. +(4) As a last resort, if you are confident that there is no work that needs to be saved + from a given external, you can remove that external (via "rm -rf [directory]") and + then rerun the {program_name} tool. This option is mainly useful as a workaround for + issues with this tool (such as https://github.com/ESMCI/manage_externals/issues/157). +The external repositories labeled with '?' above are not under version +control using the expected protocol. If you are sure you want to switch +protocols, and you don't have any work you need to save from this +directory, then run "rm -rf [directory]" before rerunning the +{program_name} tool. +""".format(program_name=program_name, config_file=config_file) # --------------------------------------------------------------------- # # main @@ -345,9 +378,9 @@ def main(args): the --all option is passed. Returns a tuple (overall_status, tree_status). overall_status is 0 - on success, non-zero on failure. tree_status gives the full status - *before* executing the checkout command - i.e., the status that it - used to determine if it's safe to proceed with the checkout. + on success, non-zero on failure. tree_status is a dict mapping local path + to ExternalStatus -- if no checkout is happening. If checkout is happening, tree_status + is None. """ if args.do_logging: logging.basicConfig(filename=LOG_FILE_NAME, @@ -363,19 +396,25 @@ def main(args): load_all = True root_dir = os.path.abspath(os.getcwd()) - external_data = read_externals_description_file(root_dir, args.externals) - external = create_externals_description( - external_data, components=args.components, exclude=args.exclude) + model_data = read_externals_description_file(root_dir, args.externals) + ext_description = create_externals_description( + model_data, components=args.components, exclude=args.exclude) for comp in args.components: - if comp not in external.keys(): + if comp not in ext_description.keys(): + # Note we can't print out the list of found externals because + # they were filtered in create_externals_description above. fatal_error( "No component {} found in {}".format( comp, args.externals)) - source_tree = SourceTree(root_dir, external, svn_ignore_ancestry=args.svn_ignore_ancestry) - printlog('Checking status of externals: ', end='') - tree_status = source_tree.status() + source_tree = SourceTree(root_dir, ext_description, svn_ignore_ancestry=args.svn_ignore_ancestry) + if args.components: + components_str = 'specified components' + else: + components_str = 'required & optional components' + printlog('Checking local status of ' + components_str + ': ', end='') + tree_status = source_tree.status(print_progress=True) printlog('') if args.status: @@ -390,43 +429,8 @@ def main(args): for comp in sorted(tree_status): tree_status[comp].log_status_message(args.verbose) # exit gracefully - msg = """The external repositories labeled with 'M' above are not in a clean state. - -The following are four options for how to proceed: - -(1) Go into each external that is not in a clean state and issue either a 'git status' or - an 'svn status' command (depending on whether the external is managed by git or - svn). Either revert or commit your changes so that all externals are in a clean - state. (To revert changes in git, follow the instructions given when you run 'git - status'.) (Note, though, that it is okay to have untracked files in your working - directory.) Then rerun {program_name}. - -(2) Alternatively, you do not have to rely on {program_name}. Instead, you can manually - update out-of-sync externals (labeled with 's' above) as described in the - configuration file {config_file}. (For example, run 'git fetch' and 'git checkout' - commands to checkout the appropriate tags for each external, as given in - {config_file}.) - -(3) You can also use {program_name} to manage most, but not all externals: You can specify - one or more externals to ignore using the '-x' or '--exclude' argument to - {program_name}. Excluding externals labeled with 'M' will allow {program_name} to - update the other, non-excluded externals. - -(4) As a last resort, if you are confident that there is no work that needs to be saved - from a given external, you can remove that external (via "rm -rf [directory]") and - then rerun the {program_name} tool. This option is mainly useful as a workaround for - issues with this tool (such as https://github.com/ESMCI/manage_externals/issues/157). - - -The external repositories labeled with '?' above are not under version -control using the expected protocol. If you are sure you want to switch -protocols, and you don't have any work you need to save from this -directory, then run "rm -rf [directory]" before rerunning the -{program_name} tool. -""".format(program_name=program_name, config_file=args.externals) - printlog('-' * 70) - printlog(msg) + printlog(_dirty_local_repo_msg(program_name, args.externals)) printlog('-' * 70) else: if not args.components: @@ -434,6 +438,8 @@ def main(args): for comp in args.components: source_tree.checkout(args.verbose, load_all, load_comp=comp) printlog('') + # New tree status is unknown, don't return anything. + tree_status = None logging.info('%s completed without exceptions.', program_name) # NOTE(bja, 2017-11) tree status is used by the systems tests diff --git a/manage_externals/manic/externals_description.py b/manage_externals/manic/externals_description.py index 6a54935935..546e7fdcb4 100644 --- a/manage_externals/manic/externals_description.py +++ b/manage_externals/manic/externals_description.py @@ -71,7 +71,8 @@ def read_externals_description_file(root_dir, file_name): root_dir = os.path.abspath(root_dir) msg = 'In directory : {0}'.format(root_dir) logging.info(msg) - printlog('Processing externals description file : {0}'.format(file_name)) + printlog('Processing externals description file : {0} ({1})'.format(file_name, + root_dir)) file_path = os.path.join(root_dir, file_name) if not os.path.exists(file_name): @@ -87,7 +88,7 @@ def read_externals_description_file(root_dir, file_name): externals_description = None if file_name == ExternalsDescription.GIT_SUBMODULES_FILENAME: - externals_description = read_gitmodules_file(root_dir, file_name) + externals_description = _read_gitmodules_file(root_dir, file_name) else: try: config = config_parser() @@ -150,9 +151,8 @@ def git_submodule_status(repo_dir): """Run the git submodule status command to obtain submodule hashes. """ # This function is here instead of GitRepository to avoid a dependency loop - cwd = os.getcwd() - os.chdir(repo_dir) - cmd = ['git', 'submodule', 'status'] + cmd = 'git -C {repo_dir} submodule status'.format( + repo_dir=repo_dir).split() git_output = execute_subprocess(cmd, output_to_caller=True) submodules = {} submods = git_output.split('\n') @@ -167,7 +167,6 @@ def git_submodule_status(repo_dir): submodules[items[1]] = {'hash':items[0], 'status':status, 'tag':tag} - os.chdir(cwd) return submodules def parse_submodules_desc_section(section_items, file_path): @@ -190,7 +189,7 @@ def parse_submodules_desc_section(section_items, file_path): return path, url -def read_gitmodules_file(root_dir, file_name): +def _read_gitmodules_file(root_dir, file_name): # pylint: disable=deprecated-method # Disabling this check because the method is only used for python2 # pylint: disable=too-many-locals @@ -202,12 +201,11 @@ def read_gitmodules_file(root_dir, file_name): root_dir = os.path.abspath(root_dir) msg = 'In directory : {0}'.format(root_dir) logging.info(msg) - printlog('Processing submodules description file : {0}'.format(file_name)) file_path = os.path.join(root_dir, file_name) if not os.path.exists(file_name): msg = ('ERROR: submodules description file, "{0}", does not ' - 'exist at path:\n {1}'.format(file_name, file_path)) + 'exist in dir:\n {1}'.format(file_name, root_dir)) fatal_error(msg) submodules_description = None @@ -281,6 +279,10 @@ def read_gitmodules_file(root_dir, file_name): def create_externals_description( model_data, model_format='cfg', components=None, exclude=None, parent_repo=None): """Create the a externals description object from the provided data + + components: list of component names to include, None to include all. If a + name isn't found, it is silently omitted from the return value. + exclude: list of component names to skip. """ externals_description = None if model_format == 'dict': @@ -357,8 +359,9 @@ class ExternalsDescription(dict): input value. """ - # keywords defining the interface into the externals description data - EXTERNALS = 'externals' + # keywords defining the interface into the externals description data; these + # are brought together by the schema below. + EXTERNALS = 'externals' # path to externals file. BRANCH = 'branch' SUBMODULE = 'from_submodule' HASH = 'hash' @@ -384,6 +387,8 @@ class ExternalsDescription(dict): _V1_BRANCH = 'BRANCH' _V1_REQ_SOURCE = 'REQ_SOURCE' + # Dictionary keys are component names. The corresponding values are laid out + # according to this schema. _source_schema = {REQUIRED: True, PATH: 'string', EXTERNALS: 'string', @@ -632,8 +637,11 @@ def _repo_config_from_submodule(self, field, submod_desc): ' Parent repo, "{1}" does not have submodules') fatal_error(msg.format(field, self._parent_repo.name())) - submod_file = read_gitmodules_file(repo_path, submod_file) - submod_desc = create_externals_description(submod_file) + printlog( + 'Processing submodules description file : {0} ({1})'.format( + submod_file, repo_path)) + submod_model_data= _read_gitmodules_file(repo_path, submod_file) + submod_desc = create_externals_description(submod_model_data) # Can we find our external? repo_url = None @@ -760,6 +768,8 @@ def __init__(self, model_data, components=None, exclude=None, parent_repo=None): """Convert the config data into a standardized dict that can be used to construct the source objects + components: list of component names to include, None to include all. + exclude: list of component names to skip. """ ExternalsDescription.__init__(self, parent_repo=parent_repo) self._schema_major = 1 @@ -783,6 +793,9 @@ def _remove_metadata(model_data): def _parse_cfg(self, cfg_data, components=None, exclude=None): """Parse a config_parser object into a externals description. + + components: list of component names to include, None to include all. + exclude: list of component names to skip. """ def list_to_dict(input_list, convert_to_lower_case=True): """Convert a list of key-value pairs into a dictionary. diff --git a/manage_externals/manic/externals_status.py b/manage_externals/manic/externals_status.py index d3d238f289..6bc29e9732 100644 --- a/manage_externals/manic/externals_status.py +++ b/manage_externals/manic/externals_status.py @@ -29,16 +29,16 @@ class ExternalStatus(object): transactions (e.g. add, remove, rename, untracked files). """ - DEFAULT = '-' + # sync_state and clean_state can be one of the following: + DEFAULT = '-' # not set yet (sync_state). clean_state can be this if sync_state is EMPTY. UNKNOWN = '?' EMPTY = 'e' - MODEL_MODIFIED = 's' # a.k.a. out-of-sync - DIRTY = 'M' - - STATUS_OK = ' ' + MODEL_MODIFIED = 's' # repo version != externals (sync_state only) + DIRTY = 'M' # repo is dirty (clean_state only) + STATUS_OK = ' ' # repo is clean (clean_state) or matches externals version (sync_state) STATUS_ERROR = '!' - # source types + # source_type can be one of the following: OPTIONAL = 'o' STANDALONE = 's' MANAGED = ' ' @@ -55,19 +55,21 @@ def __init__(self): def log_status_message(self, verbosity): """Write status message to the screen and log file """ - self._default_status_message() + printlog(self._default_status_message()) if verbosity >= VERBOSITY_VERBOSE: - self._verbose_status_message() + printlog(self._verbose_status_message()) if verbosity >= VERBOSITY_DUMP: - self._dump_status_message() + printlog(self._dump_status_message()) + + def __repr__(self): + return self._default_status_message() def _default_status_message(self): """Return the default terse status message string """ - msg = '{sync}{clean}{src_type} {path}'.format( + return '{sync}{clean}{src_type} {path}'.format( sync=self.sync_state, clean=self.clean_state, src_type=self.source_type, path=self.path) - printlog(msg) def _verbose_status_message(self): """Return the verbose status message string @@ -82,14 +84,12 @@ def _verbose_status_message(self): if self.sync_state != self.STATUS_OK: sync_str = '{current} --> {expected}'.format( current=self.current_version, expected=self.expected_version) - msg = ' {clean}, {sync}'.format(clean=clean_str, sync=sync_str) - printlog(msg) + return ' {clean}, {sync}'.format(clean=clean_str, sync=sync_str) def _dump_status_message(self): """Return the dump status message string """ - msg = indent_string(self.status_output, 12) - printlog(msg) + return indent_string(self.status_output, 12) def safe_to_update(self): """Report if it is safe to update a repository. Safe is defined as: diff --git a/manage_externals/manic/repository_factory.py b/manage_externals/manic/repository_factory.py index 80a92a9d8a..18c73ffc4b 100644 --- a/manage_externals/manic/repository_factory.py +++ b/manage_externals/manic/repository_factory.py @@ -15,6 +15,7 @@ def create_repository(component_name, repo_info, svn_ignore_ancestry=False): """Determine what type of repository we have, i.e. git or svn, and create the appropriate object. + Can return None (e.g. if protocol is 'externals_only'). """ protocol = repo_info[ExternalsDescription.PROTOCOL].lower() if protocol == 'git': diff --git a/manage_externals/manic/repository_git.py b/manage_externals/manic/repository_git.py index 3a6a0f1716..adc666cc57 100644 --- a/manage_externals/manic/repository_git.py +++ b/manage_externals/manic/repository_git.py @@ -25,7 +25,7 @@ class GitRepository(Repository): * be isolated in separate functions with no application logic * of the form: - - cmd = ['git', ...] + - cmd = 'git -C {dirname} ...'.format(dirname=dirname).split() - value = execute_subprocess(cmd, output_to_caller={T|F}, status_to_caller={T|F}) - return value @@ -39,7 +39,7 @@ class GitRepository(Repository): def __init__(self, component_name, repo): """ - Parse repo (a XML element). + repo: ExternalsDescription. """ Repository.__init__(self, component_name, repo) self._gitmodules = None @@ -99,15 +99,13 @@ def submodules_file(self, repo_path=None): # # ---------------------------------------------------------------- def _clone_repo(self, base_dir_path, repo_dir_name, verbosity): - """Prepare to execute the clone by managing directory location + """Clones repo_dir_name into base_dir_path. """ - cwd = os.getcwd() - os.chdir(base_dir_path) - self._git_clone(self._url, repo_dir_name, verbosity) - os.chdir(cwd) + self._git_clone(self._url, os.path.join(base_dir_path, repo_dir_name), + verbosity=verbosity) - def _current_ref(self): - """Determine the *name* associated with HEAD. + def _current_ref(self, dirname): + """Determine the *name* associated with HEAD at dirname. If we're on a tag, then returns the tag name; otherwise, returns the current hash. Returns an empty string if no reference can be @@ -119,21 +117,21 @@ def _current_ref(self): ref_found = False # If we're exactly at a tag, use that as the current ref - tag_found, tag_name = self._git_current_tag() + tag_found, tag_name = self._git_current_tag(dirname) if tag_found: current_ref = tag_name ref_found = True if not ref_found: # Otherwise, use current hash as the current ref - hash_found, hash_name = self._git_current_hash() + hash_found, hash_name = self._git_current_hash(dirname) if hash_found: current_ref = hash_name ref_found = True if ref_found: # If we're on a branch, include branch name in current ref - branch_found, branch_name = self._git_current_branch() + branch_found, branch_name = self._git_current_branch(dirname) if branch_found: current_ref = "{} (branch {})".format(current_ref, branch_name) else: @@ -184,17 +182,15 @@ def compare_refs(current_ref, expected_ref): status = ExternalStatus.MODEL_MODIFIED return status - cwd = os.getcwd() - os.chdir(repo_dir_path) - # get the full hash of the current commit - _, current_ref = self._git_current_hash() + _, current_ref = self._git_current_hash(repo_dir_path) if self._branch: if self._url == LOCAL_PATH_INDICATOR: expected_ref = self._branch else: - remote_name = self._determine_remote_name() + remote_name = self._remote_name_for_url(self._url, + repo_dir_path) if not remote_name: # git doesn't know about this remote. by definition # this is a modified state. @@ -211,7 +207,7 @@ def compare_refs(current_ref, expected_ref): fatal_error(msg) # record the *names* of the current and expected branches - stat.current_version = self._current_ref() + stat.current_version = self._current_ref(repo_dir_path) stat.expected_version = copy.deepcopy(expected_ref) if current_ref == EMPTY_STR: @@ -219,7 +215,7 @@ def compare_refs(current_ref, expected_ref): else: # get the underlying hash of the expected ref revparse_status, expected_ref_hash = self._git_revparse_commit( - expected_ref) + expected_ref, repo_dir_path) if revparse_status: # We failed to get the hash associated with # expected_ref. Maybe we should assign this to some special @@ -230,18 +226,13 @@ def compare_refs(current_ref, expected_ref): # compare the underlying hashes stat.sync_state = compare_refs(current_ref, expected_ref_hash) - os.chdir(cwd) - - def _determine_remote_name(self): - """Return the remote name. - - Note that this is for the *future* repo url and branch, not - the current working copy! + @classmethod + def _remote_name_for_url(cls, remote_url, dirname): + """Return the remote name matching remote_url (or None) """ - git_output = self._git_remote_verbose() + git_output = cls._git_remote_verbose(dirname) git_output = git_output.splitlines() - remote_name = '' for line in git_output: data = line.strip() if not data: @@ -249,10 +240,9 @@ def _determine_remote_name(self): data = data.split() name = data[0].strip() url = data[1].strip() - if self._url == url: - remote_name = name - break - return remote_name + if remote_url == url: + return name + return None def _create_remote_name(self): """The url specified in the externals description file was not known @@ -308,19 +298,16 @@ def _checkout_ref(self, repo_dir, verbosity, submodules): the repo's submodules """ # import pdb; pdb.set_trace() - cwd = os.getcwd() - os.chdir(repo_dir) if self._url.strip() == LOCAL_PATH_INDICATOR: - self._checkout_local_ref(verbosity, submodules) + self._checkout_local_ref(verbosity, submodules, repo_dir) else: - self._checkout_external_ref(verbosity, submodules) + self._checkout_external_ref(verbosity, submodules, repo_dir) if self._sparse: self._sparse_checkout(repo_dir, verbosity) - os.chdir(cwd) - def _checkout_local_ref(self, verbosity, submodules): + def _checkout_local_ref(self, verbosity, submodules, dirname): """Checkout the reference considering the local repo only. Do not fetch any additional remotes or specify the remote when checkout out the ref. @@ -334,13 +321,18 @@ def _checkout_local_ref(self, verbosity, submodules): else: ref = self._hash - self._check_for_valid_ref(ref) - self._git_checkout_ref(ref, verbosity, submodules) + self._check_for_valid_ref(ref, remote_name=None, + dirname=dirname) + self._git_checkout_ref(ref, verbosity, submodules, dirname) - def _checkout_external_ref(self, verbosity, submodules): - """Checkout the reference from a remote repository + def _checkout_external_ref(self, verbosity, submodules, dirname): + """Checkout the reference from a remote repository into dirname. if is True, recursively initialize and update - the repo's submodules + the repo's submodules. + Note that this results in a 'detached HEAD' state if checking out + a branch, because we check out the remote branch rather than the + local. See https://github.com/ESMCI/manage_externals/issues/34 for + more discussion. """ if self._tag: ref = self._tag @@ -349,44 +341,45 @@ def _checkout_external_ref(self, verbosity, submodules): else: ref = self._hash - remote_name = self._determine_remote_name() + remote_name = self._remote_name_for_url(self._url, dirname) if not remote_name: remote_name = self._create_remote_name() - self._git_remote_add(remote_name, self._url) - self._git_fetch(remote_name) + self._git_remote_add(remote_name, self._url, dirname) + self._git_fetch(remote_name, dirname) # NOTE(bja, 2018-03) we need to send separate ref and remote # name to check_for_vaild_ref, but the combined name to # checkout_ref! - self._check_for_valid_ref(ref, remote_name) + self._check_for_valid_ref(ref, remote_name, dirname) if self._branch: + # Prepend remote name to branch. This means we avoid various + # special cases if the local branch is not tracking the remote or + # cannot be trivially fast-forwarded to match; but, it also + # means we end up in a 'detached HEAD' state. ref = '{0}/{1}'.format(remote_name, ref) - self._git_checkout_ref(ref, verbosity, submodules) + self._git_checkout_ref(ref, verbosity, submodules, dirname) def _sparse_checkout(self, repo_dir, verbosity): """Use git read-tree to thin the working tree.""" - cwd = os.getcwd() - - cmd = ['cp', self._sparse, os.path.join(repo_dir, - '.git/info/sparse-checkout')] + cmd = ['cp', os.path.join(repo_dir, self._sparse), + os.path.join(repo_dir, + '.git/info/sparse-checkout')] if verbosity >= VERBOSITY_VERBOSE: printlog(' {0}'.format(' '.join(cmd))) execute_subprocess(cmd) - os.chdir(repo_dir) - self._git_sparse_checkout(verbosity) + self._git_sparse_checkout(verbosity, repo_dir) - os.chdir(cwd) - - def _check_for_valid_ref(self, ref, remote_name=None): + def _check_for_valid_ref(self, ref, remote_name, dirname): """Try some basic sanity checks on the user supplied reference so we can provide a more useful error message than calledprocess error... + remote_name can be NOne """ - is_tag = self._ref_is_tag(ref) - is_branch = self._ref_is_branch(ref, remote_name) - is_hash = self._ref_is_hash(ref) + is_tag = self._ref_is_tag(ref, dirname) + is_branch = self._ref_is_branch(ref, remote_name, dirname) + is_hash = self._ref_is_hash(ref, dirname) is_valid = is_tag or is_branch or is_hash if not is_valid: @@ -397,7 +390,8 @@ def _check_for_valid_ref(self, ref, remote_name=None): fatal_error(msg) if is_tag: - is_unique_tag, msg = self._is_unique_tag(ref, remote_name) + is_unique_tag, msg = self._is_unique_tag(ref, remote_name, + dirname) if not is_unique_tag: msg = ('In repo "{0}": tag "{1}" {2}'.format( self._name, self._tag, msg)) @@ -405,7 +399,7 @@ def _check_for_valid_ref(self, ref, remote_name=None): return is_valid - def _is_unique_tag(self, ref, remote_name): + def _is_unique_tag(self, ref, remote_name, dirname): """Verify that a reference is a valid tag and is unique (not a branch) Tags may be tag names, or SHA id's. It is also possible that a @@ -416,9 +410,9 @@ def _is_unique_tag(self, ref, remote_name): error! """ - is_tag = self._ref_is_tag(ref) - is_branch = self._ref_is_branch(ref, remote_name) - is_hash = self._ref_is_hash(ref) + is_tag = self._ref_is_tag(ref, dirname) + is_branch = self._ref_is_branch(ref, remote_name, dirname) + is_hash = self._ref_is_hash(ref, dirname) msg = '' is_unique_tag = False @@ -449,7 +443,7 @@ def _is_unique_tag(self, ref, remote_name): return is_unique_tag, msg - def _ref_is_tag(self, ref): + def _ref_is_tag(self, ref, dirname): """Verify that a reference is a valid tag according to git. Note: values returned by git_showref_* and git_revparse are @@ -457,28 +451,30 @@ def _ref_is_tag(self, ref): error! """ is_tag = False - value = self._git_showref_tag(ref) + value = self._git_showref_tag(ref, dirname) if value == 0: is_tag = True return is_tag - def _ref_is_branch(self, ref, remote_name=None): + def _ref_is_branch(self, ref, remote_name, dirname): """Verify if a ref is any kind of branch (local, tracked remote, untracked remote). + remote_name can be None. """ local_branch = False remote_branch = False if remote_name: - remote_branch = self._ref_is_remote_branch(ref, remote_name) - local_branch = self._ref_is_local_branch(ref) + remote_branch = self._ref_is_remote_branch(ref, remote_name, + dirname) + local_branch = self._ref_is_local_branch(ref, dirname) is_branch = False if local_branch or remote_branch: is_branch = True return is_branch - def _ref_is_local_branch(self, ref): + def _ref_is_local_branch(self, ref, dirname): """Verify that a reference is a valid branch according to git. show-ref branch returns local branches that have been @@ -491,12 +487,12 @@ def _ref_is_local_branch(self, ref): """ is_branch = False - value = self._git_showref_branch(ref) + value = self._git_showref_branch(ref, dirname) if value == 0: is_branch = True return is_branch - def _ref_is_remote_branch(self, ref, remote_name): + def _ref_is_remote_branch(self, ref, remote_name, dirname): """Verify that a reference is a valid branch according to git. show-ref branch returns local branches that have been @@ -509,12 +505,12 @@ def _ref_is_remote_branch(self, ref, remote_name): """ is_branch = False - value = self._git_lsremote_branch(ref, remote_name) + value = self._git_lsremote_branch(ref, remote_name, dirname) if value == 0: is_branch = True return is_branch - def _ref_is_commit(self, ref): + def _ref_is_commit(self, ref, dirname): """Verify that a reference is a valid commit according to git. This could be a tag, branch, sha1 id, HEAD and potentially others... @@ -524,12 +520,12 @@ def _ref_is_commit(self, ref): error! """ is_commit = False - value, _ = self._git_revparse_commit(ref) + value, _ = self._git_revparse_commit(ref, dirname) if value == 0: is_commit = True return is_commit - def _ref_is_hash(self, ref): + def _ref_is_hash(self, ref, dirname): """Verify that a reference is a valid hash according to git. Git doesn't seem to provide an exact way to determine if user @@ -544,7 +540,7 @@ def _ref_is_hash(self, ref): """ is_hash = False - status, git_output = self._git_revparse_commit(ref) + status, git_output = self._git_revparse_commit(ref, dirname) if status == 0: if git_output.strip().startswith(ref): is_hash = True @@ -554,9 +550,7 @@ def _status_summary(self, stat, repo_dir_path): """Determine the clean/dirty status of a git repository """ - cwd = os.getcwd() - os.chdir(repo_dir_path) - git_output = self._git_status_porcelain_v1z() + git_output = self._git_status_porcelain_v1z(repo_dir_path) is_dirty = self._status_v1z_is_dirty(git_output) if is_dirty: stat.clean_state = ExternalStatus.DIRTY @@ -565,8 +559,7 @@ def _status_summary(self, stat, repo_dir_path): # Now save the verbose status output incase the user wants to # see it. - stat.status_output = self._git_status_verbose() - os.chdir(cwd) + stat.status_output = self._git_status_verbose(repo_dir_path) @staticmethod def _status_v1z_is_dirty(git_output): @@ -601,7 +594,7 @@ def _status_v1z_is_dirty(git_output): # # ---------------------------------------------------------------- @staticmethod - def _git_current_hash(): + def _git_current_hash(dirname): """Return the full hash of the currently checked-out version. Returns a tuple, (hash_found, hash), where hash_found is a @@ -609,21 +602,51 @@ def _git_current_hash(): could mean we're not in a git repository at all). (If hash_found is False, then hash is ''.) """ - status, git_output = GitRepository._git_revparse_commit("HEAD") + status, git_output = GitRepository._git_revparse_commit("HEAD", + dirname) hash_found = not status if not hash_found: git_output = '' return hash_found, git_output @staticmethod - def _git_current_branch(): - """Determines the name of the current branch. + def _git_current_remote_branch(dirname): + """Determines the name of the current remote branch, if any. + + if dir is None, uses the cwd. Returns a tuple, (branch_found, branch_name), where branch_found - is a logical specifying whether a branch name was found for + is a bool specifying whether a branch name was found for + HEAD. (If branch_found is False, then branch_name is ''). + branch_name is in the format '$remote/$branch', e.g. 'origin/foo'. + """ + branch_found = False + branch_name = '' + + cmd = 'git -C {dirname} log -n 1 --pretty=%d HEAD'.format( + dirname=dirname).split() + status, git_output = execute_subprocess(cmd, + output_to_caller=True, + status_to_caller=True) + branch_found = 'HEAD,' in git_output + if branch_found: + # git_output is of the form " (HEAD, origin/blah)" + branch_name = git_output.split(',')[1].strip()[:-1] + return branch_found, branch_name + + @staticmethod + def _git_current_branch(dirname): + """Determines the name of the current local branch. + + Returns a tuple, (branch_found, branch_name), where branch_found + is a bool specifying whether a branch name was found for HEAD. (If branch_found is False, then branch_name is ''.) + Note that currently we check out the remote branch rather than + the local, so this command does not return the just-checked-out + branch. See _git_current_remote_branch. """ - cmd = ['git', 'symbolic-ref', '--short', '-q', 'HEAD'] + cmd = 'git -C {dirname} symbolic-ref --short -q HEAD'.format( + dirname=dirname).split() status, git_output = execute_subprocess(cmd, output_to_caller=True, status_to_caller=True) @@ -635,15 +658,17 @@ def _git_current_branch(): return branch_found, git_output @staticmethod - def _git_current_tag(): + def _git_current_tag(dirname): """Determines the name tag corresponding to HEAD (if any). + if dirname is None, uses the cwd. + Returns a tuple, (tag_found, tag_name), where tag_found is a - logical specifying whether we found a tag name corresponding to + bool specifying whether we found a tag name corresponding to HEAD. (If tag_found is False, then tag_name is ''.) """ - # git describe --exact-match --tags HEAD - cmd = ['git', 'describe', '--exact-match', '--tags', 'HEAD'] + cmd = 'git -C {dirname} describe --exact-match --tags HEAD'.format( + dirname=dirname).split() status, git_output = execute_subprocess(cmd, output_to_caller=True, status_to_caller=True) @@ -655,53 +680,54 @@ def _git_current_tag(): return tag_found, git_output @staticmethod - def _git_showref_tag(ref): + def _git_showref_tag(ref, dirname): """Run git show-ref check if the user supplied ref is a tag. could also use git rev-parse --quiet --verify tagname^{tag} """ - cmd = ['git', 'show-ref', '--quiet', '--verify', - 'refs/tags/{0}'.format(ref), ] + cmd = ('git -C {dirname} show-ref --quiet --verify refs/tags/{ref}' + .format(dirname=dirname, ref=ref).split()) status = execute_subprocess(cmd, status_to_caller=True) return status @staticmethod - def _git_showref_branch(ref): + def _git_showref_branch(ref, dirname): """Run git show-ref check if the user supplied ref is a local or tracked remote branch. """ - cmd = ['git', 'show-ref', '--quiet', '--verify', - 'refs/heads/{0}'.format(ref), ] + cmd = ('git -C {dirname} show-ref --quiet --verify refs/heads/{ref}' + .format(dirname=dirname, ref=ref).split()) status = execute_subprocess(cmd, status_to_caller=True) return status @staticmethod - def _git_lsremote_branch(ref, remote_name): + def _git_lsremote_branch(ref, remote_name, dirname): """Run git ls-remote to check if the user supplied ref is a remote branch that is not being tracked """ - cmd = ['git', 'ls-remote', '--exit-code', '--heads', - remote_name, ref, ] + cmd = ('git -C {dirname} ls-remote --exit-code --heads ' + '{remote_name} {ref}').format( + dirname=dirname, remote_name=remote_name, ref=ref).split() status = execute_subprocess(cmd, status_to_caller=True) return status @staticmethod - def _git_revparse_commit(ref): + def _git_revparse_commit(ref, dirname): """Run git rev-parse to detect if a reference is a SHA, HEAD or other valid commit. """ - cmd = ['git', 'rev-parse', '--quiet', '--verify', - '{0}^{1}'.format(ref, '{commit}'), ] + cmd = ('git -C {dirname} rev-parse --quiet --verify {ref}^{commit}' + .format(dirname=dirname, ref=ref, commit='{commit}').split()) status, git_output = execute_subprocess(cmd, status_to_caller=True, output_to_caller=True) git_output = git_output.strip() return status, git_output @staticmethod - def _git_status_porcelain_v1z(): + def _git_status_porcelain_v1z(dirname): """Run git status to obtain repository information. This is run with '--untracked=no' to ignore untracked files. @@ -710,36 +736,38 @@ def _git_status_porcelain_v1z(): between git versions or *user configuration*. """ - cmd = ['git', 'status', '--untracked-files=no', '--porcelain', '-z'] + cmd = ('git -C {dirname} status --untracked-files=no --porcelain -z' + .format(dirname=dirname)).split() git_output = execute_subprocess(cmd, output_to_caller=True) return git_output @staticmethod - def _git_status_verbose(): + def _git_status_verbose(dirname): """Run the git status command to obtain repository information. """ - cmd = ['git', 'status'] + cmd = 'git -C {dirname} status'.format(dirname=dirname).split() git_output = execute_subprocess(cmd, output_to_caller=True) return git_output @staticmethod - def _git_remote_verbose(): + def _git_remote_verbose(dirname): """Run the git remote command to obtain repository information. + + Returned string is of the form: + myfork git@github.com:johnpaulalex/manage_externals_jp.git (fetch) + myfork git@github.com:johnpaulalex/manage_externals_jp.git (push) """ - cmd = ['git', 'remote', '--verbose'] - git_output = execute_subprocess(cmd, output_to_caller=True) - return git_output + cmd = 'git -C {dirname} remote --verbose'.format( + dirname=dirname).split() + return execute_subprocess(cmd, output_to_caller=True) @staticmethod - def has_submodules(repo_dir_path=None): - """Return True iff the repository at (or the current - directory if is None) has a '.gitmodules' file + def has_submodules(repo_dir_path): + """Return True iff the repository at has a + '.gitmodules' file """ - if repo_dir_path is None: - fname = ExternalsDescription.GIT_SUBMODULES_FILENAME - else: - fname = os.path.join(repo_dir_path, - ExternalsDescription.GIT_SUBMODULES_FILENAME) + fname = os.path.join(repo_dir_path, + ExternalsDescription.GIT_SUBMODULES_FILENAME) return os.path.exists(fname) @@ -750,68 +778,71 @@ def has_submodules(repo_dir_path=None): # ---------------------------------------------------------------- @staticmethod def _git_clone(url, repo_dir_name, verbosity): - """Run git clone for the side effect of creating a repository. + """Clones url into repo_dir_name. """ - cmd = ['git', 'clone', '--quiet'] - subcmd = None - - cmd.extend([url, repo_dir_name]) + cmd = 'git clone --quiet {url} {repo_dir_name}'.format( + url=url, repo_dir_name=repo_dir_name).split() if verbosity >= VERBOSITY_VERBOSE: printlog(' {0}'.format(' '.join(cmd))) execute_subprocess(cmd) - if subcmd is not None: - os.chdir(repo_dir_name) - execute_subprocess(subcmd) @staticmethod - def _git_remote_add(name, url): + def _git_remote_add(name, url, dirname): """Run the git remote command for the side effect of adding a remote """ - cmd = ['git', 'remote', 'add', name, url] + cmd = 'git -C {dirname} remote add {name} {url}'.format( + dirname=dirname, name=name, url=url).split() execute_subprocess(cmd) @staticmethod - def _git_fetch(remote_name): + def _git_fetch(remote_name, dirname): """Run the git fetch command for the side effect of updating the repo """ - cmd = ['git', 'fetch', '--quiet', '--tags', remote_name] + cmd = 'git -C {dirname} fetch --quiet --tags {remote_name}'.format( + dirname=dirname, remote_name=remote_name).split() execute_subprocess(cmd) @staticmethod - def _git_checkout_ref(ref, verbosity, submodules): + def _git_checkout_ref(ref, verbosity, submodules, dirname): """Run the git checkout command for the side effect of updating the repo Param: ref is a reference to a local or remote object in the form 'origin/my_feature', or 'tag1'. """ - cmd = ['git', 'checkout', '--quiet', ref] + cmd = 'git -C {dirname} checkout --quiet {ref}'.format( + dirname=dirname, ref=ref).split() if verbosity >= VERBOSITY_VERBOSE: printlog(' {0}'.format(' '.join(cmd))) execute_subprocess(cmd) if submodules: - GitRepository._git_update_submodules(verbosity) + GitRepository._git_update_submodules(verbosity, dirname) @staticmethod - def _git_sparse_checkout(verbosity): + def _git_sparse_checkout(verbosity, dirname): """Configure repo via read-tree.""" - cmd = ['git', 'config', 'core.sparsecheckout', 'true'] + cmd = 'git -C {dirname} config core.sparsecheckout true'.format( + dirname=dirname).split() if verbosity >= VERBOSITY_VERBOSE: printlog(' {0}'.format(' '.join(cmd))) execute_subprocess(cmd) - cmd = ['git', 'read-tree', '-mu', 'HEAD'] + cmd = 'git -C {dirname} read-tree -mu HEAD'.format( + dirname=dirname).split() if verbosity >= VERBOSITY_VERBOSE: printlog(' {0}'.format(' '.join(cmd))) execute_subprocess(cmd) @staticmethod - def _git_update_submodules(verbosity): + def _git_update_submodules(verbosity, dirname): """Run git submodule update for the side effect of updating this repo's submodules. """ # First, verify that we have a .gitmodules file - if os.path.exists(ExternalsDescription.GIT_SUBMODULES_FILENAME): - cmd = ['git', 'submodule', 'update', '--init', '--recursive'] + if os.path.exists( + os.path.join(dirname, + ExternalsDescription.GIT_SUBMODULES_FILENAME)): + cmd = ('git -C {dirname} submodule update --init --recursive' + .format(dirname=dirname)).split() if verbosity >= VERBOSITY_VERBOSE: printlog(' {0}'.format(' '.join(cmd))) diff --git a/manage_externals/manic/repository_svn.py b/manage_externals/manic/repository_svn.py index 408ed84676..922855d34e 100644 --- a/manage_externals/manic/repository_svn.py +++ b/manage_externals/manic/repository_svn.py @@ -43,10 +43,15 @@ def __init__(self, component_name, repo, ignore_ancestry=False): """ Repository.__init__(self, component_name, repo) self._ignore_ancestry = ignore_ancestry + if self._url.endswith('/'): + # there is already a '/' separator in the URL; no need to add another + url_sep = '' + else: + url_sep = '/' if self._branch: - self._url = os.path.join(self._url, self._branch) + self._url = self._url + url_sep + self._branch elif self._tag: - self._url = os.path.join(self._url, self._tag) + self._url = self._url + url_sep + self._tag else: msg = "DEV_ERROR in svn repository. Shouldn't be here!" fatal_error(msg) diff --git a/manage_externals/manic/sourcetree.py b/manage_externals/manic/sourcetree.py index 54de763c30..cf2a5b7569 100644 --- a/manage_externals/manic/sourcetree.py +++ b/manage_externals/manic/sourcetree.py @@ -1,6 +1,6 @@ """ - -FIXME(bja, 2017-11) External and SourceTree have a circular dependancy! +Classes to represent an externals config file (SourceTree) and the components +within it (_External). """ import errno @@ -19,62 +19,54 @@ class _External(object): """ - _External represents an external object inside a SourceTree - """ + A single component hosted in an external repository (and any children). + The component may or may not be checked-out upon construction. + """ # pylint: disable=R0902 - def __init__(self, root_dir, name, ext_description, svn_ignore_ancestry): - """Parse an external description file into a dictionary of externals. + def __init__(self, root_dir, name, local_path, required, subexternals_path, + repo, svn_ignore_ancestry, subexternal_sourcetree): + """Create a single external component (checked out or not). Input: + root_dir : string - the (checked-out) parent repo's root dir. + local_path : string - this external's (checked-out) subdir relative + to root_dir, e.g. "components/mom" + repo: Repository - the repo object for this external. Can be None (e.g. if this external just refers to another external file). - root_dir : string - the root directory path where - 'local_path' is relative to. - - name : string - name of the ext_description object. may or may not - correspond to something in the path. + name : string - name of this external (as named by the parent + reference). May or may not correspond to something in the path. ext_description : dict - source ExternalsDescription object svn_ignore_ancestry : bool - use --ignore-externals with svn switch + subexternals_path: string - path to sub-externals config file, if any. Relative to local_path, or special value 'none'. + subexternal_sourcetree: SourceTree - corresponding to subexternals_path, if subexternals_path exists (it might not, if it is not checked out yet). """ self._name = name - self._repo = None - self._externals = EMPTY_STR - self._externals_sourcetree = None - self._stat = ExternalStatus() - self._sparse = None - # Parse the sub-elements - - # _path : local path relative to the containing source tree - self._local_path = ext_description[ExternalsDescription.PATH] - # _repo_dir : full repository directory - repo_dir = os.path.join(root_dir, self._local_path) + self._required = required + + self._stat = None # Populated in status() + + self._local_path = local_path + # _repo_dir_path : full repository directory, e.g. + # "/components/mom" + repo_dir = os.path.join(root_dir, local_path) self._repo_dir_path = os.path.abspath(repo_dir) - # _base_dir : base directory *containing* the repository + # _base_dir_path : base directory *containing* the repository, e.g. + # "/components" self._base_dir_path = os.path.dirname(self._repo_dir_path) - # repo_dir_name : base_dir_path + repo_dir_name = rep_dir_path + # _repo_dir_name : base_dir_path + repo_dir_name = repo_dir_path + # e.g., "mom" self._repo_dir_name = os.path.basename(self._repo_dir_path) - assert(os.path.join(self._base_dir_path, self._repo_dir_name) - == self._repo_dir_path) + self._repo = repo - self._required = ext_description[ExternalsDescription.REQUIRED] - self._externals = ext_description[ExternalsDescription.EXTERNALS] - # Treat a .gitmodules file as a backup externals config - if not self._externals: - if GitRepository.has_submodules(self._repo_dir_path): - self._externals = ExternalsDescription.GIT_SUBMODULES_FILENAME - - repo = create_repository( - name, ext_description[ExternalsDescription.REPO], - svn_ignore_ancestry=svn_ignore_ancestry) - if repo: - self._repo = repo - - if self._externals and (self._externals.lower() != 'none'): - self._create_externals_sourcetree() + # Does this component have subcomponents aka an externals config? + self._subexternals_path = subexternals_path + self._subexternal_sourcetree = subexternal_sourcetree + def get_name(self): """ @@ -88,81 +80,97 @@ def get_local_path(self): """ return self._local_path - def status(self): - """ - If the repo destination directory exists, ensure it is correct (from - correct URL, correct branch or tag), and possibly update the external. - If the repo destination directory does not exist, checkout the correce - branch or tag. - If load_all is True, also load all of the the externals sub-externals. + def get_repo_dir_path(self): + return self._repo_dir_path + + def get_subexternals_path(self): + return self._subexternals_path + + def get_repo(self): + return self._repo + + def status(self, force=False, print_progress=False): """ + Returns status of this component and all subcomponents. - self._stat.path = self.get_local_path() - if not self._required: - self._stat.source_type = ExternalStatus.OPTIONAL - elif self._local_path == LOCAL_PATH_INDICATOR: - # LOCAL_PATH_INDICATOR, '.' paths, are standalone - # component directories that are not managed by - # checkout_externals. - self._stat.source_type = ExternalStatus.STANDALONE - else: - # managed by checkout_externals - self._stat.source_type = ExternalStatus.MANAGED + Returns a dict mapping our local path (not component name!) to an + ExternalStatus dict. Any subcomponents will have their own top-level + path keys. Note the return value includes entries for this and all + subcomponents regardless of whether they are locally installed or not. - ext_stats = {} + Side-effect: If self._stat is empty or force is True, calculates _stat. + """ + calc_stat = force or not self._stat + + if calc_stat: + self._stat = ExternalStatus() + self._stat.path = self.get_local_path() + if not self._required: + self._stat.source_type = ExternalStatus.OPTIONAL + elif self._local_path == LOCAL_PATH_INDICATOR: + # LOCAL_PATH_INDICATOR, '.' paths, are standalone + # component directories that are not managed by + # checkout_subexternals. + self._stat.source_type = ExternalStatus.STANDALONE + else: + # managed by checkout_subexternals + self._stat.source_type = ExternalStatus.MANAGED + subcomponent_stats = {} if not os.path.exists(self._repo_dir_path): - self._stat.sync_state = ExternalStatus.EMPTY - msg = ('status check: repository directory for "{0}" does not ' - 'exist.'.format(self._name)) - logging.info(msg) - self._stat.current_version = 'not checked out' - # NOTE(bja, 2018-01) directory doesn't exist, so we cannot - # use repo to determine the expected version. We just take - # a best-guess based on the assumption that only tag or - # branch should be set, but not both. - if not self._repo: - self._stat.expected_version = 'unknown' - else: - self._stat.expected_version = self._repo.tag() + self._repo.branch() + if calc_stat: + # No local repository. + self._stat.sync_state = ExternalStatus.EMPTY + msg = ('status check: repository directory for "{0}" does not ' + 'exist.'.format(self._name)) + logging.info(msg) + self._stat.current_version = 'not checked out' + # NOTE(bja, 2018-01) directory doesn't exist, so we cannot + # use repo to determine the expected version. We just take + # a best-guess based on the assumption that only tag or + # branch should be set, but not both. + if not self._repo: + self._stat.expected_version = 'unknown' + else: + self._stat.expected_version = self._repo.tag() + self._repo.branch() else: - if self._repo: + # Merge local repository state (e.g. clean/dirty) into self._stat. + if calc_stat and self._repo: self._repo.status(self._stat, self._repo_dir_path) - if self._externals and self._externals_sourcetree: - # we expect externals and they exist + # Status of subcomponents, if any. + if self._subexternals_path and self._subexternal_sourcetree: cwd = os.getcwd() - # SourceTree expects to be called from the correct + # SourceTree.status() expects to be called from the correct # root directory. os.chdir(self._repo_dir_path) - ext_stats = self._externals_sourcetree.status(self._local_path) + subcomponent_stats = self._subexternal_sourcetree.status(self._local_path, force=force, print_progress=print_progress) os.chdir(cwd) + # Merge our status + subcomponent statuses into one return dict keyed + # by component path. all_stats = {} # don't add the root component because we don't manage it # and can't provide useful info about it. if self._local_path != LOCAL_PATH_INDICATOR: - # store the stats under tha local_path, not comp name so + # store the stats under the local_path, not comp name so # it will be sorted correctly all_stats[self._stat.path] = self._stat - if ext_stats: - all_stats.update(ext_stats) + if subcomponent_stats: + all_stats.update(subcomponent_stats) return all_stats - def checkout(self, verbosity, load_all): + def checkout(self, verbosity): """ If the repo destination directory exists, ensure it is correct (from - correct URL, correct branch or tag), and possibly update the external. + correct URL, correct branch or tag), and possibly updateit. If the repo destination directory does not exist, checkout the correct branch or tag. - If load_all is True, also load all of the the externals sub-externals. + Does not check out sub-externals, see SourceTree.checkout(). """ - if load_all: - pass # Make sure we are in correct location - if not os.path.exists(self._repo_dir_path): # repository directory doesn't exist. Need to check it # out, and for that we need the base_dir_path to exist @@ -174,6 +182,10 @@ def checkout(self, verbosity, load_all): self._base_dir_path) fatal_error(msg) + if not self._stat: + self.status() + assert self._stat + if self._stat.source_type != ExternalStatus.STANDALONE: if verbosity >= VERBOSITY_VERBOSE: # NOTE(bja, 2018-01) probably do not want to pass @@ -194,120 +206,147 @@ def checkout(self, verbosity, load_all): self._repo.checkout(self._base_dir_path, self._repo_dir_name, checkout_verbosity, self.clone_recursive()) - def checkout_externals(self, verbosity, load_all): - """Checkout the sub-externals for this object - """ - if self.load_externals(): - if self._externals_sourcetree: - # NOTE(bja, 2018-02): the subtree externals objects - # were created during initial status check. Updating - # the external may have changed which sub-externals - # are needed. We need to delete those objects and - # re-read the potentially modified externals - # description file. - self._externals_sourcetree = None - self._create_externals_sourcetree() - self._externals_sourcetree.checkout(verbosity, load_all) - - def load_externals(self): - 'Return True iff an externals file should be loaded' - load_ex = False - if os.path.exists(self._repo_dir_path): - if self._externals: - if self._externals.lower() != 'none': - load_ex = os.path.exists(os.path.join(self._repo_dir_path, - self._externals)) - - return load_ex + def replace_subexternal_sourcetree(self, sourcetree): + self._subexternal_sourcetree = sourcetree def clone_recursive(self): 'Return True iff any .gitmodules files should be processed' - # Try recursive unless there is an externals entry - recursive = not self._externals + # Try recursive .gitmodules unless there is an externals entry + recursive = not self._subexternals_path return recursive - def _create_externals_sourcetree(self): - """ + +class SourceTree(object): + """ + SourceTree represents a group of managed externals. + + Those externals may not be checked out locally yet, they might only + have Repository objects pointing to their respective repositories. + """ + + @classmethod + def from_externals_file(cls, parent_repo_dir_path, parent_repo, + externals_path): + """Creates a SourceTree representing the given externals file. + + Looks up a git submodules file as an optional backup if there is no + externals file specified. + + Returns None if there is no externals file (i.e. it's None or 'none'), + or if the externals file hasn't been checked out yet. + + parent_repo_dir_path: parent repo root dir + parent_repo: parent repo. + externals_path: path to externals file, relative to parent_repo_dir_path. """ - if not os.path.exists(self._repo_dir_path): + if not os.path.exists(parent_repo_dir_path): # NOTE(bja, 2017-10) repository has not been checked out # yet, can't process the externals file. Assume we are # checking status before code is checkoud out and this # will be handled correctly later. - return + return None - cwd = os.getcwd() - os.chdir(self._repo_dir_path) - if self._externals.lower() == 'none': - msg = ('Internal: Attempt to create source tree for ' - 'externals = none in {}'.format(self._repo_dir_path)) - fatal_error(msg) + if externals_path.lower() == 'none': + # With explicit 'none', do not look for git submodules file. + return None - if not os.path.exists(self._externals): - if GitRepository.has_submodules(): - self._externals = ExternalsDescription.GIT_SUBMODULES_FILENAME + cwd = os.getcwd() + os.chdir(parent_repo_dir_path) + + if not externals_path: + if GitRepository.has_submodules(parent_repo_dir_path): + externals_path = ExternalsDescription.GIT_SUBMODULES_FILENAME + else: + return None - if not os.path.exists(self._externals): - # NOTE(bja, 2017-10) this check is redundent with the one + if not os.path.exists(externals_path): + # NOTE(bja, 2017-10) this check is redundant with the one # in read_externals_description_file! - msg = ('External externals description file "{0}" ' + msg = ('Externals description file "{0}" ' 'does not exist! In directory: {1}'.format( - self._externals, self._repo_dir_path)) + externals_path, parent_repo_dir_path)) fatal_error(msg) - externals_root = self._repo_dir_path + externals_root = parent_repo_dir_path + # model_data is a dict-like object which mirrors the file format. model_data = read_externals_description_file(externals_root, - self._externals) - externals = create_externals_description(model_data, - parent_repo=self._repo) - self._externals_sourcetree = SourceTree(externals_root, externals) + externals_path) + # ext_description is another dict-like object (see ExternalsDescription) + ext_description = create_externals_description(model_data, + parent_repo=parent_repo) + externals_sourcetree = SourceTree(externals_root, ext_description) os.chdir(cwd) - -class SourceTree(object): - """ - SourceTree represents a group of managed externals - """ - - def __init__(self, root_dir, model, svn_ignore_ancestry=False): + return externals_sourcetree + + def __init__(self, root_dir, ext_description, svn_ignore_ancestry=False): """ - Build a SourceTree object from a model description + Build a SourceTree object from an ExternalDescription. + + root_dir: the (checked-out) parent repo root dir. """ self._root_dir = os.path.abspath(root_dir) - self._all_components = {} + self._all_components = {} # component_name -> _External self._required_compnames = [] - for comp in model: - src = _External(self._root_dir, comp, model[comp], svn_ignore_ancestry) + for comp, desc in ext_description.items(): + local_path = desc[ExternalsDescription.PATH] + required = desc[ExternalsDescription.REQUIRED] + repo_info = desc[ExternalsDescription.REPO] + subexternals_path = desc[ExternalsDescription.EXTERNALS] + + repo = create_repository(comp, + repo_info, + svn_ignore_ancestry=svn_ignore_ancestry) + + sourcetree = None + # Treat a .gitmodules file as a backup externals config + if not subexternals_path: + parent_repo_dir_path = os.path.abspath(os.path.join(root_dir, + local_path)) + if GitRepository.has_submodules(parent_repo_dir_path): + subexternals_path = ExternalsDescription.GIT_SUBMODULES_FILENAME + + # Might return None (if the subexternal isn't checked out yet, or subexternal is None or 'none') + subexternal_sourcetree = SourceTree.from_externals_file( + os.path.join(self._root_dir, local_path), + repo, + subexternals_path) + src = _External(self._root_dir, comp, local_path, required, + subexternals_path, repo, svn_ignore_ancestry, + subexternal_sourcetree) + self._all_components[comp] = src - if model[comp][ExternalsDescription.REQUIRED]: + if required: self._required_compnames.append(comp) - def status(self, relative_path_base=LOCAL_PATH_INDICATOR): - """Report the status components - - FIXME(bja, 2017-10) what do we do about situations where the - user checked out the optional components, but didn't add - optional for running status? What do we do where the user - didn't add optional to the checkout but did add it to the - status. -- For now, we run status on all components, and try - to do the right thing based on the results.... - - """ + def status(self, relative_path_base=LOCAL_PATH_INDICATOR, + force=False, print_progress=False): + """Return a dictionary of local path->ExternalStatus. + + Notes about the returned dictionary: + * It is keyed by local path (e.g. 'components/mom'), not by + component name (e.g. 'mom'). + * It contains top-level keys for all traversed components, whether + discovered by recursion or top-level. + * It contains entries for all components regardless of whether they + are locally installed or not, or required or optional. +x """ load_comps = self._all_components.keys() - summary = {} + summary = {} # Holds merged statuses from all components. for comp in load_comps: - printlog('{0}, '.format(comp), end='') - stat = self._all_components[comp].status() + if print_progress: + printlog('{0}, '.format(comp), end='') + stat = self._all_components[comp].status(force=force, + print_progress=print_progress) + + # Returned status dictionary is keyed by local path; prepend + # relative_path_base if not already there. stat_final = {} for name in stat.keys(): - # check if we need to append the relative_path_base to - # the path so it will be sorted in the correct order. if stat[name].path.startswith(relative_path_base): - # use as is, without any changes to path stat_final[name] = stat[name] else: - # append relative_path_base to path and store under key = updated path modified_path = os.path.join(relative_path_base, stat[name].path) stat_final[modified_path] = stat[name] @@ -316,38 +355,71 @@ def status(self, relative_path_base=LOCAL_PATH_INDICATOR): return summary + def _find_installed_optional_components(self): + """Returns a list of installed optional component names, if any.""" + installed_comps = [] + for comp_name, ext in self._all_components.items(): + if comp_name in self._required_compnames: + continue + # Note that in practice we expect this status to be cached. + path_to_stat = ext.status() + + # If any part of this component exists locally, consider it + # installed and therefore eligible for updating. + if any(s.sync_state != ExternalStatus.EMPTY + for s in path_to_stat.values()): + installed_comps.append(comp_name) + return installed_comps + def checkout(self, verbosity, load_all, load_comp=None): """ - Checkout or update indicated components into the the configured - subdirs. + Checkout or update indicated components into the configured subdirs. - If load_all is True, recursively checkout all externals. - If load_all is False, load_comp is an optional set of components to load. - If load_all is True and load_comp is None, only load the required externals. + If load_all is True, checkout all externals (required + optional), recursively. + If load_all is False and load_comp is set, checkout load_comp (and any required subexternals, plus any optional subexternals that are already checked out, recursively) + If load_all is False and load_comp is None, checkout all required externals, plus any optionals that are already checked out, recursively. """ - if verbosity >= VERBOSITY_VERBOSE: - printlog('Checking out externals: ') - else: - printlog('Checking out externals: ', end='') - if load_all: tmp_comps = self._all_components.keys() elif load_comp is not None: tmp_comps = [load_comp] else: - tmp_comps = self._required_compnames + local_optional_compnames = self._find_installed_optional_components() + tmp_comps = self._required_compnames + local_optional_compnames + if local_optional_compnames: + printlog('Found locally installed optional components: ' + + ', '.join(local_optional_compnames)) + bad_compnames = set(local_optional_compnames) - set(self._all_components.keys()) + if bad_compnames: + printlog('Internal error: found locally installed components that are not in the global list of all components: ' + ','.join(bad_compnames)) + + if verbosity >= VERBOSITY_VERBOSE: + printlog('Checking out externals: ') + else: + printlog('Checking out externals: ', end='') + # Sort by path so that if paths are nested the # parent repo is checked out first. load_comps = sorted(tmp_comps, key=lambda comp: self._all_components[comp].get_local_path()) - # checkout the primary externals - for comp in load_comps: + + # checkout. + for comp_name in load_comps: if verbosity < VERBOSITY_VERBOSE: - printlog('{0}, '.format(comp), end='') + printlog('{0}, '.format(comp_name), end='') else: # verbose output handled by the _External object, just # output a newline printlog(EMPTY_STR) - self._all_components[comp].checkout(verbosity, load_all) - # now give each external an opportunitity to checkout it's externals. - self._all_components[comp].checkout_externals(verbosity, load_all) + c = self._all_components[comp_name] + # Does not recurse. + c.checkout(verbosity) + # Recursively check out subexternals, if any. Returns None + # if there's no subexternals path. + component_subexternal_sourcetree = SourceTree.from_externals_file( + c.get_repo_dir_path(), + c.get_repo(), + c.get_subexternals_path()) + c.replace_subexternal_sourcetree(component_subexternal_sourcetree) + if component_subexternal_sourcetree: + component_subexternal_sourcetree.checkout(verbosity, load_all) printlog('') diff --git a/manage_externals/test/README.md b/manage_externals/test/README.md index 938a900eec..1e8f2eaa77 100644 --- a/manage_externals/test/README.md +++ b/manage_externals/test/README.md @@ -1,45 +1,25 @@ # Testing for checkout_externals -NOTE: Python2 is the supported runtime environment. Python3 compatibility is -in progress, complicated by the different proposed input methods -(yaml, xml, cfg/ini, json) and their different handling of strings -(unicode vs byte) in python2. Full python3 compatibility will be -possible once the number of possible input formats has been narrowed. - -## Setup development environment - -Development environments should be setup for python2 and python3: +## Unit tests ```SH cd checkout_externals/test - make python=python2 env - make python=python3 env + make utest ``` -## Unit tests - -Tests should be run for both python2 and python3. It is recommended -that you have seperate terminal windows open python2 and python3 -testing to avoid errors activating and deactivating environments. +## System tests ```SH cd checkout_externals/test - . env_python2/bin/activate - make utest - deactivate + make stest ``` +Example to run a single test: ```SH - cd checkout_externals/test - . env_python2/bin/activate - make utest - deactivate + cd checkout_externals + python -m unittest test.test_sys_checkout.TestSysCheckout.test_container_simple_required ``` -## System tests - -Not yet implemented. - ## Static analysis checkout_externals is difficult to test thoroughly because it relies @@ -51,9 +31,7 @@ regularly for automatic code formatting and linting. ```SH cd checkout_externals/test - . env_python2/bin/activate make lint - deactivate ``` The canonical formatting for the code is whatever autopep8 @@ -68,10 +46,8 @@ coverage, run the code coverage tool: ```SH cd checkout_externals/test - . env_python2/bin/activate make coverage open -a Firefox.app htmlcov/index.html - deactivate ``` diff --git a/manage_externals/test/repos/README.md b/manage_externals/test/repos/README.md new file mode 100644 index 0000000000..8a3502c35f --- /dev/null +++ b/manage_externals/test/repos/README.md @@ -0,0 +1,33 @@ +Git repositories for testing git-related behavior. For usage and terminology notes, see test/test_sys_checkout.py. + +To list files and view file contents at HEAD: +``` +cd +git ls-tree --full-tree -r --name-only HEAD +git cat-file -p HEAD: +``` + +File contents at a glance: +``` +container.git/ + readme.txt + +simple-ext.git/ + (has branches: feature2, feature3) + (has tags: tag1, tag2) + readme.txt + simple_subdir/subdir_file.txt + +simple-ext-fork.git/ + (has tags: abandoned-feature, forked-feature-v1, tag1) + (has branch: feature2) + readme.txt + +mixed-cont-ext.git/ + (has branch: new-feature) + readme.txt + sub-externals.cfg ('simp_branch' section refers to 'feature2' branch in simple-ext.git/ repo) + +error/ + (no git repo here, just a readme.txt in the clear) +``` diff --git a/manage_externals/test/test_sys_checkout.py b/manage_externals/test/test_sys_checkout.py index b13e96a17d..ab4f77e88f 100644 --- a/manage_externals/test/test_sys_checkout.py +++ b/manage_externals/test/test_sys_checkout.py @@ -2,6 +2,14 @@ """Unit test driver for checkout_externals +Terminology: + * 'container': a repo that has externals + * 'simple': a repo that has no externals, but is referenced as an external by another repo. + * 'mixed': a repo that both has externals and is referenced as an external by another repo. + + * 'clean': the local repo matches the version in the externals and has no local modifications. + * 'empty': the external isn't checked out at all. + Note: this script assume the path to the manic and checkout_externals module is already in the python path. This is usually handled by the makefile. If you call it directly, you may need @@ -21,7 +29,6 @@ * Erase any existing repos at the begining of the module in setUpModule. - """ # NOTE(bja, 2017-11) pylint complains that the module is too big, but @@ -66,27 +73,52 @@ # # --------------------------------------------------------------------- -# environment variable names -MANIC_TEST_BARE_REPO_ROOT = 'MANIC_TEST_BARE_REPO_ROOT' -MANIC_TEST_TMP_REPO_ROOT = 'MANIC_TEST_TMP_REPO_ROOT' -# directory names -TMP_REPO_DIR_NAME = 'tmp' +# Module-wide root directory for all the per-test subdirs we'll create on +# the fly (which are placed under wherever $CWD is when the test runs). +# Set by setupModule(). +module_tmp_root_dir = None +TMP_REPO_DIR_NAME = 'tmp' # subdir under $CWD + +# subdir under test/ that holds all of our checked-in repositories (which we +# will clone for these tests). BARE_REPO_ROOT_NAME = 'repos' -CONTAINER_REPO_NAME = 'container.git' -MIXED_REPO_NAME = 'mixed-cont-ext.git' -SIMPLE_REPO_NAME = 'simple-ext.git' -SIMPLE_FORK_NAME = 'simple-ext-fork.git' + +# Environment var referenced by checked-in externals file in mixed-cont-ext.git, +# which should be pointed to the fully-resolved BARE_REPO_ROOT_NAME directory. +# We explicitly clear this after every test, via tearDown(). +MIXED_CONT_EXT_ROOT_ENV_VAR = 'MANIC_TEST_BARE_REPO_ROOT' + +# Subdirs under bare repo root, each holding a repository. For more info +# on the contents of these repositories, see test/repos/README.md. In these +# tests the 'parent' repos are cloned as a starting point, whereas the 'child' +# repos are checked out when the tests run checkout_externals. +CONTAINER_REPO = 'container.git' # Parent repo +SIMPLE_REPO = 'simple-ext.git' # Child repo +SIMPLE_FORK_REPO = 'simple-ext-fork.git' # Child repo +MIXED_REPO = 'mixed-cont-ext.git' # Both parent and child + +# Standard (arbitrary) external names for test configs +TAG_SECTION = 'simp_tag' +BRANCH_SECTION = 'simp_branch' +HASH_SECTION = 'simp_hash' + +# All the configs we construct check out their externals into these local paths. +EXTERNALS_PATH = 'externals' +SUB_EXTERNALS_PATH = 'src' # For mixed test repos, + +# For testing behavior with '.' instead of an explicit paths. SIMPLE_LOCAL_ONLY_NAME = '.' -ERROR_REPO_NAME = 'error' -EXTERNALS_NAME = 'externals' -SUB_EXTERNALS_PATH = 'src' -CFG_NAME = 'externals.cfg' -CFG_SUB_NAME = 'sub-externals.cfg' -README_NAME = 'readme.txt' -REMOTE_BRANCH_FEATURE2 = 'feature2' -NESTED_NAME = ['./fred', './fred/wilma', './fred/wilma/barney'] +# Externals files. +CFG_NAME = 'externals.cfg' # We construct this on a per-test basis. +CFG_SUB_NAME = 'sub-externals.cfg' # Already exists in mixed-cont-ext repo. + +# Arbitrary text file in all the test repos. +README_NAME = 'readme.txt' + +# Branch that exists in both the simple and simple-fork repos. +REMOTE_BRANCH_FEATURE2 = 'feature2' SVN_TEST_REPO = 'https://github.com/escomp/cesm' @@ -109,153 +141,113 @@ def setUpModule(): # pylint: disable=C0103 pass # create clean dir for this run os.mkdir(repo_root) - # set into the environment so var will be expanded in externals - # filess when executables are run - os.environ[MANIC_TEST_TMP_REPO_ROOT] = repo_root - - -class GenerateExternalsDescriptionCfgV1(object): - """Class to provide building blocks to create - ExternalsDescriptionCfgV1 files. - - Includes predefined files used in tests. - """ - - def __init__(self): - self._schema_version = '1.1.0' - self._config = None + # Make available to all tests in this file. + global module_tmp_root_dir + assert module_tmp_root_dir == None, module_tmp_root_dir + module_tmp_root_dir = repo_root - def container_full(self, dest_dir): - """Create the full container config file with simple and mixed use - externals - - """ - self.create_config() - self.create_section(SIMPLE_REPO_NAME, 'simp_tag', - tag='tag1') - - self.create_section(SIMPLE_REPO_NAME, 'simp_branch', - branch=REMOTE_BRANCH_FEATURE2) - - self.create_section(SIMPLE_REPO_NAME, 'simp_opt', - tag='tag1', required=False) - - self.create_section(MIXED_REPO_NAME, 'mixed_req', - branch='master', externals=CFG_SUB_NAME) - - self.write_config(dest_dir) - - def container_simple_required(self, dest_dir): - """Create a container externals file with only simple externals. - - """ - self.create_config() - self.create_section(SIMPLE_REPO_NAME, 'simp_tag', - tag='tag1') - - self.create_section(SIMPLE_REPO_NAME, 'simp_branch', - branch=REMOTE_BRANCH_FEATURE2) - - self.create_section(SIMPLE_REPO_NAME, 'simp_hash', - ref_hash='60b1cc1a38d63') - - self.write_config(dest_dir) - - def container_nested_required(self, dest_dir, order): - """Create a container externals file with only simple externals. - - """ - self.create_config() - self.create_section(SIMPLE_REPO_NAME, 'simp_tag', nested=True, - tag='tag1', path=NESTED_NAME[order[0]]) - - self.create_section(SIMPLE_REPO_NAME, 'simp_branch', nested=True, - branch=REMOTE_BRANCH_FEATURE2, path=NESTED_NAME[order[1]]) - - self.create_section(SIMPLE_REPO_NAME, 'simp_hash', nested=True, - ref_hash='60b1cc1a38d63', path=NESTED_NAME[order[2]]) - - self.write_config(dest_dir) - - - def container_simple_optional(self, dest_dir): - """Create a container externals file with optional simple externals +class RepoUtils(object): + """Convenience methods for interacting with git repos.""" + @staticmethod + def create_branch(repo_base_dir, external_name, branch, with_commit=False): + """Create branch and optionally (with_commit) add a single commit. """ - self.create_config() - self.create_section(SIMPLE_REPO_NAME, 'simp_req', - tag='tag1') - - self.create_section(SIMPLE_REPO_NAME, 'simp_opt', - tag='tag1', required=False) - - self.write_config(dest_dir) - - def container_simple_svn(self, dest_dir): - """Create a container externals file with only simple externals. + # pylint: disable=R0913 + cwd = os.getcwd() + repo_root = os.path.join(repo_base_dir, EXTERNALS_PATH, external_name) + os.chdir(repo_root) + cmd = ['git', 'checkout', '-b', branch, ] + execute_subprocess(cmd) + if with_commit: + msg = 'start work on {0}'.format(branch) + with open(README_NAME, 'a') as handle: + handle.write(msg) + cmd = ['git', 'add', README_NAME, ] + execute_subprocess(cmd) + cmd = ['git', 'commit', '-m', msg, ] + execute_subprocess(cmd) + os.chdir(cwd) + @staticmethod + def create_commit(repo_base_dir, external_name): + """Make a commit to the given external. + + This is used to test sync state changes from local commits on + detached heads and tracking branches. """ - self.create_config() - self.create_section(SIMPLE_REPO_NAME, 'simp_tag', tag='tag1') - - self.create_svn_external('svn_branch', branch='trunk') - self.create_svn_external('svn_tag', tag='tags/cesm2.0.beta07') + cwd = os.getcwd() + repo_root = os.path.join(repo_base_dir, EXTERNALS_PATH, external_name) + os.chdir(repo_root) - self.write_config(dest_dir) + msg = 'work on great new feature!' + with open(README_NAME, 'a') as handle: + handle.write(msg) + cmd = ['git', 'add', README_NAME, ] + execute_subprocess(cmd) + cmd = ['git', 'commit', '-m', msg, ] + execute_subprocess(cmd) + os.chdir(cwd) - def container_sparse(self, dest_dir): - """Create a container with a full external and a sparse external + @staticmethod + def clone_test_repo(bare_root, test_id, parent_repo_name, dest_dir_in): + """Clone repo at / into dest_dir_in or local per-test-subdir. + Returns output dir. """ - # Create a file for a sparse pattern match - sparse_filename = 'sparse_checkout' - with open(os.path.join(dest_dir, sparse_filename), 'w') as sfile: - sfile.write('readme.txt') - - self.create_config() - self.create_section(SIMPLE_REPO_NAME, 'simp_tag', - tag='tag2') - - sparse_relpath = '../../{}'.format(sparse_filename) - self.create_section(SIMPLE_REPO_NAME, 'simp_sparse', - tag='tag2', sparse=sparse_relpath) + parent_repo_dir = os.path.join(bare_root, parent_repo_name) + if dest_dir_in is None: + # create unique subdir for this test + test_dir_name = test_id + print("Test repository name: {0}".format(test_dir_name)) + dest_dir = os.path.join(module_tmp_root_dir, test_dir_name) + else: + dest_dir = dest_dir_in - self.write_config(dest_dir) + # pylint: disable=W0212 + GitRepository._git_clone(parent_repo_dir, dest_dir, VERBOSITY_DEFAULT) + return dest_dir - def mixed_simple_base(self, dest_dir): - """Create a mixed-use base externals file with only simple externals. + @staticmethod + def add_file_to_repo(under_test_dir, filename, tracked): + """Add a file to the repository so we can put it into a dirty state """ - self.create_config() - self.create_section_ext_only('mixed_base') - self.create_section(SIMPLE_REPO_NAME, 'simp_tag', - tag='tag1') - - self.create_section(SIMPLE_REPO_NAME, 'simp_branch', - branch=REMOTE_BRANCH_FEATURE2) + cwd = os.getcwd() + os.chdir(under_test_dir) + with open(filename, 'w') as tmp: + tmp.write('Hello, world!') - self.create_section(SIMPLE_REPO_NAME, 'simp_hash', - ref_hash='60b1cc1a38d63') + if tracked: + # NOTE(bja, 2018-01) brittle hack to obtain repo dir and + # file name + path_data = filename.split('/') + repo_dir = os.path.join(path_data[0], path_data[1]) + os.chdir(repo_dir) + tracked_file = path_data[2] + cmd = ['git', 'add', tracked_file] + execute_subprocess(cmd) - self.write_config(dest_dir) + os.chdir(cwd) - def mixed_simple_sub(self, dest_dir): - """Create a mixed-use sub externals file with only simple externals. +class GenerateExternalsDescriptionCfgV1(object): + """Building blocks to create ExternalsDescriptionCfgV1 files. - """ - self.create_config() - self.create_section(SIMPLE_REPO_NAME, 'simp_tag', - tag='tag1', path=SUB_EXTERNALS_PATH) + Basic usage: create_config() multiple create_*(), then write_config(). + Optionally after that: write_with_*(). + """ - self.create_section(SIMPLE_REPO_NAME, 'simp_branch', - branch=REMOTE_BRANCH_FEATURE2, - path=SUB_EXTERNALS_PATH) + def __init__(self, bare_root): + self._schema_version = '1.1.0' + self._config = None - self.write_config(dest_dir, filename=CFG_SUB_NAME) + # directory where we have test repositories (which we will clone for + # tests) + self._bare_root = bare_root def write_config(self, dest_dir, filename=CFG_NAME): - """Write the configuration file to disk + """Write self._config to disk """ dest_path = os.path.join(dest_dir, filename) @@ -277,14 +269,23 @@ def create_metadata(self): self._config.set(DESCRIPTION_SECTION, VERSION_ITEM, self._schema_version) - def create_section(self, repo_type, name, tag='', branch='', - ref_hash='', required=True, path=EXTERNALS_NAME, - externals='', repo_path=None, from_submodule=False, + def url_for_repo_path(self, repo_path, repo_path_abs=None): + if repo_path_abs is not None: + return repo_path_abs + else: + return os.path.join(self._bare_root, repo_path) + + def create_section(self, repo_path, name, tag='', branch='', + ref_hash='', required=True, path=EXTERNALS_PATH, + sub_externals='', repo_path_abs=None, from_submodule=False, sparse='', nested=False): # pylint: disable=too-many-branches - """Create a config section with autofilling some items and handling - optional items. + """Create a config ExternalsDescription section with the given name. + + Autofills some items and handles some optional items. + repo_path_abs overrides repo_path (which is relative to the bare repo) + path is a subdir under repo_path to check out to. """ # pylint: disable=R0913 self._config.add_section(name) @@ -300,7 +301,7 @@ def create_section(self, repo_type, name, tag='', branch='', # from_submodules is incompatible with some other options, turn them off if (from_submodule and - ((repo_path is not None) or tag or ref_hash or branch)): + ((repo_path_abs is not None) or tag or ref_hash or branch)): printlog('create_section: "from_submodule" is incompatible with ' '"repo_url", "tag", "hash", and "branch" options;\n' 'Ignoring those options for {}'.format(name)) @@ -309,10 +310,7 @@ def create_section(self, repo_type, name, tag='', branch='', ref_hash = '' branch = '' - if repo_path is not None: - repo_url = repo_path - else: - repo_url = os.path.join('${MANIC_TEST_BARE_REPO_ROOT}', repo_type) + repo_url = self.url_for_repo_path(repo_path, repo_path_abs) if not from_submodule: self._config.set(name, ExternalsDescription.REPO_URL, repo_url) @@ -328,8 +326,9 @@ def create_section(self, repo_type, name, tag='', branch='', if ref_hash: self._config.set(name, ExternalsDescription.HASH, ref_hash) - if externals: - self._config.set(name, ExternalsDescription.EXTERNALS, externals) + if sub_externals: + self._config.set(name, ExternalsDescription.EXTERNALS, + sub_externals) if sparse: self._config.set(name, ExternalsDescription.SPARSE, sparse) @@ -337,10 +336,8 @@ def create_section(self, repo_type, name, tag='', branch='', if from_submodule: self._config.set(name, ExternalsDescription.SUBMODULE, "True") - def create_section_ext_only(self, name, - required=True, externals=CFG_SUB_NAME): - """Create a config section with autofilling some items and handling - optional items. + def create_section_reference_to_subexternal(self, name): + """Just a reference to another externals file. """ # pylint: disable=R0913 @@ -353,10 +350,9 @@ def create_section_ext_only(self, name, self._config.set(name, ExternalsDescription.REPO_URL, LOCAL_PATH_INDICATOR) - self._config.set(name, ExternalsDescription.REQUIRED, str(required)) + self._config.set(name, ExternalsDescription.REQUIRED, str(True)) - if externals: - self._config.set(name, ExternalsDescription.EXTERNALS, externals) + self._config.set(name, ExternalsDescription.EXTERNALS, CFG_SUB_NAME) def create_svn_external(self, name, tag='', branch=''): """Create a config section for an svn repository. @@ -364,7 +360,7 @@ def create_svn_external(self, name, tag='', branch=''): """ self._config.add_section(name) self._config.set(name, ExternalsDescription.PATH, - os.path.join(EXTERNALS_NAME, name)) + os.path.join(EXTERNALS_PATH, name)) self._config.set(name, ExternalsDescription.PROTOCOL, ExternalsDescription.PROTOCOL_SVN) @@ -379,65 +375,19 @@ def create_svn_external(self, name, tag='', branch=''): if branch: self._config.set(name, ExternalsDescription.BRANCH, branch) - @staticmethod - def create_branch(dest_dir, repo_name, branch, with_commit=False): - """Update a repository branch, and potentially the remote. - """ - # pylint: disable=R0913 - cwd = os.getcwd() - repo_root = os.path.join(dest_dir, EXTERNALS_NAME) - repo_root = os.path.join(repo_root, repo_name) - os.chdir(repo_root) - cmd = ['git', 'checkout', '-b', branch, ] - execute_subprocess(cmd) - if with_commit: - msg = 'start work on {0}'.format(branch) - with open(README_NAME, 'a') as handle: - handle.write(msg) - cmd = ['git', 'add', README_NAME, ] - execute_subprocess(cmd) - cmd = ['git', 'commit', '-m', msg, ] - execute_subprocess(cmd) - os.chdir(cwd) - - @staticmethod - def create_commit(dest_dir, repo_name, local_tracking_branch=None): - """Make a commit on whatever is currently checked out. - - This is used to test sync state changes from local commits on - detached heads and tracking branches. + def write_with_git_branch(self, dest_dir, name, branch, new_remote_repo_path=None): + """Update fields in our config and write it to disk. - """ - cwd = os.getcwd() - repo_root = os.path.join(dest_dir, EXTERNALS_NAME) - repo_root = os.path.join(repo_root, repo_name) - os.chdir(repo_root) - if local_tracking_branch: - cmd = ['git', 'checkout', '-b', local_tracking_branch, ] - execute_subprocess(cmd) - - msg = 'work on great new feature!' - with open(README_NAME, 'a') as handle: - handle.write(msg) - cmd = ['git', 'add', README_NAME, ] - execute_subprocess(cmd) - cmd = ['git', 'commit', '-m', msg, ] - execute_subprocess(cmd) - os.chdir(cwd) - - def update_branch(self, dest_dir, name, branch, repo_type=None, - filename=CFG_NAME): - """Update a repository branch, and potentially the remote. + name is the key of the ExternalsDescription in self._config to update. """ # pylint: disable=R0913 self._config.set(name, ExternalsDescription.BRANCH, branch) - if repo_type: - if repo_type == SIMPLE_LOCAL_ONLY_NAME: + if new_remote_repo_path: + if new_remote_repo_path == SIMPLE_LOCAL_ONLY_NAME: repo_url = SIMPLE_LOCAL_ONLY_NAME else: - repo_url = os.path.join('${MANIC_TEST_BARE_REPO_ROOT}', - repo_type) + repo_url = os.path.join(self._bare_root, new_remote_repo_path) self._config.set(name, ExternalsDescription.REPO_URL, repo_url) try: @@ -446,9 +396,9 @@ def update_branch(self, dest_dir, name, branch, repo_type=None, except BaseException: pass - self.write_config(dest_dir, filename) + self.write_config(dest_dir) - def update_svn_branch(self, dest_dir, name, branch, filename=CFG_NAME): + def write_with_svn_branch(self, dest_dir, name, branch): """Update a repository branch, and potentially the remote. """ # pylint: disable=R0913 @@ -460,11 +410,11 @@ def update_svn_branch(self, dest_dir, name, branch, filename=CFG_NAME): except BaseException: pass - self.write_config(dest_dir, filename) + self.write_config(dest_dir) - def update_tag(self, dest_dir, name, tag, repo_type=None, - filename=CFG_NAME, remove_branch=True): - """Update a repository tag, and potentially the remote + def write_with_tag_and_remote_repo(self, dest_dir, name, tag, new_remote_repo_path, + remove_branch=True): + """Update a repository tag and the remote. NOTE(bja, 2017-11) remove_branch=False should result in an overspecified external with both a branch and tag. This is @@ -474,8 +424,8 @@ def update_tag(self, dest_dir, name, tag, repo_type=None, # pylint: disable=R0913 self._config.set(name, ExternalsDescription.TAG, tag) - if repo_type: - repo_url = os.path.join('${MANIC_TEST_BARE_REPO_ROOT}', repo_type) + if new_remote_repo_path: + repo_url = os.path.join(self._bare_root, new_remote_repo_path) self._config.set(name, ExternalsDescription.REPO_URL, repo_url) try: @@ -485,10 +435,9 @@ def update_tag(self, dest_dir, name, tag, repo_type=None, except BaseException: pass - self.write_config(dest_dir, filename) + self.write_config(dest_dir) - def update_underspecify_branch_tag(self, dest_dir, name, - filename=CFG_NAME): + def write_without_branch_tag(self, dest_dir, name): """Update a repository protocol, and potentially the remote """ # pylint: disable=R0913 @@ -504,10 +453,9 @@ def update_underspecify_branch_tag(self, dest_dir, name, except BaseException: pass - self.write_config(dest_dir, filename) + self.write_config(dest_dir) - def update_underspecify_remove_url(self, dest_dir, name, - filename=CFG_NAME): + def write_without_repo_url(self, dest_dir, name): """Update a repository protocol, and potentially the remote """ # pylint: disable=R0913 @@ -517,22 +465,59 @@ def update_underspecify_remove_url(self, dest_dir, name, except BaseException: pass - self.write_config(dest_dir, filename) + self.write_config(dest_dir) - def update_protocol(self, dest_dir, name, protocol, repo_type=None, - filename=CFG_NAME): + def write_with_protocol(self, dest_dir, name, protocol, repo_path=None): """Update a repository protocol, and potentially the remote """ # pylint: disable=R0913 self._config.set(name, ExternalsDescription.PROTOCOL, protocol) - if repo_type: - repo_url = os.path.join('${MANIC_TEST_BARE_REPO_ROOT}', repo_type) + if repo_path: + repo_url = os.path.join(self._bare_root, repo_path) self._config.set(name, ExternalsDescription.REPO_URL, repo_url) - self.write_config(dest_dir, filename) + self.write_config(dest_dir) + +def _execute_checkout_in_dir(dirname, args, debug_env=''): + """Execute the checkout command in the appropriate repo dir with the + specified additional args. + args should be a list of strings. + debug_env shuld be a string of the form 'FOO=bar' or the empty string. + + Note that we are calling the command line processing and main + routines and not using a subprocess call so that we get code + coverage results! Note this means that environment variables are passed + to checkout_externals via os.environ; debug_env is just used to aid + manual reproducibility of a given call. + + Returns (overall_status, tree_status) + where overall_status is 0 for success, nonzero otherwise. + and tree_status is set if --status was passed in, None otherwise. + + Note this command executes the checkout command, it doesn't + necessarily do any checking out (e.g. if --status is passed in). + """ + cwd = os.getcwd() + + # Construct a command line for reproducibility; this command is not + # actually executed in the test. + os.chdir(dirname) + cmdline = ['--externals', CFG_NAME, ] + cmdline += args + manual_cmd = ('Running equivalent of:\n' + 'pushd {dirname}; ' + '{debug_env} /path/to/checkout_externals {args}'.format( + dirname=dirname, debug_env=debug_env, + args=' '.join(cmdline))) + printlog(manual_cmd) + options = checkout.commandline_arguments(cmdline) + overall_status, tree_status = checkout.main(options) + os.chdir(cwd) + return overall_status, tree_status + class BaseTestSysCheckout(unittest.TestCase): """Base class of reusable systems level test setup for checkout_externals @@ -543,6 +528,7 @@ class BaseTestSysCheckout(unittest.TestCase): # cryptic. # pylint: disable=invalid-name + # Command-line args for checkout_externals, used in execute_checkout_in_dir() status_args = ['--status'] checkout_args = [] optional_args = ['--optional'] @@ -574,423 +560,57 @@ def setUp(self): # path to the executable self._checkout = os.path.join(root_dir, 'checkout_externals') - # directory where we have test repositories - test_dir = os.path.join(root_dir, 'test') - self._bare_root = os.path.join(test_dir, BARE_REPO_ROOT_NAME) - self._bare_root = os.path.abspath(self._bare_root) - - # set into the environment so var will be expanded in externals files - os.environ[MANIC_TEST_BARE_REPO_ROOT] = self._bare_root + # directory where we have test repositories (which we will clone for + # tests) + self._bare_root = os.path.abspath( + os.path.join(root_dir, 'test', BARE_REPO_ROOT_NAME)) # set the input file generator - self._generator = GenerateExternalsDescriptionCfgV1() + self._generator = GenerateExternalsDescriptionCfgV1(self._bare_root) # set the input file generator for secondary externals - self._sub_generator = GenerateExternalsDescriptionCfgV1() + self._sub_generator = GenerateExternalsDescriptionCfgV1(self._bare_root) def tearDown(self): """Tear down for individual tests """ - # remove the env var we added in setup - del os.environ[MANIC_TEST_BARE_REPO_ROOT] - # return to our common starting point os.chdir(self._return_dir) - - def setup_test_repo(self, parent_repo_name, dest_dir_in=None): - """Setup the paths and clone the base test repo - - """ - # unique repo for this test - test_dir_name = self._test_id - print("Test repository name: {0}".format(test_dir_name)) - - parent_repo_dir = os.path.join(self._bare_root, parent_repo_name) - if dest_dir_in is None: - dest_dir = os.path.join(os.environ[MANIC_TEST_TMP_REPO_ROOT], - test_dir_name) - else: - dest_dir = dest_dir_in - - # pylint: disable=W0212 - GitRepository._git_clone(parent_repo_dir, dest_dir, VERBOSITY_DEFAULT) - return dest_dir - - @staticmethod - def _add_file_to_repo(under_test_dir, filename, tracked): - """Add a file to the repository so we can put it into a dirty state - - """ - cwd = os.getcwd() - os.chdir(under_test_dir) - with open(filename, 'w') as tmp: - tmp.write('Hello, world!') - - if tracked: - # NOTE(bja, 2018-01) brittle hack to obtain repo dir and - # file name - path_data = filename.split('/') - repo_dir = os.path.join(path_data[0], path_data[1]) - os.chdir(repo_dir) - tracked_file = path_data[2] - cmd = ['git', 'add', tracked_file] - execute_subprocess(cmd) - - os.chdir(cwd) + + # (in case this was set) Don't pollute environment of other tests. + os.environ.pop(MIXED_CONT_EXT_ROOT_ENV_VAR, + None) # Don't care if key wasn't set. + + def clone_test_repo(self, parent_repo_name, dest_dir_in=None): + """Clones repo under self._bare_root""" + return RepoUtils.clone_test_repo(self._bare_root, self._test_id, + parent_repo_name, dest_dir_in) + + def execute_checkout_in_dir(self, dirname, args, debug_env=''): + overall_status, tree_status = _execute_checkout_in_dir(dirname, args, + debug_env=debug_env) + self.assertEqual(overall_status, 0) + return tree_status + + def execute_checkout_with_status(self, dirname, args, debug_env=''): + """Calls checkout a second time to get status if needed.""" + tree_status = self.execute_checkout_in_dir( + dirname, args, debug_env=debug_env) + if tree_status is None: + tree_status = self.execute_checkout_in_dir(dirname, + self.status_args, + debug_env=debug_env) + self.assertNotEqual(tree_status, None) + return tree_status + + def _check_sync_clean(self, ext_status, expected_sync_state, + expected_clean_state): + self.assertEqual(ext_status.sync_state, expected_sync_state) + self.assertEqual(ext_status.clean_state, expected_clean_state) @staticmethod - def execute_cmd_in_dir(under_test_dir, args): - """Extecute the checkout command in the appropriate repo dir with the - specified additional args - - Note that we are calling the command line processing and main - routines and not using a subprocess call so that we get code - coverage results! - - """ - cwd = os.getcwd() - checkout_path = os.path.abspath('{0}/../../checkout_externals') - os.chdir(under_test_dir) - cmdline = ['--externals', CFG_NAME, ] - cmdline += args - repo_root = 'MANIC_TEST_BARE_REPO_ROOT={root}'.format( - root=os.environ[MANIC_TEST_BARE_REPO_ROOT]) - manual_cmd = ('Test cmd:\npushd {cwd}; {env} {checkout} {args}'.format( - cwd=under_test_dir, env=repo_root, checkout=checkout_path, - args=' '.join(cmdline))) - printlog(manual_cmd) - options = checkout.commandline_arguments(cmdline) - overall_status, tree_status = checkout.main(options) - os.chdir(cwd) - return overall_status, tree_status - - # ---------------------------------------------------------------- - # - # Check results for generic perturbation of states - # - # ---------------------------------------------------------------- - def _check_generic_empty_default_required(self, tree, name): - self.assertEqual(tree[name].sync_state, ExternalStatus.EMPTY) - self.assertEqual(tree[name].clean_state, ExternalStatus.DEFAULT) - self.assertEqual(tree[name].source_type, ExternalStatus.MANAGED) - - def _check_generic_ok_clean_required(self, tree, name): - self.assertEqual(tree[name].sync_state, ExternalStatus.STATUS_OK) - self.assertEqual(tree[name].clean_state, ExternalStatus.STATUS_OK) - self.assertEqual(tree[name].source_type, ExternalStatus.MANAGED) - - def _check_generic_ok_dirty_required(self, tree, name): - self.assertEqual(tree[name].sync_state, ExternalStatus.STATUS_OK) - self.assertEqual(tree[name].clean_state, ExternalStatus.DIRTY) - self.assertEqual(tree[name].source_type, ExternalStatus.MANAGED) - - def _check_generic_modified_ok_required(self, tree, name): - self.assertEqual(tree[name].sync_state, ExternalStatus.MODEL_MODIFIED) - self.assertEqual(tree[name].clean_state, ExternalStatus.STATUS_OK) - self.assertEqual(tree[name].source_type, ExternalStatus.MANAGED) - - def _check_generic_empty_default_optional(self, tree, name): - self.assertEqual(tree[name].sync_state, ExternalStatus.EMPTY) - self.assertEqual(tree[name].clean_state, ExternalStatus.DEFAULT) - self.assertEqual(tree[name].source_type, ExternalStatus.OPTIONAL) - - def _check_generic_ok_clean_optional(self, tree, name): - self.assertEqual(tree[name].sync_state, ExternalStatus.STATUS_OK) - self.assertEqual(tree[name].clean_state, ExternalStatus.STATUS_OK) - self.assertEqual(tree[name].source_type, ExternalStatus.OPTIONAL) - - # ---------------------------------------------------------------- - # - # Check results for individual named externals - # - # ---------------------------------------------------------------- - def _check_simple_tag_empty(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_tag'.format(directory) - self._check_generic_empty_default_required(tree, name) - - def _check_nested_tag_empty(self, tree, name=EXTERNALS_NAME): - self._check_generic_empty_default_required(tree, name) - - def _check_simple_tag_ok(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_tag'.format(directory) - self._check_generic_ok_clean_required(tree, name) - - def _check_nested_tag_ok(self, tree, name=EXTERNALS_NAME): - self._check_generic_ok_clean_required(tree, name) - - def _check_simple_tag_dirty(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_tag'.format(directory) - self._check_generic_ok_dirty_required(tree, name) - - def _check_simple_tag_modified(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_tag'.format(directory) - self._check_generic_modified_ok_required(tree, name) - - def _check_simple_branch_empty(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_branch'.format(directory) - self._check_generic_empty_default_required(tree, name) - - def _check_nested_branch_empty(self, tree, name=EXTERNALS_NAME): - self._check_generic_empty_default_required(tree, name) - - def _check_simple_branch_ok(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_branch'.format(directory) - self._check_generic_ok_clean_required(tree, name) - - def _check_nested_branch_ok(self, tree, name=EXTERNALS_NAME): - self._check_generic_ok_clean_required(tree, name) - - def _check_simple_branch_modified(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_branch'.format(directory) - self._check_generic_modified_ok_required(tree, name) - - def _check_simple_hash_empty(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_hash'.format(directory) - self._check_generic_empty_default_required(tree, name) - - def _check_nested_hash_empty(self, tree, name=EXTERNALS_NAME): - self._check_generic_empty_default_required(tree, name) - - def _check_simple_hash_ok(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_hash'.format(directory) - self._check_generic_ok_clean_required(tree, name) - - def _check_nested_hash_ok(self, tree, name=EXTERNALS_NAME): - self._check_generic_ok_clean_required(tree, name) - - def _check_simple_hash_modified(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_hash'.format(directory) - self._check_generic_modified_ok_required(tree, name) - - def _check_simple_req_empty(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_req'.format(directory) - self._check_generic_empty_default_required(tree, name) - - def _check_simple_req_ok(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_req'.format(directory) - self._check_generic_ok_clean_required(tree, name) - - def _check_simple_opt_empty(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_opt'.format(directory) - self._check_generic_empty_default_optional(tree, name) - - def _check_simple_opt_ok(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_opt'.format(directory) - self._check_generic_ok_clean_optional(tree, name) - - def _check_mixed_ext_branch_empty(self, tree, directory=EXTERNALS_NAME): - name = './{0}/mixed_req'.format(directory) - self._check_generic_empty_default_required(tree, name) - - def _check_mixed_ext_branch_ok(self, tree, directory=EXTERNALS_NAME): - name = './{0}/mixed_req'.format(directory) - self._check_generic_ok_clean_required(tree, name) - - def _check_mixed_ext_branch_modified(self, tree, directory=EXTERNALS_NAME): - name = './{0}/mixed_req'.format(directory) - self._check_generic_modified_ok_required(tree, name) - - def _check_simple_sparse_empty(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_sparse'.format(directory) - self._check_generic_empty_default_required(tree, name) - - def _check_simple_sparse_ok(self, tree, directory=EXTERNALS_NAME): - name = './{0}/simp_sparse'.format(directory) - self._check_generic_ok_clean_required(tree, name) - - # ---------------------------------------------------------------- - # - # Check results for groups of externals under specific conditions - # - # ---------------------------------------------------------------- - def _check_container_simple_required_pre_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_empty(tree) - self._check_simple_branch_empty(tree) - self._check_simple_hash_empty(tree) - - def _check_container_nested_required_pre_checkout(self, overall, tree, order): - self.assertEqual(overall, 0) - self._check_nested_tag_empty(tree, name=NESTED_NAME[order[0]]) - self._check_nested_branch_empty(tree, name=NESTED_NAME[order[1]]) - self._check_nested_hash_empty(tree, name=NESTED_NAME[order[2]]) - - def _check_container_simple_required_checkout(self, overall, tree): - # Note, this is the internal tree status just before checkout - self.assertEqual(overall, 0) - self._check_simple_tag_empty(tree) - self._check_simple_branch_empty(tree) - self._check_simple_hash_empty(tree) - - def _check_container_nested_required_checkout(self, overall, tree, order): - # Note, this is the internal tree status just before checkout - self.assertEqual(overall, 0) - self._check_nested_tag_empty(tree, name=NESTED_NAME[order[0]]) - self._check_nested_branch_empty(tree, name=NESTED_NAME[order[1]]) - self._check_nested_hash_empty(tree, name=NESTED_NAME[order[2]]) - - def _check_container_simple_required_post_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree) - self._check_simple_branch_ok(tree) - self._check_simple_hash_ok(tree) - - def _check_container_nested_required_post_checkout(self, overall, tree, order): - self.assertEqual(overall, 0) - self._check_nested_tag_ok(tree, name=NESTED_NAME[order[0]]) - self._check_nested_branch_ok(tree, name=NESTED_NAME[order[1]]) - self._check_nested_hash_ok(tree, name=NESTED_NAME[order[2]]) - - def _check_container_simple_required_out_of_sync(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_modified(tree) - self._check_simple_branch_modified(tree) - self._check_simple_hash_modified(tree) - - def _check_container_simple_optional_pre_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_req_empty(tree) - self._check_simple_opt_empty(tree) - - def _check_container_simple_optional_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_req_empty(tree) - self._check_simple_opt_empty(tree) - - def _check_container_simple_optional_post_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_req_ok(tree) - self._check_simple_opt_empty(tree) - - def _check_container_simple_optional_post_optional(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_req_ok(tree) - self._check_simple_opt_ok(tree) - - def _check_container_simple_required_sb_modified(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree) - self._check_simple_branch_modified(tree) - self._check_simple_hash_ok(tree) - - def _check_container_simple_optional_st_dirty(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_dirty(tree) - self._check_simple_branch_ok(tree) - - def _check_container_full_pre_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_empty(tree) - self._check_simple_branch_empty(tree) - self._check_simple_opt_empty(tree) - self._check_mixed_ext_branch_required_pre_checkout(overall, tree) - - def _check_container_component_post_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_opt_ok(tree) - self._check_simple_tag_empty(tree) - self._check_simple_branch_empty(tree) - - def _check_container_component_post_checkout2(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_empty(tree) - self._check_simple_branch_ok(tree) - - def _check_container_component_post_checkout3(self, overall, tree): - self.assertEqual(overall, 0) - self.assertFalse("simp_opt" in tree) - self._check_simple_tag_ok(tree) - self._check_simple_branch_ok(tree) - - def _check_container_full_post_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree) - self._check_simple_branch_ok(tree) - self._check_simple_opt_empty(tree) - self._check_mixed_ext_branch_required_post_checkout(overall, tree) - - def _check_container_full_pre_checkout_ext_change(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree) - self._check_simple_branch_ok(tree) - self._check_simple_opt_empty(tree) - self._check_mixed_ext_branch_required_pre_checkout_ext_change( - overall, tree) - - def _check_container_full_post_checkout_subext_modified( - self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree) - self._check_simple_branch_ok(tree) - self._check_simple_opt_empty(tree) - self._check_mixed_ext_branch_required_post_checkout_subext_modified( - overall, tree) - - def _check_mixed_ext_branch_required_pre_checkout(self, overall, tree): - # Note, this is the internal tree status just before checkout - self.assertEqual(overall, 0) - self._check_mixed_ext_branch_empty(tree, directory=EXTERNALS_NAME) - # NOTE: externals/mixed_req/src should not exist in the tree - # since this is the status before checkout of mixed_req. - - def _check_mixed_ext_branch_required_post_checkout(self, overall, tree): - # Note, this is the internal tree status just before checkout - self.assertEqual(overall, 0) - self._check_mixed_ext_branch_ok(tree, directory=EXTERNALS_NAME) - check_dir = "{0}/{1}/{2}".format(EXTERNALS_NAME, "mixed_req", - SUB_EXTERNALS_PATH) - self._check_simple_branch_ok(tree, directory=check_dir) - - def _check_mixed_ext_branch_required_pre_checkout_ext_change( - self, overall, tree): - # Note, this is the internal tree status just after change the - # externals description file, but before checkout - self.assertEqual(overall, 0) - self._check_mixed_ext_branch_modified(tree, directory=EXTERNALS_NAME) - check_dir = "{0}/{1}/{2}".format(EXTERNALS_NAME, "mixed_req", - SUB_EXTERNALS_PATH) - self._check_simple_branch_ok(tree, directory=check_dir) - - def _check_mixed_ext_branch_required_post_checkout_subext_modified( - self, overall, tree): - # Note, this is the internal tree status just after change the - # externals description file, but before checkout - self.assertEqual(overall, 0) - self._check_mixed_ext_branch_ok(tree, directory=EXTERNALS_NAME) - check_dir = "{0}/{1}/{2}".format(EXTERNALS_NAME, "mixed_req", - SUB_EXTERNALS_PATH) - self._check_simple_branch_modified(tree, directory=check_dir) - - def _check_mixed_cont_simple_required_pre_checkout(self, overall, tree): - # Note, this is the internal tree status just before checkout - self.assertEqual(overall, 0) - self._check_simple_tag_empty(tree, directory=EXTERNALS_NAME) - self._check_simple_branch_empty(tree, directory=EXTERNALS_NAME) - self._check_simple_branch_empty(tree, directory=SUB_EXTERNALS_PATH) - - def _check_mixed_cont_simple_required_checkout(self, overall, tree): - # Note, this is the internal tree status just before checkout - self.assertEqual(overall, 0) - self._check_simple_tag_empty(tree, directory=EXTERNALS_NAME) - self._check_simple_branch_empty(tree, directory=EXTERNALS_NAME) - self._check_simple_branch_empty(tree, directory=SUB_EXTERNALS_PATH) - - def _check_mixed_cont_simple_required_post_checkout(self, overall, tree): - # Note, this is the internal tree status just before checkout - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree, directory=EXTERNALS_NAME) - self._check_simple_branch_ok(tree, directory=EXTERNALS_NAME) - self._check_simple_branch_ok(tree, directory=SUB_EXTERNALS_PATH) - - def _check_container_sparse_pre_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_empty(tree) - self._check_simple_sparse_empty(tree) - - def _check_container_sparse_post_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree) - self._check_simple_sparse_ok(tree) - + def _external_path(section_name, base_path=EXTERNALS_PATH): + return './{0}/{1}'.format(base_path, section_name) + def _check_file_exists(self, repo_dir, pathname): "Check that exists in " self.assertTrue(os.path.exists(os.path.join(repo_dir, pathname))) @@ -999,9 +619,9 @@ def _check_file_absent(self, repo_dir, pathname): "Check that does not exist in " self.assertFalse(os.path.exists(os.path.join(repo_dir, pathname))) + class TestSysCheckout(BaseTestSysCheckout): """Run systems level tests of checkout_externals - """ # NOTE(bja, 2017-11) pylint complains about long method names, but # it is hard to differentiate tests without making them more @@ -1013,245 +633,431 @@ class TestSysCheckout(BaseTestSysCheckout): # Run systems tests # # ---------------------------------------------------------------- - def test_container_simple_required(self): - """Verify that a container with simple subrepos - generates the correct initial status. - - """ - # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) - - # status of empty repo - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_pre_checkout(overall, tree) - - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_checkout(overall, tree) - - # status clean checked out - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_post_checkout(overall, tree) - + def test_required_bytag(self): + """Check out a required external pointing to a git tag.""" + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, TAG_SECTION, + tag='tag1') + self._generator.write_config(cloned_repo_dir) + + # externals start out 'empty' aka not checked out. + tree = self.execute_checkout_in_dir(cloned_repo_dir, + self.status_args) + local_path_rel = self._external_path(TAG_SECTION) + self._check_sync_clean(tree[local_path_rel], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + local_path_abs = os.path.join(cloned_repo_dir, local_path_rel) + self.assertFalse(os.path.exists(local_path_abs)) + + # after checkout, the external is 'clean' aka at the correct version. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[local_path_rel], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + # Actually checked out the desired repo. + self.assertEqual('origin', GitRepository._remote_name_for_url( + # Which url to look up + self._generator.url_for_repo_path(SIMPLE_REPO), + # Which directory has the local checked-out repo. + dirname=local_path_abs)) + + # Actually checked out the desired tag. + (tag_found, tag_name) = GitRepository._git_current_tag(local_path_abs) + self.assertEqual(tag_name, 'tag1') + + # Check existence of some simp_tag files + tag_path = os.path.join('externals', TAG_SECTION) + self._check_file_exists(cloned_repo_dir, + os.path.join(tag_path, README_NAME)) + # Subrepo should not exist (not referenced by configs). + self._check_file_absent(cloned_repo_dir, os.path.join(tag_path, + 'simple_subdir', + 'subdir_file.txt')) + + def test_required_bybranch(self): + """Check out a required external pointing to a git branch.""" + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) + + # externals start out 'empty' aka not checked out. + tree = self.execute_checkout_in_dir(cloned_repo_dir, + self.status_args) + local_path_rel = self._external_path(BRANCH_SECTION) + self._check_sync_clean(tree[local_path_rel], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + local_path_abs = os.path.join(cloned_repo_dir, local_path_rel) + self.assertFalse(os.path.exists(local_path_abs)) + + # after checkout, the external is 'clean' aka at the correct version. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[local_path_rel], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self.assertTrue(os.path.exists(local_path_abs)) + + # Actually checked out the desired repo. + self.assertEqual('origin', GitRepository._remote_name_for_url( + # Which url to look up + self._generator.url_for_repo_path(SIMPLE_REPO), + # Which directory has the local checked-out repo. + dirname=local_path_abs)) + + # Actually checked out the desired branch. + (branch_found, branch_name) = GitRepository._git_current_remote_branch( + local_path_abs) + self.assertEquals(branch_name, 'origin/' + REMOTE_BRANCH_FEATURE2) + + def test_required_byhash(self): + """Check out a required external pointing to a git hash.""" + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, HASH_SECTION, + ref_hash='60b1cc1a38d63') + self._generator.write_config(cloned_repo_dir) + + # externals start out 'empty' aka not checked out. + tree = self.execute_checkout_in_dir(cloned_repo_dir, + self.status_args) + local_path_rel = self._external_path(HASH_SECTION) + self._check_sync_clean(tree[local_path_rel], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + local_path_abs = os.path.join(cloned_repo_dir, local_path_rel) + self.assertFalse(os.path.exists(local_path_abs)) + + # after checkout, the externals are 'clean' aka at their correct version. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[local_path_rel], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + # Actually checked out the desired repo. + self.assertEqual('origin', GitRepository._remote_name_for_url( + # Which url to look up + self._generator.url_for_repo_path(SIMPLE_REPO), + # Which directory has the local checked-out repo. + dirname=local_path_abs)) + + # Actually checked out the desired hash. + (hash_found, hash_name) = GitRepository._git_current_hash( + local_path_abs) + self.assertTrue(hash_name.startswith('60b1cc1a38d63'), + msg=hash_name) + def test_container_nested_required(self): - """Verify that a container with nested subrepos - generates the correct initial status. + """Verify that a container with nested subrepos generates the correct initial status. Tests over all possible permutations """ + # Output subdirs for each of the externals, to test that one external can be + # checked out in a subdir of another. + NESTED_SUBDIR = ['./fred', './fred/wilma', './fred/wilma/barney'] + # Assert that each type of external (e.g. tag vs branch) can be at any parent level + # (e.g. child/parent/grandparent). orders = [[0, 1, 2], [1, 2, 0], [2, 0, 1], [0, 2, 1], [2, 1, 0], [1, 0, 2]] for n, order in enumerate(orders): - # create repo - dest_dir = os.path.join(os.environ[MANIC_TEST_TMP_REPO_ROOT], - self._test_id, "test"+str(n)) - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME, - dest_dir_in=dest_dir) - self._generator.container_nested_required(under_test_dir, order) - - # status of empty repo - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_nested_required_pre_checkout(overall, tree, order) - - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_nested_required_checkout(overall, tree, order) - - # status clean checked out - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_nested_required_post_checkout(overall, tree, order) - + dest_dir = os.path.join(module_tmp_root_dir, self._test_id, + "test"+str(n)) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO, + dest_dir_in=dest_dir) + self._generator.create_config() + # We happen to check out each section via a different reference (tag/branch/hash) but + # those don't really matter, we just need to check out three repos into a nested set of + # directories. + self._generator.create_section( + SIMPLE_REPO, TAG_SECTION, nested=True, + tag='tag1', path=NESTED_SUBDIR[order[0]]) + self._generator.create_section( + SIMPLE_REPO, BRANCH_SECTION, nested=True, + branch=REMOTE_BRANCH_FEATURE2, path=NESTED_SUBDIR[order[1]]) + self._generator.create_section( + SIMPLE_REPO, HASH_SECTION, nested=True, + ref_hash='60b1cc1a38d63', path=NESTED_SUBDIR[order[2]]) + self._generator.write_config(cloned_repo_dir) + + # all externals start out 'empty' aka not checked out. + tree = self.execute_checkout_in_dir(cloned_repo_dir, + self.status_args) + self._check_sync_clean(tree[NESTED_SUBDIR[order[0]]], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + self._check_sync_clean(tree[NESTED_SUBDIR[order[1]]], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + self._check_sync_clean(tree[NESTED_SUBDIR[order[2]]], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + + # after checkout, all the repos are 'clean'. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[NESTED_SUBDIR[order[0]]], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[NESTED_SUBDIR[order[1]]], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[NESTED_SUBDIR[order[2]]], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + def test_container_simple_optional(self): - """Verify that container with an optional simple subrepos - generates the correct initial status. + """Verify that container with an optional simple subrepos generates + the correct initial status. """ - # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_optional(under_test_dir) - - # check status of empty repo - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_optional_pre_checkout(overall, tree) - - # checkout required - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_optional_checkout(overall, tree) - - # status - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_optional_post_checkout(overall, tree) + # create repo and externals config. + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, 'simp_req', + tag='tag1') - # checkout optional - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.optional_args) - self._check_container_simple_optional_post_checkout(overall, tree) + self._generator.create_section(SIMPLE_REPO, 'simp_opt', + tag='tag1', required=False) - # status - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_optional_post_optional(overall, tree) + self._generator.write_config(cloned_repo_dir) + + # all externals start out 'empty' aka not checked out. + tree = self.execute_checkout_in_dir(cloned_repo_dir, + self.status_args) + req_status = tree[self._external_path('simp_req')] + self._check_sync_clean(req_status, + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + self.assertEqual(req_status.source_type, ExternalStatus.MANAGED) + + opt_status = tree[self._external_path('simp_opt')] + self._check_sync_clean(opt_status, + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + self.assertEqual(opt_status.source_type, ExternalStatus.OPTIONAL) + + # after checkout, required external is clean, optional is still empty. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + req_status = tree[self._external_path('simp_req')] + self._check_sync_clean(req_status, + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self.assertEqual(req_status.source_type, ExternalStatus.MANAGED) + + opt_status = tree[self._external_path('simp_opt')] + self._check_sync_clean(opt_status, + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + self.assertEqual(opt_status.source_type, ExternalStatus.OPTIONAL) + + # after checking out optionals, the optional external is also clean. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.optional_args) + req_status = tree[self._external_path('simp_req')] + self._check_sync_clean(req_status, + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self.assertEqual(req_status.source_type, ExternalStatus.MANAGED) + + opt_status = tree[self._external_path('simp_opt')] + self._check_sync_clean(opt_status, + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self.assertEqual(opt_status.source_type, ExternalStatus.OPTIONAL) def test_container_simple_verbose(self): - """Verify that container with simple subrepos runs with verbose status - output and generates the correct initial status. - + """Verify that verbose status matches non-verbose. """ - # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) - - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_checkout(overall, tree) - - # check verbose status - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.verbose_args) - self._check_container_simple_required_post_checkout(overall, tree) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, TAG_SECTION, + tag='tag1') + self._generator.write_config(cloned_repo_dir) + + # after checkout, all externals should be 'clean'. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + # 'Verbose' status should tell the same story. + tree = self.execute_checkout_in_dir(cloned_repo_dir, + self.verbose_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) def test_container_simple_dirty(self): - """Verify that a container with simple subrepos - and a dirty status exits gracefully. - + """Verify that a container with a new tracked file is marked dirty. """ - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) - - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_checkout(overall, tree) - - # add a file to the repo - tracked = True - self._add_file_to_repo(under_test_dir, 'externals/simp_tag/tmp.txt', - tracked) - - # checkout: pre-checkout status should be dirty, did not - # modify working copy. - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_optional_st_dirty(overall, tree) - - # verify status is still dirty - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_optional_st_dirty(overall, tree) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, TAG_SECTION, + tag='tag1') + self._generator.write_config(cloned_repo_dir) + + # checkout, should start out clean. + tree = self.execute_checkout_with_status(cloned_repo_dir, self.checkout_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + # add a tracked file to the simp_tag external, should be dirty. + RepoUtils.add_file_to_repo(cloned_repo_dir, + 'externals/{0}/tmp.txt'.format(TAG_SECTION), + tracked=True) + tree = self.execute_checkout_in_dir(cloned_repo_dir, self.status_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.DIRTY) + + # Re-checkout; simp_tag should still be dirty. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.DIRTY) def test_container_simple_untracked(self): """Verify that a container with simple subrepos and a untracked files is not considered 'dirty' and will attempt an update. """ - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) - - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_checkout(overall, tree) - - # add a file to the repo - tracked = False - self._add_file_to_repo(under_test_dir, 'externals/simp_tag/tmp.txt', - tracked) - - # checkout: pre-checkout status should be clean, ignoring the - # untracked file. - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_post_checkout(overall, tree) - - # verify status is still clean - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_post_checkout(overall, tree) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, TAG_SECTION, + tag='tag1') + self._generator.write_config(cloned_repo_dir) + + # checkout, should start out clean. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + # add an untracked file to the simp_tag external, should stay clean. + RepoUtils.add_file_to_repo(cloned_repo_dir, + 'externals/{0}/tmp.txt'.format(TAG_SECTION), + tracked=False) + tree = self.execute_checkout_in_dir(cloned_repo_dir, self.status_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + # After checkout, the external should still be 'clean'. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) def test_container_simple_detached_sync(self): """Verify that a container with simple subrepos generates the correct out of sync status when making commits from a detached head - state. + state. + For more info about 'detached head' state: https://www.cloudbees.com/blog/git-detached-head """ - # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) - - # status of empty repo - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_pre_checkout(overall, tree) - - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_checkout(overall, tree) - - # make a commit on the detached head of the tag and hash externals - self._generator.create_commit(under_test_dir, 'simp_tag') - self._generator.create_commit(under_test_dir, 'simp_hash') - self._generator.create_commit(under_test_dir, 'simp_branch') - - # status of repo, branch, tag and hash should all be out of sync! - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_out_of_sync(overall, tree) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, TAG_SECTION, + tag='tag1') + + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + + self._generator.create_section(SIMPLE_REPO, 'simp_hash', + ref_hash='60b1cc1a38d63') + + self._generator.write_config(cloned_repo_dir) + + # externals start out 'empty' aka not checked out. + tree = self.execute_checkout_in_dir(cloned_repo_dir, self.status_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + self._check_sync_clean(tree[self._external_path(HASH_SECTION)], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - # same pre-checkout out of sync status - self._check_container_simple_required_out_of_sync(overall, tree) - - # now status should be in-sync - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_post_checkout(overall, tree) + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) + + # Commit on top of the tag and hash (creating the detached head state in those two + # externals' repos) + # The branch commit does not create the detached head state, but here for completeness. + RepoUtils.create_commit(cloned_repo_dir, TAG_SECTION) + RepoUtils.create_commit(cloned_repo_dir, HASH_SECTION) + RepoUtils.create_commit(cloned_repo_dir, BRANCH_SECTION) + + # sync status of all three should be 'modified' (uncommitted changes) + # clean status is 'ok' (matches externals version) + tree = self.execute_checkout_in_dir(cloned_repo_dir, self.status_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.MODEL_MODIFIED, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.MODEL_MODIFIED, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path(HASH_SECTION)], + ExternalStatus.MODEL_MODIFIED, + ExternalStatus.STATUS_OK) + + # after checkout, all externals should be totally clean (no uncommitted changes, + # and matches externals version). + tree = self.execute_checkout_with_status(cloned_repo_dir, self.checkout_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path(HASH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) def test_container_remote_branch(self): """Verify that a container with remote branch change works """ - # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) - - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_checkout(overall, tree) - - # update the config file to point to a different remote with - # the same branch - self._generator.update_branch(under_test_dir, 'simp_branch', - REMOTE_BRANCH_FEATURE2, SIMPLE_FORK_NAME) - - # status of simp_branch should be out of sync - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_sb_modified(overall, tree) - - # checkout new externals - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_sb_modified(overall, tree) - - # status should be synced - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_post_checkout(overall, tree) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) + + # initial checkout + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) + + # update the branch external to point to a different remote with the same branch, + # then simp_branch should be out of sync + self._generator.write_with_git_branch(cloned_repo_dir, + name=BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2, + new_remote_repo_path=SIMPLE_FORK_REPO) + tree = self.execute_checkout_in_dir(cloned_repo_dir, self.status_args) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.MODEL_MODIFIED, + ExternalStatus.STATUS_OK) + + # checkout new externals, now simp_branch should be clean. + tree = self.execute_checkout_with_status(cloned_repo_dir, self.checkout_args) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) def test_container_remote_tag_same_branch(self): """Verify that a container with remote tag change works. The new tag @@ -1260,275 +1066,324 @@ def test_container_remote_tag_same_branch(self): the branch. """ - # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_checkout(overall, tree) + # initial checkout + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) # update the config file to point to a different remote with - # the tag instead of branch. Tag MUST NOT be in the original - # repo! - self._generator.update_tag(under_test_dir, 'simp_branch', - 'forked-feature-v1', SIMPLE_FORK_NAME) - - # status of simp_branch should be out of sync - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_sb_modified(overall, tree) - - # checkout new externals - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_sb_modified(overall, tree) - - # status should be synced - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_post_checkout(overall, tree) + # the new tag replacing the old branch. Tag MUST NOT be in the original + # repo! status of simp_branch should then be out of sync + self._generator.write_with_tag_and_remote_repo(cloned_repo_dir, BRANCH_SECTION, + tag='forked-feature-v1', + new_remote_repo_path=SIMPLE_FORK_REPO) + tree = self.execute_checkout_in_dir(cloned_repo_dir, + self.status_args) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.MODEL_MODIFIED, + ExternalStatus.STATUS_OK) + + # checkout new externals, then should be synced. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) def test_container_remote_tag_fetch_all(self): """Verify that a container with remote tag change works. The new tag should not be in the original repo, only the new remote - fork. It should also not be on a branch that will be fetch, + fork. It should also not be on a branch that will be fetched, and therefore not fetched by default with 'git fetch'. It will - only be retreived by 'git fetch --tags' - + only be retrieved by 'git fetch --tags' """ - # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_checkout(overall, tree) + # initial checkout + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) # update the config file to point to a different remote with - # the tag instead of branch. Tag MUST NOT be in the original - # repo! - self._generator.update_tag(under_test_dir, 'simp_branch', - 'abandoned-feature', SIMPLE_FORK_NAME) - - # status of simp_branch should be out of sync - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_sb_modified(overall, tree) - - # checkout new externals - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_sb_modified(overall, tree) - - # status should be synced - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_post_checkout(overall, tree) + # the new tag instead of the old branch. Tag MUST NOT be in the original + # repo! status of simp_branch should then be out of sync. + self._generator.write_with_tag_and_remote_repo(cloned_repo_dir, BRANCH_SECTION, + tag='abandoned-feature', + new_remote_repo_path=SIMPLE_FORK_REPO) + tree = self.execute_checkout_in_dir(cloned_repo_dir, self.status_args) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.MODEL_MODIFIED, + ExternalStatus.STATUS_OK) + + # checkout new externals, should be clean again. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) def test_container_preserve_dot(self): """Verify that after inital checkout, modifying an external git repo url to '.' and the current branch will leave it unchanged. """ - # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_required_checkout(overall, tree) + # initial checkout + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) # update the config file to point to a different remote with - # the same branch - self._generator.update_branch(under_test_dir, 'simp_branch', - REMOTE_BRANCH_FEATURE2, SIMPLE_FORK_NAME) - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - - # verify status is clean and unmodified - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_post_checkout(overall, tree) + # the same branch. + self._generator.write_with_git_branch(cloned_repo_dir, name=BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2, + new_remote_repo_path=SIMPLE_FORK_REPO) + # after checkout, should be clean again. + tree = self.execute_checkout_with_status(cloned_repo_dir, self.checkout_args) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) # update branch to point to a new branch that only exists in # the local fork - self._generator.create_branch(under_test_dir, 'simp_branch', - 'private-feature', with_commit=True) - self._generator.update_branch(under_test_dir, 'simp_branch', - 'private-feature', - SIMPLE_LOCAL_ONLY_NAME) - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - - # verify status is clean and unmodified - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_required_post_checkout(overall, tree) - - def test_container_full(self): - """Verify that 'full' container with simple and mixed subrepos - generates the correct initial status. + RepoUtils.create_branch(cloned_repo_dir, external_name=BRANCH_SECTION, + branch='private-feature', with_commit=True) + self._generator.write_with_git_branch(cloned_repo_dir, name=BRANCH_SECTION, + branch='private-feature', + new_remote_repo_path=SIMPLE_LOCAL_ONLY_NAME) + # after checkout, should be clean again. + tree = self.execute_checkout_with_status(cloned_repo_dir, self.checkout_args) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + def test_container_mixed_subrepo(self): + """Verify container with mixed subrepo. The mixed subrepo has a sub-externals file with different sub-externals on different branches. """ - # create the test repository - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - - # create the top level externals file - self._generator.container_full(under_test_dir) - - # inital checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_full_pre_checkout(overall, tree) - - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_full_post_checkout(overall, tree) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) - # Check existance of some files - subrepo_path = os.path.join('externals', 'simp_tag') - self._check_file_exists(under_test_dir, - os.path.join(subrepo_path, 'readme.txt')) - self._check_file_absent(under_test_dir, os.path.join(subrepo_path, - 'simple_subdir', - 'subdir_file.txt')) - - # update the mixed-use repo to point to different branch - self._generator.update_branch(under_test_dir, 'mixed_req', - 'new-feature', MIXED_REPO_NAME) - - # check status out of sync for mixed_req, but sub-externals + self._generator.create_config() + self._generator.create_section(MIXED_REPO, 'mixed_req', + branch='master', sub_externals=CFG_SUB_NAME) + self._generator.write_config(cloned_repo_dir) + + # The subrepo has a repo_url that uses this environment variable. + # It'll be cleared in tearDown(). + os.environ[MIXED_CONT_EXT_ROOT_ENV_VAR] = self._bare_root + debug_env = MIXED_CONT_EXT_ROOT_ENV_VAR + '=' + self._bare_root + + # inital checkout: all requireds are clean, and optional is empty. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args, + debug_env=debug_env) + mixed_req_path = self._external_path('mixed_req') + self._check_sync_clean(tree[mixed_req_path], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + sub_ext_base_path = "{0}/{1}/{2}".format(EXTERNALS_PATH, 'mixed_req', SUB_EXTERNALS_PATH) + # The already-checked-in subexternals file has a 'simp_branch' section + self._check_sync_clean(tree[self._external_path('simp_branch', base_path=sub_ext_base_path)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + # update the mixed-use external to point to different branch + # status should become out of sync for mixed_req, but sub-externals # are still in sync - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_full_pre_checkout_ext_change(overall, tree) - - # run the checkout. Now the mixed use external and it's - # sub-exterals should be changed. Returned status is - # pre-checkout! - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_full_pre_checkout_ext_change(overall, tree) - - # check status out of sync for mixed_req, and sub-externals - # are in sync. - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_full_post_checkout(overall, tree) - + self._generator.write_with_git_branch(cloned_repo_dir, name='mixed_req', + branch='new-feature', + new_remote_repo_path=MIXED_REPO) + tree = self.execute_checkout_in_dir(cloned_repo_dir, self.status_args, + debug_env=debug_env) + self._check_sync_clean(tree[mixed_req_path], + ExternalStatus.MODEL_MODIFIED, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path('simp_branch', base_path=sub_ext_base_path)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + # run the checkout. Now the mixed use external and its sub-externals should be clean. + tree = self.execute_checkout_with_status(cloned_repo_dir, self.checkout_args, + debug_env=debug_env) + self._check_sync_clean(tree[mixed_req_path], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path('simp_branch', base_path=sub_ext_base_path)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + def test_container_component(self): """Verify that optional component checkout works """ - # create the test repository - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) # create the top level externals file - self._generator.container_full(under_test_dir) - - # inital checkout, first try a nonexistant component argument noref + self._generator.create_config() + # Optional external, by tag. + self._generator.create_section(SIMPLE_REPO, 'simp_opt', + tag='tag1', required=False) + + # Required external, by branch. + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + + # Required external, by hash. + self._generator.create_section(SIMPLE_REPO, HASH_SECTION, + ref_hash='60b1cc1a38d63') + self._generator.write_config(cloned_repo_dir) + + # inital checkout, first try a nonexistent component argument noref checkout_args = ['simp_opt', 'noref'] checkout_args.extend(self.checkout_args) with self.assertRaises(RuntimeError): - self.execute_cmd_in_dir(under_test_dir, checkout_args) + self.execute_checkout_in_dir(cloned_repo_dir, checkout_args) + # Now explicitly check out one optional component.. + # Explicitly listed component (opt) should be present, the other two not. checkout_args = ['simp_opt'] checkout_args.extend(self.checkout_args) + tree = self.execute_checkout_with_status(cloned_repo_dir, + checkout_args) + self._check_sync_clean(tree[self._external_path('simp_opt')], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + self._check_sync_clean(tree[self._external_path(HASH_SECTION)], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + + # Check out a second component, this one required. + # Explicitly listed component (branch) should be present, the still-unlisted one (tag) not. + checkout_args.append(BRANCH_SECTION) + tree = self.execute_checkout_with_status(cloned_repo_dir, + checkout_args) + self._check_sync_clean(tree[self._external_path('simp_opt')], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path(HASH_SECTION)], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) - overall, tree = self.execute_cmd_in_dir(under_test_dir, - checkout_args) - - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_component_post_checkout(overall, tree) - checkout_args.append('simp_branch') - overall, tree = self.execute_cmd_in_dir(under_test_dir, - checkout_args) - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_component_post_checkout2(overall, tree) def test_container_exclude_component(self): """Verify that exclude component checkout works """ - # create the test repository - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - - # create the top level externals file - self._generator.container_full(under_test_dir) - - # inital checkout, exclude simp_opt - checkout_args = ['--exclude', 'simp_opt'] + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, TAG_SECTION, + tag='tag1') + + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + + self._generator.create_section(SIMPLE_REPO, 'simp_hash', + ref_hash='60b1cc1a38d63') + + self._generator.write_config(cloned_repo_dir) + + # inital checkout should result in all externals being clean except excluded TAG_SECTION. + checkout_args = ['--exclude', TAG_SECTION] checkout_args.extend(self.checkout_args) - overall, tree = self.execute_cmd_in_dir(under_test_dir, checkout_args) - checkout_args.append("--status") - overall, tree = self.execute_cmd_in_dir(under_test_dir, checkout_args) - self._check_container_component_post_checkout3(overall, tree) - - def test_mixed_simple(self): - """Verify that a mixed use repo can serve as a 'full' container, - pulling in a set of externals and a seperate set of sub-externals. + tree = self.execute_checkout_with_status(cloned_repo_dir, checkout_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.EMPTY, + ExternalStatus.DEFAULT) + self._check_sync_clean(tree[self._external_path(BRANCH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path(HASH_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + def test_subexternal(self): + """Verify that an externals file can be brought in as a reference. """ - #import pdb; pdb.set_trace() - # create repository - under_test_dir = self.setup_test_repo(MIXED_REPO_NAME) - # create top level externals file - self._generator.mixed_simple_base(under_test_dir) - # NOTE: sub-externals file is already in the repo so we can - # switch branches during testing. Since this is a mixed-repo - # serving as the top level container repo, we can't switch - # during this test. + cloned_repo_dir = self.clone_test_repo(MIXED_REPO) - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_mixed_cont_simple_required_checkout(overall, tree) - - # verify status is clean and unmodified - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_mixed_cont_simple_required_post_checkout(overall, tree) + self._generator.create_config() + self._generator.create_section_reference_to_subexternal('mixed_base') + self._generator.write_config(cloned_repo_dir) + + # The subrepo has a repo_url that uses this environment variable. + # It'll be cleared in tearDown(). + os.environ[MIXED_CONT_EXT_ROOT_ENV_VAR] = self._bare_root + debug_env = MIXED_CONT_EXT_ROOT_ENV_VAR + '=' + self._bare_root + + # After checkout, confirm required's are clean and the referenced + # subexternal's contents are also clean. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args, + debug_env=debug_env) + + self._check_sync_clean( + tree[self._external_path(BRANCH_SECTION, base_path=SUB_EXTERNALS_PATH)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) def test_container_sparse(self): """Verify that 'full' container with simple subrepo can run a sparse checkout and generate the correct initial status. """ - # create the test repository - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - - # create the top level externals file - self._generator.container_sparse(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) - # inital checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_sparse_pre_checkout(overall, tree) - - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_sparse_post_checkout(overall, tree) + # Create a file to list filenames to checkout. + sparse_filename = 'sparse_checkout' + with open(os.path.join(cloned_repo_dir, sparse_filename), 'w') as sfile: + sfile.write(README_NAME) - # Check existance of some files - subrepo_path = os.path.join('externals', 'simp_tag') - self._check_file_exists(under_test_dir, - os.path.join(subrepo_path, 'readme.txt')) - self._check_file_exists(under_test_dir, os.path.join(subrepo_path, + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, TAG_SECTION, + tag='tag2') + + # Same tag as above, but with a sparse file too. + sparse_relpath = '../../' + sparse_filename + self._generator.create_section(SIMPLE_REPO, 'simp_sparse', + tag='tag2', sparse=sparse_relpath) + + self._generator.write_config(cloned_repo_dir) + + # inital checkout, confirm required's are clean. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._external_path('simp_sparse')], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + + # Check existence of some files - full set in TAG_SECTION, and sparse set + # in 'simp_sparse'. + subrepo_path = os.path.join('externals', TAG_SECTION) + self._check_file_exists(cloned_repo_dir, + os.path.join(subrepo_path, README_NAME)) + self._check_file_exists(cloned_repo_dir, os.path.join(subrepo_path, 'simple_subdir', 'subdir_file.txt')) subrepo_path = os.path.join('externals', 'simp_sparse') - self._check_file_exists(under_test_dir, - os.path.join(subrepo_path, 'readme.txt')) - self._check_file_absent(under_test_dir, os.path.join(subrepo_path, + self._check_file_exists(cloned_repo_dir, + os.path.join(subrepo_path, README_NAME)) + self._check_file_absent(cloned_repo_dir, os.path.join(subrepo_path, 'simple_subdir', 'subdir_file.txt')) @@ -1564,42 +1419,27 @@ class TestSysCheckoutSVN(BaseTestSysCheckout): """ - def _check_svn_branch_ok(self, tree, directory=EXTERNALS_NAME): - name = './{0}/svn_branch'.format(directory) - self._check_generic_ok_clean_required(tree, name) - - def _check_svn_branch_dirty(self, tree, directory=EXTERNALS_NAME): - name = './{0}/svn_branch'.format(directory) - self._check_generic_ok_dirty_required(tree, name) - - def _check_svn_tag_ok(self, tree, directory=EXTERNALS_NAME): - name = './{0}/svn_tag'.format(directory) - self._check_generic_ok_clean_required(tree, name) - - def _check_svn_tag_modified(self, tree, directory=EXTERNALS_NAME): - name = './{0}/svn_tag'.format(directory) - self._check_generic_modified_ok_required(tree, name) - - def _check_container_simple_svn_post_checkout(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree) - self._check_svn_branch_ok(tree) - self._check_svn_tag_ok(tree) - - def _check_container_simple_svn_sb_dirty_st_mod(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree) - self._check_svn_tag_modified(tree) - self._check_svn_branch_dirty(tree) + @staticmethod + def _svn_branch_name(): + return './{0}/svn_branch'.format(EXTERNALS_PATH) - def _check_container_simple_svn_sb_clean_st_mod(self, overall, tree): - self.assertEqual(overall, 0) - self._check_simple_tag_ok(tree) - self._check_svn_tag_modified(tree) - self._check_svn_branch_ok(tree) + @staticmethod + def _svn_tag_name(): + return './{0}/svn_tag'.format(EXTERNALS_PATH) + + def _check_tag_branch_svn_tag_clean(self, tree): + self._check_sync_clean(tree[self._external_path(TAG_SECTION)], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._svn_branch_name()], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) + self._check_sync_clean(tree[self._svn_tag_name()], + ExternalStatus.STATUS_OK, + ExternalStatus.STATUS_OK) @staticmethod - def have_svn_access(): + def _have_svn_access(): """Check if we have svn access so we can enable tests that use svn. """ @@ -1612,10 +1452,10 @@ def have_svn_access(): pass return have_svn - def skip_if_no_svn_access(self): + def _skip_if_no_svn_access(self): """Function decorator to disable svn tests when svn isn't available """ - have_svn = self.have_svn_access() + have_svn = self._have_svn_access() if not have_svn: raise unittest.SkipTest("No svn access") @@ -1623,60 +1463,55 @@ def test_container_simple_svn(self): """Verify that a container repo can pull in an svn branch and svn tag. """ - self.skip_if_no_svn_access() + self._skip_if_no_svn_access() # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_svn(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) + self._generator.create_config() + # Git repo. + self._generator.create_section(SIMPLE_REPO, TAG_SECTION, tag='tag1') - # verify status is clean and unmodified - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_svn_post_checkout(overall, tree) + # Svn repos. + self._generator.create_svn_external('svn_branch', branch='trunk') + self._generator.create_svn_external('svn_tag', tag='tags/cesm2.0.beta07') + + self._generator.write_config(cloned_repo_dir) + + # checkout, make sure all sections are clean. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_tag_branch_svn_tag_clean(tree) # update description file to make the tag into a branch and # trigger a switch - self._generator.update_svn_branch(under_test_dir, 'svn_tag', 'trunk') + self._generator.write_with_svn_branch(cloned_repo_dir, 'svn_tag', + 'trunk') - # checkout - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - - # verify status is clean and unmodified - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.status_args) - self._check_container_simple_svn_post_checkout(overall, tree) + # checkout, again the results should be clean. + tree = self.execute_checkout_with_status(cloned_repo_dir, + self.checkout_args) + self._check_tag_branch_svn_tag_clean(tree) # add an untracked file to the repo tracked = False - self._add_file_to_repo(under_test_dir, - 'externals/svn_branch/tmp.txt', tracked) + RepoUtils.add_file_to_repo(cloned_repo_dir, + 'externals/svn_branch/tmp.txt', tracked) - # run a no-op checkout: pre-checkout status should be clean, - # ignoring the untracked file. - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_svn_post_checkout(overall, tree) + # run a no-op checkout. + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) # update description file to make the branch into a tag and # trigger a modified sync status - self._generator.update_svn_branch(under_test_dir, 'svn_tag', - 'tags/cesm2.0.beta07') + self._generator.write_with_svn_branch(cloned_repo_dir, 'svn_tag', + 'tags/cesm2.0.beta07') - # checkout: pre-checkout status should be clean and modified, - # will modify working copy. - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.checkout_args) - self._check_container_simple_svn_sb_clean_st_mod(overall, tree) + self.execute_checkout_in_dir(cloned_repo_dir,self.checkout_args) # verify status is still clean and unmodified, last # checkout modified the working dir state. - overall, tree = self.execute_cmd_in_dir(under_test_dir, - self.verbose_args) - self._check_container_simple_svn_post_checkout(overall, tree) + tree = self.execute_checkout_in_dir(cloned_repo_dir, + self.verbose_args) + self._check_tag_branch_svn_tag_clean(tree) class TestSubrepoCheckout(BaseTestSysCheckout): # Need to store information at setUp time for checking @@ -1703,20 +1538,19 @@ def setUp(self): self._bare_branch_name = 'subrepo_branch' self._config_branch_name = 'subrepo_config_branch' self._container_extern_name = 'externals_container.cfg' - self._my_test_dir = os.path.join(os.environ[MANIC_TEST_TMP_REPO_ROOT], - self._test_id) + self._my_test_dir = os.path.join(module_tmp_root_dir, self._test_id) self._repo_dir = os.path.join(self._my_test_dir, self._test_repo_name) self._checkout_dir = 'repo_with_submodules' - check_dir = self.setup_test_repo(CONTAINER_REPO_NAME, + check_dir = self.clone_test_repo(CONTAINER_REPO, dest_dir_in=self._repo_dir) self.assertTrue(self._repo_dir == check_dir) # Add the submodules cwd = os.getcwd() - fork_repo_dir = os.path.join(self._bare_root, SIMPLE_FORK_NAME) - simple_repo_dir = os.path.join(self._bare_root, SIMPLE_REPO_NAME) - self._simple_ext_fork_name = os.path.splitext(SIMPLE_FORK_NAME)[0] + fork_repo_dir = os.path.join(self._bare_root, SIMPLE_FORK_REPO) + simple_repo_dir = os.path.join(self._bare_root, SIMPLE_REPO) + self._simple_ext_fork_name = os.path.splitext(SIMPLE_FORK_REPO)[0] self._simple_ext_name = os.path.join('sourc', - os.path.splitext(SIMPLE_REPO_NAME)[0]) + os.path.splitext(SIMPLE_REPO)[0]) os.chdir(self._repo_dir) # Add a branch with a subrepo cmd = ['git', 'branch', self._bare_branch_name, 'master'] @@ -1737,7 +1571,7 @@ def setUp(self): execute_subprocess(cmd) cmd = ['git', 'checkout', self._config_branch_name] execute_subprocess(cmd) - cmd = ['git', 'submodule', 'add', '--name', SIMPLE_REPO_NAME, + cmd = ['git', 'submodule', 'add', '--name', SIMPLE_REPO, simple_repo_dir, self._simple_ext_name] execute_subprocess(cmd) # Checkout feature2 @@ -1749,8 +1583,8 @@ def setUp(self): # Save the fork repo hash for comparison self._simple_hash_check = self.get_git_hash() os.chdir(self._repo_dir) - self.create_externals_file(filename=self._container_extern_name, - dest_dir=self._repo_dir, from_submodule=True) + self.write_externals_config(filename=self._container_extern_name, + dest_dir=self._repo_dir, from_submodule=True) cmd = ['git', 'add', self._container_extern_name] execute_subprocess(cmd) cmd = ['git', 'commit', '-am', "'Added simple-ext as a submodule'"] @@ -1767,9 +1601,10 @@ def get_git_hash(revision="HEAD"): git_out = execute_subprocess(cmd, output_to_caller=True) return git_out.strip() - def create_externals_file(self, name='', filename=CFG_NAME, dest_dir=None, - branch_name=None, sub_externals=None, - from_submodule=False): + def write_externals_config(self, name='', dest_dir=None, + filename=CFG_NAME, + branch_name=None, sub_externals=None, + from_submodule=False): # pylint: disable=too-many-arguments """Create a container externals file with only simple externals. @@ -1780,10 +1615,10 @@ def create_externals_file(self, name='', filename=CFG_NAME, dest_dir=None, dest_dir = self._my_test_dir if from_submodule: - self._generator.create_section(SIMPLE_FORK_NAME, + self._generator.create_section(SIMPLE_FORK_REPO, self._simple_ext_fork_name, from_submodule=True) - self._generator.create_section(SIMPLE_REPO_NAME, + self._generator.create_section(SIMPLE_REPO, self._simple_ext_name, branch='feature3', path='', from_submodule=False) @@ -1794,8 +1629,8 @@ def create_externals_file(self, name='', filename=CFG_NAME, dest_dir=None, self._generator.create_section(self._test_repo_name, self._checkout_dir, branch=branch_name, - path=name, externals=sub_externals, - repo_path=self._repo_dir) + path=name, sub_externals=sub_externals, + repo_path_abs=self._repo_dir) self._generator.write_config(dest_dir, filename=filename) @@ -1804,12 +1639,10 @@ def idempotence_check(self, checkout_dir): checkout_externals --status does not cause errors""" cwd = os.getcwd() os.chdir(checkout_dir) - overall, _ = self.execute_cmd_in_dir(self._my_test_dir, - self.checkout_args) - self.assertTrue(overall == 0) - overall, _ = self.execute_cmd_in_dir(self._my_test_dir, - self.status_args) - self.assertTrue(overall == 0) + self.execute_checkout_in_dir(self._my_test_dir, + self.checkout_args) + self.execute_checkout_in_dir(self._my_test_dir, + self.status_args) os.chdir(cwd) def test_submodule_checkout_bare(self): @@ -1821,17 +1654,17 @@ def test_submodule_checkout_bare(self): """ simple_ext_fork_tag = "(tag1)" simple_ext_fork_status = " " - self.create_externals_file(branch_name=self._bare_branch_name) - overall, _ = self.execute_cmd_in_dir(self._my_test_dir, - self.checkout_args) - self.assertTrue(overall == 0) + self.write_externals_config(branch_name=self._bare_branch_name) + self.execute_checkout_in_dir(self._my_test_dir, + self.checkout_args) cwd = os.getcwd() checkout_dir = os.path.join(self._my_test_dir, self._checkout_dir) fork_file = os.path.join(checkout_dir, self._simple_ext_fork_name, "readme.txt") self.assertTrue(os.path.exists(fork_file)) - os.chdir(checkout_dir) + submods = git_submodule_status(checkout_dir) + print('checking status of', checkout_dir, ':', submods) self.assertEqual(len(submods.keys()), 1) self.assertTrue(self._simple_ext_fork_name in submods) submod = submods[self._simple_ext_fork_name] @@ -1841,7 +1674,6 @@ def test_submodule_checkout_bare(self): self.assertEqual(submod['status'], simple_ext_fork_status) self.assertTrue('tag' in submod) self.assertEqual(submod['tag'], simple_ext_fork_tag) - os.chdir(cwd) self.idempotence_check(checkout_dir) def test_submodule_checkout_none(self): @@ -1850,11 +1682,10 @@ def test_submodule_checkout_none(self): externals cfg file. Correct behavior is the submodle is not checked out. """ - self.create_externals_file(branch_name=self._bare_branch_name, - sub_externals="none") - overall, _ = self.execute_cmd_in_dir(self._my_test_dir, - self.checkout_args) - self.assertTrue(overall == 0) + self.write_externals_config(branch_name=self._bare_branch_name, + sub_externals="none") + self.execute_checkout_in_dir(self._my_test_dir, + self.checkout_args) cwd = os.getcwd() checkout_dir = os.path.join(self._my_test_dir, self._checkout_dir) fork_file = os.path.join(checkout_dir, @@ -1872,11 +1703,10 @@ def test_submodule_checkout_config(self): # pylint: disable=too-many-locals """ tag_check = None # Not checked out as submodule status_check = "-" # Not checked out as submodule - self.create_externals_file(branch_name=self._config_branch_name, - sub_externals=self._container_extern_name) - overall, _ = self.execute_cmd_in_dir(self._my_test_dir, - self.checkout_args) - self.assertTrue(overall == 0) + self.write_externals_config(branch_name=self._config_branch_name, + sub_externals=self._container_extern_name) + self.execute_checkout_in_dir(self._my_test_dir, + self.checkout_args) cwd = os.getcwd() checkout_dir = os.path.join(self._my_test_dir, self._checkout_dir) fork_file = os.path.join(checkout_dir, @@ -1938,17 +1768,20 @@ def test_error_unknown_protocol(self): """ # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) # update the config file to point to a different remote with # the tag instead of branch. Tag MUST NOT be in the original # repo! - self._generator.update_protocol(under_test_dir, 'simp_branch', - 'this-protocol-does-not-exist') + self._generator.write_with_protocol(cloned_repo_dir, BRANCH_SECTION, + 'this-protocol-does-not-exist') with self.assertRaises(RuntimeError): - self.execute_cmd_in_dir(under_test_dir, self.checkout_args) + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) def test_error_switch_protocol(self): """Verify that a runtime error is raised when the user switches @@ -1959,15 +1792,18 @@ def test_error_switch_protocol(self): """ # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) # update the config file to point to a different remote with # the tag instead of branch. Tag MUST NOT be in the original # repo! - self._generator.update_protocol(under_test_dir, 'simp_branch', 'svn') + self._generator.write_with_protocol(cloned_repo_dir, BRANCH_SECTION, 'svn') with self.assertRaises(RuntimeError): - self.execute_cmd_in_dir(under_test_dir, self.checkout_args) + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) def test_error_unknown_tag(self): """Verify that a runtime error is raised when the user specified tag @@ -1975,17 +1811,21 @@ def test_error_unknown_tag(self): """ # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) # update the config file to point to a different remote with # the tag instead of branch. Tag MUST NOT be in the original # repo! - self._generator.update_tag(under_test_dir, 'simp_branch', - 'this-tag-does-not-exist', SIMPLE_REPO_NAME) + self._generator.write_with_tag_and_remote_repo(cloned_repo_dir, BRANCH_SECTION, + tag='this-tag-does-not-exist', + new_remote_repo_path=SIMPLE_REPO) with self.assertRaises(RuntimeError): - self.execute_cmd_in_dir(under_test_dir, self.checkout_args) + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) def test_error_overspecify_tag_branch(self): """Verify that a runtime error is raised when the user specified both @@ -1993,18 +1833,22 @@ def test_error_overspecify_tag_branch(self): """ # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) # update the config file to point to a different remote with # the tag instead of branch. Tag MUST NOT be in the original # repo! - self._generator.update_tag(under_test_dir, 'simp_branch', - 'this-tag-does-not-exist', SIMPLE_REPO_NAME, - remove_branch=False) + self._generator.write_with_tag_and_remote_repo(cloned_repo_dir, BRANCH_SECTION, + tag='this-tag-does-not-exist', + new_remote_repo_path=SIMPLE_REPO, + remove_branch=False) with self.assertRaises(RuntimeError): - self.execute_cmd_in_dir(under_test_dir, self.checkout_args) + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) def test_error_underspecify_tag_branch(self): """Verify that a runtime error is raised when the user specified @@ -2012,17 +1856,19 @@ def test_error_underspecify_tag_branch(self): """ # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) # update the config file to point to a different remote with # the tag instead of branch. Tag MUST NOT be in the original # repo! - self._generator.update_underspecify_branch_tag(under_test_dir, - 'simp_branch') + self._generator.write_without_branch_tag(cloned_repo_dir, BRANCH_SECTION) with self.assertRaises(RuntimeError): - self.execute_cmd_in_dir(under_test_dir, self.checkout_args) + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) def test_error_missing_url(self): """Verify that a runtime error is raised when the user specified @@ -2030,17 +1876,20 @@ def test_error_missing_url(self): """ # create repo - under_test_dir = self.setup_test_repo(CONTAINER_REPO_NAME) - self._generator.container_simple_required(under_test_dir) + cloned_repo_dir = self.clone_test_repo(CONTAINER_REPO) + self._generator.create_config() + self._generator.create_section(SIMPLE_REPO, BRANCH_SECTION, + branch=REMOTE_BRANCH_FEATURE2) + self._generator.write_config(cloned_repo_dir) # update the config file to point to a different remote with # the tag instead of branch. Tag MUST NOT be in the original # repo! - self._generator.update_underspecify_remove_url(under_test_dir, - 'simp_branch') + self._generator.write_without_repo_url(cloned_repo_dir, + BRANCH_SECTION) with self.assertRaises(RuntimeError): - self.execute_cmd_in_dir(under_test_dir, self.checkout_args) + self.execute_checkout_in_dir(cloned_repo_dir, self.checkout_args) if __name__ == '__main__': diff --git a/manage_externals/test/test_sys_repository_git.py b/manage_externals/test/test_sys_repository_git.py index 29d5433b95..7e5fb5020d 100644 --- a/manage_externals/test/test_sys_repository_git.py +++ b/manage_externals/test/test_sys_repository_git.py @@ -131,12 +131,12 @@ def tearDown(self): shutil.rmtree(self._tmpdir, ignore_errors=True) @staticmethod - def make_git_repo(): + def make_cwd_git_repo(): """Turn the current directory into an empty git repository""" execute_subprocess(['git', 'init']) @staticmethod - def add_git_commit(): + def add_cwd_git_commit(): """Add a git commit in the current directory""" with open('README', 'a') as myfile: myfile.write('more info') @@ -144,17 +144,17 @@ def add_git_commit(): execute_subprocess(['git', 'commit', '-m', 'my commit message']) @staticmethod - def checkout_git_branch(branchname): + def checkout_cwd_git_branch(branchname): """Checkout a new branch in the current directory""" execute_subprocess(['git', 'checkout', '-b', branchname]) @staticmethod - def make_git_tag(tagname): + def make_cwd_git_tag(tagname): """Make a lightweight tag at the current commit""" execute_subprocess(['git', 'tag', '-m', 'making a tag', tagname]) @staticmethod - def checkout_ref(refname): + def checkout_cwd_ref(refname): """Checkout the given refname in the current directory""" execute_subprocess(['git', 'checkout', refname]) @@ -164,72 +164,72 @@ def checkout_ref(refname): def test_currentHash_returnsHash(self): """Ensure that the _git_current_hash function returns a hash""" - self.make_git_repo() - self.add_git_commit() - hash_found, myhash = self._repo._git_current_hash() + self.make_cwd_git_repo() + self.add_cwd_git_commit() + hash_found, myhash = self._repo._git_current_hash(os.getcwd()) self.assertTrue(hash_found) self.assertIsHash(myhash) def test_currentHash_outsideGitRepo(self): """Ensure that the _git_current_hash function returns False when outside a git repository""" - hash_found, myhash = self._repo._git_current_hash() + hash_found, myhash = self._repo._git_current_hash(os.getcwd()) self.assertFalse(hash_found) self.assertEqual('', myhash) def test_currentBranch_onBranch(self): """Ensure that the _git_current_branch function returns the name of the branch""" - self.make_git_repo() - self.add_git_commit() - self.checkout_git_branch('foo') - branch_found, mybranch = self._repo._git_current_branch() + self.make_cwd_git_repo() + self.add_cwd_git_commit() + self.checkout_cwd_git_branch('foo') + branch_found, mybranch = self._repo._git_current_branch(os.getcwd()) self.assertTrue(branch_found) self.assertEqual('foo', mybranch) def test_currentBranch_notOnBranch(self): """Ensure that the _git_current_branch function returns False when not on a branch""" - self.make_git_repo() - self.add_git_commit() - self.make_git_tag('mytag') - self.checkout_ref('mytag') - branch_found, mybranch = self._repo._git_current_branch() + self.make_cwd_git_repo() + self.add_cwd_git_commit() + self.make_cwd_git_tag('mytag') + self.checkout_cwd_ref('mytag') + branch_found, mybranch = self._repo._git_current_branch(os.getcwd()) self.assertFalse(branch_found) self.assertEqual('', mybranch) def test_currentBranch_outsideGitRepo(self): """Ensure that the _git_current_branch function returns False when outside a git repository""" - branch_found, mybranch = self._repo._git_current_branch() + branch_found, mybranch = self._repo._git_current_branch(os.getcwd()) self.assertFalse(branch_found) self.assertEqual('', mybranch) def test_currentTag_onTag(self): """Ensure that the _git_current_tag function returns the name of the tag""" - self.make_git_repo() - self.add_git_commit() - self.make_git_tag('some_tag') - tag_found, mytag = self._repo._git_current_tag() + self.make_cwd_git_repo() + self.add_cwd_git_commit() + self.make_cwd_git_tag('some_tag') + tag_found, mytag = self._repo._git_current_tag(os.getcwd()) self.assertTrue(tag_found) self.assertEqual('some_tag', mytag) def test_currentTag_notOnTag(self): """Ensure tha the _git_current_tag function returns False when not on a tag""" - self.make_git_repo() - self.add_git_commit() - self.make_git_tag('some_tag') - self.add_git_commit() - tag_found, mytag = self._repo._git_current_tag() + self.make_cwd_git_repo() + self.add_cwd_git_commit() + self.make_cwd_git_tag('some_tag') + self.add_cwd_git_commit() + tag_found, mytag = self._repo._git_current_tag(os.getcwd()) self.assertFalse(tag_found) self.assertEqual('', mytag) def test_currentTag_outsideGitRepo(self): """Ensure that the _git_current_tag function returns False when outside a git repository""" - tag_found, mytag = self._repo._git_current_tag() + tag_found, mytag = self._repo._git_current_tag(os.getcwd()) self.assertFalse(tag_found) self.assertEqual('', mytag) diff --git a/manage_externals/test/test_unit_repository_git.py b/manage_externals/test/test_unit_repository_git.py index a6ad9f1003..1c01098acf 100644 --- a/manage_externals/test/test_unit_repository_git.py +++ b/manage_externals/test/test_unit_repository_git.py @@ -67,7 +67,7 @@ def setUp(self): def _git_current_branch(branch_found, branch_name): """Return a function that takes the place of repo._git_current_branch, which returns the given output.""" - def my_git_current_branch(): + def my_git_current_branch(dirname): """mock function that can take the place of repo._git_current_branch""" return branch_found, branch_name return my_git_current_branch @@ -76,7 +76,7 @@ def my_git_current_branch(): def _git_current_tag(tag_found, tag_name): """Return a function that takes the place of repo._git_current_tag, which returns the given output.""" - def my_git_current_tag(): + def my_git_current_tag(dirname): """mock function that can take the place of repo._git_current_tag""" return tag_found, tag_name return my_git_current_tag @@ -85,7 +85,7 @@ def my_git_current_tag(): def _git_current_hash(hash_found, hash_name): """Return a function that takes the place of repo._git_current_hash, which returns the given output.""" - def my_git_current_hash(): + def my_git_current_hash(dirname): """mock function that can take the place of repo._git_current_hash""" return hash_found, hash_name return my_git_current_hash @@ -102,7 +102,7 @@ def test_ref_branch(self): self._repo._git_current_tag = self._git_current_tag(True, 'foo_tag') self._repo._git_current_hash = self._git_current_hash(True, 'abc123') expected = 'foo_tag (branch feature3)' - result = self._repo._current_ref() + result = self._repo._current_ref(os.getcwd()) self.assertEqual(result, expected) def test_ref_detached_tag(self): @@ -112,7 +112,7 @@ def test_ref_detached_tag(self): self._repo._git_current_tag = self._git_current_tag(True, 'foo_tag') self._repo._git_current_hash = self._git_current_hash(True, 'abc123') expected = 'foo_tag' - result = self._repo._current_ref() + result = self._repo._current_ref(os.getcwd()) self.assertEqual(result, expected) def test_ref_detached_hash(self): @@ -123,7 +123,7 @@ def test_ref_detached_hash(self): self._repo._git_current_tag = self._git_current_tag(False, '') self._repo._git_current_hash = self._git_current_hash(True, 'abc123') expected = 'abc123' - result = self._repo._current_ref() + result = self._repo._current_ref(os.getcwd()) self.assertEqual(result, expected) def test_ref_none(self): @@ -132,7 +132,7 @@ def test_ref_none(self): self._repo._git_current_branch = self._git_current_branch(False, '') self._repo._git_current_tag = self._git_current_tag(False, '') self._repo._git_current_hash = self._git_current_hash(False, '') - result = self._repo._current_ref() + result = self._repo._current_ref(os.getcwd()) self.assertEqual(result, EMPTY_STR) @@ -206,11 +206,19 @@ def setUp(self): self._repo._current_ref = self._current_ref_empty self._create_tmp_git_dir() + # We have to override this class method rather than the self._repo + # instance method because it is called via + # GitRepository._remote_name_for_url, which is itself a @classmethod + # calls cls._git_remote_verbose(). + self._orignal_git_remote_verbose = GitRepository._git_remote_verbose + GitRepository._git_remote_verbose = self._git_remote_origin_upstream def tearDown(self): """Cleanup tmp stuff on the file system """ self._remove_tmp_git_dir() + GitRepository._git_remote_verbose = self._orignal_git_remote_verbose + def _create_tmp_git_dir(self): """Create a temporary fake git directory for testing purposes. """ @@ -227,29 +235,27 @@ def _remove_tmp_git_dir(self): # mock methods replacing git system calls # @staticmethod - def _current_ref_empty(): + def _current_ref_empty(dirname): """Return an empty string. + + Drop-in for GitRepository._current_ref """ return EMPTY_STR @staticmethod - def _git_remote_origin_upstream(): - """Return an info string that is a checkout hash - """ - return GIT_REMOTE_OUTPUT_ORIGIN_UPSTREAM + def _git_remote_origin_upstream(dirname): + """Return an info string that is a checkout hash. - @staticmethod - def _git_remote_none(): - """Return an info string that is a checkout hash + Drop-in for GitRepository._git_remote_verbose. """ - return EMPTY_STR + return GIT_REMOTE_OUTPUT_ORIGIN_UPSTREAM @staticmethod def _git_current_hash(myhash): """Return a function that takes the place of repo._git_current_hash, which returns the given hash """ - def my_git_current_hash(): + def my_git_current_hash(dirname): """mock function that can take the place of repo._git_current_hash""" return 0, myhash return my_git_current_hash @@ -263,7 +269,7 @@ def _git_revparse_commit(self, expected_ref, mystatus, myhash): status = 0 implies success, non-zero implies failure """ - def my_git_revparse_commit(ref): + def my_git_revparse_commit(ref, dirname): """mock function that can take the place of repo._git_revparse_commit""" self.assertEqual(expected_ref, ref) return mystatus, myhash @@ -291,9 +297,6 @@ def test_sync_dir_exist_no_git_info(self): """Test that a non-existent git repo returns an unknown status """ stat = ExternalStatus() - # Now we over-ride the _git_remote_verbose method on the repo to return - # a known value without requiring access to git. - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._tag = 'tag1' self._repo._git_current_hash = self._git_current_hash('') self._repo._git_revparse_commit = self._git_revparse_commit( @@ -313,7 +316,6 @@ def test_sync_invalid_reference(self): """Test that an invalid reference returns out-of-sync """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._tag = 'tag1' self._repo._git_current_hash = self._git_current_hash('abc123') self._repo._git_revparse_commit = self._git_revparse_commit( @@ -333,7 +335,6 @@ def test_sync_tag_on_same_hash(self): """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._tag = 'tag1' self._repo._git_current_hash = self._git_current_hash('abc123') self._repo._git_revparse_commit = self._git_revparse_commit( @@ -348,7 +349,6 @@ def test_sync_tag_on_different_hash(self): """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._tag = 'tag1' self._repo._git_current_hash = self._git_current_hash('def456') self._repo._git_revparse_commit = self._git_revparse_commit( @@ -368,7 +368,6 @@ def test_sync_hash_on_same_hash(self): """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._tag = '' self._repo._hash = 'abc' self._repo._git_current_hash = self._git_current_hash('abc123') @@ -384,7 +383,6 @@ def test_sync_hash_on_different_hash(self): """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._tag = '' self._repo._hash = 'abc' self._repo._git_current_hash = self._git_current_hash('def456') @@ -405,7 +403,6 @@ def test_sync_branch_on_same_hash(self): """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._branch = 'feature-2' self._repo._tag = '' self._repo._git_current_hash = self._git_current_hash('abc123') @@ -421,7 +418,6 @@ def test_sync_branch_on_diff_hash(self): """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._branch = 'feature-2' self._repo._tag = '' self._repo._git_current_hash = self._git_current_hash('abc123') @@ -433,11 +429,10 @@ def test_sync_branch_on_diff_hash(self): self.assertEqual(stat.clean_state, ExternalStatus.DEFAULT) def test_sync_branch_diff_remote(self): - """Test _determine_remote_name with a different remote + """Test _remote_name_for_url with a different remote """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._branch = 'feature-2' self._repo._tag = '' self._repo._url = '/path/to/other/repo' @@ -449,11 +444,10 @@ def test_sync_branch_diff_remote(self): # expected argument def test_sync_branch_diff_remote2(self): - """Test _determine_remote_name with a different remote + """Test _remote_name_for_url with a different remote """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._branch = 'feature-2' self._repo._tag = '' self._repo._url = '/path/to/local/repo2' @@ -469,7 +463,6 @@ def test_sync_branch_on_unknown_remote(self): """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._branch = 'feature-2' self._repo._tag = '' self._repo._url = '/path/to/unknown/repo' @@ -491,7 +484,6 @@ def test_sync_branch_on_untracked_local(self): """ stat = ExternalStatus() - self._repo._git_remote_verbose = self._git_remote_origin_upstream self._repo._branch = 'feature3' self._repo._tag = '' self._repo._url = '.' @@ -611,24 +603,20 @@ def setUp(self): self._repo = GitRepository('test', repo) @staticmethod - def _shell_true(url, remote=None): - _ = url - _ = remote + def _shell_true(*args, **kwargs): return 0 @staticmethod - def _shell_false(url, remote=None): - _ = url - _ = remote + def _shell_false(*args, **kwargs): return 1 @staticmethod - def _mock_function_true(ref): + def _mock_revparse_commit(ref, dirname): _ = ref return (TestValidRef._shell_true, '97ebc0e0deadc0de') @staticmethod - def _mock_function_false(ref): + def _mock_revparse_commit_false(ref, dirname): _ = ref return (TestValidRef._shell_false, '97ebc0e0deadc0de') @@ -638,10 +626,11 @@ def test_tag_not_tag_branch_commit(self): self._repo._git_showref_tag = self._shell_false self._repo._git_showref_branch = self._shell_false self._repo._git_lsremote_branch = self._shell_false - self._repo._git_revparse_commit = self._mock_function_false + self._repo._git_revparse_commit = self._mock_revparse_commit_false self._repo._tag = 'something' remote_name = 'origin' - received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name) + received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name, + os.getcwd()) self.assertFalse(received) def test_tag_not_tag(self): @@ -650,10 +639,11 @@ def test_tag_not_tag(self): self._repo._git_showref_tag = self._shell_false self._repo._git_showref_branch = self._shell_true self._repo._git_lsremote_branch = self._shell_true - self._repo._git_revparse_commit = self._mock_function_false + self._repo._git_revparse_commit = self._mock_revparse_commit_false self._repo._tag = 'tag1' remote_name = 'origin' - received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name) + received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name, + os.getcwd()) self.assertFalse(received) def test_tag_indeterminant(self): @@ -662,10 +652,11 @@ def test_tag_indeterminant(self): self._repo._git_showref_tag = self._shell_true self._repo._git_showref_branch = self._shell_true self._repo._git_lsremote_branch = self._shell_true - self._repo._git_revparse_commit = self._mock_function_true + self._repo._git_revparse_commit = self._mock_revparse_commit self._repo._tag = 'something' remote_name = 'origin' - received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name) + received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name, + os.getcwd()) self.assertFalse(received) def test_tag_is_unique(self): @@ -674,10 +665,11 @@ def test_tag_is_unique(self): self._repo._git_showref_tag = self._shell_true self._repo._git_showref_branch = self._shell_false self._repo._git_lsremote_branch = self._shell_false - self._repo._git_revparse_commit = self._mock_function_true + self._repo._git_revparse_commit = self._mock_revparse_commit self._repo._tag = 'tag1' remote_name = 'origin' - received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name) + received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name, + os.getcwd()) self.assertTrue(received) def test_tag_is_not_hash(self): @@ -686,10 +678,11 @@ def test_tag_is_not_hash(self): self._repo._git_showref_tag = self._shell_false self._repo._git_showref_branch = self._shell_false self._repo._git_lsremote_branch = self._shell_false - self._repo._git_revparse_commit = self._mock_function_true + self._repo._git_revparse_commit = self._mock_revparse_commit self._repo._tag = '97ebc0e0' remote_name = 'origin' - received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name) + received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name, + os.getcwd()) self.assertFalse(received) def test_hash_is_commit(self): @@ -698,10 +691,11 @@ def test_hash_is_commit(self): self._repo._git_showref_tag = self._shell_false self._repo._git_showref_branch = self._shell_false self._repo._git_lsremote_branch = self._shell_false - self._repo._git_revparse_commit = self._mock_function_true + self._repo._git_revparse_commit = self._mock_revparse_commit self._repo._tag = '97ebc0e0' remote_name = 'origin' - received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name) + received, _ = self._repo._is_unique_tag(self._repo._tag, remote_name, + os.getcwd()) self.assertFalse(received) @@ -746,13 +740,14 @@ def _shell_false(url, remote=None): return 1 @staticmethod - def _mock_function_false(ref): + def _mock_revparse_commit_false(ref, dirname): _ = ref return (TestValidRef._shell_false, '') @staticmethod - def _mock_function_true(ref): + def _mock_revparse_commit_true(ref, dirname): _ = ref + _ = dirname return (TestValidRef._shell_true, '') def test_valid_ref_is_invalid(self): @@ -761,10 +756,12 @@ def test_valid_ref_is_invalid(self): self._repo._git_showref_tag = self._shell_false self._repo._git_showref_branch = self._shell_false self._repo._git_lsremote_branch = self._shell_false - self._repo._git_revparse_commit = self._mock_function_false + self._repo._git_revparse_commit = self._mock_revparse_commit_false self._repo._tag = 'invalid_ref' with self.assertRaises(RuntimeError): - self._repo._check_for_valid_ref(self._repo._tag) + self._repo._check_for_valid_ref(self._repo._tag, + remote_name=None, + dirname=os.getcwd()) def test_valid_tag(self): """Verify a valid tag return true @@ -772,9 +769,11 @@ def test_valid_tag(self): self._repo._git_showref_tag = self._shell_true self._repo._git_showref_branch = self._shell_false self._repo._git_lsremote_branch = self._shell_false - self._repo._git_revparse_commit = self._mock_function_true + self._repo._git_revparse_commit = self._mock_revparse_commit_true self._repo._tag = 'tag1' - received = self._repo._check_for_valid_ref(self._repo._tag) + received = self._repo._check_for_valid_ref(self._repo._tag, + remote_name=None, + dirname=os.getcwd()) self.assertTrue(received) def test_valid_branch(self): @@ -783,24 +782,28 @@ def test_valid_branch(self): self._repo._git_showref_tag = self._shell_false self._repo._git_showref_branch = self._shell_true self._repo._git_lsremote_branch = self._shell_false - self._repo._git_revparse_commit = self._mock_function_true + self._repo._git_revparse_commit = self._mock_revparse_commit_true self._repo._tag = 'tag1' - received = self._repo._check_for_valid_ref(self._repo._tag) + received = self._repo._check_for_valid_ref(self._repo._tag, + remote_name=None, + dirname=os.getcwd()) self.assertTrue(received) def test_valid_hash(self): """Verify a valid hash return true """ - def _mock_revparse_commit(ref): + def _mock_revparse_commit_true(ref, dirname): _ = ref return (0, '56cc0b539426eb26810af9e') self._repo._git_showref_tag = self._shell_false self._repo._git_showref_branch = self._shell_false self._repo._git_lsremote_branch = self._shell_false - self._repo._git_revparse_commit = _mock_revparse_commit + self._repo._git_revparse_commit = _mock_revparse_commit_true self._repo._hash = '56cc0b5394' - received = self._repo._check_for_valid_ref(self._repo._hash) + received = self._repo._check_for_valid_ref(self._repo._hash, + remote_name=None, + dirname=os.getcwd()) self.assertTrue(received) diff --git a/manage_externals/test/test_unit_repository_svn.py b/manage_externals/test/test_unit_repository_svn.py old mode 100644 new mode 100755 index 41b173bf3d..d9309df7f6 --- a/manage_externals/test/test_unit_repository_svn.py +++ b/manage_externals/test/test_unit_repository_svn.py @@ -60,7 +60,7 @@ def setUp(self): self._name = 'component' rdata = {ExternalsDescription.PROTOCOL: 'svn', ExternalsDescription.REPO_URL: - 'https://svn-ccsm-models.cgd.ucar.edu/', + 'https://svn-ccsm-models.cgd.ucar.edu', ExternalsDescription.TAG: 'mosart/trunk_tags/mosart1_0_26', } diff --git a/py_env_create b/py_env_create index 59bf13c222..c323a374df 100755 --- a/py_env_create +++ b/py_env_create @@ -23,7 +23,7 @@ if [ $error != 0 ]; then exit -1 fi rm condahelp.txt -ctsm_python=ctsm_py +ctsm_python=ctsm_pylib condadir="$dir/python" diff --git a/python/Makefile b/python/Makefile index 4ea5fba85d..271e977046 100644 --- a/python/Makefile +++ b/python/Makefile @@ -22,8 +22,16 @@ PYLINT=pylint PYLINT_ARGS=-j 4 --rcfile=ctsm/.pylintrc PYLINT_SRC = \ ctsm +# NOTE: These don't pass pylint checking and should be added when we put into effort to get them to pass +# ../cime_config/SystemTests \ +# ../cime_config/buildlib \ +# ../cime_config/buildnml all: test lint black + @echo + @echo + @echo "Successfully ran all standard tests" + test: utest stest .PHONY: utest @@ -39,10 +47,16 @@ lint: FORCE $(PYLINT) $(PYLINT_ARGS) $(PYLINT_SRC) .PHONY: black -# Run black on all of the python files here and undeneath. +# Run the black check on all of the python files here and undeneath. # Use the black configure file to explicitly set a few things and specifiy the exact files. black: FORCE - black --check --config pyproject.toml . + black --check --config pyproject.toml . ../cime_config/SystemTests ../cime_config/buildlib ../cime_config/buildnml + +.PHONY: run_black +# Run black on all of the python files here and undeneath. +# Use the black configure file to explicitly set a few things and specifiy the exact files. +run_black: FORCE + black --config pyproject.toml . ../cime_config/SystemTests ../cime_config/buildlib ../cime_config/buildnml .PHONY: clean clean: FORCE diff --git a/python/README.md b/python/README.md index 910cc2ca2e..c40f55c6c7 100644 --- a/python/README.md +++ b/python/README.md @@ -16,7 +16,7 @@ You can also use the script in the top level directory to do all the conda commands and do this for you. ../py_env_create - conda activate ctsm_py + conda activate ctsm_pylib Conda requirements files: @@ -47,7 +47,8 @@ thing, but support different options: 2. via `./run_ctsm_py_tests` You can specify various arguments to this; run `./run_ctsm_py_tests - -h` for details + -h` for details. Please specify either --unit or --sys rather than + not including any arguments. In any configuration where you run the system tests, you need to first execute `module load nco`. diff --git a/python/conda_env_ctsm_py.txt b/python/conda_env_ctsm_py.txt index 8c110e8f3f..e621081591 100644 --- a/python/conda_env_ctsm_py.txt +++ b/python/conda_env_ctsm_py.txt @@ -1,20 +1,26 @@ # -# NOTE: Changes here should be coordinated with the cgd python environment file +# NOTE: Changes here should be coordinated with the cgd python environment file +# +# NOTE: Derecho already has conda installed for you, so you just need to do the following... # -# To install this on cheyenne with conda loaded in modules # use the top level bash script: -# ../py_env_create # Do this each time you update your CTSM Version -# conda activate ctsm_py # Do this anytime you want to run a CTSM python script +# ../py_env_create # Do this each time you update your CTSM Version +# conda activate ctsm_pylib # Do this anytime you want to run a CTSM python script # Or the individual conda commands: -# conda create -n ctsm_py # Do this one time for each machine -# conda install -n ctsm_py --file conda_env_ctsm_py.txt # Do this each time you update your CTSM Version -# conda activate ctsm_py # Do this anytime you want to run a CTSM python script +# conda create -n ctsm_pylib # Do this one time for each machine +# conda install -n ctsm_pylib --file conda_env_ctsm_py.txt # Do this each time you update your CTSM Version +# conda activate ctsm_pylib # Do this anytime you want to run a CTSM python script # python=3.7.9 +pandas +tqdm scipy netcdf4 requests +packaging numpy=1.18.5 -xarray=0.16.2 +xarray=0.17.0 +xesmf +numba=0.55.2 # Avoid 0.56 until numpy>=1.20. This is the minimum for xesmf pylint=2.8.3 -black=22.3.0 # NOTE: The version here needs to be coordinated with the black github action under ../.github/workflows +black=22.3.0 # NOTE: The version here needs to be coordinated with the black github action under ../.github/workflows diff --git a/python/conda_env_ctsm_py_cgd.txt b/python/conda_env_ctsm_py_cgd.txt index 45025506a8..3afcf4bba2 100644 --- a/python/conda_env_ctsm_py_cgd.txt +++ b/python/conda_env_ctsm_py_cgd.txt @@ -3,20 +3,22 @@ # # This should be coordinated with the main python environment file! # -# To install this on cheyenne with conda loaded in modules # use the top level bash script: -# ../py_env_create # Do this each time you update your CTSM Version -# conda activate ctsm_py # Do this anytime you want to run a CTSM python script +# ../py_env_create # Do this each time you update your CTSM Version +# conda activate ctsm_pylib # Do this anytime you want to run a CTSM python script # Or the individual conda commands: -# conda create -n ctsm_py # Do this one time for each machine -# conda install -n ctsm_py --file conda_env_ctsm_py.txt # Do this each time you update your CTSM Version -# conda activate ctsm_py # Do this anytime you want to run a CTSM python script +# conda create -n ctsm_pylib # Do this one time for each machine +# conda install -n ctsm_pylib --file conda_env_ctsm_py.txt # Do this each time you update your CTSM Version +# conda activate ctsm_pylib # Do this anytime you want to run a CTSM python script # python=3.7.0 # The python version MUST match the python version available on CGD systems through modules exactly +pandas +tqdm scipy netcdf4 requests +packaging numpy=1.18.5 -xarray=0.16.2 +xarray=0.17.0 pylint=2.8.3 black=22.3.0 # NOTE: The version here needs to be coordinated with the black github action under ../.github/workflows diff --git a/python/conda_env_ctsm_py_latest.txt b/python/conda_env_ctsm_py_latest.txt index a7a28c9fc3..cc442503c4 100644 --- a/python/conda_env_ctsm_py_latest.txt +++ b/python/conda_env_ctsm_py_latest.txt @@ -1,8 +1,12 @@ # This is a test python environment intended to represent the latest environment that can be built python>=3.9.13,<3.10 # Moving to 3.10 runs into conflicts +pandas>=1.5.1 +tqdm>=4.64.1 scipy netcdf4 requests +packaging +xesmf numpy>=1.23.0 xarray>=2022.3.0 pylint>=2.8.3,<2.9.0 # Once, you move off of 2.8.3, make lint shows 2 errors, at 2.11.1 there are 2 more errors beyond that diff --git a/python/ctsm/config_utils.py b/python/ctsm/config_utils.py index 847a1804f6..bd53825f14 100644 --- a/python/ctsm/config_utils.py +++ b/python/ctsm/config_utils.py @@ -86,6 +86,40 @@ def get_config_value( return val +def get_config_value_or_array( + config, + section, + item, + convert_to_type=None, +): + """Get a config value as a single value or as an array if it's expressed as an array + for cases when you don't know how it's going to be expressed""" + val = config.get(section, item) + vallist = val.split() + if convert_to_type is not None: + if ( + convert_to_type is not float + and convert_to_type is not int + and convert_to_type is not str + ): + abort( + "get_config_value_or_array can only have convert_to_type as float, int or str not " + + str(convert_to_type) + ) + is_list = bool(len(vallist) > 1) + + val = _handle_config_value( + var=val, + default=None, + item=item, + is_list=is_list, + convert_to_type=convert_to_type, + can_be_unset=False, + allowed_values=None, + ) + return val + + def _handle_config_value( var, default, item, is_list, convert_to_type, can_be_unset, allowed_values ): diff --git a/python/ctsm/crop_calendars/__init__.py b/python/ctsm/crop_calendars/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/ctsm/crop_calendars/check_rxboth_run.py b/python/ctsm/crop_calendars/check_rxboth_run.py new file mode 100644 index 0000000000..6dae071937 --- /dev/null +++ b/python/ctsm/crop_calendars/check_rxboth_run.py @@ -0,0 +1,123 @@ +# %% Setup + +import numpy as np +import sys, argparse +import cropcal_module as cc +import glob, os + + +def main(argv): + # Set arguments + parser = argparse.ArgumentParser(description="ADD DESCRIPTION HERE") + parser.add_argument( + "-d", "--directory", help="Directory with CLM output history files", required=True + ) + parser.add_argument( + "--rx_sdates_file", "--rx-sdates-file", help="Prescribed sowing dates file", required=True + ) + parser.add_argument( + "--rx_gdds_file", + "--rx-gdds-file", + help="Prescribed maturity requirements file", + required=True, + ) + parser.add_argument( + "-y1", + "--first_usable_year", + "--first-usable-year", + type=int, + help="First usable year in the outputs", + required=True, + ) + parser.add_argument( + "-yN", + "--last_usable_year", + "--last-usable-year", + type=int, + help="Last usable year in the outputs", + required=True, + ) + args = parser.parse_args(argv) + + # Note that _PERHARV will be stripped off upon import + myVars = [ + "GRAINC_TO_FOOD_PERHARV", + "GRAINC_TO_FOOD_ANN", + "SDATES", + "SDATES_PERHARV", + "SYEARS_PERHARV", + "HDATES", + "HYEARS", + "GDDHARV_PERHARV", + "GDDACCUM_PERHARV", + "HUI_PERHARV", + "SOWING_REASON_PERHARV", + "HARVEST_REASON_PERHARV", + ] + + annual_outfiles = glob.glob(os.path.join(args.directory, "*.clm2.h1.*.nc")) + + # These should be constant in a Prescribed Calendars (rxboth) run, as long as the inputs were + # static. + case = { + "constantVars": ["SDATES", "GDDHARV"], + "rx_sdates_file": args.rx_sdates_file, + "rx_gdds_file": args.rx_gdds_file, + } + + case["ds"] = cc.import_output( + annual_outfiles, + myVars=myVars, + y1=args.first_usable_year, + yN=args.last_usable_year, + ) + cc.check_constant_vars(case["ds"], case, ignore_nan=True, verbose=True, throw_error=True) + + # Import GGCMI sowing and harvest dates, and check sims + casename = "Prescribed Calendars" + gdd_min = None + if "rx_sdates_file" in case: + if case["rx_sdates_file"]: + case["rx_sdates_ds"] = cc.import_rx_dates("sdate", case["rx_sdates_file"], case["ds"]) + if case["rx_gdds_file"]: + case["rx_gdds_ds"] = cc.import_rx_dates("gdd", case["rx_gdds_file"], case["ds"]) + + # Equalize lons/lats + lonlat_tol = 1e-4 + for v in ["rx_sdates_ds", "rx_gdds_ds"]: + if v in case: + for l in ["lon", "lat"]: + max_diff_orig = np.max(np.abs(case[v][l].values - case["ds"][l].values)) + if max_diff_orig > lonlat_tol: + raise RuntimeError( + f"{v} {l} values differ too much ({max_diff_orig} > {lonlat_tol})" + ) + elif max_diff_orig > 0: + case[v] = case[v].assign_coords({l: case["ds"][l].values}) + max_diff = np.max(np.abs(case[v][l].values - case["ds"][l].values)) + print(f"{v} {l} max_diff {max_diff_orig} → {max_diff}") + else: + print(f"{v} {l} max_diff {max_diff_orig}") + + # Check + if case["rx_sdates_file"]: + cc.check_rx_obeyed( + case["ds"].vegtype_str.values, + case["rx_sdates_ds"].isel(time=0), + case["ds"], + casename, + "SDATES", + ) + if case["rx_gdds_file"]: + cc.check_rx_obeyed( + case["ds"].vegtype_str.values, + case["rx_gdds_ds"].isel(time=0), + case["ds"], + casename, + "GDDHARV", + gdd_min=gdd_min, + ) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/python/ctsm/crop_calendars/cropcal_figs_module.py b/python/ctsm/crop_calendars/cropcal_figs_module.py new file mode 100644 index 0000000000..8d7f472fec --- /dev/null +++ b/python/ctsm/crop_calendars/cropcal_figs_module.py @@ -0,0 +1,208 @@ +import numpy as np + +import cartopy.crs as ccrs +import matplotlib.pyplot as plt +import matplotlib.colors as mcolors +from matplotlib import cm +import matplotlib.collections as mplcol + + +# Colormaps (maps) +cropcal_colors = { + "seq_timeofyear": "twilight_shifted", + "seq_other": "plasma", # magma_r? CMRmap_r? + "div_yieldirr": "BrBG", + "div_timeofyear": "twilight_shifted", + "div_other_nonnorm": "PuOr_r", + "div_other_norm": "RdBu_r", + "underlay": [0.75, 0.75, 0.75, 1], + "underlay_lighter": [0.85, 0.85, 0.85, 1], + "underlay_lightest": [0.92, 0.92, 0.92, 1], +} + + +# Cases (line and scatter plots) +def cropcal_colors_cases(casename): + case_color_dict = { + "clm default": [x / 255 for x in [92, 219, 219]], + "prescribed calendars": [x / 255 for x in [250, 102, 240]], + "prescribed maturity": [x / 255 for x in [128, 0, 0]], + "prescribed sowing": [x / 255 for x in [133, 92, 255]], + } + case_color_dict["5.0 lu"] = case_color_dict["clm default"] + case_color_dict["5.2 lu"] = case_color_dict["prescribed calendars"] + + case_color = None + casename_for_colors = casename.lower().replace(" (0)", "").replace(" (1)", "") + if casename_for_colors in case_color_dict: + case_color = case_color_dict[casename_for_colors] + return case_color + + +def make_map( + ax, + this_map, + fontsize, + bounds=None, + cbar=None, + cbar_labelpad=4.0, + cbar_max=None, + cbar_spacing="uniform", + cmap=cropcal_colors["seq_other"], + extend_bounds="both", + extend_nonbounds="both", + linewidth=1.0, + lonlat_bin_width=None, + show_cbar=False, + subplot_label=None, + this_title=None, + ticklabels=None, + ticklocations=None, + underlay=None, + underlay_color=None, + units=None, + vmax=None, + vmin=None, + vrange=None, +): + if underlay is not None: + if underlay_color is None: + underlay_color = cropcal_colors["underlay"] + underlay_cmap = mcolors.ListedColormap(np.array([underlay_color, [1, 1, 1, 1]])) + ax.pcolormesh(underlay.lon.values, underlay.lat.values, underlay, cmap=underlay_cmap) + + if bounds is not None: + norm = mcolors.BoundaryNorm(bounds, cmap.N, extend=extend_bounds) + im = ax.pcolormesh( + this_map.lon.values, this_map.lat.values, this_map, shading="auto", norm=norm, cmap=cmap + ) + else: + im = ax.pcolormesh( + this_map.lon.values, + this_map.lat.values, + this_map, + shading="auto", + cmap=cmap, + vmin=vmin, + vmax=vmax, + ) + if vrange: + im.set_clim(vrange[0], vrange[1]) + ax.set_extent([-180, 180, -63, 90], crs=ccrs.PlateCarree()) + + if subplot_label is not None: + plt.text( + 0, 0.95, f"({subplot_label})", transform=ax.transAxes, fontsize=fontsize["axislabels"] + ) + + # # Country borders + # ax.add_feature(cfeature.BORDERS, linewidth=linewidth, edgecolor="white", alpha=0.5) + # ax.add_feature(cfeature.BORDERS, linewidth=linewidth*0.6, alpha=0.3) + + # Coastlines + ax.coastlines(linewidth=linewidth, color="white", alpha=0.5) + ax.coastlines(linewidth=linewidth * 0.6, alpha=0.3) + + if this_title: + ax.set_title(this_title, fontsize=fontsize["titles"]) + if show_cbar: + if cbar: + cbar.remove() + + if bounds is not None: + cbar = plt.colorbar( + cm.ScalarMappable(norm=norm, cmap=cmap), + ax=ax, + orientation="horizontal", + fraction=0.1, + pad=0.02, + spacing=cbar_spacing, + ) + else: + cbar = plt.colorbar( + im, + ax=ax, + orientation="horizontal", + fraction=0.1, + pad=0.02, + extend=extend_nonbounds, + spacing=cbar_spacing, + ) + + deal_with_ticklabels(cbar, cbar_max, ticklabels, ticklocations, units, im) + cbar.set_label( + label=units, + fontsize=fontsize["axislabels"], + verticalalignment="center", + labelpad=cbar_labelpad, + ) + cbar.ax.tick_params(labelsize=fontsize["ticklabels"]) + if units is not None and "month" in units.lower(): + cbar.ax.tick_params(length=0) + + if lonlat_bin_width: + set_ticks(lonlat_bin_width, fontsize, "y") + # set_ticks(lonlat_bin_width, fontsize, "x") + else: + # Need to do this for subplot row labels + set_ticks(-1, fontsize, "y") + plt.yticks([]) + for x in ax.spines: + ax.spines[x].set_visible(False) + + if show_cbar: + return im, cbar + else: + return im, None + + +def deal_with_ticklabels(cbar, cbar_max, ticklabels, ticklocations, units, im): + if ticklocations is not None: + cbar.set_ticks(ticklocations) + if units is not None and units.lower() == "month": + cbar.set_ticklabels( + ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + ) + units == "Month" + elif ticklabels is not None: + cbar.set_ticklabels(ticklabels) + if isinstance(im, mplcol.QuadMesh): + clim_max = im.get_clim()[1] + else: + clim_max = im + if cbar_max is not None and clim_max > cbar_max: + if ticklabels is not None: + raise RuntimeError( + "How to handle this now that you are specifying ticklocations separate from ticklabels?" + ) + ticks = cbar.get_ticks() + if ticks[-2] > cbar_max: + raise RuntimeError( + f"Specified cbar_max is {cbar_max} but highest bin BEGINS at {ticks[-2]}" + ) + ticklabels = ticks.copy() + ticklabels[-1] = cbar_max + for i, x in enumerate(ticklabels): + if x == int(x): + ticklabels[i] = str(int(x)) + cbar.set_ticks( + ticks + ) # Calling this before set_xticklabels() avoids "UserWarning: FixedFormatter should only be used together with FixedLocator" (https://stackoverflow.com/questions/63723514/userwarning-fixedformatter-should-only-be-used-together-with-fixedlocator) + cbar.set_ticklabels(ticklabels) + + +def set_ticks(lonlat_bin_width, fontsize, x_or_y): + if x_or_y == "x": + ticks = np.arange(-180, 181, lonlat_bin_width) + else: + ticks = np.arange(-60, 91, lonlat_bin_width) + + ticklabels = [str(x) for x in ticks] + for i, x in enumerate(ticks): + if x % 2: + ticklabels[i] = "" + + if x_or_y == "x": + plt.xticks(ticks, labels=ticklabels, fontsize=fontsize["ticklabels"]) + else: + plt.yticks(ticks, labels=ticklabels, fontsize=fontsize["ticklabels"]) diff --git a/python/ctsm/crop_calendars/cropcal_module.py b/python/ctsm/crop_calendars/cropcal_module.py new file mode 100644 index 0000000000..76c295974d --- /dev/null +++ b/python/ctsm/crop_calendars/cropcal_module.py @@ -0,0 +1,1266 @@ +import numpy as np +import xarray as xr +import warnings +import sys +import os +import glob + +# Import the CTSM Python utilities. +# sys.path.insert() is necessary for RXCROPMATURITY to work. The fact that it's calling this script in the RUN phase seems to require the python/ directory to be manually added to path. +_CTSM_PYTHON = os.path.join( + os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, os.pardir, "python" +) +sys.path.insert(1, _CTSM_PYTHON) +import ctsm.crop_calendars.cropcal_utils as utils + +try: + import pandas as pd +except: + pass + + +# Define conversion multipliers, {from: {to1, to2, ...}, ...} +multiplier_dict = { + # Mass + "g": { + "Mt": 1e-12, + }, + "t": { + "Mt": 1e-6, + }, + # Volume + "m3": { + "km3": 1e-9, + }, + # Yield + "g/m2": { + "t/ha": 1e-6 * 1e4, + }, +} + + +# After importing a file, restrict it to years of interest. +def check_and_trim_years(y1, yN, ds_in): + ### In annual outputs, file with name Y is actually results from year Y-1. + ### Note that time values refer to when it was SAVED. So 1981-01-01 is for year 1980. + + def get_year_from_cftime(cftime_date): + # Subtract 1 because the date for annual files is when it was SAVED + return cftime_date.year - 1 + + # Check that all desired years are included + if get_year_from_cftime(ds_in.time.values[0]) > y1: + raise RuntimeError( + f"Requested y1 is {y1} but first year in outputs is {get_year_from_cftime(ds_in.time.values[0])}" + ) + elif get_year_from_cftime(ds_in.time.values[-1]) < y1: + raise RuntimeError( + f"Requested yN is {yN} but last year in outputs is {get_year_from_cftime(ds_in.time.values[-1])}" + ) + + # Remove years outside range of interest + ### Include an extra year at the end to finish out final seasons. + ds_in = utils.safer_timeslice(ds_in, slice(f"{y1+1}-01-01", f"{yN+2}-01-01")) + + # Make sure you have the expected number of timesteps (including extra year) + Nyears_expected = yN - y1 + 2 + if ds_in.dims["time"] != Nyears_expected: + raise RuntimeError( + f"Expected {Nyears_expected} timesteps in output but got {ds_in.dims['time']}" + ) + + return ds_in + + +def open_lu_ds(filename, y1, yN, existing_ds, ungrid=True): + # Open and trim to years of interest + dsg = xr.open_dataset(filename).sel(time=slice(y1, yN)) + + # Assign actual lon/lat coordinates + dsg = dsg.assign_coords( + lon=("lsmlon", existing_ds.lon.values), lat=("lsmlat", existing_ds.lat.values) + ) + dsg = dsg.swap_dims({"lsmlon": "lon", "lsmlat": "lat"}) + + if "AREA" in dsg: + dsg["AREA_CFT"] = dsg.AREA * 1e6 * dsg.LANDFRAC_PFT * dsg.PCT_CROP / 100 * dsg.PCT_CFT / 100 + dsg["AREA_CFT"].attrs = {"units": "m2"} + dsg["AREA_CFT"].load() + else: + print("Warning: AREA missing from Dataset, so AREA_CFT will not be created") + + if not ungrid: + return dsg + + # Un-grid + query_ilons = [int(x) - 1 for x in existing_ds["patches1d_ixy"].values] + query_ilats = [int(x) - 1 for x in existing_ds["patches1d_jxy"].values] + query_ivts = [list(dsg.cft.values).index(x) for x in existing_ds["patches1d_itype_veg"].values] + + ds = xr.Dataset(attrs=dsg.attrs) + for v in ["AREA", "LANDFRAC_PFT", "PCT_CFT", "PCT_CROP", "AREA_CFT"]: + if v not in dsg: + continue + if "time" in dsg[v].dims: + new_coords = existing_ds["GRAINC_TO_FOOD_ANN"].coords + else: + new_coords = existing_ds["patches1d_lon"].coords + if "cft" in dsg[v].dims: + ds[v] = ( + dsg[v] + .isel( + lon=xr.DataArray(query_ilons, dims="patch"), + lat=xr.DataArray(query_ilats, dims="patch"), + cft=xr.DataArray(query_ivts, dims="patch"), + drop=True, + ) + .assign_coords(new_coords) + ) + else: + ds[v] = ( + dsg[v] + .isel( + lon=xr.DataArray(query_ilons, dims="patch"), + lat=xr.DataArray(query_ilats, dims="patch"), + drop=True, + ) + .assign_coords(new_coords) + ) + for v in existing_ds: + if "patches1d_" in v or "grid1d_" in v: + ds[v] = existing_ds[v] + ds["lon"] = dsg["lon"] + ds["lat"] = dsg["lat"] + + # Which crops are irrigated? + is_irrigated = np.full_like(ds["patches1d_itype_veg"], False) + for vegtype_str in np.unique(ds["patches1d_itype_veg_str"].values): + if "irrigated" not in vegtype_str: + continue + vegtype_int = utils.ivt_str2int(vegtype_str) + is_this_vegtype = np.where(ds["patches1d_itype_veg"].values == vegtype_int)[0] + is_irrigated[is_this_vegtype] = True + ["irrigated" in x for x in ds["patches1d_itype_veg_str"].values] + ds["IRRIGATED"] = xr.DataArray( + data=is_irrigated, + coords=ds["patches1d_itype_veg_str"].coords, + attrs={"long_name": "Is patch irrigated?"}, + ) + + # How much area is irrigated? + ds["IRRIGATED_AREA_CFT"] = ds["IRRIGATED"] * ds["AREA_CFT"] + ds["IRRIGATED_AREA_CFT"].attrs = { + "long name": "CFT area (irrigated types only)", + "units": "m^2", + } + ds["IRRIGATED_AREA_GRID"] = ( + ds["IRRIGATED_AREA_CFT"] + .groupby(ds["patches1d_gi"]) + .sum() + .rename({"patches1d_gi": "gridcell"}) + ) + ds["IRRIGATED_AREA_GRID"].attrs = {"long name": "Irrigated area in gridcell", "units": "m^2"} + + return ds + + +def check_constant_vars( + this_ds, case, ignore_nan, constantGSs=None, verbose=True, throw_error=True +): + if isinstance(case, str): + constantVars = [case] + elif isinstance(case, list): + constantVars = case + elif isinstance(case, dict): + constantVars = case["constantVars"] + else: + raise TypeError(f"case must be str or dict, not {type(case)}") + + if not constantVars: + return None + + if constantGSs: + gs0 = this_ds.gs.values[0] + gsN = this_ds.gs.values[-1] + if constantGSs.start > gs0 or constantGSs.stop < gsN: + print( + f"❗ Only checking constantVars over {constantGSs.start}-{constantGSs.stop} (run includes {gs0}-{gsN})" + ) + this_ds = this_ds.sel(gs=constantGSs) + + any_bad = False + any_bad_before_checking_rx = False + if throw_error: + emojus = "❌" + else: + emojus = "❗" + if not isinstance(constantVars, list): + constantVars = [constantVars] + + for v in constantVars: + ok = True + + if "gs" in this_ds[v].dims: + time_coord = "gs" + elif "time" in this_ds[v].dims: + time_coord = "time" + else: + raise RuntimeError(f"Which of these is the time coordinate? {this_ds[v].dims}") + i_time_coord = this_ds[v].dims.index(time_coord) + + this_da = this_ds[v] + ra_sp = np.moveaxis(this_da.copy().values, i_time_coord, 0) + incl_patches = [] + bad_patches = np.array([]) + strList = [] + + # Read prescription file, if needed + rx_ds = None + if isinstance(case, dict): + if v == "GDDHARV" and "rx_gdds_file" in case: + rx_ds = import_rx_dates( + "gdd", case["rx_gdds_file"], this_ds, set_neg1_to_nan=False + ).squeeze() + + for t1 in np.arange(this_ds.dims[time_coord] - 1): + condn = ~np.isnan(ra_sp[t1, ...]) + if t1 > 0: + condn = np.bitwise_and(condn, np.all(np.isnan(ra_sp[:t1, ...]), axis=0)) + thesePatches = np.where(condn)[0] + if thesePatches.size == 0: + continue + thesePatches = list(np.where(condn)[0]) + incl_patches += thesePatches + # print(f't1 {t1}: {thesePatches}') + + t1_yr = this_ds[time_coord].values[t1] + t1_vals = np.squeeze(this_da.isel({time_coord: t1, "patch": thesePatches}).values) + + for t in np.arange(t1 + 1, this_ds.dims[time_coord]): + t_yr = this_ds[time_coord].values[t] + t_vals = np.squeeze(this_da.isel({time_coord: t, "patch": thesePatches}).values) + ok_p = t1_vals == t_vals + + # If allowed, ignore where either t or t1 is NaN. Should only be used for runs where land use varies over time. + if ignore_nan: + ok_p = np.squeeze(np.bitwise_or(ok_p, np.isnan(t1_vals + t_vals))) + + if not np.all(ok_p): + any_bad_before_checking_rx = True + bad_patches_thisT = list(np.where(np.bitwise_not(ok_p))[0]) + bad_patches = np.concatenate( + (bad_patches, np.array(thesePatches)[bad_patches_thisT]) + ) + if rx_ds: + found_in_rx = np.array([False for x in bad_patches]) + varyPatches = list(np.array(thesePatches)[bad_patches_thisT]) + varyLons = this_ds.patches1d_lon.values[bad_patches_thisT] + varyLats = this_ds.patches1d_lat.values[bad_patches_thisT] + varyCrops = this_ds.patches1d_itype_veg_str.values[bad_patches_thisT] + varyCrops_int = this_ds.patches1d_itype_veg.values[bad_patches_thisT] + + any_bad_anyCrop = False + for c in np.unique(varyCrops_int): + rx_var = f"gs1_{c}" + varyLons_thisCrop = varyLons[np.where(varyCrops_int == c)] + varyLats_thisCrop = varyLats[np.where(varyCrops_int == c)] + theseRxVals = np.diag( + rx_ds[rx_var].sel(lon=varyLons_thisCrop, lat=varyLats_thisCrop).values + ) + if len(theseRxVals) != len(varyLats_thisCrop): + raise RuntimeError( + f"Expected {len(varyLats_thisCrop)} rx values; got {len(theseRxVals)}" + ) + if not np.any(theseRxVals != -1): + continue + any_bad_anyCrop = True + break + if not any_bad_anyCrop: + continue + + # This bit is pretty inefficient, but I'm not going to optimize it until I actually need to use it. + for i, p in enumerate(bad_patches_thisT): + thisPatch = varyPatches[i] + thisLon = varyLons[i] + thisLat = varyLats[i] + thisCrop = varyCrops[i] + thisCrop_int = varyCrops_int[i] + + # If prescribed input had missing value (-1), it's fine for it to vary. + if rx_ds: + rx_var = f"gs1_{thisCrop_int}" + if thisLon in rx_ds.lon.values and thisLat in rx_ds.lat.values: + rx = rx_ds[rx_var].sel(lon=thisLon, lat=thisLat).values + Nunique = len(np.unique(rx)) + if Nunique == 1: + found_in_rx[i] = True + if rx == -1: + continue + elif Nunique > 1: + raise RuntimeError( + f"How does lon {thisLon} lat {thisLat} {thisCrop} have time-varying {v}?" + ) + else: + raise RuntimeError( + "lon {thisLon} lat {thisLat} {thisCrop} not in rx dataset?" + ) + + # Print info (or save to print later) + any_bad = True + if verbose: + thisStr = f" Patch {thisPatch} (lon {thisLon} lat {thisLat}) {thisCrop} ({thisCrop_int})" + if rx_ds and not found_in_rx[i]: + thisStr = thisStr.replace("(lon", "* (lon") + if not np.isnan(t1_vals[p]): + t1_val_print = int(t1_vals[p]) + else: + t1_val_print = "NaN" + if not np.isnan(t_vals[p]): + t_val_print = int(t_vals[p]) + else: + t_val_print = "NaN" + if v == "SDATES": + strList.append( + f"{thisStr}: Sowing {t1_yr} jday {t1_val_print}, {t_yr} jday {t_val_print}" + ) + else: + strList.append( + f"{thisStr}: {t1_yr} {v} {t1_val_print}, {t_yr} {v} {t_val_print}" + ) + else: + if ok: + print(f"{emojus} CLM output {v} unexpectedly vary over time:") + ok = False + print(f"{v} timestep {t} does not match timestep {t1}") + break + if verbose and any_bad: + print(f"{emojus} CLM output {v} unexpectedly vary over time:") + strList.sort() + if rx_ds and np.any(~found_in_rx): + strList = [ + "*: Not found in prescribed input file (maybe minor lon/lat mismatch)" + ] + strList + elif not rx_ds: + strList = ["(No rx file checked)"] + strList + print("\n".join(strList)) + + # Make sure every patch was checked once (or is all-NaN except possibly final season) + incl_patches = np.sort(incl_patches) + if not np.array_equal(incl_patches, np.unique(incl_patches)): + raise RuntimeError("Patch(es) checked more than once!") + incl_patches = list(incl_patches) + incl_patches += list( + np.where( + np.all( + np.isnan( + ra_sp[ + :-1, + ] + ), + axis=0, + ) + )[0] + ) + incl_patches = np.sort(incl_patches) + if not np.array_equal(incl_patches, np.unique(incl_patches)): + raise RuntimeError("Patch(es) checked but also all-NaN??") + if not np.array_equal(incl_patches, np.arange(this_ds.dims["patch"])): + for p in np.arange(this_ds.dims["patch"]): + if p not in incl_patches: + break + raise RuntimeError( + f"Not all patches checked! E.g., {p}: {this_da.isel(patch=p).values}" + ) + + if not any_bad: + if any_bad_before_checking_rx: + print( + f"✅ CLM output {v} do not vary through {this_ds.dims[time_coord]} growing seasons of output (except for patch(es) with missing rx)." + ) + else: + print( + f"✅ CLM output {v} do not vary through {this_ds.dims[time_coord]} growing seasons of output." + ) + + if any_bad and throw_error: + raise RuntimeError("Stopping due to failed check_constant_vars().") + + bad_patches = np.unique(bad_patches) + return [int(p) for p in bad_patches] + + +def check_rx_obeyed( + vegtype_list, rx_ds, dates_ds, which_ds, output_var, gdd_min=None, verbose=False +): + all_ok = 2 + diff_str_list = [] + gdd_tolerance = 1 + + if "GDDHARV" in output_var and verbose: + harvest_reason_da = dates_ds["HARVEST_REASON"] + unique_harvest_reasons = np.unique( + harvest_reason_da.values[np.where(~np.isnan(harvest_reason_da.values))] + ) + pct_harv_at_mature = get_pct_harv_at_mature(harvest_reason_da) + print( + f"{which_ds} harvest reasons: {unique_harvest_reasons} ({pct_harv_at_mature}% harv at maturity)" + ) + + for vegtype_str in vegtype_list: + thisVeg_patches = np.where(dates_ds.patches1d_itype_veg_str == vegtype_str)[0] + if thisVeg_patches.size == 0: + continue + ds_thisVeg = dates_ds.isel(patch=thisVeg_patches) + patch_inds_lon_thisVeg = ds_thisVeg.patches1d_ixy.values.astype(int) - 1 + patch_inds_lat_thisVeg = ds_thisVeg.patches1d_jxy.values.astype(int) - 1 + patch_lons_thisVeg = ds_thisVeg.patches1d_lon + patch_lats_thisVeg = ds_thisVeg.patches1d_lat + + vegtype_int = utils.vegtype_str2int(vegtype_str)[0] + rx_da = rx_ds[f"gs1_{vegtype_int}"] + rx_array = rx_da.values[patch_inds_lat_thisVeg, patch_inds_lon_thisVeg] + rx_array = np.expand_dims(rx_array, axis=1) + sim_array = ds_thisVeg[output_var].values + sim_array_dims = ds_thisVeg[output_var].dims + + # Ignore patches without prescribed value + with np.errstate(invalid="ignore"): + rx_array[np.where(rx_array < 0)] = np.nan + + # Account for... + if "GDDHARV" in output_var: + # ...GDD harvest threshold minimum set in PlantCrop() + if gdd_min == None: + gdd_min = default_gdd_min() + print( + f"gdd_min not provided when doing check_rx_obeyed() for {output_var}; using default {gdd_min}" + ) + with np.errstate(invalid="ignore"): + rx_array[(rx_array >= 0) & (rx_array < gdd_min)] = gdd_min + + # ...harvest reason + # 0: Should never happen in any simulation + # 1: Harvesting at maturity + # 2: Harvesting at max season length (mxmat) + # 3: Crop was incorrectly planted in last time step of Dec. 31 + # 4: Today was supposed to be the planting day, but the previous crop still hasn't been harvested. + # 5: Harvest the day before the next sowing date this year. + # 6: Same as #5. + # 7: Harvest the day before the next sowing date (today is Dec. 31 and the sowing date is Jan. 1) + harvest_reason_da = ds_thisVeg["HARVEST_REASON"] + unique_harvest_reasons = np.unique( + harvest_reason_da.values[np.where(~np.isnan(harvest_reason_da.values))] + ) + pct_harv_at_mature = get_pct_harv_at_mature(harvest_reason_da) + + if np.any(sim_array != rx_array): + diff_array = sim_array - rx_array + + # Allow negative GDDHARV values when harvest occurred because sowing was scheduled for the next day + if output_var == "GDDHARV_PERHARV": + diff_array = np.ma.masked_array( + diff_array, + mask=(diff_array < 0) & (ds_thisVeg["HARVEST_REASON_PERHARV"].values == 5), + ) + elif output_var == "GDDHARV": + with np.errstate(invalid="ignore"): + diff_lt_0 = diff_array < 0 + harv_reason_5 = ds_thisVeg["HARVEST_REASON"].values == 5 + diff_array = np.ma.masked_array(diff_array, mask=diff_lt_0 & harv_reason_5) + + with np.errstate(invalid="ignore"): + abs_gt_0 = abs(diff_array) > 0 + if np.any(np.abs(diff_array[abs_gt_0]) > 0): + min_diff, minLon, minLat, minGS, minRx = get_extreme_info( + diff_array, + rx_array, + np.nanmin, + sim_array_dims, + dates_ds.gs, + patch_lons_thisVeg, + patch_lats_thisVeg, + ) + max_diff, maxLon, maxLat, maxGS, maxRx = get_extreme_info( + diff_array, + rx_array, + np.nanmax, + sim_array_dims, + dates_ds.gs, + patch_lons_thisVeg, + patch_lats_thisVeg, + ) + + diffs_eg_txt = f"{vegtype_str} ({vegtype_int}): diffs range {min_diff} (lon {minLon}, lat {minLat}, gs {minGS}, rx ~{minRx}) to {max_diff} (lon {maxLon}, lat {maxLat}, gs {maxGS}, rx ~{maxRx})" + if "GDDHARV" in output_var: + diffs_eg_txt += f"; harvest reasons: {unique_harvest_reasons} ({pct_harv_at_mature}% harvested at maturity)" + if "GDDHARV" in output_var and np.nanmax(abs(diff_array)) <= gdd_tolerance: + if all_ok > 0: + all_ok = 1 + diff_str_list.append(f" {diffs_eg_txt}") + else: + all_ok = 0 + if verbose: + print( + f"❌ {which_ds}: Prescribed {output_var} *not* always obeyed. E.g., {diffs_eg_txt}" + ) + else: + break + + if all_ok == 2: + print(f"✅ {which_ds}: Prescribed {output_var} always obeyed") + elif all_ok == 1: + # print(f"🟨 {which_ds}: Prescribed {output_var} *not* always obeyed, but acceptable:") + # for x in diff_str_list: print(x) + print( + f"🟨 {which_ds}: Prescribed {output_var} *not* always obeyed, but acceptable (diffs <= {gdd_tolerance})" + ) + elif not verbose: + print(f"❌ {which_ds}: Prescribed {output_var} *not* always obeyed. E.g., {diffs_eg_txt}") + + +# Make sure that, e.g., GDDACCUM_PERHARV is always <= HUI_PERHARV +def check_v0_le_v1(this_ds, vars, msg_txt=" ", both_nan_ok=False, throw_error=False): + v0 = vars[0] + v1 = vars[1] + gdd_lt_hui = this_ds[v0] <= this_ds[v1] + if both_nan_ok: + gdd_lt_hui = gdd_lt_hui | (np.isnan(this_ds[v0]) & np.isnan(this_ds[v1])) + if np.all(gdd_lt_hui): + print(f"✅{msg_txt}{v0} always <= {v1}") + else: + msg = f"❌{msg_txt}{v0} *not* always <= {v1}" + gdd_lt_hui_vals = gdd_lt_hui.values + p = np.where(~gdd_lt_hui_vals)[0][0] + msg = ( + msg + + f"\ne.g., patch {p}: {this_ds.patches1d_itype_veg_str.values[p]}, lon {this_ds.patches1d_lon.values[p]} lat {this_ds.patches1d_lat.values[p]}:" + ) + msg = msg + f"\n{this_ds[v0].values[p,:]}" + msg = msg + f"\n{this_ds[v1].values[p,:]}" + if throw_error: + print(msg) + else: + raise RuntimeError(msg) + + +# Convert time*mxharvests axes to growingseason axis +def convert_axis_time2gs(this_ds, verbose=False, myVars=None, incl_orig=False): + # How many non-NaN patch-seasons do we expect to have once we're done organizing things? + Npatch = this_ds.dims["patch"] + # Because some patches will be planted in the last year but not complete, we have to ignore any finalyear-planted seasons that do complete. + Ngs = this_ds.dims["time"] - 1 + expected_valid = Npatch * Ngs + + mxharvests = this_ds.dims["mxharvests"] + + if verbose: + print( + f"Start: discrepancy of {np.sum(~np.isnan(this_ds.HDATES.values)) - expected_valid} patch-seasons" + ) + + # Set all non-positive date values to NaN. These are seasons that were never harvested (or never started): "non-seasons." + if this_ds.HDATES.dims != ("time", "mxharvests", "patch"): + raise RuntimeError( + f"This code relies on HDATES dims ('time', 'mxharvests', 'patch'), not {this_ds.HDATES.dims}" + ) + hdates_ymp = this_ds.HDATES.copy().where(this_ds.HDATES > 0).values + hdates_pym = np.transpose(hdates_ymp.copy(), (2, 0, 1)) + sdates_ymp = this_ds.SDATES_PERHARV.copy().where(this_ds.SDATES_PERHARV > 0).values + sdates_pym = np.transpose(sdates_ymp.copy(), (2, 0, 1)) + with np.errstate(invalid="ignore"): + hdates_pym[hdates_pym <= 0] = np.nan + + # Find years where patch was inactive + inactive_py = np.transpose( + np.isnan(this_ds.HDATES).all(dim="mxharvests").values + & np.isnan(this_ds.SDATES_PERHARV).all(dim="mxharvests").values + ) + # Find seasons that were planted while the patch was inactive + with np.errstate(invalid="ignore"): + sown_inactive_py = inactive_py[:, :-1] & (hdates_pym[:, 1:, 0] < sdates_pym[:, 1:, 0]) + sown_inactive_py = np.concatenate((np.full((Npatch, 1), False), sown_inactive_py), axis=1) + + # "Ignore harvests from seasons sown (a) before this output began or (b) when the crop was inactive" + with np.errstate(invalid="ignore"): + first_season_before_first_year_p = hdates_pym[:, 0, 0] < sdates_pym[:, 0, 0] + first_season_before_first_year_py = np.full(hdates_pym.shape[:-1], fill_value=False) + first_season_before_first_year_py[:, 0] = first_season_before_first_year_p + sown_prerun_or_inactive_py = first_season_before_first_year_py | sown_inactive_py + sown_prerun_or_inactive_pym = np.concatenate( + ( + np.expand_dims(sown_prerun_or_inactive_py, axis=2), + np.full((Npatch, Ngs + 1, mxharvests - 1), False), + ), + axis=2, + ) + where_sown_prerun_or_inactive_pym = np.where(sown_prerun_or_inactive_pym) + hdates_pym[where_sown_prerun_or_inactive_pym] = np.nan + sdates_pym[where_sown_prerun_or_inactive_pym] = np.nan + if verbose: + print( + f'After "Ignore harvests from before this output began: discrepancy of {np.sum(~np.isnan(hdates_pym)) - expected_valid} patch-seasons' + ) + + # We need to keep some non-seasons---it's possible that "the yearY growing season" never happened (sowing conditions weren't met), but we still need something there so that we can make an array of dimension Npatch*Ngs. We do this by changing those non-seasons from NaN to -Inf before doing the filtering and reshaping, after which we'll convert them back to NaNs. + + # "In years with no sowing, pretend the first no-harvest is meaningful, unless that was intentionally ignored above." + sdates_orig_ymp = this_ds.SDATES.copy().values + sdates_orig_pym = np.transpose(sdates_orig_ymp.copy(), (2, 0, 1)) + hdates_pym2 = hdates_pym.copy() + sdates_pym2 = sdates_pym.copy() + with np.errstate(invalid="ignore"): + sdates_gt_0 = sdates_orig_pym > 0 + nosow_py = np.all(~sdates_gt_0, axis=2) + nosow_py_1st = nosow_py & np.isnan(hdates_pym[:, :, 0]) + where_nosow_py_1st = np.where(nosow_py_1st) + hdates_pym2[where_nosow_py_1st[0], where_nosow_py_1st[1], 0] = -np.inf + sdates_pym2[where_nosow_py_1st[0], where_nosow_py_1st[1], 0] = -np.inf + for h in np.arange(mxharvests - 1): + if h == 0: + continue + elif h == 1: + print("Warning: Untested with mxharvests > 2") + where_nosow_py = np.where( + nosow_py + & ~np.any(np.isnan(hdates_pym[:, :, 0:h]), axis=2) + & np.isnan(hdates_pym[:, :, h]) + ) + hdates_pym2[where_nosow_py[0], where_nosow_py[1], h + 1] = -np.inf + sdates_pym2[where_nosow_py[0], where_nosow_py[1], h + 1] = -np.inf + + # "In years with sowing that are followed by inactive years, check whether the last sowing was harvested before the patch was deactivated. If not, pretend the LAST [easier to implement!] no-harvest is meaningful." + sdates_orig_masked_pym = sdates_orig_pym.copy() + with np.errstate(invalid="ignore"): + sdates_le_0 = sdates_orig_masked_pym <= 0 + sdates_orig_masked_pym[np.where(sdates_le_0)] = np.nan + with warnings.catch_warnings(): + warnings.filterwarnings(action="ignore", message="All-NaN slice encountered") + last_sdate_firstNgs_py = np.nanmax(sdates_orig_masked_pym[:, :-1, :], axis=2) + last_hdate_firstNgs_py = np.nanmax(hdates_pym2[:, :-1, :], axis=2) + with np.errstate(invalid="ignore"): + hdate_lt_sdate = last_hdate_firstNgs_py < last_sdate_firstNgs_py + last_sowing_not_harvested_sameyear_firstNgs_py = hdate_lt_sdate | np.isnan( + last_hdate_firstNgs_py + ) + inactive_lastNgs_py = inactive_py[:, 1:] + last_sowing_never_harvested_firstNgs_py = ( + last_sowing_not_harvested_sameyear_firstNgs_py & inactive_lastNgs_py + ) + last_sowing_never_harvested_py = np.concatenate( + (last_sowing_never_harvested_firstNgs_py, np.full((Npatch, 1), False)), axis=1 + ) + last_sowing_never_harvested_pym = np.concatenate( + ( + np.full((Npatch, Ngs + 1, mxharvests - 1), False), + np.expand_dims(last_sowing_never_harvested_py, axis=2), + ), + axis=2, + ) + where_last_sowing_never_harvested_pym = last_sowing_never_harvested_pym + hdates_pym3 = hdates_pym2.copy() + sdates_pym3 = sdates_pym2.copy() + hdates_pym3[where_last_sowing_never_harvested_pym] = -np.inf + sdates_pym3[where_last_sowing_never_harvested_pym] = -np.inf + + # Convert to growingseason axis + def pym_to_pg(pym, quiet=False): + pg = np.reshape(pym, (pym.shape[0], -1)) + ok_pg = pg[~np.isnan(pg)] + if not quiet: + print( + f"{ok_pg.size} included; unique N seasons = {np.unique(np.sum(~np.isnan(pg), axis=1))}" + ) + return pg + + hdates_pg = pym_to_pg(hdates_pym3.copy(), quiet=~verbose) + sdates_pg = pym_to_pg(sdates_pym3.copy(), quiet=True) + if verbose: + print( + f'After "In years with no sowing, pretend the first no-harvest is meaningful: discrepancy of {np.sum(~np.isnan(hdates_pg)) - expected_valid} patch-seasons' + ) + + # "Ignore any harvests that were planted in the final year, because some cells will have incomplete growing seasons for the final year." + with np.errstate(invalid="ignore"): + hdates_ge_sdates = hdates_pg[:, -mxharvests:] >= sdates_pg[:, -mxharvests:] + lastyear_complete_season = hdates_ge_sdates | np.isinf(hdates_pg[:, -mxharvests:]) + + def ignore_lastyear_complete_season(pg, excl, mxharvests): + tmp_L = pg[:, :-mxharvests] + tmp_R = pg[:, -mxharvests:] + tmp_R[np.where(excl)] = np.nan + pg = np.concatenate((tmp_L, tmp_R), axis=1) + return pg + + hdates_pg2 = ignore_lastyear_complete_season( + hdates_pg.copy(), lastyear_complete_season, mxharvests + ) + sdates_pg2 = ignore_lastyear_complete_season( + sdates_pg.copy(), lastyear_complete_season, mxharvests + ) + is_valid = ~np.isnan(hdates_pg2) + is_fake = np.isneginf(hdates_pg2) + is_fake = np.reshape(is_fake[is_valid], (this_ds.dims["patch"], Ngs)) + discrepancy = np.sum(is_valid) - expected_valid + unique_Nseasons = np.unique(np.sum(is_valid, axis=1)) + if verbose: + print( + f'After "Ignore any harvests that were planted in the final year, because other cells will have incomplete growing seasons for the final year": discrepancy of {discrepancy} patch-seasons' + ) + if "pandas" in sys.modules: + bc = np.bincount(np.sum(is_valid, axis=1)) + bc = bc[bc > 0] + df = pd.DataFrame({"Ngs": unique_Nseasons, "Count": bc}) + print(df) + else: + print(f"unique N seasons = {unique_Nseasons}") + print(" ") + + # Create Dataset with time axis as "gs" (growing season) instead of what CLM puts out + if discrepancy == 0: + this_ds_gs = set_up_ds_with_gs_axis(this_ds) + for v in this_ds.data_vars: + if this_ds[v].dims != ("time", "mxharvests", "patch") or (myVars and v not in myVars): + continue + + # Set invalid values to NaN + da_yhp = this_ds[v].copy() + da_yhp = da_yhp.where(~np.isneginf(da_yhp)) + + # Remove the nans and reshape to patches*growingseasons + da_pyh = da_yhp.transpose("patch", "time", "mxharvests") + ar_pg = np.reshape(da_pyh.values, (this_ds.dims["patch"], -1)) + ar_valid_pg = np.reshape(ar_pg[is_valid], (this_ds.dims["patch"], Ngs)) + # Change -infs to nans + ar_valid_pg[is_fake] = np.nan + # Save as DataArray to new Dataset, stripping _PERHARV from variable name + newname = v.replace("_PERHARV", "") + if newname in this_ds_gs: + raise RuntimeError(f"{newname} already in dataset!") + da_pg = xr.DataArray( + data=ar_valid_pg, + coords=[this_ds_gs.coords["patch"], this_ds_gs.coords["gs"]], + name=newname, + attrs=da_yhp.attrs, + ) + this_ds_gs[newname] = da_pg + this_ds_gs[newname].attrs["units"] = this_ds[v].attrs["units"] + else: + # Print details about example bad patch(es) + if min(unique_Nseasons) < Ngs: + print(f"Too few seasons (min {min(unique_Nseasons)} < {Ngs})") + p = np.where(np.sum(~np.isnan(hdates_pg2), axis=1) == min(unique_Nseasons))[0][0] + print_onepatch_wrongNgs( + p, + this_ds, + sdates_ymp, + hdates_ymp, + sdates_pym, + hdates_pym, + sdates_pym2, + hdates_pym2, + sdates_pym3, + hdates_pym3, + sdates_pg, + hdates_pg, + sdates_pg2, + hdates_pg2, + ) + if max(unique_Nseasons) > Ngs: + print(f"Too many seasons (max {max(unique_Nseasons)} > {Ngs})") + p = np.where(np.sum(~np.isnan(hdates_pg2), axis=1) == max(unique_Nseasons))[0][0] + print_onepatch_wrongNgs( + p, + this_ds, + sdates_ymp, + hdates_ymp, + sdates_pym, + hdates_pym, + sdates_pym2, + hdates_pym2, + sdates_pym3, + hdates_pym3, + sdates_pg, + hdates_pg, + sdates_pg2, + hdates_pg2, + ) + raise RuntimeError( + f"Can't convert time*mxharvests axes to growingseason axis: discrepancy of {discrepancy} patch-seasons" + ) + + # Preserve units + for v1 in this_ds_gs: + v0 = v1 + if v0 not in this_ds: + v0 += "_PERHARV" + if v0 not in this_ds: + continue + if "units" in this_ds[v0].attrs: + this_ds_gs[v1].attrs["units"] = this_ds[v0].attrs["units"] + + if incl_orig: + return this_ds_gs, this_ds + else: + return this_ds_gs + + +# Minimum harvest threshold allowed in PlantCrop() +# Was 50 before cropcal runs 2023-01-28 +def default_gdd_min(): + return 1.0 + + +# Get information about extreme gridcells (for debugging) +def get_extreme_info(diff_array, rx_array, mxn, dims, gs, patches1d_lon, patches1d_lat): + if mxn == np.min: + diff_array = np.ma.masked_array(diff_array, mask=(np.abs(diff_array) == 0)) + themxn = mxn(diff_array) + + # Find the first patch-gs that has the mxn value + matching_indices = np.where(diff_array == themxn) + first_indices = [x[0] for x in matching_indices] + + # Get the lon, lat, and growing season of that patch-gs + p = first_indices[dims.index("patch")] + thisLon = patches1d_lon.values[p] + thisLat = patches1d_lat.values[p] + s = first_indices[dims.index("gs")] + thisGS = gs.values[s] + + # Get the prescribed value for this patch-gs + thisRx = rx_array[p][0] + + return round(themxn, 3), round(thisLon, 3), round(thisLat, 3), thisGS, round(thisRx) + + +# Get growing season lengths from a DataArray of hdate-sdate +def get_gs_len_da(this_da): + tmp = this_da.values + with np.errstate(invalid="ignore"): + tmp_lt_0 = tmp < 0 + tmp[tmp_lt_0] = 365 + tmp[tmp_lt_0] + this_da.values = tmp + this_da.attrs["units"] = "days" + return this_da + + +def get_pct_harv_at_mature(harvest_reason_da): + Nharv_at_mature = len(np.where(harvest_reason_da.values == 1)[0]) + with np.errstate(invalid="ignore"): + harv_reason_gt_0 = harvest_reason_da.values > 0 + Nharv = len(np.where(harv_reason_gt_0)[0]) + if Nharv == 0: + return np.nan + pct_harv_at_mature = Nharv_at_mature / Nharv * 100 + pct_harv_at_mature = np.format_float_positional( + pct_harv_at_mature, precision=2, unique=False, fractional=False, trim="k" + ) # Round to 2 significant digits + return pct_harv_at_mature + + +def import_max_gs_length(paramfile_dir, my_clm_ver, my_clm_subver): + # Get parameter file + pattern = os.path.join(paramfile_dir, f"*{my_clm_ver}_params.{my_clm_subver}.nc") + paramfile = glob.glob(pattern) + if len(paramfile) != 1: + raise RuntimeError(f"Expected to find 1 match of {pattern}; found {len(paramfile)}") + paramfile_ds = xr.open_dataset(paramfile[0]) + + # Import max growing season length (stored in netCDF as nanoseconds!) + paramfile_mxmats = paramfile_ds["mxmat"].values / np.timedelta64(1, "D") + + # Import PFT name list + paramfile_pftnames = [ + x.decode("UTF-8").replace(" ", "") for x in paramfile_ds["pftname"].values + ] + + # Build dict + mxmat_dict = {} + for i, pftname in enumerate(paramfile_pftnames): + mxmat = paramfile_mxmats[i] + if not np.isnan(mxmat): + mxmat_dict[pftname] = int(mxmat) + else: + mxmat_dict[pftname] = np.inf + + return mxmat_dict + + +# E.g. import_rx_dates("sdate", sdates_rx_file, dates_ds0_orig) +def import_rx_dates(var_prefix, date_inFile, dates_ds, set_neg1_to_nan=True): + # Get run info: + # Max number of growing seasons per year + if "mxsowings" in dates_ds: + mxsowings = dates_ds.dims["mxsowings"] + else: + mxsowings = 1 + + # Which vegetation types were simulated? + itype_veg_toImport = np.unique(dates_ds.patches1d_itype_veg) + + date_varList = [] + for i in itype_veg_toImport: + for g in np.arange(mxsowings): + thisVar = f"{var_prefix}{g+1}_{i}" + date_varList = date_varList + [thisVar] + + ds = utils.import_ds(date_inFile, myVars=date_varList) + + did_warn = False + for v in ds: + v_new = v.replace(var_prefix, "gs") + ds = ds.rename({v: v_new}) + + # Set -1 prescribed GDD values to NaN. Only warn the first time. + if set_neg1_to_nan and var_prefix == "gdd" and v_new != v and np.any(ds[v_new].values < 0): + if np.any((ds[v_new].values < 0) & (ds[v_new].values != -1)): + raise RuntimeError(f"Unexpected negative value in {v}") + if not did_warn: + print(f"Setting -1 rx GDD values to NaN") + did_warn = True + ds[v_new] = ds[v_new].where(ds[v_new] != -1) + + return ds + + +def import_output( + filename, + myVars, + y1=None, + yN=None, + myVegtypes=utils.define_mgdcrop_list(), + sdates_rx_ds=None, + gdds_rx_ds=None, + verbose=False, +): + # Import + this_ds = utils.import_ds(filename, myVars=myVars, myVegtypes=myVegtypes) + + # Trim to years of interest (do not include extra year needed for finishing last growing season) + if y1 and yN: + this_ds = check_and_trim_years(y1, yN, this_ds) + else: # Assume including all growing seasons except last complete one are "of interest" + y1 = this_ds.time.values[0].year + yN = this_ds.time.values[-1].year - 2 + this_ds = check_and_trim_years(y1, yN, this_ds) + + # What vegetation types are included? + vegtype_list = [ + x for x in this_ds.vegtype_str.values if x in this_ds.patches1d_itype_veg_str.values + ] + + # Check for consistency among sowing/harvest date/year info + date_vars = ["SDATES_PERHARV", "SYEARS_PERHARV", "HDATES", "HYEARS"] + all_nan = np.full(this_ds[date_vars[0]].shape, True) + all_nonpos = np.full(this_ds[date_vars[0]].shape, True) + all_pos = np.full(this_ds[date_vars[0]].shape, True) + for v in date_vars: + all_nan = all_nan & np.isnan(this_ds[v].values) + with np.errstate(invalid="ignore"): + all_nonpos = all_nonpos & (this_ds[v].values <= 0) + all_pos = all_pos & (this_ds[v].values > 0) + if np.any(np.bitwise_not(all_nan | all_nonpos | all_pos)): + raise RuntimeError("Inconsistent missing/present values on mxharvests axis") + + # When doing transient runs, it's somehow possible for crops in newly-active patches to be *already alive*. They even have a sowing date (idop)! This will of course not show up in SDATES, but it does show up in SDATES_PERHARV. + # I could put the SDATES_PERHARV dates into where they "should" be, but instead I'm just going to invalidate those "seasons." + # + # In all but the last calendar year, which patches had no sowing? + no_sowing_yp = np.all(np.isnan(this_ds.SDATES.values[:-1, :, :]), axis=1) + # In all but the first calendar year, which harvests' jdays are < their sowings' jdays? (Indicates sowing the previous calendar year.) + with np.errstate(invalid="ignore"): + hsdate1_gt_hdate1_yp = ( + this_ds.SDATES_PERHARV.values[1:, 0, :] > this_ds.HDATES.values[1:, 0, :] + ) + # Where both, we have the problem. + falsely_alive_yp = no_sowing_yp & hsdate1_gt_hdate1_yp + if np.any(falsely_alive_yp): + print( + f"Warning: {np.sum(falsely_alive_yp)} patch-seasons being ignored: Seemingly sown the year before harvest, but no sowings occurred that year." + ) + falsely_alive_yp = np.concatenate( + (np.full((1, this_ds.dims["patch"]), False), falsely_alive_yp), axis=0 + ) + falsely_alive_y1p = np.expand_dims(falsely_alive_yp, axis=1) + dummy_false_y1p = np.expand_dims(np.full_like(falsely_alive_yp, False), axis=1) + falsely_alive_yhp = np.concatenate((falsely_alive_y1p, dummy_false_y1p), axis=1) + for v in this_ds.data_vars: + if this_ds[v].dims != ("time", "mxharvests", "patch"): + continue + this_ds[v] = this_ds[v].where(~falsely_alive_yhp) + + def check_no_negative(this_ds_in, varList_no_negative, which_file, verbose=False): + tiny_negOK = 1e-12 + this_ds = this_ds_in.copy() + for v in this_ds: + if not any(x in v for x in varList_no_negative): + continue + the_min = np.nanmin(this_ds[v].values) + if the_min < 0: + if np.abs(the_min) <= tiny_negOK: + if verbose: + print( + f"Tiny negative value(s) in {v} (abs <= {tiny_negOK}) being set to 0 ({which_file})" + ) + else: + print( + f"WARNING: Unexpected negative value(s) in {v}; minimum {the_min} ({which_file})" + ) + values = this_ds[v].copy().values + with np.errstate(invalid="ignore"): + do_setto_0 = (values < 0) & (values >= -tiny_negOK) + values[np.where(do_setto_0)] = 0 + this_ds[v] = xr.DataArray( + values, coords=this_ds[v].coords, dims=this_ds[v].dims, attrs=this_ds[v].attrs + ) + + elif verbose: + print(f"No negative value(s) in {v}; min {the_min} ({which_file})") + return this_ds + + def check_no_zeros(this_ds, varList_no_zero, which_file): + for v in this_ds: + if not any(x in v for x in varList_no_zero): + continue + if np.any(this_ds[v].values == 0): + print(f"WARNING: Unexpected zero(s) in {v} ({which_file})") + elif verbose: + print(f"No zero value(s) in {v} ({which_file})") + + # Check for no zero values where there shouldn't be + varList_no_zero = ["DATE", "YEAR"] + check_no_zeros(this_ds, varList_no_zero, "original file") + + # Convert time*mxharvests axes to growingseason axis + this_ds_gs = convert_axis_time2gs(this_ds, verbose=verbose, incl_orig=False) + + # These are needed for calculating yield later + this_ds_gs["GRAINC_TO_FOOD_PERHARV"] = this_ds["GRAINC_TO_FOOD_PERHARV"] + this_ds_gs["GDDHARV_PERHARV"] = this_ds["GDDHARV_PERHARV"] + + # Get growing season length + this_ds["GSLEN_PERHARV"] = get_gs_len_da(this_ds["HDATES"] - this_ds["SDATES_PERHARV"]) + this_ds_gs["GSLEN"] = get_gs_len_da(this_ds_gs["HDATES"] - this_ds_gs["SDATES"]) + this_ds_gs["GSLEN_PERHARV"] = this_ds["GSLEN_PERHARV"] + + # Get HUI accumulation as fraction of required + this_ds_gs["HUIFRAC"] = this_ds_gs["HUI"] / this_ds_gs["GDDHARV"] + this_ds_gs["HUIFRAC_PERHARV"] = this_ds["HUI_PERHARV"] / this_ds["GDDHARV_PERHARV"] + for v in ["HUIFRAC", "HUIFRAC_PERHARV"]: + this_ds_gs[v].attrs["units"] = "Fraction of required" + + # Avoid tiny negative values + varList_no_negative = ["GRAIN", "REASON", "GDD", "HUI", "YEAR", "DATE", "GSLEN"] + this_ds_gs = check_no_negative(this_ds_gs, varList_no_negative, "new file", verbose=verbose) + + # Check for no zero values where there shouldn't be + varList_no_zero = ["REASON", "DATE"] + check_no_zeros(this_ds_gs, varList_no_zero, "new file") + + # Check that e.g., GDDACCUM <= HUI + for vars in [["GDDACCUM", "HUI"], ["SYEARS", "HYEARS"]]: + if all(v in this_ds_gs for v in vars): + check_v0_le_v1(this_ds_gs, vars, both_nan_ok=True, throw_error=True) + + # Check that prescribed calendars were obeyed + if sdates_rx_ds: + check_rx_obeyed(vegtype_list, sdates_rx_ds, this_ds, "this_ds", "SDATES") + if gdds_rx_ds: + check_rx_obeyed( + vegtype_list, + gdds_rx_ds, + this_ds, + "this_ds", + "SDATES", + "GDDHARV", + gdd_min=default_gdd_min(), + ) + + # Convert time axis to integer year, saving original as 'cftime' + this_ds_gs = this_ds_gs.assign_coords( + {"cftime": this_ds["time_bounds"].isel({"hist_interval": 0})} + ) + this_ds_gs = this_ds_gs.assign_coords({"time": [t.year for t in this_ds_gs["cftime"].values]}) + + # Get number of harvests + this_ds_gs["NHARVESTS"] = (this_ds_gs["GDDHARV_PERHARV"] > 0).sum(dim="mxharvests") + # Get number of harvests that would be missed if only seeing max 1 per calendar year + if np.any(this_ds_gs["NHARVESTS"] > 2): + raise RuntimeError("How to get NHARVEST_DISCREP for NHARVESTS > 2?") + this_ds_gs["NHARVEST_DISCREP"] = (this_ds_gs["NHARVESTS"] == 2).astype(int) + + return this_ds_gs + + +# Print information about a patch (for debugging) +def print_onepatch_wrongNgs( + p, + this_ds_orig, + sdates_ymp, + hdates_ymp, + sdates_pym, + hdates_pym, + sdates_pym2, + hdates_pym2, + sdates_pym3, + hdates_pym3, + sdates_pg, + hdates_pg, + sdates_pg2, + hdates_pg2, +): + try: + import pandas as pd + except: + print("Couldn't import pandas, so not displaying example bad patch ORIGINAL.") + + print( + f"patch {p}: {this_ds_orig.patches1d_itype_veg_str.values[p]}, lon" + f" {this_ds_orig.patches1d_lon.values[p]} lat {this_ds_orig.patches1d_lat.values[p]}" + ) + + print("Original SDATES (per sowing):") + print(this_ds_orig.SDATES.values[:, :, p]) + + print("Original HDATES (per harvest):") + print(this_ds_orig.HDATES.values[:, :, p]) + + if "pandas" in sys.modules: + + def print_pandas_ymp(msg, cols, arrs_tuple): + print(f"{msg} ({np.sum(~np.isnan(arrs_tuple[0]))})") + mxharvests = arrs_tuple[0].shape[1] + arrs_list2 = [] + cols2 = [] + for h in np.arange(mxharvests): + for i, a in enumerate(arrs_tuple): + arrs_list2.append(a[:, h]) + cols2.append(cols[i] + str(h)) + arrs_tuple2 = tuple(arrs_list2) + df = pd.DataFrame(np.stack(arrs_tuple2, axis=1)) + df.columns = cols2 + print(df) + + print_pandas_ymp( + "Original", + ["sdate", "hdate"], + (this_ds_orig.SDATES_PERHARV.values[:, :, p], this_ds_orig.HDATES.values[:, :, p]), + ) + + print_pandas_ymp("Masked", ["sdate", "hdate"], (sdates_ymp[:, :, p], hdates_ymp[:, :, p])) + + print_pandas_ymp( + 'After "Ignore harvests from before this output began"', + ["sdate", "hdate"], + ( + np.transpose(sdates_pym, (1, 2, 0))[:, :, p], + np.transpose(hdates_pym, (1, 2, 0))[:, :, p], + ), + ) + + print_pandas_ymp( + 'After "In years with no sowing, pretend the first no-harvest is meaningful"', + ["sdate", "hdate"], + ( + np.transpose(sdates_pym2, (1, 2, 0))[:, :, p], + np.transpose(hdates_pym2, (1, 2, 0))[:, :, p], + ), + ) + + print_pandas_ymp( + ( + 'After "In years with sowing that are followed by inactive years, check whether the' + " last sowing was harvested before the patch was deactivated. If not, pretend the" + ' LAST no-harvest is meaningful."' + ), + ["sdate", "hdate"], + ( + np.transpose(sdates_pym3, (1, 2, 0))[:, :, p], + np.transpose(hdates_pym3, (1, 2, 0))[:, :, p], + ), + ) + + def print_pandas_pg(msg, cols, arrs_tuple): + print(f"{msg} ({np.sum(~np.isnan(arrs_tuple[0]))})") + arrs_list = list(arrs_tuple) + for i, a in enumerate(arrs_tuple): + arrs_list[i] = np.reshape(a, (-1)) + arrs_tuple2 = tuple(arrs_list) + df = pd.DataFrame(np.stack(arrs_tuple2, axis=1)) + df.columns = cols + print(df) + + print_pandas_pg( + "Same, but converted to gs axis", ["sdate", "hdate"], (sdates_pg[p, :], hdates_pg[p, :]) + ) + + print_pandas_pg( + ( + 'After "Ignore any harvests that were planted in the final year, because some cells' + ' will have incomplete growing seasons for the final year"' + ), + ["sdate", "hdate"], + (sdates_pg2[p, :], hdates_pg2[p, :]), + ) + else: + + def print_nopandas(a1, a2, msg): + print(msg) + if a1.ndim == 1: + # I don't know why these aren't side-by-side! + print(np.stack((a1, a2), axis=1)) + else: + print(np.concatenate((a1, a2), axis=1)) + + print_nopandas(sdates_ymp[:, :, p], hdates_ymp[:, :, p], "Masked:") + + print_nopandas( + np.transpose(sdates_pym, (1, 2, 0))[:, :, p], + np.transpose(hdates_pym, (1, 2, 0))[:, :, p], + 'After "Ignore harvests from before this output began"', + ) + + print_nopandas( + np.transpose(sdates_pym2, (1, 2, 0))[:, :, p], + np.transpose(hdates_pym2, (1, 2, 0))[:, :, p], + 'After "In years with no sowing, pretend the first no-harvest is meaningful"', + ) + + print_nopandas( + np.transpose(sdates_pym3, (1, 2, 0))[:, :, p], + np.transpose(hdates_pym3, (1, 2, 0))[:, :, p], + ( + 'After "In years with sowing that are followed by inactive years, check whether the' + " last sowing was harvested before the patch was deactivated. If not, pretend the" + ' LAST [easier to implement!] no-harvest is meaningful."' + ), + ) + + print_nopandas(sdates_pg[p, :], hdates_pg[p, :], "Same, but converted to gs axis") + + print_nopandas( + sdates_pg2[p, :], + hdates_pg2[p, :], + ( + 'After "Ignore any harvests that were planted in the final year, because some cells' + ' will have incomplete growing seasons for the final year"' + ), + ) + + print("\n\n") + + +# Set up empty Dataset with time axis as "gs" (growing season) instead of what CLM puts out. +# Includes all the same variables as the input dataset, minus any that had dimensions mxsowings or mxharvests. +def set_up_ds_with_gs_axis(ds_in): + # Get the data variables to include in the new dataset + data_vars = dict() + for v in ds_in.data_vars: + if not any([x in ["mxsowings", "mxharvests"] for x in ds_in[v].dims]): + data_vars[v] = ds_in[v] + # Set up the new dataset + gs_years = [t.year - 1 for t in ds_in.time.values[:-1]] + coords = ds_in.coords + coords["gs"] = gs_years + ds_out = xr.Dataset(data_vars=data_vars, coords=coords, attrs=ds_in.attrs) + return ds_out diff --git a/python/ctsm/crop_calendars/cropcal_utils.py b/python/ctsm/crop_calendars/cropcal_utils.py new file mode 100644 index 0000000000..ba6c0b6e41 --- /dev/null +++ b/python/ctsm/crop_calendars/cropcal_utils.py @@ -0,0 +1,934 @@ +"""utility functions""" +"""copied from klindsay, https://github.com/klindsay28/CESM2_coup_carb_cycle_JAMES/blob/master/utils.py""" + +import re +import warnings +import importlib + +with warnings.catch_warnings(): + warnings.filterwarnings(action="ignore", category=DeprecationWarning) + if importlib.find_loader("cf_units") is not None: + import cf_units as cf + if importlib.find_loader("cartopy") is not None: + from cartopy.util import add_cyclic_point +import cftime +import numpy as np +import xarray as xr + +# from xr_ds_ex import xr_ds_ex + + +# generate annual means, weighted by days / month +def weighted_annual_mean(array, time_in="time", time_out="time"): + if isinstance(array[time_in].values[0], cftime.datetime): + month_length = array[time_in].dt.days_in_month + + # After https://docs.xarray.dev/en/v0.5.1/examples/monthly-means.html + group = f"{time_in}.year" + weights = month_length.groupby(group) / month_length.groupby(group).sum() + np.testing.assert_allclose(weights.groupby(group).sum().values, 1) + array = (array * weights).groupby(group).sum(dim=time_in, skipna=True) + if time_out != "year": + array = array.rename({"year": time_out}) + + else: + mon_day = xr.DataArray( + np.array([31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]), dims=["month"] + ) + mon_wgt = mon_day / mon_day.sum() + array = ( + array.rolling({time_in: 12}, center=False) # rolling + .construct("month") # construct the array + .isel( + {time_in: slice(11, None, 12)} + ) # slice so that the first element is [1..12], second is [13..24] + .dot(mon_wgt, dims=["month"]) + ) + if time_in != time_out: + array = array.rename({time_in: time_out}) + + return array + + +# List of PFTs used in CLM +def define_pftlist(): + pftlist = [ + "not_vegetated", + "needleleaf_evergreen_temperate_tree", + "needleleaf_evergreen_boreal_tree", + "needleleaf_deciduous_boreal_tree", + "broadleaf_evergreen_tropical_tree", + "broadleaf_evergreen_temperate_tree", + "broadleaf_deciduous_tropical_tree", + "broadleaf_deciduous_temperate_tree", + "broadleaf_deciduous_boreal_tree", + "broadleaf_evergreen_shrub", + "broadleaf_deciduous_temperate_shrub", + "broadleaf_deciduous_boreal_shrub", + "c3_arctic_grass", + "c3_non-arctic_grass", + "c4_grass", + "unmanaged_c3_crop", + "unmanaged_c3_irrigated", + "temperate_corn", + "irrigated_temperate_corn", + "spring_wheat", + "irrigated_spring_wheat", + "winter_wheat", + "irrigated_winter_wheat", + "soybean", + "irrigated_soybean", + "barley", + "irrigated_barley", + "winter_barley", + "irrigated_winter_barley", + "rye", + "irrigated_rye", + "winter_rye", + "irrigated_winter_rye", + "cassava", + "irrigated_cassava", + "citrus", + "irrigated_citrus", + "cocoa", + "irrigated_cocoa", + "coffee", + "irrigated_coffee", + "cotton", + "irrigated_cotton", + "datepalm", + "irrigated_datepalm", + "foddergrass", + "irrigated_foddergrass", + "grapes", + "irrigated_grapes", + "groundnuts", + "irrigated_groundnuts", + "millet", + "irrigated_millet", + "oilpalm", + "irrigated_oilpalm", + "potatoes", + "irrigated_potatoes", + "pulses", + "irrigated_pulses", + "rapeseed", + "irrigated_rapeseed", + "rice", + "irrigated_rice", + "sorghum", + "irrigated_sorghum", + "sugarbeet", + "irrigated_sugarbeet", + "sugarcane", + "irrigated_sugarcane", + "sunflower", + "irrigated_sunflower", + "miscanthus", + "irrigated_miscanthus", + "switchgrass", + "irrigated_switchgrass", + "tropical_corn", + "irrigated_tropical_corn", + "tropical_soybean", + "irrigated_tropical_soybean", + ] + return pftlist + + +# Get CLM ivt number corresponding to a given name +def ivt_str2int(ivt_str): + pftlist = define_pftlist() + if isinstance(ivt_str, str): + ivt_int = pftlist.index(ivt_str) + elif isinstance(ivt_str, list) or isinstance(ivt_str, np.ndarray): + ivt_int = [ivt_str2int(x) for x in ivt_str] + if isinstance(ivt_str, np.ndarray): + ivt_int = np.array(ivt_int) + else: + raise RuntimeError( + f"Update ivt_str_to_int() to handle input of type {type(ivt_str)} (if possible)" + ) + + return ivt_int + + +# Get CLM ivt name corresponding to a given number +def ivt_int2str(ivt_int): + pftlist = define_pftlist() + if np.issubdtype(type(ivt_int), np.integer) or int(ivt_int) == ivt_int: + ivt_str = pftlist[int(ivt_int)] + elif isinstance(ivt_int, list) or isinstance(ivt_int, np.ndarray): + ivt_str = [ivt_int2str(x) for x in ivt_int] + if isinstance(ivt_int, np.ndarray): + ivt_str = np.array(ivt_str) + elif isinstance(ivt_int, float): + raise RuntimeError("List indices must be integers") + else: + raise RuntimeError( + f"Update ivt_str_to_int() to handle input of type {type(ivt_int)} (if possible)" + ) + + return ivt_str + + +# Does this vegetation type's name match (for a given comparison method) any member of a filtering list? +""" +Methods: + ok_contains: True if any member of this_filter is found in this_vegtype. + notok_contains: True of no member of this_filter is found in this_vegtype. + ok_exact: True if this_vegtype matches any member of this_filter + exactly. + notok_exact: True if this_vegtype does not match any member of + this_filter exactly. +""" + + +def is_this_vegtype(this_vegtype, this_filter, this_method): + # Make sure data type of this_vegtype is acceptable + if isinstance(this_vegtype, float) and int(this_vegtype) == this_vegtype: + this_vegtype = int(this_vegtype) + data_type_ok = lambda x: isinstance(x, str) or isinstance(x, int) or isinstance(x, np.int64) + ok_input = True + if not data_type_ok(this_vegtype): + if isinstance(this_vegtype, xr.core.dataarray.DataArray): + this_vegtype = this_vegtype.values + if isinstance(this_vegtype, (list, np.ndarray)): + if len(this_vegtype) == 1 and data_type_ok(this_vegtype[0]): + this_vegtype = this_vegtype[0] + elif data_type_ok(this_vegtype[0]): + raise TypeError( + "is_this_vegtype(): this_vegtype must be a single string or integer, not a list" + " of them. Did you mean to call is_each_vegtype() instead?" + ) + else: + ok_input = False + else: + ok_input = False + if not ok_input: + raise TypeError( + "is_this_vegtype(): First argument (this_vegtype) must be a string or integer, not" + f" {type(this_vegtype)}" + ) + + # Make sure data type of this_filter is acceptable + if not np.iterable(this_filter): + raise TypeError( + "is_this_vegtype(): Second argument (this_filter) must be iterable (e.g., a list), not" + f" {type(this_filter)}" + ) + + # Perform the comparison + if this_method == "ok_contains": + return any(n in this_vegtype for n in this_filter) + elif this_method == "notok_contains": + return not any(n in this_vegtype for n in this_filter) + elif this_method == "ok_exact": + return any(n == this_vegtype for n in this_filter) + elif this_method == "notok_exact": + return not any(n == this_vegtype for n in this_filter) + else: + raise ValueError(f"Unknown comparison method: '{this_method}'") + + +# Get boolean list of whether each vegetation type in list is a managed crop +""" + this_vegtypelist: The list of vegetation types whose members you want to + test. + this_filter: The list of strings against which you want to compare + each member of this_vegtypelist. + this_method: How you want to do the comparison. See is_this_vegtype(). +""" + + +def is_each_vegtype(this_vegtypelist, this_filter, this_method): + if isinstance(this_vegtypelist, xr.DataArray): + this_vegtypelist = this_vegtypelist.values + + return [is_this_vegtype(x, this_filter, this_method) for x in this_vegtypelist] + + +# List (strings) of managed crops in CLM. +def define_mgdcrop_list(): + notcrop_list = ["tree", "grass", "shrub", "unmanaged", "not_vegetated"] + defined_pftlist = define_pftlist() + is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") + return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] + + +# Convert list of vegtype strings to integer index equivalents. +def vegtype_str2int(vegtype_str, vegtype_mainlist=None): + convert_to_ndarray = not isinstance(vegtype_str, np.ndarray) + if convert_to_ndarray: + vegtype_str = np.array(vegtype_str) + + if isinstance(vegtype_mainlist, xr.Dataset): + vegtype_mainlist = vegtype_mainlist.vegtype_str.values + elif isinstance(vegtype_mainlist, xr.DataArray): + vegtype_mainlist = vegtype_mainlist.values + elif vegtype_mainlist == None: + vegtype_mainlist = define_pftlist() + if not isinstance(vegtype_mainlist, list) and isinstance(vegtype_mainlist[0], str): + if isinstance(vegtype_mainlist, list): + raise TypeError( + f"Not sure how to handle vegtype_mainlist as list of {type(vegtype_mainlist[0])}" + ) + else: + raise TypeError( + f"Not sure how to handle vegtype_mainlist as type {type(vegtype_mainlist[0])}" + ) + + if vegtype_str.shape == (): + indices = np.array([-1]) + else: + indices = np.full(len(vegtype_str), -1) + for v in np.unique(vegtype_str): + indices[np.where(vegtype_str == v)] = vegtype_mainlist.index(v) + if convert_to_ndarray: + indices = [int(x) for x in indices] + return indices + + +# Flexibly subset time(s) and/or vegetation type(s) from an xarray Dataset or DataArray. Keyword arguments like dimension=selection. Selections can be individual values or slice()s. Optimize memory usage by beginning keyword argument list with the selections that will result in the largest reduction of object size. Use dimension "vegtype" to extract patches of designated vegetation type (can be string or integer). +# Can also do dimension=function---e.g., time=np.mean will take the mean over the time dimension. +def xr_flexsel(xr_object, patches1d_itype_veg=None, warn_about_seltype_interp=True, **kwargs): + # Setup + havewarned = False + delimiter = "__" + + for key, selection in kwargs.items(): + if callable(selection): + # It would have been really nice to do selection(xr_object, axis=key), but numpy methods and xarray methods disagree on "axis" vs. "dimension." So instead, just do this manually. + if selection == np.mean: + try: + xr_object = xr_object.mean(dim=key) + except: + raise ValueError( + f"Failed to take mean of dimension {key}. Try doing so outside of" + " xr_flexsel()." + ) + else: + raise ValueError(f"xr_flexsel() doesn't recognize function {selection}") + + elif key == "vegtype": + # Convert to list, if needed + if not isinstance(selection, list): + selection = [selection] + + # Convert to indices, if needed + if isinstance(selection[0], str): + selection = vegtype_str2int(selection) + + # Get list of boolean(s) + if isinstance(selection[0], int): + if isinstance(patches1d_itype_veg, type(None)): + patches1d_itype_veg = xr_object.patches1d_itype_veg.values + elif isinstance(patches1d_itype_veg, xr.core.dataarray.DataArray): + patches1d_itype_veg = patches1d_itype_veg.values + is_vegtype = is_each_vegtype(patches1d_itype_veg, selection, "ok_exact") + elif isinstance(selection[0], bool): + if len(selection) != len(xr_object.patch): + raise ValueError( + "If providing boolean 'vegtype' argument to xr_flexsel(), it must be the" + f" same length as xr_object.patch ({len(selection)} vs." + f" {len(xr_object.patch)})" + ) + is_vegtype = selection + else: + raise TypeError(f"Not sure how to handle 'vegtype' of type {type(selection[0])}") + xr_object = xr_object.isel(patch=[i for i, x in enumerate(is_vegtype) if x]) + if "ivt" in xr_object: + xr_object = xr_object.isel( + ivt=is_each_vegtype(xr_object.ivt.values, selection, "ok_exact") + ) + + else: + # Parse selection type, if provided + if delimiter in key: + key, selection_type = key.split(delimiter) + + # Check type of selection + else: + is_inefficient = False + if isinstance(selection, slice): + slice_members = [] + if selection == slice(0): + raise ValueError("slice(0) will be empty") + if selection.start != None: + slice_members = slice_members + [selection.start] + if selection.stop != None: + slice_members = slice_members + [selection.stop] + if selection.step != None: + slice_members = slice_members + [selection.step] + if slice_members == []: + raise TypeError("slice is all None?") + this_type = int + for x in slice_members: + if x < 0 or not isinstance(x, int): + this_type = "values" + break + elif isinstance(selection, np.ndarray): + if selection.dtype.kind in np.typecodes["AllInteger"]: + this_type = int + else: + is_inefficient = True + this_type = None + for x in selection: + if x < 0 or x % 1 > 0: + if isinstance(x, int): + this_type = "values" + else: + this_type = type(x) + break + if this_type == None: + this_type = int + selection = selection.astype(int) + else: + this_type = type(selection) + + warn_about_this_seltype_interp = warn_about_seltype_interp + if this_type == list and isinstance(selection[0], str): + selection_type = "values" + warn_about_this_seltype_interp = False + elif this_type == int: + selection_type = "indices" + else: + selection_type = "values" + + if warn_about_this_seltype_interp: + # Suggest suppressing selection type interpretation warnings + if not havewarned: + print( + "xr_flexsel(): Suppress all 'selection type interpretation' messages by" + " specifying warn_about_seltype_interp=False" + ) + havewarned = True + if is_inefficient: + extra = " This will also improve efficiency for large selections." + else: + extra = "" + print( + f"xr_flexsel(): Selecting {key} as {selection_type} because selection was" + f" interpreted as {this_type}. If not correct, specify selection type" + " ('indices' or 'values') in keyword like" + f" '{key}{delimiter}SELECTIONTYPE=...' instead of '{key}=...'.{extra}" + ) + + # Trim along relevant 1d axes + if isinstance(xr_object, xr.Dataset) and key in ["lat", "lon"]: + if selection_type == "indices": + inclCoords = xr_object[key].values[selection] + elif selection_type == "values": + if isinstance(selection, slice): + inclCoords = xr_object.sel({key: selection}, drop=False)[key].values + else: + inclCoords = selection + else: + raise TypeError(f"selection_type {selection_type} not recognized") + if key == "lat": + thisXY = "jxy" + elif key == "lon": + thisXY = "ixy" + else: + raise KeyError( + f"Key '{key}' not recognized: What 1d_ suffix should I use for variable" + " name?" + ) + pattern = re.compile(f"1d_{thisXY}") + matches = [x for x in list(xr_object.keys()) if pattern.search(x) != None] + for thisVar in matches: + if len(xr_object[thisVar].dims) != 1: + raise RuntimeError( + f"Expected {thisVar} to have 1 dimension, but it has" + f" {len(xr_object[thisVar].dims)}: {xr_object[thisVar].dims}" + ) + thisVar_dim = xr_object[thisVar].dims[0] + # print(f"Variable {thisVar} has dimension {thisVar_dim}") + thisVar_coords = xr_object[key].values[ + xr_object[thisVar].values.astype(int) - 1 + ] + # print(f"{thisVar_dim} size before: {xr_object.sizes[thisVar_dim]}") + ok_ind = [] + new_1d_thisXY = [] + for i, x in enumerate(thisVar_coords): + if x in inclCoords: + ok_ind = ok_ind + [i] + new_1d_thisXY = new_1d_thisXY + [(inclCoords == x).nonzero()[0] + 1] + xr_object = xr_object.isel({thisVar_dim: ok_ind}) + new_1d_thisXY = np.array(new_1d_thisXY).squeeze() + xr_object[thisVar].values = new_1d_thisXY + # print(f"{thisVar_dim} size after: {xr_object.sizes[thisVar_dim]}") + + # Perform selection + if selection_type == "indices": + # Have to select like this instead of with index directly because otherwise assign_coords() will throw an error. Not sure why. + if isinstance(selection, int): + # Single integer? Turn it into a slice. + selection = slice(selection, selection + 1) + elif ( + isinstance(selection, np.ndarray) + and not selection.dtype.kind in np.typecodes["AllInteger"] + ): + selection = selection.astype(int) + xr_object = xr_object.isel({key: selection}) + elif selection_type == "values": + xr_object = xr_object.sel({key: selection}) + else: + raise TypeError(f"selection_type {selection_type} not recognized") + + return xr_object + + +# Get PFT of each patch, in both integer and string forms. +def get_patch_ivts(this_ds, this_pftlist): + # First, get all the integer values; should be time*pft or pft*time. We will eventually just take the first timestep. + vegtype_int = this_ds.patches1d_itype_veg + vegtype_int.values = vegtype_int.values.astype(int) + + # Convert to strings. + vegtype_str = list(np.array(this_pftlist)[vegtype_int.values]) + + # Return a dictionary with both results + return {"int": vegtype_int, "str": vegtype_str, "all_str": this_pftlist} + + +# Convert a list of strings with vegetation type names into a DataArray. Used to add vegetation type info in import_ds(). +def get_vegtype_str_da(vegtype_str): + nvt = len(vegtype_str) + thisName = "vegtype_str" + vegtype_str_da = xr.DataArray( + vegtype_str, coords={"ivt": np.arange(0, nvt)}, dims=["ivt"], name=thisName + ) + return vegtype_str_da + + +# Function to drop unwanted variables in preprocessing of open_mfdataset(), making sure to NOT drop any unspecified variables that will be useful in gridding. Also adds vegetation type info in the form of a DataArray of strings. +# Also renames "pft" dimension (and all like-named variables, e.g., pft1d_itype_veg_str) to be named like "patch". This can later be reversed, for compatibility with other code, using patch2pft(). +def mfdataset_preproc(ds, vars_to_import, vegtypes_to_import, timeSlice): + # Rename "pft" dimension and variables to "patch", if needed + if "pft" in ds.dims: + pattern = re.compile("pft.*1d") + matches = [x for x in list(ds.keys()) if pattern.search(x) != None] + pft2patch_dict = {"pft": "patch"} + for m in matches: + pft2patch_dict[m] = m.replace("pft", "patch").replace("patchs", "patches") + ds = ds.rename(pft2patch_dict) + + derived_vars = [] + if vars_to_import != None: + # Split vars_to_import into variables that are vs. aren't already in ds + derived_vars = [v for v in vars_to_import if v not in ds] + present_vars = [v for v in vars_to_import if v in ds] + vars_to_import = present_vars + + # Get list of dimensions present in variables in vars_to_import. + dimList = [] + for thisVar in vars_to_import: + # list(set(x)) returns a list of the unique items in x + dimList = list(set(dimList + list(ds.variables[thisVar].dims))) + + # Get any _1d variables that are associated with those dimensions. These will be useful in gridding. Also, if any dimension is "pft", set up to rename it and all like-named variables to "patch" + onedVars = [] + for thisDim in dimList: + pattern = re.compile(f"{thisDim}.*1d") + matches = [x for x in list(ds.keys()) if pattern.search(x) != None] + onedVars = list(set(onedVars + matches)) + + # Add dimensions and _1d variables to vars_to_import + vars_to_import = list(set(vars_to_import + list(ds.dims) + onedVars)) + + # Add any _bounds variables + bounds_vars = [] + for v in vars_to_import: + bounds_var = v + "_bounds" + if bounds_var in ds: + bounds_vars = bounds_vars + [bounds_var] + vars_to_import = vars_to_import + bounds_vars + + # Get list of variables to drop + varlist = list(ds.variables) + vars_to_drop = list(np.setdiff1d(varlist, vars_to_import)) + + # Drop them + ds = ds.drop_vars(vars_to_drop) + + # Add vegetation type info + if "patches1d_itype_veg" in list(ds): + this_pftlist = define_pftlist() + get_patch_ivts( + ds, this_pftlist + ) # Includes check of whether vegtype changes over time anywhere + vegtype_da = get_vegtype_str_da(this_pftlist) + patches1d_itype_veg_str = vegtype_da.values[ + ds.isel(time=0).patches1d_itype_veg.values.astype(int) + ] + npatch = len(patches1d_itype_veg_str) + patches1d_itype_veg_str = xr.DataArray( + patches1d_itype_veg_str, + coords={"patch": np.arange(0, npatch)}, + dims=["patch"], + name="patches1d_itype_veg_str", + ) + ds = xr.merge([ds, vegtype_da, patches1d_itype_veg_str]) + + # Restrict to veg. types of interest, if any + if vegtypes_to_import != None: + ds = xr_flexsel(ds, vegtype=vegtypes_to_import) + + # Restrict to time slice, if any + if timeSlice: + ds = safer_timeslice(ds, timeSlice) + + # Finish import + ds = xr.decode_cf(ds, decode_times=True) + + # Compute derived variables + for v in derived_vars: + if v == "HYEARS" and "HDATES" in ds and ds.HDATES.dims == ("time", "mxharvests", "patch"): + yearList = np.array([np.float32(x.year - 1) for x in ds.time.values]) + hyears = ds["HDATES"].copy() + hyears.values = np.tile( + np.expand_dims(yearList, (1, 2)), (1, ds.dims["mxharvests"], ds.dims["patch"]) + ) + with np.errstate(invalid="ignore"): + is_le_zero = ~np.isnan(ds.HDATES.values) & (ds.HDATES.values <= 0) + hyears.values[is_le_zero] = ds.HDATES.values[is_le_zero] + hyears.values[np.isnan(ds.HDATES.values)] = np.nan + hyears.attrs["long_name"] = "DERIVED: actual crop harvest years" + hyears.attrs["units"] = "year" + ds["HYEARS"] = hyears + + return ds + + +# Import a dataset that can be spread over multiple files, only including specified variables and/or vegetation types and/or timesteps, concatenating by time. DOES actually read the dataset into memory, but only AFTER dropping unwanted variables and/or vegetation types. +def import_ds( + filelist, + myVars=None, + myVegtypes=None, + timeSlice=None, + myVars_missing_ok=[], + only_active_patches=False, + rename_lsmlatlon=False, + chunks=None, +): + # Convert myVegtypes here, if needed, to avoid repeating the process each time you read a file in xr.open_mfdataset(). + if myVegtypes is not None: + if not isinstance(myVegtypes, list): + myVegtypes = [myVegtypes] + if isinstance(myVegtypes[0], str): + myVegtypes = vegtype_str2int(myVegtypes) + + # Same for these variables. + if myVars != None: + if not isinstance(myVars, list): + myVars = [myVars] + if myVars_missing_ok: + if not isinstance(myVars_missing_ok, list): + myVars_missing_ok = [myVars_missing_ok] + + # Make sure lists are actually lists + if not isinstance(filelist, list): + filelist = [filelist] + if not isinstance(myVars_missing_ok, list): + myVars_missing_ok = [myVars_missing_ok] + + # Remove files from list if they don't contain requested timesteps. + # timeSlice should be in the format slice(start,end[,step]). start or end can be None to be unbounded on one side. Note that the standard slice() documentation suggests that only elements through end-1 will be selected, but that seems not to be the case in the xarray implementation. + if timeSlice: + new_filelist = [] + for file in sorted(filelist): + filetime = xr.open_dataset(file).time + filetime_sel = safer_timeslice(filetime, timeSlice) + include_this_file = filetime_sel.size + if include_this_file: + new_filelist.append(file) + + # If you found some matching files, but then you find one that doesn't, stop going through the list. + elif new_filelist: + break + if not new_filelist: + raise RuntimeError(f"No files found in timeSlice {timeSlice}") + filelist = new_filelist + + # The xarray open_mfdataset() "preprocess" argument requires a function that takes exactly one variable (an xarray.Dataset object). Wrapping mfdataset_preproc() in this lambda function allows this. Could also just allow mfdataset_preproc() to access myVars and myVegtypes directly, but that's bad practice as it could lead to scoping issues. + mfdataset_preproc_closure = lambda ds: mfdataset_preproc(ds, myVars, myVegtypes, timeSlice) + + # Import + if isinstance(filelist, list) and len(filelist) == 1: + filelist = filelist[0] + if isinstance(filelist, list): + with warnings.catch_warnings(): + warnings.filterwarnings(action="ignore", category=DeprecationWarning) + if importlib.find_loader("dask") is None: + raise ModuleNotFoundError( + "You have asked xarray to import a list of files as a single Dataset using" + " open_mfdataset(), but this requires dask, which is not available.\nFile" + f" list: {filelist}" + ) + this_ds = xr.open_mfdataset( + sorted(filelist), + data_vars="minimal", + preprocess=mfdataset_preproc_closure, + compat="override", + coords="all", + concat_dim="time", + combine="nested", + chunks=chunks, + ) + elif isinstance(filelist, str): + this_ds = xr.open_dataset(filelist, chunks=chunks) + this_ds = mfdataset_preproc(this_ds, myVars, myVegtypes, timeSlice) + this_ds = this_ds.compute() + + # Include only active patches (or whatever) + if only_active_patches: + is_active = this_ds.patches1d_active.values + p_active = np.where(is_active)[0] + this_ds_active = this_ds.isel(patch=p_active) + + # Warn and/or error about variables that couldn't be imported or derived + if myVars: + missing_vars = [v for v in myVars if v not in this_ds] + ok_missing_vars = [v for v in missing_vars if v in myVars_missing_ok] + bad_missing_vars = [v for v in missing_vars if v not in myVars_missing_ok] + if ok_missing_vars: + print( + "Could not import some variables; either not present or not deriveable:" + f" {ok_missing_vars}" + ) + if bad_missing_vars: + raise RuntimeError( + "Could not import some variables; either not present or not deriveable:" + f" {bad_missing_vars}" + ) + + if rename_lsmlatlon: + if "lsmlat" in this_ds.dims: + this_ds = this_ds.rename({"lsmlat": "lat"}) + if "lsmlon" in this_ds.dims: + this_ds = this_ds.rename({"lsmlon": "lon"}) + + return this_ds + + +# Return a DataArray, with defined coordinates, for a given variable in a dataset. +def get_thisVar_da(thisVar, this_ds): + # Make DataArray for this variable + thisvar_da = np.array(this_ds.variables[thisVar]) + theseDims = this_ds.variables[thisVar].dims + thisvar_da = xr.DataArray(thisvar_da, dims=theseDims) + + # Define coordinates of this variable's DataArray + dimsDict = dict() + for thisDim in theseDims: + dimsDict[thisDim] = this_ds[thisDim] + thisvar_da = thisvar_da.assign_coords(dimsDict) + thisvar_da.attrs = this_ds[thisVar].attrs + + return thisvar_da + + +# Make a geographically gridded DataArray (with dimensions time, vegetation type [as string], lat, lon) of one variable within a Dataset. Optional keyword arguments will be passed to xr_flexsel() to select single steps or slices along the specified ax(ie)s. +# +# fillValue: Default None means grid will be filled with NaN, unless the variable in question already has a fillValue, in which case that will be used. +def grid_one_variable(this_ds, thisVar, fillValue=None, **kwargs): + # Get this Dataset's values for selection(s), if provided + this_ds = xr_flexsel(this_ds, **kwargs) + + # Get DataArrays needed for gridding + thisvar_da = get_thisVar_da(thisVar, this_ds) + vt_da = None + if "patch" in thisvar_da.dims: + spatial_unit = "patch" + xy_1d_prefix = "patches" + if "patches1d_itype_veg" in this_ds: + vt_da = get_thisVar_da("patches1d_itype_veg", this_ds) + elif "gridcell" in thisvar_da.dims: + spatial_unit = "gridcell" + xy_1d_prefix = "grid" + else: + raise RuntimeError( + f"What variables to use for _ixy and _jxy of variable with dims {thisvar_da.dims}?" + ) + ixy_da = get_thisVar_da(xy_1d_prefix + "1d_ixy", this_ds) + jxy_da = get_thisVar_da(xy_1d_prefix + "1d_jxy", this_ds) + + if not fillValue and "_FillValue" in thisvar_da.attrs: + fillValue = thisvar_da.attrs["_FillValue"] + + # Renumber vt_da to work as indices on new ivt dimension, if needed. + ### Ensures that the unique set of vt_da values begins with 1 and + ### contains no missing steps. + if "ivt" in this_ds and vt_da is not None: + vt_da.values = np.array([np.where(this_ds.ivt.values == x)[0][0] for x in vt_da.values]) + + # Get new dimension list + new_dims = list(thisvar_da.dims) + ### Remove "[spatial_unit]". + if spatial_unit in new_dims: + new_dims.remove(spatial_unit) + # Add "ivt_str" (vegetation type, as string). This needs to go at the end, to avoid a possible situation where you wind up with multiple Ellipsis members of fill_indices. + if "ivt" in this_ds and spatial_unit == "patch": + new_dims.append("ivt_str") + ### Add lat and lon to end of list + new_dims = new_dims + ["lat", "lon"] + + # Set up empty array + n_list = [] + for dim in new_dims: + if dim == "ivt_str": + n = this_ds.sizes["ivt"] + elif dim in thisvar_da.coords: + n = thisvar_da.sizes[dim] + else: + n = this_ds.sizes[dim] + n_list = n_list + [n] + thisvar_gridded = np.empty(n_list) + if fillValue: + thisvar_gridded[:] = fillValue + else: + thisvar_gridded[:] = np.NaN + + # Fill with this variable + fill_indices = [] + for dim in new_dims: + if dim == "lat": + fill_indices.append(jxy_da.values.astype(int) - 1) + elif dim == "lon": + fill_indices.append(ixy_da.values.astype(int) - 1) + elif dim == "ivt_str": + fill_indices.append(vt_da) + elif not fill_indices: + # I.e., if fill_indices is empty. Could also do "elif len(fill_indices)==0". + fill_indices.append(Ellipsis) + try: + thisvar_gridded[tuple(fill_indices[: len(fill_indices)])] = thisvar_da.values + except: + thisvar_gridded[tuple(fill_indices[: len(fill_indices)])] = thisvar_da.values.transpose() + if not np.any(np.bitwise_not(np.isnan(thisvar_gridded))): + if np.all(np.isnan(thisvar_da.values)): + print("Warning: This DataArray (and thus map) is all NaN") + else: + raise RuntimeError("thisvar_gridded was not filled!") + + # Assign coordinates, attributes and name + thisvar_gridded = xr.DataArray(thisvar_gridded, dims=tuple(new_dims), attrs=thisvar_da.attrs) + for dim in new_dims: + if dim == "ivt_str": + values = this_ds.vegtype_str.values + elif dim in thisvar_da.coords: + values = thisvar_da[dim] + else: + values = this_ds[dim].values + thisvar_gridded = thisvar_gridded.assign_coords({dim: values}) + thisvar_gridded.name = thisVar + + # Add FillValue attribute + if fillValue: + thisvar_gridded.attrs["_FillValue"] = fillValue + + return thisvar_gridded + + +# ctsm_pylib can't handle time slicing like Dataset.sel(time=slice("1998-01-01", "2005-12-31")) for some reason. This function tries to fall back to slicing by integers. It should work with both Datasets and DataArrays. +def safer_timeslice(ds, timeSlice, timeVar="time"): + try: + ds = ds.sel({timeVar: timeSlice}) + except: + # If the issue might have been slicing using strings, try to fall back to integer slicing + if ( + isinstance(timeSlice.start, str) + and isinstance(timeSlice.stop, str) + and len(timeSlice.start.split("-")) == 3 + and timeSlice.start.split("-")[1:] == ["01", "01"] + and len(timeSlice.stop.split("-")) == 3 + and ( + timeSlice.stop.split("-")[1:] == ["12", "31"] + or timeSlice.stop.split("-")[1:] == ["01", "01"] + ) + ): + fileyears = np.array([x.year for x in ds.time.values]) + if len(np.unique(fileyears)) != len(fileyears): + print("Could not fall back to integer slicing of years: Time axis not annual") + raise + yStart = int(timeSlice.start.split("-")[0]) + yStop = int(timeSlice.stop.split("-")[0]) + where_in_timeSlice = np.where((fileyears >= yStart) & (fileyears <= yStop))[0] + ds = ds.isel({timeVar: where_in_timeSlice}) + else: + print(f"Could not fall back to integer slicing for timeSlice {timeSlice}") + raise + + return ds + + +# Convert a longitude axis that's -180 to 180 around the international date line to one that's 0 to 360 around the prime meridian. If you pass in a Dataset or DataArray, the "lon" coordinates will be changed. Otherwise, it assumes you're passing in numeric data. +def lon_idl2pm(lons_in, fail_silently=False): + def check_ok(tmp, fail_silently): + msg = "" + + if np.any(tmp > 180): + msg = f"Maximum longitude is already > 180 ({np.max(tmp)})" + elif np.any(tmp < -180): + msg = f"Minimum longitude is < -180 ({np.min(tmp)})" + + if msg == "": + return True + elif fail_silently: + return False + else: + raise ValueError(msg) + + def do_it(tmp): + tmp = tmp + 360 + tmp = np.mod(tmp, 360) + return tmp + + if isinstance(lons_in, (xr.DataArray, xr.Dataset)): + if not check_ok(lons_in.lon.values, fail_silently): + return lons_in + lons_out = lons_in + lons_out = lons_out.assign_coords(lon=do_it(lons_in.lon.values)) + lons_out = make_lon_increasing(lons_out) + else: + if not check_ok(lons_in, fail_silently): + return lons_in + lons_out = do_it(lons_in) + if not is_strictly_increasing(lons_out): + print( + "WARNING: You passed in numeric longitudes to lon_idl2pm() and these have been" + " converted, but they're not strictly increasing." + ) + print( + "To assign the new longitude coordinates to an Xarray object, use" + " xarrayobject.assign_coordinates()! (Pass the object directly in to lon_idl2pm() in" + " order to suppress this message.)" + ) + + return lons_out + + +# Helper function to check that a list is strictly increasing +def is_strictly_increasing(L): + # https://stackoverflow.com/a/4983359/2965321 + return all(x < y for x, y in zip(L, L[1:])) + + +# Ensure that longitude axis coordinates are monotonically increasing +def make_lon_increasing(xr_obj): + if not "lon" in xr_obj.dims: + return xr_obj + + lons = xr_obj.lon.values + if is_strictly_increasing(lons): + return xr_obj + + shift = 0 + while not is_strictly_increasing(lons) and shift < lons.size: + shift = shift + 1 + lons = np.roll(lons, 1, axis=0) + if not is_strictly_increasing(lons): + raise RuntimeError("Unable to rearrange longitude axis so it's monotonically increasing") + + return xr_obj.roll(lon=shift, roll_coords=True) diff --git a/python/ctsm/crop_calendars/generate_gdds.py b/python/ctsm/crop_calendars/generate_gdds.py new file mode 100644 index 0000000000..16e3e130da --- /dev/null +++ b/python/ctsm/crop_calendars/generate_gdds.py @@ -0,0 +1,492 @@ +paramfile_dir = "/glade/campaign/cesm/cesmdata/cseg/inputdata/lnd/clm2/paramdata" + +# Import other shared functions +import os +import inspect +import sys + +# Import the CTSM Python utilities. +# sys.path.insert() is necessary for RXCROPMATURITY to work. The fact that it's calling this script in the RUN phase seems to require the python/ directory to be manually added to path. +_CTSM_PYTHON = os.path.join( + os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, os.pardir, "python" +) +sys.path.insert(1, _CTSM_PYTHON) +import ctsm.crop_calendars.cropcal_module as cc +import ctsm.crop_calendars.generate_gdds_functions as gddfn + +# Import everything else +import os +import sys +import numpy as np +import xarray as xr +import pickle +import datetime as dt +import argparse +import logging + +# Info re: PFT parameter set +my_clm_ver = 51 +my_clm_subver = "c211112" + + +def main( + input_dir=None, + first_season=None, + last_season=None, + sdates_file=None, + hdates_file=None, + output_dir=None, + save_figs=True, + only_make_figs=False, + run1_name=None, + run2_name=None, + land_use_file=None, + first_land_use_year=None, + last_land_use_year=None, + unlimited_season_length=False, + skip_crops=None, + logger=None, +): + # Directories to save output files and figures + if not output_dir: + if only_make_figs: + output_dir = input_dir + else: + output_dir = os.path.join(input_dir, "generate_gdds") + if not unlimited_season_length: + output_dir += ".mxmat" + output_dir += "." + dt.datetime.now().strftime("%Y-%m-%d-%H%M%S") + if not os.path.exists(output_dir): + os.makedirs(output_dir) + outdir_figs = os.path.join(output_dir, "figs") + + # Set up log file and function, if needed + if logger is None: + logging.basicConfig( + level=logging.INFO, + format="", + filename=os.path.join(output_dir, "generate_gdds.log"), + filemode="a", + ) + logger = logging.getLogger("") + + # Disable plotting if any plotting module is unavailable + if save_figs: + try: + import cartopy + import matplotlib + except: + if only_make_figs: + raise RuntimeError("only_make_figs True but not all plotting modules are available") + gddfn.log(logger, "Not all plotting modules are available; disabling save_figs") + save_figs = False + + # Print some info + gddfn.log(logger, f"Saving to {output_dir}") + + # Parse list of crops to skip + if "," in skip_crops: + skip_crops = skip_crops.split(",") + else: + skip_crops = skip_crops.split(" ") + + ########################## + ### Import and process ### + ########################## + + if not only_make_figs: + # Keep 1 extra year to avoid incomplete final growing season for crops harvested after Dec. 31. + y1_import_str = f"{first_season+1}-01-01" + yN_import_str = f"{last_season+2}-01-01" + + gddfn.log( + logger, + f"Importing netCDF time steps {y1_import_str} through {yN_import_str} (years are +1 because of CTSM output naming)", + ) + + pickle_file = os.path.join(output_dir, f"{first_season}-{last_season}.pickle") + h2_ds_file = os.path.join(output_dir, f"{first_season}-{last_season}.h2_ds.nc") + if os.path.exists(pickle_file): + with open(pickle_file, "rb") as f: + ( + first_season, + last_season, + pickle_year, + gddaccum_yp_list, + gddharv_yp_list, + skip_patches_for_isel_nan_lastyear, + lastYear_active_patch_indices_list, + incorrectly_daily, + save_figs, + incl_vegtypes_str, + incl_patches1d_itype_veg, + mxsowings, + skip_crops, + ) = pickle.load(f) + print(f"Will resume import at {pickle_year+1}") + h2_ds = None + else: + incorrectly_daily = False + skip_patches_for_isel_nan_lastyear = np.ndarray([]) + pickle_year = -np.inf + gddaccum_yp_list = [] + gddharv_yp_list = [] + incl_vegtypes_str = None + lastYear_active_patch_indices_list = None + sdates_rx = sdates_file + hdates_rx = hdates_file + + if not unlimited_season_length: + mxmats = cc.import_max_gs_length(paramfile_dir, my_clm_ver, my_clm_subver) + else: + mxmats = None + + for y, thisYear in enumerate(np.arange(first_season + 1, last_season + 3)): + if thisYear <= pickle_year: + continue + + ( + h2_ds, + sdates_rx, + hdates_rx, + gddaccum_yp_list, + gddharv_yp_list, + skip_patches_for_isel_nan_lastyear, + lastYear_active_patch_indices_list, + incorrectly_daily, + incl_vegtypes_str, + incl_patches1d_itype_veg, + mxsowings, + ) = gddfn.import_and_process_1yr( + first_season, + last_season, + y, + thisYear, + sdates_rx, + hdates_rx, + gddaccum_yp_list, + gddharv_yp_list, + skip_patches_for_isel_nan_lastyear, + lastYear_active_patch_indices_list, + incorrectly_daily, + input_dir, + incl_vegtypes_str, + h2_ds_file, + mxmats, + cc.get_gs_len_da, + skip_crops, + logger, + ) + + gddfn.log(logger, f" Saving pickle file ({pickle_file})...") + with open(pickle_file, "wb") as f: + pickle.dump( + [ + first_season, + last_season, + thisYear, + gddaccum_yp_list, + gddharv_yp_list, + skip_patches_for_isel_nan_lastyear, + lastYear_active_patch_indices_list, + incorrectly_daily, + save_figs, + incl_vegtypes_str, + incl_patches1d_itype_veg, + mxsowings, + skip_crops, + ], + f, + protocol=-1, + ) + + if isinstance(incl_vegtypes_str, list): + incl_vegtypes_str = np.array(incl_vegtypes_str) + plot_vegtypes_str = incl_vegtypes_str[ + [i for i, c in enumerate(gddaccum_yp_list) if not isinstance(c, type(None))] + ] + + gddfn.log(logger, "Done") + + if not h2_ds: + h2_ds = xr.open_dataset(h2_ds_file) + + ###################################################### + ### Get and grid mean GDDs in GGCMI growing season ### + ###################################################### + + if not only_make_figs: + longname_prefix = "GDD harvest target for " + + # Could skip this by saving sdates_rx['time_bounds'] + sdates_rx = gddfn.import_rx_dates( + "s", sdates_rx, incl_patches1d_itype_veg, mxsowings, logger + ) + + gddfn.log(logger, "Getting and gridding mean GDDs...") + gdd_maps_ds = gddfn.yp_list_to_ds( + gddaccum_yp_list, h2_ds, incl_vegtypes_str, sdates_rx, longname_prefix, logger + ) + gddharv_maps_ds = gddfn.yp_list_to_ds( + gddharv_yp_list, h2_ds, incl_vegtypes_str, sdates_rx, longname_prefix, logger + ) + + # Fill NAs with dummy values + dummy_fill = -1 + gdd_maps_ds = gdd_maps_ds.fillna(dummy_fill) + gddfn.log(logger, "Done getting and gridding means.") + + # Add dummy variables for crops not actually simulated + gddfn.log(logger, "Adding dummy variables...") + # Unnecessary? + template_ds = xr.open_dataset(sdates_file, decode_times=True) + all_vars = [v.replace("sdate", "gdd") for v in template_ds if "sdate" in v] + all_longnames = [ + template_ds[v].attrs["long_name"].replace("Planting day ", longname_prefix) + " (dummy)" + for v in template_ds + if "sdate" in v + ] + dummy_vars = [] + dummy_longnames = [] + for v, thisVar in enumerate(all_vars): + if thisVar not in gdd_maps_ds: + dummy_vars.append(thisVar) + dummy_longnames.append(all_longnames[v]) + + def make_dummy(thisCrop_gridded, addend): + dummy_gridded = thisCrop_gridded + dummy_gridded.values = dummy_gridded.values * 0 + addend + return dummy_gridded + + for v in gdd_maps_ds: + thisCrop_gridded = gdd_maps_ds[v].copy() + break + dummy_gridded = make_dummy(thisCrop_gridded, -1) + + for v, thisVar in enumerate(dummy_vars): + if thisVar in gdd_maps_ds: + gddfn.error( + logger, f"{thisVar} is already in gdd_maps_ds. Why overwrite it with dummy?" + ) + dummy_gridded.name = thisVar + dummy_gridded.attrs["long_name"] = dummy_longnames[v] + gdd_maps_ds[thisVar] = dummy_gridded + + # Add lon/lat attributes + def add_lonlat_attrs(ds): + ds.lon.attrs = {"long_name": "coordinate_longitude", "units": "degrees_east"} + ds.lat.attrs = {"long_name": "coordinate_latitude", "units": "degrees_north"} + return ds + + gdd_maps_ds = add_lonlat_attrs(gdd_maps_ds) + gddharv_maps_ds = add_lonlat_attrs(gddharv_maps_ds) + + gddfn.log(logger, "Done.") + + ###################### + ### Save to netCDF ### + ###################### + + if not only_make_figs: + gddfn.log(logger, "Saving...") + + # Get output file path + datestr = dt.datetime.now().strftime("%Y%m%d_%H%M%S") + outfile = os.path.join(output_dir, "gdds_" + datestr + ".nc") + + def save_gdds(sdates_file, hdates_file, outfile, gdd_maps_ds, sdates_rx): + # Set up output file from template (i.e., prescribed sowing dates). + template_ds = xr.open_dataset(sdates_file, decode_times=True) + for v in template_ds: + if "sdate" in v: + template_ds = template_ds.drop(v) + template_ds.to_netcdf(path=outfile, format="NETCDF3_CLASSIC") + template_ds.close() + + # Add global attributes + comment = f"Derived from CLM run plus crop calendar input files {os.path.basename(sdates_file) and {os.path.basename(hdates_file)}}." + gdd_maps_ds.attrs = { + "author": "Sam Rabin (sam.rabin@gmail.com)", + "comment": comment, + "created": dt.datetime.now().astimezone().isoformat(), + } + + # Add time_bounds + if "time_bounds" in sdates_rx: + gdd_maps_ds["time_bounds"] = sdates_rx.time_bounds + + # Save cultivar GDDs + gdd_maps_ds.to_netcdf(outfile, mode="w", format="NETCDF3_CLASSIC") + + save_gdds(sdates_file, hdates_file, outfile, gdd_maps_ds, sdates_rx) + + gddfn.log(logger, "Done saving.") + + ######################################## + ### Save things needed for mapmaking ### + ######################################## + + def add_attrs_to_map_ds( + map_ds, incl_vegtypes_str, dummy_fill, outdir_figs, first_season, last_season + ): + return map_ds.assign_attrs( + { + "incl_vegtypes_str": incl_vegtypes_str, + "dummy_fill": dummy_fill, + "outdir_figs": outdir_figs, + "y1": first_season, + "yN": last_season, + } + ) + + if not only_make_figs: + if not os.path.exists(outdir_figs): + os.makedirs(outdir_figs) + + gdd_maps_ds = add_attrs_to_map_ds( + gdd_maps_ds, plot_vegtypes_str, dummy_fill, outdir_figs, first_season, last_season + ) + gddharv_maps_ds = add_attrs_to_map_ds( + gddharv_maps_ds, plot_vegtypes_str, dummy_fill, outdir_figs, first_season, last_season + ) + + gdd_maps_ds.to_netcdf(os.path.join(outdir_figs, "gdd_maps.nc")) + gddharv_maps_ds.to_netcdf(os.path.join(outdir_figs, "gddharv_maps.nc")) + + ################################################# + ### Save before/after map and boxplot figures ### + ################################################# + + if save_figs: + if only_make_figs: + gdd_maps_ds = xr.open_dataset(os.path.join(input_dir, "figs", "gdd_maps.nc")) + gddharv_maps_ds = xr.open_dataset(os.path.join(input_dir, "figs", "gddharv_maps.nc")) + gddfn.make_figures( + first_land_use_year, + last_land_use_year, + land_use_file, + run1_name, + run2_name, + logger, + gdd_maps_ds=gdd_maps_ds, + gddharv_maps_ds=gddharv_maps_ds, + outdir_figs=outdir_figs, + ) + + +if __name__ == "__main__": + ############################### + ### Process input arguments ### + ############################### + parser = argparse.ArgumentParser(description="ADD DESCRIPTION HERE") + + # Required + parser.add_argument( + "-i", + "--input-dir", + help="Directory where run outputs can be found (and where outputs will go). If --only-make-figs, this is the directory with the preprocessed files (e.g., *.pickle file).", + required=True, + ) + parser.add_argument( + "-1", + "--first-season", + help="First growing season to include in calculation of mean", + required=True, + type=int, + ) + parser.add_argument( + "-n", + "-N", + "--last-season", + help="Last growing season to include in calculation of mean", + required=True, + type=int, + ) + parser.add_argument( + "-o", + "--output-dir", + help="Output directory. Default is auto-generated subdir of -i/--input-dir.", + ) + parser.add_argument( + "-sd", "--sdates-file", help="File of prescribed sowing dates", required=True + ) + parser.add_argument( + "-hd", "--hdates-file", help="File of prescribed harvest dates", required=True + ) + + # Optional + figsgroup = parser.add_mutually_exclusive_group() + figsgroup.add_argument( + "--dont-save-figs", help="Do not save figures", action="store_true", default=False + ) + figsgroup.add_argument( + "--only-make-figs", + help="Use preprocessed files to make figures only", + action="store_true", + default=False, + ) + parser.add_argument( + "--run1-name", help="Name of original values to show in figures", default="Old" + ) + parser.add_argument("--run2-name", help="Name of new values to show in figures", default="New") + parser.add_argument( + "-lu", + "--land-use-file", + help="Path to CLM land use timeseries file, for masking figures", + default=None, + ) + parser.add_argument( + "--first-land-use-year", + help="First year in land use file to use for masking. Default --first-season.", + default=None, + type=int, + ) + parser.add_argument( + "--last-land-use-year", + help="Last year in land use file to use for masking. Default --last-season.", + default=None, + type=int, + ) + parser.add_argument( + "--unlimited-season-length", + help="Limit mean growing season length based on CLM CFT parameter mxmat.", + action="store_true", + default=False, + ) + parser.add_argument( + "--skip-crops", + help="Skip processing of these crops. Comma- or space-separated list.", + type=str, + default="", + ) + + # Get arguments + args = parser.parse_args(sys.argv[1:]) + for k, v in sorted(vars(args).items()): + print(f"{k}: {v}") + save_figs = not args.dont_save_figs + + # Call main() + main( + input_dir=args.input_dir, + first_season=args.first_season, + last_season=args.last_season, + sdates_file=args.sdates_file, + hdates_file=args.hdates_file, + output_dir=args.output_dir, + save_figs=save_figs, + only_make_figs=args.only_make_figs, + run1_name=args.run1_name, + run2_name=args.run2_name, + land_use_file=args.land_use_file, + first_land_use_year=args.first_land_use_year, + last_land_use_year=args.last_land_use_year, + unlimited_season_length=args.unlimited_season_length, + skip_crops=args.skip_crops, + ) + +# main(input_dir="/Users/Shared/CESM_runs/tests_10x15_20230329_gddgen/202303301820", +# sdates_file="/Users/Shared/CESM_work/crop_dates_mostrice/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", +# hdates_file="/Users/Shared/CESM_work/crop_dates_mostrice/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", +# first_season=1997, last_season=2003, +# save_figs=False) diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py new file mode 100644 index 0000000000..cb05f1920d --- /dev/null +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -0,0 +1,1215 @@ +import numpy as np +import xarray as xr +import warnings +import os +import glob +import datetime as dt +from importlib import util as importlib_util + +# Import the CTSM Python utilities. +# sys.path.insert() is necessary for RXCROPMATURITY to work. The fact that it's calling this script in the RUN phase seems to require the python/ directory to be manually added to path. +_CTSM_PYTHON = os.path.join( + os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, os.pardir, "python" +) +import sys + +sys.path.insert(1, _CTSM_PYTHON) +import ctsm.crop_calendars.cropcal_utils as utils +import ctsm.crop_calendars.cropcal_module as cc + +can_plot = True +try: + from ctsm.crop_calendars.cropcal_figs_module import * + from matplotlib.transforms import Bbox + + warnings.filterwarnings( + "ignore", + message="__len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the number of parts of a multi-part geometry.", + ) + warnings.filterwarnings( + "ignore", + message="Iteration over multi-part geometries is deprecated and will be removed in Shapely 2.0. Use the `geoms` property to access the constituent parts of a multi-part geometry.", + ) + + print("Will (attempt to) produce harvest requirement map figure files.") + +except: + print("Will NOT produce harvest requirement map figure files.") + can_plot = False + + +# Functions to simultaneously print to console and to log file +def log(logger, string): + print(string) + logger.info(string) + + +def error(logger, string): + logger.error(string) + raise RuntimeError(string) + + +def check_sdates(dates_ds, sdates_rx, logger, verbose=False): + log(logger, " Checking that input and output sdates match...") + + sdates_grid = utils.grid_one_variable(dates_ds, "SDATES") + + all_ok = True + any_found = False + vegtypes_skipped = [] + vegtypes_included = [] + for i, vt_str in enumerate(dates_ds.vegtype_str.values): + # Input + vt = dates_ds.ivt.values[i] + thisVar = f"gs1_{vt}" + if thisVar not in sdates_rx: + vegtypes_skipped = vegtypes_skipped + [vt_str] + # log(logger, f" {vt_str} ({vt}) SKIPPED...") + continue + vegtypes_included = vegtypes_included + [vt_str] + any_found = True + if verbose: + log(logger, f" {vt_str} ({vt})...") + in_map = sdates_rx[thisVar].squeeze(drop=True) + + # Output + out_map = sdates_grid.sel(ivt_str=vt_str).squeeze(drop=True) + + # Check for differences + diff_map = out_map - in_map + diff_map_notnan = diff_map.values[np.invert(np.isnan(diff_map.values))] + if np.any(diff_map_notnan): + log(logger, f"Difference(s) found in {vt_str}") + here = np.where(diff_map_notnan) + log(logger, "in:") + in_map_notnan = in_map.values[np.invert(np.isnan(diff_map.values))] + log(logger, in_map_notnan[here][0:4]) + out_map_notnan = out_map.values[np.invert(np.isnan(diff_map.values))] + log(logger, "out:") + log(logger, out_map_notnan[here][0:4]) + log(logger, "diff:") + log(logger, diff_map_notnan[here][0:4]) + all_ok = False + + if not (any_found): + error(logger, "No matching variables found in sdates_rx!") + + # Sanity checks for included vegetation types + vegtypes_skipped = np.unique([x.replace("irrigated_", "") for x in vegtypes_skipped]) + vegtypes_skipped_weird = [x for x in vegtypes_skipped if x in vegtypes_included] + if np.array_equal(vegtypes_included, [x.replace("irrigated_", "") for x in vegtypes_included]): + log(logger, "\nWARNING: No irrigated crops included!!!\n") + elif vegtypes_skipped_weird: + log( + logger, + f"\nWarning: Some crop types had output rainfed patches but no irrigated patches: {vegtypes_skipped_weird}", + ) + + if all_ok: + log(logger, " ✅ Input and output sdates match!") + else: + error(logger, " ❌ Input and output sdates differ.") + + +def import_rx_dates(s_or_h, date_inFile, incl_patches1d_itype_veg, mxsowings, logger): + if isinstance(date_inFile, xr.Dataset): + return date_inFile + elif not isinstance(date_inFile, str): + error( + logger, + f"Importing {s_or_h}dates_rx: Expected date_inFile to be str or DataArray, not {type(date_inFile)}", + ) + + # Which vegetation types were simulated? + itype_veg_toImport = np.unique(incl_patches1d_itype_veg) + + date_varList = [] + for i in itype_veg_toImport: + for g in np.arange(mxsowings): + thisVar = f"{s_or_h}date{g+1}_{i}" + date_varList = date_varList + [thisVar] + + ds = utils.import_ds(date_inFile, myVars=date_varList) + + for v in ds: + ds = ds.rename({v: v.replace(f"{s_or_h}date", "gs")}) + + return ds + + +def thisCrop_map_to_patches(lon_points, lat_points, map_ds, vegtype_int): + # xarray pointwise indexing; see https://xarray.pydata.org/en/stable/user-guide/indexing.html#more-advanced-indexing + return ( + map_ds[f"gs1_{vegtype_int}"] + .sel(lon=xr.DataArray(lon_points, dims="patch"), lat=xr.DataArray(lat_points, dims="patch")) + .squeeze(drop=True) + ) + + +# Get and grid mean GDDs in GGCMI growing season +def yp_list_to_ds(yp_list, daily_ds, incl_vegtypes_str, dates_rx, longname_prefix, logger): + # Get means + warnings.filterwarnings( + "ignore", message="Mean of empty slice" + ) # Happens when you do np.nanmean() of an all-NaN array (or slice, if doing selected axis/es) + p_list = [np.nanmean(x, axis=0) if not isinstance(x, type(None)) else x for x in yp_list] + warnings.filterwarnings("always", message="Mean of empty slice") + + if isinstance(incl_vegtypes_str, xr.DataArray): + incl_vegtypes_str = incl_vegtypes_str.values + + # Grid + ds_out = xr.Dataset() + for c, ra in enumerate(p_list): + if isinstance(ra, type(None)): + continue + thisCrop_str = incl_vegtypes_str[c] + log(logger, f" {thisCrop_str}...") + newVar = f"gdd1_{utils.ivt_str2int(thisCrop_str)}" + ds = daily_ds.isel( + patch=np.where(daily_ds.patches1d_itype_veg_str.values == thisCrop_str)[0] + ) + template_da = ds.patches1d_itype_veg_str + da = xr.DataArray( + data=ra, + coords=template_da.coords, + attrs={"units": "GDD", "long_name": f"{longname_prefix}{thisCrop_str}"}, + ) + + # Grid this crop + ds["tmp"] = da + da_gridded = utils.grid_one_variable(ds, "tmp", vegtype=thisCrop_str).squeeze(drop=True) + + # Add singleton time dimension and save to output Dataset + da_gridded = da_gridded.expand_dims(time=dates_rx.time) + ds_out[newVar] = da_gridded + + return ds_out + + +def import_and_process_1yr( + y1, + yN, + y, + thisYear, + sdates_rx, + hdates_rx, + gddaccum_yp_list, + gddharv_yp_list, + skip_patches_for_isel_nan_lastyear, + lastYear_active_patch_indices_list, + incorrectly_daily, + indir, + incl_vegtypes_str_in, + h2_ds_file, + mxmats, + get_gs_len_da, + skip_crops, + logger, +): + save_figs = True + log(logger, f"netCDF year {thisYear}...") + log(logger, dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + + # Without dask, this can take a LONG time at resolutions finer than 2-deg + if importlib_util.find_spec("dask"): + chunks = {"time": 1} + else: + chunks = None + + # Get h2 file (list) + h1_pattern = os.path.join(indir, "*h1.*.nc") + h1_filelist = glob.glob(h1_pattern) + if not h1_filelist: + h1_pattern = os.path.join(indir, "*h1.*.nc.base") + h1_filelist = glob.glob(h1_pattern) + if not h1_filelist: + error(logger, "No files found matching pattern '*h1.*.nc(.base)'") + + # Get list of crops to include + if skip_crops is not None: + crops_to_read = [c for c in utils.define_mgdcrop_list() if c not in skip_crops] + else: + crops_to_read = utils.define_mgdcrop_list() + + print(h1_filelist) + dates_ds = utils.import_ds( + h1_filelist, + myVars=["SDATES", "HDATES"], + myVegtypes=crops_to_read, + timeSlice=slice(f"{thisYear}-01-01", f"{thisYear}-12-31"), + chunks=chunks, + ) + + if dates_ds.dims["time"] > 1: + if dates_ds.dims["time"] == 365: + if not incorrectly_daily: + log( + logger, + " ℹ️ You saved SDATES and HDATES daily, but you only needed annual. Fixing.", + ) + incorrectly_daily = True + dates_ds = dates_ds.isel(time=-1) + else: + dates_ds = dates_ds.isel(time=0) + + # Make sure NaN masks match + sdates_all_nan = ( + np.sum(~np.isnan(dates_ds.SDATES.values), axis=dates_ds.SDATES.dims.index("mxsowings")) == 0 + ) + hdates_all_nan = ( + np.sum(~np.isnan(dates_ds.HDATES.values), axis=dates_ds.HDATES.dims.index("mxharvests")) + == 0 + ) + N_unmatched_nans = np.sum(sdates_all_nan != hdates_all_nan) + if N_unmatched_nans > 0: + error(logger, "Output SDATE and HDATE NaN masks do not match.") + if np.sum(~np.isnan(dates_ds.SDATES.values)) == 0: + error(logger, "All SDATES are NaN!") + + # Just work with non-NaN patches for now + skip_patches_for_isel_nan = np.where(sdates_all_nan)[0] + incl_patches_for_isel_nan = np.where(~sdates_all_nan)[0] + different_nan_mask = y > 0 and not np.array_equal( + skip_patches_for_isel_nan_lastyear, skip_patches_for_isel_nan + ) + if different_nan_mask: + log(logger, " Different NaN mask than last year") + incl_thisyr_but_nan_lastyr = [ + dates_ds.patch.values[p] + for p in incl_patches_for_isel_nan + if p in skip_patches_for_isel_nan_lastyear + ] + else: + incl_thisyr_but_nan_lastyr = [] + skipping_patches_for_isel_nan = len(skip_patches_for_isel_nan) > 0 + if skipping_patches_for_isel_nan: + log( + logger, + f" Ignoring {len(skip_patches_for_isel_nan)} patches with all-NaN sowing and harvest dates.", + ) + dates_incl_ds = dates_ds.isel(patch=incl_patches_for_isel_nan) + else: + dates_incl_ds = dates_ds + incl_patches1d_itype_veg = dates_incl_ds.patches1d_itype_veg + + if y == 0: + incl_vegtypes_str = [c for c in dates_incl_ds.vegtype_str.values if c not in skip_crops] + else: + incl_vegtypes_str = incl_vegtypes_str_in + if isinstance(incl_vegtypes_str, xr.DataArray): + incl_vegtypes_str = incl_vegtypes_str.values + if isinstance(incl_vegtypes_str, np.ndarray): + incl_vegtypes_str = list(incl_vegtypes_str) + if incl_vegtypes_str != list(dates_incl_ds.vegtype_str.values): + error( + logger, + f"Included veg types differ. Previously {incl_vegtypes_str}, now {dates_incl_ds.vegtype_str.values}", + ) + + if np.sum(~np.isnan(dates_incl_ds.SDATES.values)) == 0: + error(logger, "All SDATES are NaN after ignoring those patches!") + + # Some patches can have -1 sowing date?? Hopefully just an artifact of me incorrectly saving SDATES/HDATES daily. + mxsowings = dates_ds.dims["mxsowings"] + mxsowings_dim = dates_ds.SDATES.dims.index("mxsowings") + skip_patches_for_isel_sdatelt1 = np.where(dates_incl_ds.SDATES.values < 1)[1] + skipping_patches_for_isel_sdatelt1 = len(skip_patches_for_isel_sdatelt1) > 0 + if skipping_patches_for_isel_sdatelt1: + unique_hdates = np.unique( + dates_incl_ds.HDATES.isel(mxharvests=0, patch=skip_patches_for_isel_sdatelt1).values + ) + if incorrectly_daily and list(unique_hdates) == [364]: + log( + logger, + f" ❗ {len(skip_patches_for_isel_sdatelt1)} patches have SDATE < 1, but this might have just been because of incorrectly daily outputs. Setting them to 365.", + ) + new_sdates_ar = dates_incl_ds.SDATES.values + if mxsowings_dim != 0: + error(logger, "Code this up") + new_sdates_ar[0, skip_patches_for_isel_sdatelt1] = 365 + dates_incl_ds["SDATES"] = xr.DataArray( + data=new_sdates_ar, + coords=dates_incl_ds["SDATES"].coords, + attrs=dates_incl_ds["SDATES"].attrs, + ) + else: + error( + logger, + f"{len(skip_patches_for_isel_sdatelt1)} patches have SDATE < 1. Unique affected hdates: {unique_hdates}", + ) + + # Some patches can have -1 harvest date?? Hopefully just an artifact of me incorrectly saving SDATES/HDATES daily. Can also happen if patch wasn't active last year + mxharvests = dates_ds.dims["mxharvests"] + mxharvests_dim = dates_ds.HDATES.dims.index("mxharvests") + # If a patch was inactive last year but was either (a) harvested the last time it was active or (b) was never active, it will have -1 as its harvest date this year. Such instances are okay. + hdates_thisyr = dates_incl_ds.HDATES.isel(mxharvests=0) + skip_patches_for_isel_hdatelt1 = np.where(hdates_thisyr.values < 1)[0] + skipping_patches_for_isel_hdatelt1 = len(skip_patches_for_isel_hdatelt1) > 0 + if incl_thisyr_but_nan_lastyr and list(skip_patches_for_isel_hdatelt1): + hdates_thisyr_where_nan_lastyr = hdates_thisyr.sel(patch=incl_thisyr_but_nan_lastyr) + sdates_thisyr_where_nan_lastyr = dates_incl_ds.SDATES.isel(mxsowings=0).sel( + patch=incl_thisyr_but_nan_lastyr + ) + if np.any(hdates_thisyr_where_nan_lastyr < 1): + # patches_to_fix = hdates_thisyr_where_nan_lastyr.isel(patch=np.where(hdates_thisyr_where_nan_lastyr < 1)[0]).patch.values + new_hdates = dates_incl_ds.HDATES.values + if mxharvests_dim != 0: + error(logger, "Code this up") + patch_list = list(hdates_thisyr.patch.values) + here = [patch_list.index(x) for x in incl_thisyr_but_nan_lastyr] + log( + logger, + f" ❗ {len(here)} patches have harvest date -1 because they weren't active last year (and were either never active or were harvested when last active). Ignoring, but you should have done a run with patches always active if they are ever active in the real LU timeseries.", + ) + new_hdates[0, here] = sdates_thisyr_where_nan_lastyr.values - 1 + dates_incl_ds["HDATES"] = xr.DataArray( + data=new_hdates, + coords=dates_incl_ds.HDATES.coords, + attrs=dates_incl_ds.HDATES.attrs, + ) + # Recalculate these + skip_patches_for_isel_hdatelt1 = np.where( + dates_incl_ds.HDATES.isel(mxharvests=0).values < 1 + )[0] + skipping_patches_for_isel_hdatelt1 = len(skip_patches_for_isel_hdatelt1) > 0 + + # Resolve other issues + if skipping_patches_for_isel_hdatelt1: + unique_sdates = np.unique( + dates_incl_ds.SDATES.isel(patch=skip_patches_for_isel_hdatelt1).values + ) + if incorrectly_daily and list(unique_sdates) == [1]: + log( + logger, + f" ❗ {len(skip_patches_for_isel_hdatelt1)} patches have HDATE < 1??? Seems like this might have just been because of incorrectly daily outputs; setting them to 365.", + ) + new_hdates_ar = dates_incl_ds.HDATES.values + if mxharvests_dim != 0: + error(logger, "Code this up") + new_hdates_ar[0, skip_patches_for_isel_hdatelt1] = 365 + dates_incl_ds["HDATES"] = xr.DataArray( + data=new_hdates_ar, + coords=dates_incl_ds["HDATES"].coords, + attrs=dates_incl_ds["HDATES"].attrs, + ) + else: + error( + logger, + f"{len(skip_patches_for_isel_hdatelt1)} patches have HDATE < 1. Possible causes:\n * Not using constant crop areas (e.g., flanduse_timeseries from make_lu_for_gddgen.py)\n * Not skipping the first 2 years of output\nUnique affected sdates: {unique_sdates}", + ) + + # Make sure there was only one harvest per year + N_extra_harv = np.sum( + np.nanmax( + dates_incl_ds.HDATES.isel(mxharvests=slice(1, mxharvests)).values, axis=mxharvests_dim + ) + >= 1 + ) + if N_extra_harv > 0: + error(logger, f"{N_extra_harv} patches have >1 harvest.") + + # Make sure harvest happened the day before sowing + sdates_clm = dates_incl_ds.SDATES.values.squeeze() + hdates_clm = dates_incl_ds.HDATES.isel(mxharvests=0).values + diffdates_clm = sdates_clm - hdates_clm + diffdates_clm[(sdates_clm == 1) & (hdates_clm == 365)] = 1 + if list(np.unique(diffdates_clm)) != [1]: + error(logger, f"Not all sdates-hdates are 1: {np.unique(diffdates_clm)}") + + # Import expected sowing dates. This will also be used as our template output file. + imported_sdates = isinstance(sdates_rx, str) + sdates_rx = import_rx_dates("s", sdates_rx, incl_patches1d_itype_veg, mxsowings, logger) + check_sdates(dates_incl_ds, sdates_rx, logger) + + # Import hdates, if needed + imported_hdates = isinstance(hdates_rx, str) + hdates_rx_orig = import_rx_dates( + "h", hdates_rx, incl_patches1d_itype_veg, mxsowings, logger + ) # Yes, mxsowings even when importing harvests + + # Limit growing season to CLM max growing season length, if needed + if mxmats and (imported_sdates or imported_hdates): + print(" Limiting growing season length...") + hdates_rx = hdates_rx_orig.copy() + for v in hdates_rx_orig: + if v == "time_bounds": + continue + + # Get max growing season length + vegtype_int = int( + v.split("_")[1] + ) # netCDF variable name v should be something like gs1_17 + vegtype_str = utils.ivt_int2str(vegtype_int) + if vegtype_str == "soybean": + vegtype_str = "temperate_soybean" + elif vegtype_str == "irrigated_soybean": + vegtype_str = "irrigated_temperate_soybean" + + mxmat = mxmats[vegtype_str] + if np.isinf(mxmat): + print(f" Not limiting {vegtype_str}: No mxmat value") + continue + + # Get "prescribed" growing season length + gs_len_rx_da = get_gs_len_da(hdates_rx_orig[v] - sdates_rx[v]) + not_ok = gs_len_rx_da.values > mxmat + if not np.any(not_ok): + print(f" Not limiting {vegtype_str}: No rx season > {mxmat} days") + continue + + hdates_limited = hdates_rx_orig[v].copy().values + hdates_limited[np.where(not_ok)] = sdates_rx[v].values[np.where(not_ok)] + mxmat + hdates_limited[np.where(hdates_limited > 365)] -= 365 + if np.any(hdates_limited < 1): + raise RuntimeError("Limited hdates < 1") + elif np.any(hdates_limited > 365): + raise RuntimeError("Limited hdates > 365") + hdates_rx[v] = xr.DataArray( + data=hdates_limited, coords=hdates_rx_orig[v].coords, attrs=hdates_rx_orig[v].attrs + ) + print( + f" Limited {vegtype_str} growing season length to {mxmat}. Longest was {int(np.max(gs_len_rx_da.values))}, now {int(np.max(get_gs_len_da(hdates_rx[v] - sdates_rx[v]).values))}." + ) + else: + hdates_rx = hdates_rx_orig + + log(logger, f" Importing accumulated GDDs...") + clm_gdd_var = "GDDACCUM" + myVars = [clm_gdd_var, "GDDHARV"] + pattern = os.path.join(indir, f"*h2.{thisYear-1}-01-01*.nc") + h2_files = glob.glob(pattern) + if not h2_files: + pattern = os.path.join(indir, f"*h2.{thisYear-1}-01-01*.nc.base") + h2_files = glob.glob(pattern) + if not h2_files: + error(logger, f"No files found matching pattern '*h2.{thisYear-1}-01-01*.nc(.base)'") + h2_ds = utils.import_ds( + h2_files, + myVars=myVars, + myVegtypes=crops_to_read, + chunks=chunks, + ) + + # Restrict to patches we're including + if skipping_patches_for_isel_nan: + if not np.array_equal(dates_ds.patch.values, h2_ds.patch.values): + error(logger, "dates_ds and h2_ds don't have the same patch list!") + h2_incl_ds = h2_ds.isel(patch=incl_patches_for_isel_nan) + else: + h2_incl_ds = h2_ds + + if not np.any(h2_incl_ds[clm_gdd_var].values != 0): + error(logger, f"All {clm_gdd_var} values are zero!") + + # Get standard datetime axis for outputs + Nyears = yN - y1 + 1 + + if len(gddaccum_yp_list) == 0: + lastYear_active_patch_indices_list = [None for vegtype_str in incl_vegtypes_str] + gddaccum_yp_list = [None for vegtype_str in incl_vegtypes_str] + if save_figs: + gddharv_yp_list = [None for vegtype_str in incl_vegtypes_str] + + incl_vegtype_indices = [] + for v, vegtype_str in enumerate(incl_vegtypes_str): + if vegtype_str in skip_crops: + log(logger, f" SKIPPING {vegtype_str}") + continue + + vegtype_int = utils.vegtype_str2int(vegtype_str)[0] + thisCrop_full_patchlist = list(utils.xr_flexsel(h2_ds, vegtype=vegtype_str).patch.values) + + # Get time series for each patch of this type + thisCrop_ds = utils.xr_flexsel(h2_incl_ds, vegtype=vegtype_str) + thisCrop_gddaccum_da = thisCrop_ds[clm_gdd_var] + if save_figs: + thisCrop_gddharv_da = thisCrop_ds["GDDHARV"] + if not thisCrop_gddaccum_da.size: + continue + log(logger, f" {vegtype_str}...") + incl_vegtype_indices = incl_vegtype_indices + [v] + + # Get prescribed harvest dates for these patches + lon_points = thisCrop_ds.patches1d_lon.values + lat_points = thisCrop_ds.patches1d_lat.values + thisCrop_hdates_rx = thisCrop_map_to_patches(lon_points, lat_points, hdates_rx, vegtype_int) + + if isinstance(gddaccum_yp_list[v], type(None)): + gddaccum_yp_list[v] = np.full((Nyears + 1, len(thisCrop_full_patchlist)), np.nan) + if save_figs: + gddharv_yp_list[v] = np.full((Nyears + 1, len(thisCrop_full_patchlist)), np.nan) + + # Get the accumulated GDDs at each prescribed harvest date + gddaccum_atharv_p = np.full(thisCrop_hdates_rx.shape, np.nan) + if save_figs: + gddharv_atharv_p = np.full(thisCrop_hdates_rx.shape, np.nan) + unique_rx_hdates = np.unique(thisCrop_hdates_rx.values) + # Build an indexing tuple + patches = [] + i_patches = [] + i_times = [] + for i, hdate in enumerate(unique_rx_hdates): + here = np.where(thisCrop_hdates_rx.values == hdate)[0] + patches += list(thisCrop_gddaccum_da.patch.values[here]) + i_patches += list(here) + i_times += list(np.full((len(here),), int(hdate - 1))) + # Sort back to correct order + if not np.all( + thisCrop_gddaccum_da.patch.values[:-1] <= thisCrop_gddaccum_da.patch.values[1:] + ): + error(logger, "This code depends on DataArray patch list being sorted.") + sortorder = np.argsort(patches) + i_patches = list(np.array(i_patches)[np.array(sortorder)]) + i_times = list(np.array(i_times)[np.array(sortorder)]) + # Select using the indexing tuple + gddaccum_atharv_p = thisCrop_gddaccum_da.values[(i_times, i_patches)] + if save_figs: + gddharv_atharv_p = thisCrop_gddharv_da.values[(i_times, i_patches)] + if np.any(np.isnan(gddaccum_atharv_p)): + log( + logger, + f" ❗ {np.sum(np.isnan(gddaccum_atharv_p))}/{len(gddaccum_atharv_p)} NaN after extracting GDDs accumulated at harvest", + ) + if save_figs and np.any(np.isnan(gddharv_atharv_p)): + log( + logger, + f" ❗ {np.sum(np.isnan(gddharv_atharv_p))}/{len(gddharv_atharv_p)} NaN after extracting GDDHARV", + ) + + # Assign these to growing seasons based on whether gs crossed new year + thisYear_active_patch_indices = [ + thisCrop_full_patchlist.index(x) for x in thisCrop_ds.patch.values + ] + thisCrop_sdates_rx = thisCrop_map_to_patches(lon_points, lat_points, sdates_rx, vegtype_int) + where_gs_thisyr = np.where(thisCrop_sdates_rx < thisCrop_hdates_rx)[0] + tmp_gddaccum = np.full(thisCrop_sdates_rx.shape, np.nan) + tmp_gddaccum[where_gs_thisyr] = gddaccum_atharv_p[where_gs_thisyr] + if save_figs: + tmp_gddharv = np.full(tmp_gddaccum.shape, np.nan) + tmp_gddharv[where_gs_thisyr] = gddharv_atharv_p[where_gs_thisyr] + if y > 0: + lastYear_active_patch_indices = lastYear_active_patch_indices_list[v] + where_gs_lastyr = np.where(thisCrop_sdates_rx > thisCrop_hdates_rx)[0] + active_thisYear_where_gs_lastyr_indices = [ + thisYear_active_patch_indices[x] for x in where_gs_lastyr + ] + if not np.array_equal(lastYear_active_patch_indices, thisYear_active_patch_indices): + if incorrectly_daily: + log( + logger, + " ❗ This year's active patch indices differ from last year's. Allowing because this might just be an artifact of incorrectly daily outputs, BUT RESULTS MUST NOT BE TRUSTED.", + ) + else: + error(logger, "This year's active patch indices differ from last year's.") + # Make sure we're not about to overwrite any existing values. + if np.any( + ~np.isnan(gddaccum_yp_list[v][y - 1, active_thisYear_where_gs_lastyr_indices]) + ): + if incorrectly_daily: + log( + logger, + " ❗ Unexpected non-NaN for last season's GDD accumulation. Allowing because this might just be an artifact of incorrectly daily outputs, BUT RESULTS MUST NOT BE TRUSTED.", + ) + else: + error(logger, "Unexpected non-NaN for last season's GDD accumulation") + if save_figs and np.any( + ~np.isnan(gddharv_yp_list[v][y - 1, active_thisYear_where_gs_lastyr_indices]) + ): + if incorrectly_daily: + log( + logger, + " ❗ Unexpected non-NaN for last season's GDDHARV. Allowing because this might just be an artifact of incorrectly daily outputs, BUT RESULTS MUST NOT BE TRUSTED.", + ) + else: + error(logger, "Unexpected non-NaN for last season's GDDHARV") + # Fill. + gddaccum_yp_list[v][y - 1, active_thisYear_where_gs_lastyr_indices] = gddaccum_atharv_p[ + where_gs_lastyr + ] + if save_figs: + gddharv_yp_list[v][ + y - 1, active_thisYear_where_gs_lastyr_indices + ] = gddharv_atharv_p[where_gs_lastyr] + # Last year's season should be filled out now; make sure. + if np.any( + np.isnan(gddaccum_yp_list[v][y - 1, active_thisYear_where_gs_lastyr_indices]) + ): + if incorrectly_daily: + log( + logger, + " ❗ Unexpected NaN for last season's GDD accumulation. Allowing because this might just be an artifact of incorrectly daily outputs, BUT RESULTS MUST NOT BE TRUSTED.", + ) + else: + error(logger, "Unexpected NaN for last season's GDD accumulation.") + if save_figs and np.any( + np.isnan(gddharv_yp_list[v][y - 1, active_thisYear_where_gs_lastyr_indices]) + ): + if incorrectly_daily: + log( + logger, + " ❗ Unexpected NaN for last season's GDDHARV. Allowing because this might just be an artifact of incorrectly daily outputs, BUT RESULTS MUST NOT BE TRUSTED.", + ) + else: + error(logger, "Unexpected NaN for last season's GDDHARV.") + gddaccum_yp_list[v][y, thisYear_active_patch_indices] = tmp_gddaccum + if save_figs: + gddharv_yp_list[v][y, thisYear_active_patch_indices] = tmp_gddharv + + # Make sure that NaN masks are the same for this year's sdates and 'filled-out' GDDs from last year + if y > 0: + nanmask_output_sdates = np.isnan( + dates_ds.SDATES.isel( + mxsowings=0, patch=np.where(dates_ds.patches1d_itype_veg_str == vegtype_str)[0] + ).values + ) + nanmask_output_gdds_lastyr = np.isnan(gddaccum_yp_list[v][y - 1, :]) + if not np.array_equal(nanmask_output_gdds_lastyr, nanmask_output_sdates): + if incorrectly_daily: + log( + logger, + " ❗ NaN masks differ between this year's sdates and 'filled-out' GDDs from last year. Allowing because this might just be an artifact of incorrectly daily outputs, BUT RESULTS MUST NOT BE TRUSTED.", + ) + else: + error( + logger, + "NaN masks differ between this year's sdates and 'filled-out' GDDs from last year", + ) + lastYear_active_patch_indices_list[v] = thisYear_active_patch_indices + + skip_patches_for_isel_nan_lastyear = skip_patches_for_isel_nan + + # Could save space by only saving variables needed for gridding + log(logger, " Saving h2_ds...") + h2_ds.to_netcdf(h2_ds_file) + + return ( + h2_ds, + sdates_rx, + hdates_rx, + gddaccum_yp_list, + gddharv_yp_list, + skip_patches_for_isel_nan_lastyear, + lastYear_active_patch_indices_list, + incorrectly_daily, + incl_vegtypes_str, + incl_patches1d_itype_veg, + mxsowings, + ) + + +def get_multicrop_maps(ds, theseVars, crop_fracs_yx, dummy_fill, gdd_units): + # Get GDDs for these crops + da_eachCFT = xr.concat((ds[x] for i, x in enumerate(theseVars)), dim="cft") + if "time" in ds.dims: + da_eachCFT = da_eachCFT.isel(time=0, drop=True) + da_eachCFT = da_eachCFT.where(da_eachCFT != dummy_fill) + da_eachCFT.attrs["units"] = gdd_units + + # What are the maximum differences seen between different crop types? + if len(theseVars) > 1: + maxDiff = np.nanmax(da_eachCFT.max(dim="cft") - da_eachCFT.min(dim="cft")) + if maxDiff > 0: + print(f" Max difference among crop types: {np.round(maxDiff)}") + + if crop_fracs_yx is None: + return da_eachCFT.isel(cft=0, drop=True) + + # Warn if GDD is NaN anywhere that there is area + da_eachCFT["cft"] = crop_fracs_yx["cft"] + gddNaN_areaPos = np.isnan(da_eachCFT) & (crop_fracs_yx > 0) + if np.any(gddNaN_areaPos): + total_bad_croparea = np.nansum(crop_fracs_yx.where(gddNaN_areaPos).values) + total_croparea = np.nansum(crop_fracs_yx.values) + print( + f" GDD reqt NaN but area positive ({np.round(total_bad_croparea/total_croparea*100, 1)}% of this crop's area)" + ) + + # Get areas and weights, masking cell-crops with NaN GDDs + crop_fracs_yx = crop_fracs_yx.where(~np.isnan(da_eachCFT)) + crop_area_yx = crop_fracs_yx.sum(dim="cft") + weights_yx = crop_fracs_yx / crop_area_yx + weights_sum_gt0 = weights_yx.sum(dim="cft").where(weights_yx > 0) + assert np.isclose(np.nanmin(weights_sum_gt0.values), 1.0) + assert np.isclose(np.nanmax(weights_sum_gt0.values), 1.0) + + # Mask GDDs and weights where there is no area + da_eachCFT = da_eachCFT.where(crop_fracs_yx > 0) + if len(theseVars) == 1: + return da_eachCFT.isel(cft=0, drop=True) + weights_yx = weights_yx.where(crop_fracs_yx > 0) + weights_sum = weights_yx.sum(dim="cft").where(crop_area_yx > 0) + assert np.isclose(np.nanmin(weights_sum.values), 1.0) + assert np.isclose(np.nanmax(weights_sum.values), 1.0) + + # Ensure grid match between GDDs and weights + if not np.array_equal(da_eachCFT["lon"].values, weights_yx["lon"].values): + raise RuntimeError("lon mismatch") + if not np.array_equal(da_eachCFT["lat"].values, weights_yx["lat"].values): + raise RuntimeError("lat mismatch") + + # Get area-weighted mean GDD requirements for all crops + da = (da_eachCFT * weights_yx).sum(dim="cft") + da.attrs["units"] = gdd_units + da = da.where(crop_area_yx > 0) + + # Ensure that weighted mean is between each cell's min and max + whereBad = (da < da_eachCFT.min(dim="cft")) | (da > da_eachCFT.max(dim="cft")) + if np.any(whereBad): + where_belowMin = da.where(da < da_eachCFT.min(dim="cft")) + worst_belowMin = np.min((da_eachCFT.min(dim="cft") - where_belowMin).values) + where_aboveMax = da.where(da > da_eachCFT.max(dim="cft")) + worst_aboveMax = np.max((where_aboveMax - da_eachCFT.max(dim="cft")).values) + worst = max(worst_belowMin, worst_aboveMax) + tol = 1e-12 + if worst > 1e-12: + raise RuntimeError( + f"Some value is outside expected range by {worst} (exceeds tolerance {tol})" + ) + + return da + + +if can_plot: + + def get_bounds_ncolors(gdd_spacing, diff_map_yx): + vmax = np.floor(np.nanmax(diff_map_yx.values) / gdd_spacing) * gdd_spacing + vmin = -vmax + epsilon = np.nextafter(0, 1) + bounds = list(np.arange(vmin, vmax, gdd_spacing)) + [vmax - epsilon] + if 0 in bounds: + bounds.remove(0) + bounds[bounds.index(-gdd_spacing)] /= 2 + bounds[bounds.index(gdd_spacing)] /= 2 + Ncolors = len(bounds) + 1 + return vmax, bounds, Ncolors + + def make_map( + ax, + this_map, + this_title, + vmax, + bin_width, + fontsize_ticklabels, + fontsize_titles, + bounds=None, + extend="both", + cmap=None, + cbar_ticks=None, + vmin=None, + ): + if bounds: + if not cmap: + raise RuntimeError("Calling make_map() with bounds requires cmap to be specified") + norm = mcolors.BoundaryNorm(bounds, cmap.N, extend=extend) + im1 = ax.pcolormesh( + this_map.lon.values, + this_map.lat.values, + this_map, + shading="auto", + norm=norm, + cmap=cmap, + ) + else: + if np.any(this_map.values < 0): + gdd_spacing = 500 + vmax = np.floor(np.nanmax(this_map.values) / gdd_spacing) * gdd_spacing + if vmin is not None: + raise RuntimeError("Do not specify vmin in this call of make_map()") + vmin = -vmax + Ncolors = vmax / gdd_spacing + if Ncolors % 2 == 0: + Ncolors += 1 + if not cmap: + cmap = cm.get_cmap(cropcal_colors["div_other_nonnorm"], Ncolors) + + if np.any(this_map.values > vmax) and np.any(this_map.values < vmin): + extend = "both" + elif np.any(this_map.values > vmax): + extend = "max" + elif np.any(this_map.values < vmin): + extend = "min" + else: + extend = "neither" + + else: + if vmin is None: + vmin = 0 + else: + vmin = np.floor(vmin / 500) * 500 + vmax = np.floor(vmax / 500) * 500 + Ncolors = int(vmax / 500) + if not cmap: + cmap = cm.get_cmap(cropcal_colors["seq_other"], Ncolors + 1) + extend = "max" + extend_color = cmap.colors[-1] + cmap = mcolors.ListedColormap(cmap.colors[:Ncolors]) + cmap.set_over(extend_color) + + im1 = ax.pcolormesh( + this_map.lon.values, + this_map.lat.values, + this_map, + shading="auto", + vmin=vmin, + vmax=vmax, + cmap=cmap, + ) + + ax.set_extent([-180, 180, -63, 90], crs=ccrs.PlateCarree()) + ax.coastlines(linewidth=0.3) + ax.set_title(this_title, fontsize=fontsize_titles, fontweight="bold", y=0.96) + cbar = plt.colorbar( + im1, + orientation="horizontal", + fraction=0.1, + pad=0.02, + aspect=40, + extend=extend, + spacing="proportional", + ) + cbar.ax.tick_params(labelsize=fontsize_ticklabels) + cbar.ax.set_xlabel(this_map.attrs["units"], fontsize=fontsize_ticklabels) + cbar.ax.xaxis.set_label_coords(x=0.115, y=2.6) + if cbar_ticks: + cbar.ax.set_xticks(cbar_ticks) + + ticks = np.arange(-60, 91, bin_width) + ticklabels = [str(x) for x in ticks] + for i, x in enumerate(ticks): + if x % 2: + ticklabels[i] = "" + plt.yticks(np.arange(-60, 91, 15), labels=ticklabels, fontsize=fontsize_ticklabels) + plt.axis("off") + + def get_non_nans(in_da, fillValue): + in_da = in_da.where(in_da != fillValue) + return in_da.values[~np.isnan(in_da.values)] + + def set_boxplot_props(bp, color, linewidth): + linewidth = 1.5 + plt.setp(bp["boxes"], color=color, linewidth=linewidth) + plt.setp(bp["whiskers"], color=color, linewidth=linewidth) + plt.setp(bp["caps"], color=color, linewidth=linewidth) + plt.setp(bp["medians"], color=color, linewidth=linewidth) + plt.setp( + bp["fliers"], + markeredgecolor=color, + markersize=6, + linewidth=linewidth, + markeredgewidth=linewidth / 2, + ) + + def make_plot(data, offset, linewidth): + offset = 0.4 * offset + bpl = plt.boxplot( + data, + positions=np.array(range(len(data))) * 2.0 + offset, + widths=0.6, + boxprops=dict(linewidth=linewidth), + whiskerprops=dict(linewidth=linewidth), + capprops=dict(linewidth=linewidth), + medianprops=dict(linewidth=linewidth), + flierprops=dict(markeredgewidth=0.5), + ) + return bpl + + def make_figures( + first_land_use_year, + last_land_use_year, + land_use_file, + run1_name, + run2_name, + logger, + thisDir=None, + gdd_maps_ds=None, + gddharv_maps_ds=None, + outdir_figs=None, + linewidth=1.5, + ): + if not gdd_maps_ds: + if not thisDir: + error( + logger, + "If not providing gdd_maps_ds, you must provide thisDir (location of gdd_maps.nc)", + ) + gdd_maps_ds = xr.open_dataset(thisDir + "gdd_maps.nc") + if not gddharv_maps_ds: + if not thisDir: + error( + logger, + "If not providing gddharv_maps_ds, you must provide thisDir (location of gddharv_maps.nc)", + ) + gddharv_maps_ds = xr.open_dataset(thisDir + "gdd_maps.nc") + + # Get info + incl_vegtypes_str = gdd_maps_ds.attrs["incl_vegtypes_str"] + if incl_vegtypes_str is None: + incl_vegtypes_str = [] + elif isinstance(incl_vegtypes_str, np.ndarray): + incl_vegtypes_str = list(incl_vegtypes_str) + dummy_fill = gdd_maps_ds.attrs["dummy_fill"] + if not outdir_figs: + outdir_figs = gdd_maps_ds.attrs["outdir_figs"] + try: + y1 = gdd_maps_ds.attrs["y1"] + yN = gdd_maps_ds.attrs["yN"] + # Backwards compatibility with a bug (fixed 2023-01-03) + except: + y1 = gdd_maps_ds.attrs["first_season"] + yN = gdd_maps_ds.attrs["last_season"] + # Import LU data, if doing so + if land_use_file: + y1_lu = y1 if first_land_use_year == None else first_land_use_year + yN_lu = yN if last_land_use_year == None else last_land_use_year + lu_ds = cc.open_lu_ds(land_use_file, y1_lu, yN_lu, gdd_maps_ds, ungrid=False) + lu_years_text = f" (masked by {y1_lu}-{yN_lu} area)" + lu_years_file = f"_mask{y1_lu}-{yN_lu}" + else: + lu_ds = None + lu_years_text = "" + lu_years_file = "" + + # layout = "3x1" + # layout = "2x2" + layout = "3x2" + bin_width = 15 + lat_bin_edges = np.arange(0, 91, bin_width) + + fontsize_titles = 12 + fontsize_axislabels = 12 + fontsize_ticklabels = 12 + + Nbins = len(lat_bin_edges) - 1 + bin_names = ["All"] + for b in np.arange(Nbins): + lower = lat_bin_edges[b] + upper = lat_bin_edges[b + 1] + bin_names.append(f"{lower}–{upper}") + + color_old = cropcal_colors_cases(run1_name) + if color_old is None: + color_old = "#beaed4" + color_new = cropcal_colors_cases(run2_name) + if color_new is None: + color_new = "#7fc97f" + gdd_units = "GDD (°C • day)" + + # Maps + ny = 3 + nx = 1 + log(logger, "Making before/after maps...") + vegtype_list = incl_vegtypes_str + if land_use_file: + vegtype_list += ["Corn", "Cotton", "Rice", "Soybean", "Sugarcane", "Wheat"] + for v, vegtype_str in enumerate(vegtype_list): + print(f"{vegtype_str}...") + + # Get component types + if vegtype_str in incl_vegtypes_str: + vegtypes_str = [vegtype_str] + elif not lu_ds: + raise RuntimeError(f"If mapping {vegtype_str}, you must provide land use dataset") + else: + vegtypes_str = [x for x in incl_vegtypes_str if vegtype_str.lower() in x] + vegtypes_int = [utils.vegtype_str2int(x)[0] for x in vegtypes_str] + + # Crop fraction map (for masking and weighting) + if lu_ds: + crop_fracs_yx = ( + lu_ds.LANDFRAC_PFT * lu_ds.PCT_CROP * lu_ds.PCT_CFT.sel(cft=vegtypes_int) + ).sum(dim="time") + if np.sum(crop_fracs_yx) == 0: + print(f"Skipping {vegtype_str} (no area)") + continue + else: + crop_fracs_yx = None + + theseVars = [f"gdd1_{x}" for x in vegtypes_int] + gddharv_map_yx = get_multicrop_maps( + gddharv_maps_ds, theseVars, crop_fracs_yx, dummy_fill, gdd_units + ) + gdd_map_yx = get_multicrop_maps( + gdd_maps_ds, theseVars, crop_fracs_yx, dummy_fill, gdd_units + ) + + # Get figure title + if len(vegtypes_str) > 1: + vegtype_str_title = vegtype_str + else: + vegtype_str_title = vegtype_str.replace("_", " ") + if "irrigated" not in vegtype_str: + vegtype_str_title = "rainfed " + vegtype_str_title + vegtype_str_title = vegtype_str_title.capitalize() + + vmin = min(np.min(gdd_map_yx), np.min(gddharv_map_yx)).values + vmax = max(np.max(gdd_map_yx), np.max(gddharv_map_yx)).values + + # Set up figure and first subplot + if layout == "3x1": + fig = plt.figure(figsize=(7.5, 14)) + ax = fig.add_subplot(ny, nx, 1, projection=ccrs.PlateCarree()) + elif layout == "2x2": + fig = plt.figure(figsize=(12, 6)) + spec = fig.add_gridspec(nrows=2, ncols=2, width_ratios=[0.4, 0.6]) + ax = fig.add_subplot(spec[0, 0], projection=ccrs.PlateCarree()) + elif layout == "3x2": + fig = plt.figure(figsize=(14, 9)) + spec = fig.add_gridspec(nrows=3, ncols=2, width_ratios=[0.5, 0.5], wspace=0.2) + ax = fig.add_subplot(spec[0, 0], projection=ccrs.PlateCarree()) + else: + error(logger, f"layout {layout} not recognized") + + thisMin = int(np.round(np.nanmin(gddharv_map_yx))) + thisMax = int(np.round(np.nanmax(gddharv_map_yx))) + thisTitle = f"{run1_name} (range {thisMin}–{thisMax})" + make_map( + ax, + gddharv_map_yx, + thisTitle, + vmax, + bin_width, + fontsize_ticklabels, + fontsize_titles, + vmin=vmin, + ) + + if layout == "3x1": + ax = fig.add_subplot(ny, nx, 2, projection=ccrs.PlateCarree()) + elif layout in ["2x2", "3x2"]: + ax = fig.add_subplot(spec[1, 0], projection=ccrs.PlateCarree()) + else: + error(logger, f"layout {layout} not recognized") + thisMin = int(np.round(np.nanmin(gdd_map_yx))) + thisMax = int(np.round(np.nanmax(gdd_map_yx))) + thisTitle = f"{run2_name} (range {thisMin}–{thisMax})" + make_map( + ax, + gdd_map_yx, + thisTitle, + vmax, + bin_width, + fontsize_ticklabels, + fontsize_titles, + vmin=vmin, + ) + + # Difference + if layout == "3x2": + ax = fig.add_subplot(spec[2, 0], projection=ccrs.PlateCarree()) + thisMin = int(np.round(np.nanmin(gdd_map_yx))) + thisMax = int(np.round(np.nanmax(gdd_map_yx))) + thisTitle = f"{run2_name} minus {run1_name}" + diff_map_yx = gdd_map_yx - gddharv_map_yx + diff_map_yx.attrs["units"] = gdd_units + + gdd_spacing = 500 + vmax, bounds, Ncolors = get_bounds_ncolors(gdd_spacing, diff_map_yx) + if Ncolors < 9: + gdd_spacing = 250 + vmax, bounds, Ncolors = get_bounds_ncolors(gdd_spacing, diff_map_yx) + + cmap = cm.get_cmap(cropcal_colors["div_other_nonnorm"], Ncolors) + cbar_ticks = [] + include_0bin_ticks = Ncolors <= 13 + if vmax <= 3000: + tick_spacing = gdd_spacing * 2 + elif vmax <= 5000: + tick_spacing = 1500 + else: + tick_spacing = 2000 + previous = -np.inf + for x in bounds: + if (not include_0bin_ticks) and (x > 0) and (previous < 0): + cbar_ticks.append(0) + if x % tick_spacing == 0 or (include_0bin_ticks and abs(x) == gdd_spacing / 2): + cbar_ticks.append(x) + previous = x + + make_map( + ax, + diff_map_yx, + thisTitle, + vmax, + bin_width, + fontsize_ticklabels, + fontsize_titles, + bounds=bounds, + extend="both", + cmap=cmap, + cbar_ticks=cbar_ticks, + ) + + # Boxplots ##################### + + gdd_vector = get_non_nans(gdd_map_yx, dummy_fill) + gddharv_vector = get_non_nans(gddharv_map_yx, dummy_fill) + + lat_abs = np.abs(gdd_map_yx.lat.values) + gdd_bybin_old = [gddharv_vector] + gdd_bybin_new = [gdd_vector] + for b in np.arange(Nbins): + lower = lat_bin_edges[b] + upper = lat_bin_edges[b + 1] + lat_inds = np.where((lat_abs >= lower) & (lat_abs < upper))[0] + gdd_vector_thisBin = get_non_nans(gdd_map_yx[lat_inds, :], dummy_fill) + gddharv_vector_thisBin = get_non_nans(gddharv_map_yx[lat_inds, :], dummy_fill) + gdd_bybin_old.append(gddharv_vector_thisBin) + gdd_bybin_new.append(gdd_vector_thisBin) + + if layout == "3x1": + ax = fig.add_subplot(ny, nx, 3) + elif layout in ["2x2", "3x2"]: + ax = fig.add_subplot(spec[:, 1]) + else: + error(logger, f"layout {layout} not recognized") + + # Shift bottom of plot up to make room for legend + ax_pos = ax.get_position() + ax.set_position(Bbox.from_extents(ax_pos.x0, 0.19, ax_pos.x1, ax_pos.y1)) + # Define legend position + legend_bbox_to_anchor = (0, -0.15, 1, 0.2) + + bpl = make_plot(gdd_bybin_old, -1, linewidth) + bpr = make_plot(gdd_bybin_new, 1, linewidth) + set_boxplot_props(bpl, color_old, linewidth) + set_boxplot_props(bpr, color_new, linewidth) + + # draw temporary lines to create a legend + plt.plot([], c=color_old, label=run1_name, linewidth=linewidth) + plt.plot([], c=color_new, label=run2_name, linewidth=linewidth) + plt.legend( + fontsize=fontsize_titles, + bbox_to_anchor=legend_bbox_to_anchor, + ncol=2, + loc="lower left", + mode="expand", + ) + + plt.xticks(range(0, len(bin_names) * 2, 2), bin_names, fontsize=fontsize_ticklabels) + plt.yticks(fontsize=fontsize_ticklabels) + ax.spines["right"].set_visible(False) + ax.spines["top"].set_visible(False) + + plt.xlabel("Latitude zone (absolute value)", fontsize=fontsize_axislabels) + plt.ylabel(gdd_units, fontsize=fontsize_axislabels) + ax.yaxis.set_label_coords(-0.11, 0.5) + plt.title(f"Zonal changes", fontsize=fontsize_titles, fontweight="bold") + + plt.suptitle( + f"Maturity requirements: {vegtype_str_title}" + lu_years_text, + fontsize=fontsize_titles * 1.2, + fontweight="bold", + y=0.95, + ) + + if vegtype_str in incl_vegtypes_str: + outfile = os.path.join( + outdir_figs, f"{theseVars[0]}_{vegtype_str}_gs{y1}-{yN}{lu_years_file}.png" + ) + else: + outfile = os.path.join(outdir_figs, f"{vegtype_str}_gs{y1}-{yN}{lu_years_file}.png") + plt.savefig(outfile, dpi=300, transparent=False, facecolor="white", bbox_inches="tight") + plt.close() + + log(logger, "Done.") diff --git a/python/ctsm/crop_calendars/process_ggcmi_shdates.py b/python/ctsm/crop_calendars/process_ggcmi_shdates.py new file mode 100644 index 0000000000..835f91cb22 --- /dev/null +++ b/python/ctsm/crop_calendars/process_ggcmi_shdates.py @@ -0,0 +1,417 @@ +import numpy as np +import xarray as xr +import os +import datetime as dt +import cftime +import sys +import argparse +import logging + +# -- add python/ctsm to path (needed if we want to run process_ggcmi_shdates stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm import ctsm_logging +import ctsm.crop_calendars.cropcal_utils as utils +import ctsm.crop_calendars.regrid_ggcmi_shdates as regrid + +logger = logging.getLogger(__name__) + + +def get_cft(y): + return cftime.DatetimeNoLeap(y, 1, 1, 0, 0, 0, 0, has_year_zero=True) + + +def get_dayssince_jan1y1(y1, y): + cft_y1 = get_cft(y1) + cft_y = get_cft(y) + time_delta = cft_y - cft_y1 + time_delta_secs = time_delta.total_seconds() + return time_delta_secs / (60 * 60 * 24) + + +def main(): + ctsm_logging.setup_logging_pre_config() + args = process_ggcmi_shdates_args() + process_ggcmi_shdates( + args.input_directory, + args.output_directory, + args.author, + args.file_specifier, + args.first_year, + args.last_year, + args.verbose, + args.ggcmi_author, + args.regrid_resolution, + args.regrid_template_file, + args.regrid_extension, + args.crop_list, + ) + + +def process_ggcmi_shdates_args(): + parser = argparse.ArgumentParser( + description="Converts raw sowing and harvest date files provided by GGCMI into a format that CLM can read, optionally at a target resolution." + ) + + # Required + parser.add_argument( + "-i", + "--input-directory", + help="Directory containing the raw GGCMI sowing/harvest date files", + type=str, + required=True, + ) + parser.add_argument( + "-o", + "--output-directory", + help="Where to save the CLM-compatible sowing and harvest date files", + type=str, + required=True, + ) + parser.add_argument( + "-a", + "--author", + help="String to be saved in author_thisfile attribute of output files. E.g., 'Author Name (authorname@ucar.edu)'", + type=str, + required=True, + ) + + # Optional + parser.add_argument( + "--file-specifier", + help="String following CROP_IRR_ in input filenames. E.g., mai_ir_FILESPECIFIER.nc4. Will also be saved to output filenames.", + type=str, + default="ggcmi_crop_calendar_phase3_v1.01", + ) + parser.add_argument( + "-y1", + "--first-year", + help="First year in output files. Must be present in template file, unless it's the same as the last year.", + type=int, + default=2000, + ) + parser.add_argument( + "-yN", + "--last-year", + help="Last year in output files. Must be present in template file, unless it's the same as the first year.", + type=int, + default=2000, + ) + parser.add_argument( + "--ggcmi-author", + help="Author of original GGCMI files", + type=str, + default="Jonas Jägermeyr (jonas.jaegermeyr@columbia.edu)", + ) + + ctsm_logging.add_logging_args(parser) + + # Arguments for regridding + parser = regrid.define_arguments(parser) + + # Get arguments + args = parser.parse_args(sys.argv[1:]) + ctsm_logging.process_logging_args(args) + + return args + + +def process_ggcmi_shdates( + input_directory, + output_directory, + author, + file_specifier, + first_year, + last_year, + verbose, + ggcmi_author, + regrid_resolution, + regrid_template_file, + regrid_extension, + crop_list, +): + + input_directory = os.path.realpath(input_directory) + output_directory = os.path.realpath(output_directory) + + ############################################################ + ### Regrid original GGCMI files to target CLM resolution ### + ############################################################ + + regridded_ggcmi_files_dir = os.path.join( + output_directory, f"regridded_ggcmi_files-{regrid_resolution}" + ) + + regrid.regrid_ggcmi_shdates( + regrid_resolution, + regrid_template_file, + input_directory, + regridded_ggcmi_files_dir, + regrid_extension, + crop_list, + ) + + ########################### + ### Define dictionaries ### + ########################### + + # First, we associate CLM crop names with (1) their integer counterpart and (2) their GGCMI counterpart. + # Some notes: + # - As "CLMname: {clm_num, thiscrop_ggcmi}" + # - CLM names and numbers taken from commit `3dcbc7499a57904750a994672fc36b4221b9def5` + # - Using one global GGCMI value for both temperate and tropical versions of corn and soybean. + # - There is no GGCMI equivalent of CLM's winter barley and rye. Using winter wheat instead. + # - Using GGCMI `pea` for CLM pulses, as suggested by GGCMI phase 3 protocol. + # - Only using GGCMI `ri1` for rice; ignoring `ri2`. + def set_crop_dict(thisnum, thisname): + return {"clm_num": thisnum, "thiscrop_ggcmi": thisname} + + crop_dict = { + "temperate_corn": set_crop_dict(17, "mai_rf"), + "irrigated_temperate_corn": set_crop_dict(18, "mai_ir"), + "spring_wheat": set_crop_dict(19, "swh_rf"), + "irrigated_spring_wheat": set_crop_dict(20, "swh_ir"), + "winter_wheat": set_crop_dict(21, "wwh_rf"), + "irrigated_winter_wheat": set_crop_dict(22, "wwh_ir"), + "temperate_soybean": set_crop_dict(23, "soy_rf"), + "irrigated_temperate_soybean": set_crop_dict(24, "soy_ir"), + "barley": set_crop_dict(25, "bar_rf"), + "irrigated_barley": set_crop_dict(26, "bar_ir"), + "winter_barley": set_crop_dict(27, "wwh_rf"), + "irrigated_winter_barley": set_crop_dict(28, "wwh_ir"), + "rye": set_crop_dict(29, "rye_rf"), + "irrigated_rye": set_crop_dict(30, "rye_ir"), + "winter_rye": set_crop_dict(31, "wwh_rf"), + "irrigated_winter_rye": set_crop_dict(32, "wwh_ir"), + "cassava": set_crop_dict(33, "cas_rf"), + "irrigated_cassava": set_crop_dict(34, "cas_ir"), + "citrus": set_crop_dict(35, None), + "irrigated_citrus": set_crop_dict(36, None), + "cocoa": set_crop_dict(37, None), + "irrigated_cocoa": set_crop_dict(38, None), + "coffee": set_crop_dict(39, None), + "irrigated_coffee": set_crop_dict(40, None), + "cotton": set_crop_dict(41, "cot_rf"), + "irrigated_cotton": set_crop_dict(42, "cot_ir"), + "datepalm": set_crop_dict(43, None), + "irrigated_datepalm": set_crop_dict(44, None), + "foddergrass": set_crop_dict(45, None), + "irrigated_foddergrass": set_crop_dict(46, None), + "grapes": set_crop_dict(47, None), + "irrigated_grapes": set_crop_dict(48, None), + "groundnuts": set_crop_dict(49, "nut_rf"), + "irrigated_groundnuts": set_crop_dict(50, "nut_ir"), + "millet": set_crop_dict(51, "mil_rf"), + "irrigated_millet": set_crop_dict(52, "mil_ir"), + "oilpalm": set_crop_dict(53, None), + "irrigated_oilpalm": set_crop_dict(54, None), + "potatoes": set_crop_dict(55, "pot_rf"), + "irrigated_potatoes": set_crop_dict(56, "pot_ir"), + "pulses": set_crop_dict(57, "pea_rf"), + "irrigated_pulses": set_crop_dict(58, "pea_ir"), + "rapeseed": set_crop_dict(59, "rap_rf"), + "irrigated_rapeseed": set_crop_dict(60, "rap_ir"), + "rice": set_crop_dict(61, "ric_rf"), + "irrigated_rice": set_crop_dict(62, "ric_ir"), + "sorghum": set_crop_dict(63, "sor_rf"), + "irrigated_sorghum": set_crop_dict(64, "sor_ir"), + "sugarbeet": set_crop_dict(65, "sgb_rf"), + "irrigated_sugarbeet": set_crop_dict(66, "sgb_ir"), + "sugarcane": set_crop_dict(67, "sgc_rf"), + "irrigated_sugarcane": set_crop_dict(68, "sgc_ir"), + "sunflower": set_crop_dict(69, "sun_rf"), + "irrigated_sunflower": set_crop_dict(70, "sun_ir"), + "miscanthus": set_crop_dict(71, None), + "irrigated_miscanthus": set_crop_dict(72, None), + "switchgrass": set_crop_dict(73, None), + "irrigated_switchgrass": set_crop_dict(74, None), + "tropical_corn": set_crop_dict(75, "mai_rf"), + "irrigated_tropical_corn": set_crop_dict(76, "mai_ir"), + "tropical_soybean": set_crop_dict(77, "soy_rf"), + "irrigated_tropical_soybean": set_crop_dict(78, "soy_ir"), + "c3_crop": set_crop_dict(15, None), + "c3_irrigated": set_crop_dict(16, None), + } + + # Next, we associate CLM variable names with their GGCMI counterparts. We also save a placeholder for output file paths associated with each variable. + # As CLMname: {GGCMIname, output_file} + def set_var_dict(name_ggcmi, outfile): + return {"name_ggcmi": name_ggcmi, "outfile": outfile} + + variable_dict = { + "sdate": set_var_dict("planting_day", ""), + "hdate": set_var_dict("maturity_day", ""), + } + + ################################ + ### Instantiate output files ### + ################################ + + # Global attributes for output files + out_attrs = { + "title": "GGCMI crop calendar for Phase 3, v1.01", + "author_thisfile": author, + "author_original": ggcmi_author, + "comment": "Day of year is 1-indexed (i.e., Jan. 1 = 1). Filled using cdo -remapnn,$original -setmisstonn", + "created": dt.datetime.now().replace(microsecond=0).astimezone().isoformat(), + } + + # Create template dataset + time_array = np.array( + [get_dayssince_jan1y1(first_year, y) for y in np.arange(first_year, last_year + 1)] + ) + time_coord = xr.IndexVariable( + "time", + data=time_array, + attrs={ + "long_name": "time", + "units": f"days since {first_year}-01-01", + "calendar": "noleap", + }, + ) + template_ds = xr.Dataset(coords={"time": time_coord}, attrs=out_attrs) + + # Create output files + datetime_string = dt.datetime.now().strftime("%Y%m%d_%H%M%S") + nninterp_suffix = "nninterp-" + regrid_resolution + for v in variable_dict: + outfile = os.path.join( + output_directory, + f"{v}s_{file_specifier}_{nninterp_suffix}.{first_year}-{last_year}.{datetime_string}.nc", + ) + variable_dict[v]["outfile"] = outfile + template_ds.to_netcdf( + path=variable_dict[v]["outfile"], + format="NETCDF3_CLASSIC", + ) + + ######################### + ### Process all crops ### + ######################### + + for thiscrop_clm in crop_dict: + + # Which crop are we on? + c = list(crop_dict.keys()).index(thiscrop_clm) + 1 + + # Get information about this crop + this_dict = crop_dict[thiscrop_clm] + thiscrop_int = this_dict["clm_num"] + thiscrop_ggcmi = this_dict["thiscrop_ggcmi"] + + # If --regrid-crop-list specified, only process crops from that list + if crop_list is not None and thiscrop_ggcmi is not None and thiscrop_ggcmi not in crop_list: + continue + + # If no corresponding GGCMI crop, skip opening dataset. + # Will use previous cropcal_ds as a template. + if thiscrop_ggcmi == None: + if c == 1: + raise ValueError(f"First crop ({thiscrop_clm}) must have a GGCMI type") + logger.info( + "Filling %s with dummy data (%d of %d)..." % (str(thiscrop_clm), c, len(crop_dict)) + ) + + # Otherwise, import crop calendar file + else: + logger.info( + "Importing %s -> %s (%d of %d)..." + % (str(thiscrop_ggcmi), str(thiscrop_clm), c, len(crop_dict)) + ) + + file_ggcmi = os.path.join( + regridded_ggcmi_files_dir, + f"{thiscrop_ggcmi}_{file_specifier}_{nninterp_suffix}.nc4", + ) + if not os.path.exists(file_ggcmi): + logger.warning( + f"Skipping {thiscrop_ggcmi} because input file not found: {file_ggcmi}" + ) + continue + cropcal_ds = xr.open_dataset(file_ggcmi) + # Flip latitude to match destination + cropcal_ds = cropcal_ds.reindex(lat=cropcal_ds.lat[::-1]) + # Rearrange longitude to match destination (does nothing if not needed) + cropcal_ds = utils.lon_idl2pm(cropcal_ds, fail_silently=True) + + for thisvar_clm in variable_dict: + # Get GGCMI netCDF info + varname_ggcmi = variable_dict[thisvar_clm]["name_ggcmi"] + logger.info(" Processing %s..." % varname_ggcmi) + + # Get CLM netCDF info + varname_clm = thisvar_clm + "1_" + str(thiscrop_int) + file_clm = variable_dict[thisvar_clm]["outfile"] + if not os.path.exists(file_clm): + raise Exception("Output file not found: " + file_clm) + + # Strip dataset to just this variable + droplist = [] + for i in list(cropcal_ds.keys()): + if i != varname_ggcmi: + droplist.append(i) + thisvar_ds = cropcal_ds.drop(droplist) + thisvar_ds = thisvar_ds.load() + + # Convert to integer + new_fillvalue = -1 + dummyvalue = -1 + thisvar_ds.variables[varname_ggcmi].encoding["_FillValue"] = new_fillvalue + if thiscrop_ggcmi == None: + thisvar_ds.variables[varname_ggcmi].values.fill(dummyvalue) + else: + thisvar_ds.variables[varname_ggcmi].values[ + np.isnan(thisvar_ds.variables[varname_ggcmi].values) + ] = new_fillvalue + thisvar_ds.variables[varname_ggcmi].values = thisvar_ds.variables[ + varname_ggcmi + ].values.astype("int16") + + # Add time dimension (https://stackoverflow.com/a/62862440) + # (Repeats original map for every timestep) + # Probably not necessary to use this method, since I only end up extracting thisvar_ds.values anyway---I could probably use some numpy method instead. + thisvar_ds = thisvar_ds.expand_dims(time=template_ds.time) + thisvar_da_tmp = thisvar_ds[varname_ggcmi] + thisvar_da = xr.DataArray( + data=thisvar_da_tmp.values.astype("int16"), + attrs=thisvar_da_tmp.attrs, + coords=thisvar_da_tmp.coords, + name=varname_clm, + ) + + # Edit/add variable attributes etc. + longname = thisvar_da.attrs["long_name"] + longname = longname.replace("rainfed", thiscrop_clm).replace("irrigated", thiscrop_clm) + + def set_var_attrs( + thisvar_da, longname, thiscrop_clm, thiscrop_ggcmi, varname_ggcmi, new_fillvalue + ): + thisvar_da.attrs["long_name"] = longname + if thiscrop_ggcmi == None: + thisvar_da.attrs["crop_name_clm"] = "none" + thisvar_da.attrs["crop_name_ggcmi"] = "none" + else: + thisvar_da.attrs["crop_name_clm"] = thiscrop_clm + thisvar_da.attrs["crop_name_ggcmi"] = thiscrop_ggcmi + thisvar_da.attrs["short_name_ggcmi"] = varname_ggcmi + thisvar_da.attrs["units"] = "day of year" + thisvar_da.encoding["_FillValue"] = new_fillvalue + # scale_factor and add_offset are required by I/O library for short data + # From https://www.unidata.ucar.edu/software/netcdf/workshops/2010/bestpractices/Packing.html: + # unpacked_value = packed_value * scale_factor + add_offset + thisvar_da.attrs["scale_factor"] = np.int16(1) + thisvar_da.attrs["add_offset"] = np.int16(0) + return thisvar_da + + thisvar_da = set_var_attrs( + thisvar_da, longname, thiscrop_clm, thiscrop_ggcmi, varname_ggcmi, new_fillvalue + ) + + # Save + logger.info(" Saving %s..." % varname_ggcmi) + thisvar_da.to_netcdf(file_clm, mode="a", format="NETCDF3_CLASSIC") + + cropcal_ds.close() + + logger.info("Done!") diff --git a/python/ctsm/crop_calendars/regrid_ggcmi_shdates.py b/python/ctsm/crop_calendars/regrid_ggcmi_shdates.py new file mode 100644 index 0000000000..911b2f93a1 --- /dev/null +++ b/python/ctsm/crop_calendars/regrid_ggcmi_shdates.py @@ -0,0 +1,237 @@ +from subprocess import run +import os +import glob +import argparse +import sys +import xarray as xr +import numpy as np +import logging + +# -- add python/ctsm to path (needed if we want to run regrid_ggcmi_shdates stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.utils import abort +from ctsm.ctsm_pylib_dependent_utils import import_coord_1d, import_coord_2d +from ctsm import ctsm_logging + +logger = logging.getLogger(__name__) + + +def main(): + """ + Description + ----------- + Calls function that regrids GGCMI sowing and harvest dates. + """ + + args = regrid_ggcmi_shdates_arg_process() + regrid_ggcmi_shdates( + args.regrid_resolution, + args.regrid_template_file, + args.regrid_input_directory, + args.regrid_output_directory, + args.regrid_extension, + args.crop_list, + ) + + +def run_and_check(cmd): + result = run( + cmd, + shell=True, + capture_output=True, + text=True, + ) + if result.returncode != 0: + abort(f"Trouble running `{result.args}` in shell:\n{result.stdout}\n{result.stderr}") + + +# Functionized because these are shared by process_ggcmi_shdates +def define_arguments(parser): + # Required + parser.add_argument( + "-rr", + "--regrid-resolution", + help="Target CLM resolution, to be saved in output filenames.", + type=str, + required=True, + ) + parser.add_argument( + "-rt", + "--regrid-template-file", + help="Template netCDF file to be used in regridding of inputs. This can be a CLM output file (i.e., something with 1-d lat and lon variables) or a CLM surface dataset (i.e., something with 2-d LATIXY and LONGXY variables).", + type=str, + required=True, + ) + + default = ".nc" + parser.add_argument( + "-x", + "--regrid-extension", + help=f"File regrid_extension of raw GGCMI sowing/harvest date files (default {default}).", + default=default, + ) + parser.add_argument( + "-c", + "--crop-list", + help="List of GGCMI crops to process; e.g., '--crop-list mai_rf,mai_ir'. If not provided, will process all GGCMI crops.", + default=None, + ) + return parser + + +def regrid_ggcmi_shdates( + regrid_resolution, + regrid_template_file_in, + regrid_input_directory, + regrid_output_directory, + regrid_extension, + crop_list, +): + logger.info(f"Regridding GGCMI crop calendars to {regrid_resolution}:") + + # Ensure we can call necessary shell script(s) + for cmd in ["module load cdo; cdo"]: + run_and_check(f"{cmd} --help") + + previous_dir = os.getcwd() + os.chdir(regrid_input_directory) + if not os.path.exists(regrid_output_directory): + os.makedirs(regrid_output_directory) + + templatefile = os.path.join(regrid_output_directory, "template.nc") + if os.path.exists(templatefile): + os.remove(templatefile) + + template_ds_in = xr.open_dataset(regrid_template_file_in) + + # Process inputs + if crop_list is not None: + crop_list = crop_list.split(",") + if regrid_extension[0] != ".": + regrid_extension = "." + regrid_extension + + # Import and format latitude + if "lat" in template_ds_in: + lat, Nlat = import_coord_1d(template_ds_in, "lat") + elif "LATIXY" in template_ds_in: + lat, Nlat = import_coord_2d(template_ds_in, "lat", "LATIXY") + lat.attrs["axis"] = "Y" + else: + abort("No latitude variable found in regrid template file") + + # Flip latitude, if needed + if lat.values[0] < lat.values[1]: + lat = lat.reindex(lat=list(reversed(lat["lat"]))) + + # Import and format longitude + if "lon" in template_ds_in: + lon, Nlon = import_coord_1d(template_ds_in, "lon") + elif "LONGXY" in template_ds_in: + lon, Nlon = import_coord_2d(template_ds_in, "lon", "LONGXY") + lon.attrs["axis"] = "Y" + else: + abort("No longitude variable found in regrid template file") + template_da_out = xr.DataArray( + data=np.full((Nlat, Nlon), 0.0), + dims={"lat": lat, "lon": lon}, + name="area", + ) + + # Save template Dataset for use by cdo + template_ds_out = xr.Dataset( + data_vars={ + "planting_day": template_da_out, + "maturity_day": template_da_out, + "growing_season_length": template_da_out, + }, + coords={"lat": lat, "lon": lon}, + ) + template_ds_out.to_netcdf(templatefile, mode="w") + + # Loop through original crop calendar files, interpolating using cdo with nearest-neighbor + pattern = "*" + regrid_extension + input_files = glob.glob(pattern) + if len(input_files) == 0: + abort(f"No files found matching {os.path.join(os.getcwd(), pattern)}") + input_files.sort() + for f in input_files: + this_crop = f[0:6] + if crop_list is not None and this_crop not in crop_list: + continue + + logger.info(" " + this_crop) + f2 = os.path.join(regrid_output_directory, f) + f3 = f2.replace(regrid_extension, f"_nninterp-{regrid_resolution}{regrid_extension}") + + if os.path.exists(f3): + os.remove(f3) + + # Sometimes cdo fails for no apparent reason. In testing this never happened more than 3x in a row. + try: + run_and_check( + f"module load cdo; cdo -L -remapnn,'{templatefile}' -setmisstonn '{f}' '{f3}'" + ) + except: + try: + run_and_check( + f"module load cdo; cdo -L -remapnn,'{templatefile}' -setmisstonn '{f}' '{f3}'" + ) + except: + try: + run_and_check( + f"module load cdo; cdo -L -remapnn,'{templatefile}' -setmisstonn '{f}' '{f3}'" + ) + except: + run_and_check( + f"module load cdo; cdo -L -remapnn,'{templatefile}' -setmisstonn '{f}' '{f3}'" + ) + + # Delete template file, which is no longer needed + os.remove(templatefile) + os.chdir(previous_dir) + + +def regrid_ggcmi_shdates_arg_process(): + """Process input arguments + + Returns: + argparse.ArgumentParser: Arguments/options + """ + + # set up logging allowing user control + ctsm_logging.setup_logging_pre_config() + + parser = argparse.ArgumentParser( + description="Regrids raw sowing and harvest date files provided by GGCMI to a target CLM resolution." + ) + + # Define arguments + parser = define_arguments(parser) + parser.add_argument( + "-i", + "--regrid-input-directory", + help="Directory containing the raw GGCMI sowing/harvest date files.", + type=str, + required=True, + ) + parser.add_argument( + "-o", + "--regrid-output-directory", + help="Directory where regridded output files should be saved.", + type=str, + required=True, + ) + ctsm_logging.add_logging_args(parser) + + # Get arguments + args = parser.parse_args(sys.argv[1:]) + ctsm_logging.process_logging_args(args) + + # Process arguments + args.regrid_template_file = os.path.realpath(args.regrid_template_file) + args.regrid_input_directory = os.path.realpath(args.regrid_input_directory) + args.regrid_output_directory = os.path.realpath(args.regrid_output_directory) + + return args diff --git a/python/ctsm/ctsm_logging.py b/python/ctsm/ctsm_logging.py index ff51c6d8f2..e14ec2754c 100644 --- a/python/ctsm/ctsm_logging.py +++ b/python/ctsm/ctsm_logging.py @@ -68,6 +68,7 @@ def add_logging_args(parser): logging_level.add_argument( "-v", "--verbose", action="store_true", help="Output extra logging info" ) + logging_level.add_argument("--silent", action="store_true", help="Only output errors") logging_level.add_argument( "--debug", @@ -84,6 +85,8 @@ def process_logging_args(args): root_logger.setLevel(logging.DEBUG) elif args.verbose: root_logger.setLevel(logging.INFO) + elif args.silent: + root_logger.setLevel(logging.ERROR) else: root_logger.setLevel(logging.WARNING) diff --git a/python/ctsm/ctsm_pylib_dependent_utils.py b/python/ctsm/ctsm_pylib_dependent_utils.py new file mode 100644 index 0000000000..13ccf7a969 --- /dev/null +++ b/python/ctsm/ctsm_pylib_dependent_utils.py @@ -0,0 +1,49 @@ +from ctsm.utils import abort +import numpy as np + + +def import_coord_1d(ds, coordName): + """Import 1-d coordinate variable + + Args: + ds (xarray Dataset): Dataset whose coordinate you want to import. + coordName (str): Name of coordinate to import + + Returns: + xarray DataArray: DataArray corresponding to the requested coordinate. + """ + da = ds[coordName] + if len(da.dims) != 1: + abort(f"Expected 1 dimension for {coordName}; found {len(da.dims)}: {da.dims}") + return da, len(da) + + +def import_coord_2d(ds, coordName, varName): + """Import 2-d latitude or longitude variable from a CESM history file (e.g., name LATIXY or LONGXY) and return it as a 1-d DataArray that can be used as a coordinate for writing CESM input files + + Args: + ds (xarray Dataset): Dataset whose coordinate you want to import. + coordName (str): Name of coordinate to import + varName (str): Name of variable with dimension coordName + + Returns: + xarray DataArray: 1-d variable that can be used as a coordinate for writing CESM input files + int: Length of that variable + """ + da = ds[varName] + thisDim = [x for x in da.dims if coordName in x] + if len(thisDim) != 1: + abort(f"Expected 1 dimension name containing {coordName}; found {len(thisDim)}: {thisDim}") + thisDim = thisDim[0] + otherDim = [x for x in da.dims if coordName not in x] + if len(otherDim) != 1: + abort( + f"Expected 1 dimension name not containing {coordName}; found {len(otherDim)}: {otherDim}" + ) + otherDim = otherDim[0] + da = da.astype(np.float32) + da = da.isel({otherDim: [0]}).squeeze().rename({thisDim: coordName}).rename(coordName) + da = da.assign_coords({coordName: da.values}) + da.attrs["long_name"] = "coordinate " + da.attrs["long_name"] + da.attrs["units"] = da.attrs["units"].replace(" ", "_") + return da, len(da) diff --git a/python/ctsm/gen_mksurf_namelist.py b/python/ctsm/gen_mksurf_namelist.py deleted file mode 100644 index 735ae0493f..0000000000 --- a/python/ctsm/gen_mksurf_namelist.py +++ /dev/null @@ -1,377 +0,0 @@ -# 2020-11-08 Negin Sobhani - -""" -|------------------------------------------------------------------| -|--------------------- Instructions -----------------------------| -|------------------------------------------------------------------| -This Python script is part of the simplified toolchain for creating -the surface dataset for ctsm cases. -This script should be used as the first step of the new toolchain. -It will automatically create namelist (control file) that is -needed for creating surface dataset and requisite intermediate files for -running CTSM cases. -For transient cases, it will also create a txt file that includes the -landuse files for every year. - -------------------------------------------------------------------- -Instructions for running on Cheyenne/Casper: - -load the following into your local environment: - - module load python - ncar_pylib -------------------------------------------------------------------- -To see the available options: - ./gen_mksurf_namelist.py --help - -To run the script: - ./gen_mksurf_namelist.py - -To remove NPL(ncar_pylib) from your environment on Cheyenne/Casper: - deactivate -------------------------------------------------------------------- -""" - -# TODO (NS) - -# -[x] Add default values in the help page. -# -[x] Add info for help page note for end_year -- by default is start_year -# -[x] Possibly remove year --years and range options -# Currently comment them out. - -# -[x] maybe a verbose option and removing debug -# -[x] --debug mode is not working... - -# -[ ] add error check for hi-res and years if they are 1850 and 2005. -# -[ ] hirespft data only for 2005? add error-check - -# -[x] different path for each range of years for transient cases. -# default should be picked based on the year. 1850 - 2015 --> -# /glade/p/cesm/cseg/inputdata/lnd/clm2/rawdata/ -# pftcftdynharv.0.25x0.25.LUH2.histsimyr1850-2015.c170629/ -# 850-1850 --> -# pftcftdynharv.0.25x0.25.LUH2.histsimyr0850-1849.c171012 - - -# Import libraries -from __future__ import print_function - -import os -import sys -import logging -import argparse - -# -- import local classes for this script -from ctsm.toolchain.ctsm_case import CtsmCase - -# -- import ctsm logging flags -from ctsm.ctsm_logging import ( - setup_logging_pre_config, - add_logging_args, - process_logging_args, -) - -logger = logging.getLogger(__name__) - -## valid options for resolution and SSP scenarios: -VALID_OPTS = { - "res": [ - "512x1024", - "360x720cru", - "128x256", - "64x128", - "48x96", - "94x192", - "0.23x0.31", - "0.47x0.63", - "0.9x1.25", - "1.9x2.5", - "2.5x3.33", - "4x5", - "10x15", - "0.125nldas2", - "5x5_amazon", - "1x1_camdenNJ", - "1x1_vancouverCAN", - "1x1_mexicocityMEX", - "1x1_asphaltjungleNJ", - "1x1_brazil,1x1_urbanc_alpha", - "1x1_numaIA,1x1_smallvilleIA", - "0.1x0.1", - "0.25x0.25", - "0.5x0.5", - "3x3min", - "5x5min", - "10x10min", - "0.33x0.33", - "0.125x0.125", - "ne4np4,ne16np4", - "ne30np4.pg2", - "ne30np4.pg3", - "ne30np4", - "ne60np4", - "ne120np4", - ], - "ssp_rcp": [ - "hist", - "SSP1-2.6", - "SSP3-7.0", - "SSP5-3.4", - "SSP2-4.5", - "SSP1-1.9", - "SSP4-3.4", - "SSP4-6.0", - "SSP5-8.5", - ], -} - - -def get_parser(): - """ - Get parser object for this script. - """ - parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter - ) - - parser.print_usage = parser.print_help - - parser.add_argument( - "--sy", - "--start-year", - help="Simulation start year. [default: %(default)s] ", - action="store", - dest="start_year", - required=False, - type=start_year_type, - default=2000, - ) - parser.add_argument( - "--ey", - "--end-year", - help="Simulation end year. [default: start_year] ", - action="store", - dest="end_year", - required=False, - type=int, - ) - parser.add_argument( - "--glc-nec", - help=""" - Number of glacier elevation classes to use. - [default: %(default)s] - """, - action="store", - dest="glc_nec", - type=glc_nec_type, - default="10", - ) - parser.add_argument( - "--rundir", - help=""" - Directory to run in. - [default: %(default)s] - """, - action="store", - dest="run_dir", - required=False, - default=os.getcwd(), - ) - parser.add_argument( - "--ssp-rcp", - help=""" - Shared Socioeconomic Pathway and Representative - Concentration Pathway Scenario name(s). - [default: %(default)s] - """, - action="store", - dest="ssp_rcp", - required=False, - choices=VALID_OPTS["ssp_rcp"], - default="hist", - ) - - ############################################## - # In mksurfdata.pl these options are -l --dinlc - # But the group decided --raw_dir is more descriptive. - # If everyone agrees, the commented out line should be removed. - # parser.add_argument('-l','--dinlc', #--raw-dir or --rawdata-dir - - parser.add_argument( - "--raw-dir", - "--rawdata-dir", - help=""" - /path/of/root/of/input/data', - [default: %(default)s] - """, - action="store", - dest="input_path", - default="/glade/p/cesm/cseg/inputdata/lnd/clm2/rawdata/", - ) - parser.add_argument( - "--vic", - help=""" - Flag for adding the fields required for the VIC model. - """, - action="store_true", - dest="vic_flag", - default=False, - ) - parser.add_argument( - "--glc", - help=""" - Flag for adding the optional 3D glacier fields for verification of the glacier model. - """, - action="store_true", - dest="glc_flag", - default=False, - ) - parser.add_argument( - "--hirespft", - help=""" - If you want to use the high-resolution pft dataset rather - than the default lower resolution dataset. - (Low resolution is at quarter-degree, high resolution at 3-minute) - [Note: hires only available for 1850 and 2005.] - """, - action="store_true", - dest="hres_flag", - default=False, - ) - parser.add_argument( - "--nocrop", - help=""" - Create datasets with the extensive list of prognostic crop types. - """, - action="store_false", - dest="crop_flag", - default=True, - ) - parser.add_argument( - "-f", - "--fast", - help="Toggle fast mode which does not user the large mapping file", - action="store_true", - dest="fast_flag", - default=False, - ) - parser.add_argument( - "-r", - "--res", - help=""" - Resolution is the supported resolution(s) to use for files. - [default: %(default)s] - """, - action="store", - dest="res", - choices=VALID_OPTS["res"], - required=False, - default="4x5", - ) - return parser - - -# -- types for this parser - - -def glc_nec_type(glc): - """ - Function for defining acceptable glc_nec input. - - Args: - x (str) : glc_nec value from command line args. - - Raises: - Error if value of glc_nec is not in the range - of 1-99. - - Returns: - x (int) : Acceptable glc_nec value. - """ - glc = int(glc) - if (glc <= 0) or (glc >= 100): - raise argparse.ArgumentTypeError("ERROR: glc_nec must be between 1 and 99.") - return glc.__str__() - - -def start_year_type(year): - """ - Function for defining acceptable start_year input. - - Args: - year (str) : start_year string from command line args. - - Raises: - Error if value of start_year is not in the range - of 850-2105. - - Returns: - year (int) : Acceptable start_year value. - """ - year = int(year) - if (year < 850) or (year > 2105): - raise argparse.ArgumentTypeError( - "ERROR: Simulation start year should be between 850 and 2105." - ) - return year - - -def main(): - """ - Main function for gen_mksurf_namelist. - """ - # -- add logging flags from ctsm_logging - setup_logging_pre_config() - parser = get_parser() - add_logging_args(parser) - - args = parser.parse_args() - process_logging_args(args) - - res = args.res - glc_nec = args.glc_nec - input_path = args.input_path - ssp_rcp = args.ssp_rcp - crop_flag = args.crop_flag - vic_flag = args.vic_flag - glc_flag = args.glc_flag - hres_flag = args.hres_flag - - start_year = args.start_year - end_year = args.end_year - - # -- determine end_year if not given as an argument: - if not end_year: - end_year = start_year - - # -- check if the input path exist - if not os.path.exists(input_path): - sys.exit( - "ERROR: \n" - + "\t raw_dir does not exist on this machine. \n" - + "\t Please point to the correct raw_dir using --raw-dir" - + "or --rawdata-dir flags." - ) - - ctsm_case = CtsmCase( - res, - glc_nec, - ssp_rcp, - crop_flag, - input_path, - vic_flag, - glc_flag, - start_year, - end_year, - hres_flag, - ) - - logger.info("--------------------------") - logger.info(" ctsm case : %s", ctsm_case) - logger.info("--------------------------") - - ctsm_case.create_namelist_file() - - -if __name__ == "__main__": - main() diff --git a/python/ctsm/lilac_build_ctsm.py b/python/ctsm/lilac_build_ctsm.py index 20231c0df9..b189cb56ea 100644 --- a/python/ctsm/lilac_build_ctsm.py +++ b/python/ctsm/lilac_build_ctsm.py @@ -718,8 +718,13 @@ def _create_case( else: machine_args = ["--machine", machine] + cmd = os.path.join(cime_path, "scripts", "create_newcase") + if not os.path.exists(cmd): + abort( + "The create_newcase command doesn't exist as expected <{}> does not exist)".format(cmd) + ) create_newcase_cmd = [ - os.path.join(cime_path, "scripts", "create_newcase"), + cmd, "--output-root", build_dir, "--case", @@ -741,7 +746,12 @@ def _create_case( create_newcase_cmd.extend(machine_args) if inputdata_path: create_newcase_cmd.extend(["--input-dir", inputdata_path]) - run_cmd_output_on_error(create_newcase_cmd, errmsg="Problem creating CTSM case directory") + if not os.path.isdir(inputdata_path): + abort("inputdata_path directory (<{}> does not exist)".format(inputdata_path)) + run_cmd_output_on_error( + create_newcase_cmd, + errmsg="Problem running create_newcase to create the CTSM case directory", + ) subprocess.check_call([xmlchange, "LILAC_MODE=on"], cwd=case_dir) if build_debug: diff --git a/python/ctsm/machine_defaults.py b/python/ctsm/machine_defaults.py index 7486237323..0f3900c152 100644 --- a/python/ctsm/machine_defaults.py +++ b/python/ctsm/machine_defaults.py @@ -47,7 +47,9 @@ "cheyenne": MachineDefaults( job_launcher_type=JOB_LAUNCHER_QSUB, scratch_dir=os.path.join(os.path.sep, "glade", "scratch", get_user()), - baseline_dir=os.path.join(os.path.sep, "glade", "p", "cgd", "tss", "ctsm_baselines"), + baseline_dir=os.path.join( + os.path.sep, "glade", "p", "cgd", "tss", "To_Be_Safely_Deleted", "ctsm_baselines" + ), account_required=True, create_test_retry=0, # NOTE(wjs, 2022-02-23) By default, use the regular queue, even for @@ -66,6 +68,25 @@ ) }, ), + "derecho": MachineDefaults( + job_launcher_type=JOB_LAUNCHER_QSUB, + scratch_dir=os.path.join(os.path.sep, "glade", "derecho", "scratch", get_user()), + baseline_dir=os.path.join(os.path.sep, "glade", "campaign", "cgd", "tss", "ctsm_baselines"), + account_required=True, + create_test_retry=0, + create_test_queue="regular", + job_launcher_defaults={ + JOB_LAUNCHER_QSUB: QsubDefaults( + queue="main", + walltime="03:50:00", + extra_args="", + # The following assumes a single node, with a single mpi proc; we may want + # to add more flexibility in the future, making the node / proc counts + # individually selectable + required_args="-l select=1:ncpus=128:mpiprocs=1 -V -r n -k oed", + ) + }, + ), "hobart": MachineDefaults( job_launcher_type=JOB_LAUNCHER_QSUB, scratch_dir=os.path.join(os.path.sep, "scratch", "cluster", get_user()), diff --git a/python/ctsm/machine_utils.py b/python/ctsm/machine_utils.py index da5c8b9c6a..970d2e9080 100644 --- a/python/ctsm/machine_utils.py +++ b/python/ctsm/machine_utils.py @@ -41,6 +41,8 @@ def _machine_from_hostname(hostname): """ if re.match(r"cheyenne\d+", hostname): machine = "cheyenne" + elif re.match(r"derecho\d+", hostname): + machine = "derecho" else: machine = hostname diff --git a/python/ctsm/modify_input_files/fsurdat_modifier.py b/python/ctsm/modify_input_files/fsurdat_modifier.py index 7b7abbc646..6d350171cc 100644 --- a/python/ctsm/modify_input_files/fsurdat_modifier.py +++ b/python/ctsm/modify_input_files/fsurdat_modifier.py @@ -11,7 +11,7 @@ from configparser import ConfigParser from ctsm.utils import abort, write_output -from ctsm.config_utils import get_config_value +from ctsm.config_utils import get_config_value, get_config_value_or_array from ctsm.ctsm_logging import ( setup_logging_pre_config, add_logging_args, @@ -29,112 +29,397 @@ def main(): Calls function that modifies an fsurdat (surface dataset) """ + args = fsurdat_modifier_arg_process() + fsurdat_modifier(args) + + +def fsurdat_modifier_arg_process(): + """Argument processing for fsurdat_modifier script""" # set up logging allowing user control setup_logging_pre_config() # read the command line argument to obtain the path to the .cfg file parser = argparse.ArgumentParser() parser.add_argument("cfg_path", help="/path/name.cfg of input file, eg ./modify.cfg") + parser.add_argument( + "-i", + "--fsurdat_in", + default="UNSET", + required=False, + type=str, + help="The input surface dataset to modify. ", + ) + parser.add_argument( + "-o", + "--fsurdat_out", + required=False, + default="UNSET", + type=str, + help="The output surface dataset with the modifications. ", + ) + parser.add_argument( + "--overwrite", + required=False, + default=False, + action="store_true", + help="Overwrite the output file if it already exists. ", + ) add_logging_args(parser) args = parser.parse_args() process_logging_args(args) - fsurdat_modifier(args.cfg_path) + # Error checking of arguments + if not os.path.exists(args.cfg_path): + abort("Config file does NOT exist: " + str(args.cfg_path)) + return args -def fsurdat_modifier(cfg_path): - """Implementation of fsurdat_modifier command""" - # read the .cfg (config) file - config = ConfigParser() - config.read(cfg_path) - section = config.sections()[0] # name of the first section - # required: user must set these in the .cfg file - fsurdat_in = get_config_value( - config=config, section=section, item="fsurdat_in", file_path=cfg_path - ) - fsurdat_out = get_config_value( - config=config, section=section, item="fsurdat_out", file_path=cfg_path - ) +def check_no_subgrid_section(config): + """Check that there isn't a subgrid section when it's processing is turned off""" + section = "modify_fsurdat_subgrid_fractions" + if config.has_section(section): + abort( + "Config file does have a section: " + + section + + " that should NOT be there since it is turned off" + ) - # required but fallback values available for variables omitted - # entirely from the .cfg file - idealized = get_config_value( + +def check_no_varlist_section(config): + """Check that there isn't a var list section when it's processing is turned off""" + section = "modify_fsurdat_variable_list" + if config.has_section(section): + abort( + "Config file does have a section: " + + section + + " that should NOT be there since it is turned off" + ) + + +def check_range(var, section, value, minval, maxval): + """Check that the value is within range""" + if value < minval or value > maxval: + abort("Variable " + var + " in " + section + " is out of range of 0 to 100 = " + str(value)) + + +def read_cfg_subgrid(config, cfg_path, numurbl=3): + """Read the subgrid fraction section from the config file""" + section = "modify_fsurdat_subgrid_fractions" + if not config.has_section(section): + abort("Config file does not have the expected section: " + section) + + subgrid_settings = {} + var_list = config.options(section) + valid_list = ["pct_natveg", "pct_crop", "pct_lake", "pct_glacier", "pct_wetland", "pct_urban"] + varsum = 0 + for var in var_list: + if valid_list.count(var) == 0: + abort( + "Variable " + + var + + " in " + + section + + " is not a valid variable name. Valid vars =" + + str(valid_list) + ) + # Urban is multidimensional + if var == "pct_urban": + vallist = get_config_value( + config=config, + section=section, + item=var, + file_path=cfg_path, + is_list=True, + convert_to_type=float, + ) + if len(vallist) != numurbl: + abort("PCT_URBAN is not a list of the expected size of " + str(numurbl)) + # so if a scalar value, must be multiplied # by the density dimension + for val in vallist: + check_range(var, section, val, 0.0, 100.0) + varsum += val + value = vallist + else: + value = get_config_value( + config=config, section=section, item=var, file_path=cfg_path, convert_to_type=float + ) + check_range(var, section, value, 0.0, 100.0) + varsum += value + + subgrid_settings[var.upper()] = value + + if varsum != 100.0: + abort( + "PCT fractions in subgrid section do NOT sum to a hundred as they should. Sum = " + + str(varsum) + ) + + return subgrid_settings + + +def read_cfg_var_list(config, idealized=True): + """Read the variable list section from the config file""" + section = "modify_fsurdat_variable_list" + if not config.has_section(section): + abort("Config file does not have the expected section: " + section) + + varlist_settings = {} + var_list = config.options(section) + ideal_list = [ + "soil_color", + "pct_sand", + "pct_clay", + "organic", + "pct_cft", + "pct_nat_pft", + "fmax", + "std_elev", + ] + subgrid_list = ["pct_natveg", "pct_crop", "pct_lake", "pct_glacier", "pct_wetland", "pct_urban"] + # List of variables that should be excluded because they are changed elsewhere, + # or they shouldn't be changed # Ds, Dsmax, and Ws are excluded because they + # are of mixed case and we only search for varaibles in lowercase + # or uppercase and not mixed case. + monthly_list = [ + "monthly_lai", + "monthly_sai", + "monthly_height_top", + "monthly_height_bot", + "ds", + "mxsoil_color", + "natpft", + "cft", + "time", + "longxy", + "latixy", + "dsmax", + "area", + "ws", + ] + for var in var_list: + if idealized and ideal_list.count(var) != 0: + abort( + var + + " is a special variable handled in the idealized section." + + " This should NOT be handled in the variable list section." + + " Special idealized vars =" + + str(ideal_list) + ) + if subgrid_list.count(var) != 0: + abort( + var + + " is a variable handled in the subgrid section." + + " This should NOT be handled in the variable list section." + + " Subgrid vars =" + + str(subgrid_list) + ) + if monthly_list.count(var) != 0: + abort( + var + + " is a variable handled as part of the dom_pft handling." + + " This should NOT be handled in the variable list section." + + " Monthly vars handled this way =" + + str(monthly_list) + ) + value = get_config_value_or_array( + config=config, section=section, item=var, convert_to_type=float + ) + varlist_settings[var] = value + + return varlist_settings + + +def modify_optional( + modify_fsurdat, + idealized, + include_nonveg, + max_sat_area, + std_elev, + soil_color, + dom_pft, + evenly_split_cropland, + lai, + sai, + hgt_top, + hgt_bot, +): + """Modify the dataset according to the optional settings""" + + # Set fsurdat variables in a rectangle that could be global (default). + # Note that the land/ocean mask gets specified in the domain file for + # MCT or the ocean mesh files for NUOPC. Here the user may specify + # fsurdat variables inside a box but cannot change which points will + # run as land and which as ocean. + if idealized: + modify_fsurdat.set_idealized() # set 2D variables + # set 3D and 4D variables pertaining to natural vegetation + # to default values here; allow override values with the later call + # to set_dom_pft + modify_fsurdat.set_dom_pft(dom_pft=0, lai=[], sai=[], hgt_top=[], hgt_bot=[]) + logger.info("idealized complete") + + if max_sat_area is not None: # overwrite "idealized" value + modify_fsurdat.setvar_lev0("FMAX", max_sat_area) + logger.info("max_sat_area complete") + + if std_elev is not None: # overwrite "idealized" value + modify_fsurdat.setvar_lev0("STD_ELEV", std_elev) + logger.info("std_elev complete") + + if soil_color is not None: # overwrite "idealized" value + modify_fsurdat.setvar_lev0("SOIL_COLOR", soil_color) + logger.info("soil_color complete") + + if not include_nonveg: + modify_fsurdat.zero_nonveg() + logger.info("zero_nonveg complete") + + # set_dom_pft follows idealized and zero_nonveg because it modifies + # PCT_NATVEG and PCT_CROP in the user-defined rectangle + if dom_pft is not None: + modify_fsurdat.set_dom_pft( + dom_pft=dom_pft, lai=lai, sai=sai, hgt_top=hgt_top, hgt_bot=hgt_bot + ) + logger.info("dom_pft complete") + + if evenly_split_cropland: + modify_fsurdat.evenly_split_cropland() + logger.info("evenly_split_cropland complete") + + +def read_cfg_optional_basic_opts(modify_fsurdat, config, cfg_path, section): + """Read the optional parts of the main section of the config file. + The main section is called modify_fsurdat_basic_options. + Users may set these optional parts but are not required to do so.""" + + lai = get_config_value( config=config, section=section, - item="idealized", + item="lai", file_path=cfg_path, - convert_to_type=bool, + is_list=True, + convert_to_type=float, + can_be_unset=True, ) - include_nonveg = get_config_value( + sai = get_config_value( config=config, section=section, - item="include_nonveg", + item="sai", file_path=cfg_path, - convert_to_type=bool, + is_list=True, + convert_to_type=float, + can_be_unset=True, ) - - lnd_lat_1 = get_config_value( + hgt_top = get_config_value( config=config, section=section, - item="lnd_lat_1", + item="hgt_top", file_path=cfg_path, + is_list=True, convert_to_type=float, + can_be_unset=True, ) - lnd_lat_2 = get_config_value( + hgt_bot = get_config_value( config=config, section=section, - item="lnd_lat_2", + item="hgt_bot", file_path=cfg_path, + is_list=True, convert_to_type=float, + can_be_unset=True, ) - lnd_lon_1 = get_config_value( + + max_soil_color = int(modify_fsurdat.file.mxsoil_color) + soil_color = get_config_value( config=config, section=section, - item="lnd_lon_1", + item="soil_color", file_path=cfg_path, - convert_to_type=float, + allowed_values=range(1, max_soil_color + 1), # 1 to max_soil_color + convert_to_type=int, + can_be_unset=True, ) - lnd_lon_2 = get_config_value( + + std_elev = get_config_value( config=config, section=section, - item="lnd_lon_2", + item="std_elev", file_path=cfg_path, convert_to_type=float, + can_be_unset=True, ) - - landmask_file = get_config_value( + max_sat_area = get_config_value( config=config, section=section, - item="landmask_file", + item="max_sat_area", file_path=cfg_path, + convert_to_type=float, can_be_unset=True, ) + return ( + max_sat_area, + std_elev, + soil_color, + lai, + sai, + hgt_top, + hgt_bot, + ) - lat_dimname = get_config_value( - config=config, section=section, item="lat_dimname", file_path=cfg_path, can_be_unset=True + +def read_cfg_option_control( + modify_fsurdat, + config, + section, + cfg_path, +): + """Read the option control section""" + # required but fallback values available for variables omitted + # entirely from the .cfg file + idealized = get_config_value( + config=config, + section=section, + item="idealized", + file_path=cfg_path, + convert_to_type=bool, ) - lon_dimname = get_config_value( - config=config, section=section, item="lon_dimname", file_path=cfg_path, can_be_unset=True + if idealized: + logger.info("idealized option is on") + else: + logger.info("idealized option is off") + process_subgrid = get_config_value( + config=config, + section=section, + item="process_subgrid_section", + file_path=cfg_path, + convert_to_type=bool, ) - - # Create ModifyFsurdat object - modify_fsurdat = ModifyFsurdat.init_from_file( - fsurdat_in, - lnd_lon_1, - lnd_lon_2, - lnd_lat_1, - lnd_lat_2, - landmask_file, - lat_dimname, - lon_dimname, + if process_subgrid: + logger.info("process_subgrid_section option is on") + else: + logger.info("process_subgrid_section option is off") + process_var_list = get_config_value( + config=config, + section=section, + item="process_var_list_section", + file_path=cfg_path, + convert_to_type=bool, ) - - # If output file exists, abort before starting work - if os.path.exists(fsurdat_out): - errmsg = "Output file already exists: " + fsurdat_out - abort(errmsg) - - # not required: user may set these in the .cfg file + if process_var_list: + logger.info("process_var_list_section option is on") + else: + logger.info("process_var_list_section option is off") + include_nonveg = get_config_value( + config=config, + section=section, + item="include_nonveg", + file_path=cfg_path, + convert_to_type=bool, + ) + if include_nonveg: + logger.info("include_nonveg option is on") + else: + logger.info("include_nonveg option is off") max_pft = int(max(modify_fsurdat.file.lsmpft)) dom_pft = get_config_value( config=config, @@ -145,110 +430,209 @@ def fsurdat_modifier(cfg_path): convert_to_type=int, can_be_unset=True, ) - - lai = get_config_value( + if dom_pft: + logger.info("dom_pft option is on and = %s", str(dom_pft)) + else: + logger.info("dom_pft option is off") + evenly_split_cropland = get_config_value( config=config, section=section, - item="lai", + item="evenly_split_cropland", file_path=cfg_path, - is_list=True, - convert_to_type=float, - can_be_unset=True, + convert_to_type=bool, ) - sai = get_config_value( + if ( + evenly_split_cropland + and dom_pft is not None + and dom_pft > int(max(modify_fsurdat.file.natpft.values)) + ): + abort("dom_pft must not be set to a crop PFT when evenly_split_cropland is True") + if process_subgrid and idealized: + abort("idealized AND process_subgrid_section can NOT both be on, pick one or the other") + + return ( + idealized, + process_subgrid, + process_var_list, + include_nonveg, + dom_pft, + evenly_split_cropland, + ) + + +def read_cfg_required_basic_opts(config, section, cfg_path): + """Read the required part of the control section""" + lnd_lat_1 = get_config_value( config=config, section=section, - item="sai", + item="lnd_lat_1", file_path=cfg_path, - is_list=True, convert_to_type=float, - can_be_unset=True, ) - hgt_top = get_config_value( + lnd_lat_2 = get_config_value( config=config, section=section, - item="hgt_top", + item="lnd_lat_2", file_path=cfg_path, - is_list=True, convert_to_type=float, - can_be_unset=True, ) - hgt_bot = get_config_value( + lnd_lon_1 = get_config_value( config=config, section=section, - item="hgt_bot", + item="lnd_lon_1", file_path=cfg_path, - is_list=True, convert_to_type=float, - can_be_unset=True, ) - - max_soil_color = int(modify_fsurdat.file.mxsoil_color) - soil_color = get_config_value( + lnd_lon_2 = get_config_value( config=config, section=section, - item="soil_color", + item="lnd_lon_2", file_path=cfg_path, - allowed_values=range(1, max_soil_color + 1), # 1 to max_soil_color - convert_to_type=int, - can_be_unset=True, + convert_to_type=float, ) - std_elev = get_config_value( + landmask_file = get_config_value( config=config, section=section, - item="std_elev", + item="landmask_file", file_path=cfg_path, - convert_to_type=float, can_be_unset=True, ) - max_sat_area = get_config_value( - config=config, - section=section, - item="max_sat_area", - file_path=cfg_path, - convert_to_type=float, - can_be_unset=True, + + lat_dimname = get_config_value( + config=config, section=section, item="lat_dimname", file_path=cfg_path, can_be_unset=True ) + lon_dimname = get_config_value( + config=config, section=section, item="lon_dimname", file_path=cfg_path, can_be_unset=True + ) + return (lnd_lat_1, lnd_lat_2, lnd_lon_1, lnd_lon_2, landmask_file, lat_dimname, lon_dimname) - # ------------------------------ - # modify surface data properties - # ------------------------------ - # Set fsurdat variables in a rectangle that could be global (default). - # Note that the land/ocean mask gets specified in the domain file for - # MCT or the ocean mesh files for NUOPC. Here the user may specify - # fsurdat variables inside a box but cannot change which points will - # run as land and which as ocean. - if idealized: - modify_fsurdat.set_idealized() # set 2D variables - # set 3D and 4D variables pertaining to natural vegetation - modify_fsurdat.set_dom_pft(dom_pft=0, lai=[], sai=[], hgt_top=[], hgt_bot=[]) - logger.info("idealized complete") +def fsurdat_modifier(parser): + """Implementation of fsurdat_modifier command""" + # read the .cfg (config) file + cfg_path = str(parser.cfg_path) + config = ConfigParser() + config.read(cfg_path) + section = "modify_fsurdat_basic_options" + if not config.has_section(section): + abort("Config file does not have the expected section: " + section) - if max_sat_area is not None: # overwrite "idealized" value - modify_fsurdat.setvar_lev0("FMAX", max_sat_area) - logger.info("max_sat_area complete") + if parser.fsurdat_in == "UNSET": + # required: user must set these in the .cfg file + fsurdat_in = get_config_value( + config=config, section=section, item="fsurdat_in", file_path=cfg_path + ) + else: + if config.has_option(section=section, option="fsurdat_in"): + abort("fsurdat_in is specified in both the command line and the config file, pick one") + fsurdat_in = str(parser.fsurdat_in) - if std_elev is not None: # overwrite "idealized" value - modify_fsurdat.setvar_lev0("STD_ELEV", std_elev) - logger.info("std_elev complete") + # Error checking of input file + if not os.path.exists(fsurdat_in): + abort("Input fsurdat_in file does NOT exist: " + str(fsurdat_in)) - if soil_color is not None: # overwrite "idealized" value - modify_fsurdat.setvar_lev0("SOIL_COLOR", soil_color) - logger.info("soil_color complete") + if parser.fsurdat_out == "UNSET": + fsurdat_out = get_config_value( + config=config, section=section, item="fsurdat_out", file_path=cfg_path + ) + else: + if config.has_option(section=section, option="fsurdat_out"): + abort("fsurdat_out is specified in both the command line and the config file, pick one") + fsurdat_out = str(parser.fsurdat_out) - if not include_nonveg: - modify_fsurdat.zero_nonveg() - logger.info("zero_nonveg complete") + # If output file exists, abort before starting work + if os.path.exists(fsurdat_out): + if not parser.overwrite: + errmsg = "Output file already exists: " + fsurdat_out + abort(errmsg) + else: + warnmsg = ( + "Output file already exists" + + ", but the overwrite option was selected so the file will be overwritten." + ) + logger.warning(warnmsg) + ( + lnd_lat_1, + lnd_lat_2, + lnd_lon_1, + lnd_lon_2, + landmask_file, + lat_dimname, + lon_dimname, + ) = read_cfg_required_basic_opts(config, section, cfg_path) + # Create ModifyFsurdat object + modify_fsurdat = ModifyFsurdat.init_from_file( + fsurdat_in, + lnd_lon_1, + lnd_lon_2, + lnd_lat_1, + lnd_lat_2, + landmask_file, + lat_dimname, + lon_dimname, + ) - # set_dom_pft follows zero_nonveg because it modifies PCT_NATVEG - # and PCT_CROP in the user-defined rectangle - if dom_pft is not None: - modify_fsurdat.set_dom_pft( - dom_pft=dom_pft, lai=lai, sai=sai, hgt_top=hgt_top, hgt_bot=hgt_bot - ) - logger.info("dom_pft complete") + # Read control information about the optional sections + ( + idealized, + process_subgrid, + process_var_list, + include_nonveg, + dom_pft, + evenly_split_cropland, + ) = read_cfg_option_control( + modify_fsurdat, + config, + section, + cfg_path, + ) + + # Read parts that are optional + ( + max_sat_area, + std_elev, + soil_color, + lai, + sai, + hgt_top, + hgt_bot, + ) = read_cfg_optional_basic_opts(modify_fsurdat, config, cfg_path, section) + # ------------------------------ + # modify surface data properties + # ------------------------------ + + modify_optional( + modify_fsurdat, + idealized, + include_nonveg, + max_sat_area, + std_elev, + soil_color, + dom_pft, + evenly_split_cropland, + lai, + sai, + hgt_top, + hgt_bot, + ) + # + # Handle optional sections + # + if process_subgrid: + subgrid = read_cfg_subgrid(config, cfg_path, numurbl=modify_fsurdat.get_urb_dens()) + modify_fsurdat.set_varlist(subgrid, cfg_path) + logger.info("process_subgrid is complete") + else: + check_no_subgrid_section(config) + + if process_var_list: + varlist = read_cfg_var_list(config, idealized=idealized) + update_list = modify_fsurdat.check_varlist(varlist, allow_uppercase_vars=True) + modify_fsurdat.set_varlist(update_list, cfg_path) + logger.info("process_var_list is complete") + else: + check_no_varlist_section(config) # ---------------------------------------------- # Output the now modified CTSM surface data file diff --git a/python/ctsm/modify_input_files/modify_fsurdat.py b/python/ctsm/modify_input_files/modify_fsurdat.py index ba1fd18fdb..53f06d7dc8 100644 --- a/python/ctsm/modify_input_files/modify_fsurdat.py +++ b/python/ctsm/modify_input_files/modify_fsurdat.py @@ -29,7 +29,12 @@ def __init__( self, my_data, lon_1, lon_2, lat_1, lat_2, landmask_file, lat_dimname, lon_dimname ): + self.numurbl = 3 # Number of urban density types self.file = my_data + if "numurbl" in self.file.dims: + self.numurbl = self.file.dims["numurbl"] + else: + abort("numurbl is not a dimension on the input surface dataset file and needs to be") self.rectangle = self._get_rectangle( lon_1=lon_1, @@ -115,6 +120,10 @@ def _get_rectangle(lon_1, lon_2, lat_1, lat_2, longxy, latixy): return rectangle + def get_urb_dens(self): + """Get the number of urban density classes""" + return self.numurbl + def write_output(self, fsurdat_in, fsurdat_out): """ Description @@ -156,6 +165,18 @@ def write_output(self, fsurdat_in, fsurdat_out): logger.info("Successfully created fsurdat_out: %s", fsurdat_out) self.file.close() + def evenly_split_cropland(self): + """ + Description + ----------- + In rectangle selected by user (or default -90 to 90 and 0 to 360), + replace fsurdat file's PCT_CFT with equal values for all crop types. + """ + pct_cft = np.full_like(self.file["PCT_CFT"].values, 100 / self.file.dims["cft"]) + self.file["PCT_CFT"] = xr.DataArray( + data=pct_cft, attrs=self.file["PCT_CFT"].attrs, dims=self.file["PCT_CFT"].dims + ) + def set_dom_pft(self, dom_pft, lai, sai, hgt_top, hgt_bot): """ Description @@ -171,6 +192,8 @@ def set_dom_pft(self, dom_pft, lai, sai, hgt_top, hgt_bot): --------- dom_pft: (int) User's entry of PFT/CFT to be set to 100% everywhere + If user left this UNSET in the configure file, then it + will default to 0 (bare ground). lai: (float) User's entry of MONTHLY_LAI for their dom_pft sai: @@ -214,6 +237,96 @@ def set_dom_pft(self, dom_pft, lai, sai, hgt_top, hgt_bot): if val is not None: self.set_lai_sai_hgts(dom_pft=dom_pft, var=var, val=val) + def check_varlist(self, settings, allow_uppercase_vars=False): + """ + Check a list of variables from a dictionary of settings + """ + settings_return = {} + varlist = settings.keys() + for var in varlist: + varname = var + val = settings[varname] + if not var in self.file: + if not allow_uppercase_vars: + errmsg = "Error: Variable " + varname + " is NOT in the file" + abort(errmsg) + if not varname.upper() in self.file: + errmsg = "Error: Variable " + varname.upper() + " is NOT in the file" + abort(errmsg) + varname = varname.upper() + + settings_return[varname] = val + # + # Check that dimensions are as expected + # + if len(self.file[varname].dims) == 2: + if not isinstance(val, float): + abort( + "For 2D vars, there should only be a single value for variable = " + varname + ) + elif len(self.file[varname].dims) >= 3: + dim1 = int(self.file.sizes[self.file[varname].dims[0]]) + if not isinstance(val, list): + abort( + "For higher dimensional vars, the variable needs to be expressed " + + "as a list of values of the dimension size = " + + str(dim1) + + " for variable=" + + varname + ) + if len(val) != dim1: + abort( + "Variable " + varname + " is of the wrong size. It should be = " + str(dim1) + ) + return settings_return + + def set_varlist(self, settings, cfg_path="unknown-config-file"): + """ + Set a list of variables from a dictionary of settings + """ + for var in settings.keys(): + if var in self.file: + if len(self.file[var].dims) == 2: + if not isinstance(settings[var], float): + abort( + "For 2D vars, there should only be a single value for variable = " + var + ) + self.setvar_lev0(var, settings[var]) + elif len(self.file[var].dims) == 3: + dim1 = int(self.file.sizes[self.file[var].dims[0]]) + vallist = settings[var] + if not isinstance(vallist, list): + abort( + "For higher dimensional vars, there must be a list of values " + + "for variable= " + + var + + " from the config file = " + + cfg_path + ) + if len(vallist) != dim1: + abort( + "Variable " + var + " is of the wrong size. It should be = " + str(dim1) + ) + for lev1 in range(dim1): + self.setvar_lev1(var, vallist[lev1], lev1_dim=lev1) + elif len(self.file[var].dims) == 4: + dim_lev1 = int(self.file.sizes[self.file[var].dims[1]]) + dim_lev2 = int(self.file.sizes[self.file[var].dims[0]]) + vallist = settings[var] + for lev1 in range(dim_lev1): + for lev2 in range(dim_lev2): + self.setvar_lev2(var, vallist[lev2], lev1_dim=lev1, lev2_dim=lev2) + else: + abort( + "Error: Variable " + + var + + " is a higher dimension than currently allowed = " + + str(self.file[var].dims) + ) + else: + errmsg = "Error: Variable " + var + " is NOT in the file" + abort(errmsg) + def set_lai_sai_hgts(self, dom_pft, var, val): """ Description diff --git a/python/ctsm/run_ctsm_py_tests.py b/python/ctsm/run_ctsm_py_tests.py index 0542dc41cb..8b39d69afa 100644 --- a/python/ctsm/run_ctsm_py_tests.py +++ b/python/ctsm/run_ctsm_py_tests.py @@ -45,7 +45,10 @@ def main(description): def _commandline_args(description): - """Parse and return command-line arguments""" + """Parse and return command-line arguments + Note that run_ctsm_py_tests is not intended to be + used without argument specifications + """ parser = argparse.ArgumentParser( description=description, formatter_class=argparse.RawTextHelpFormatter ) diff --git a/python/ctsm/run_sys_tests.py b/python/ctsm/run_sys_tests.py index 992f2b544a..de93081504 100644 --- a/python/ctsm/run_sys_tests.py +++ b/python/ctsm/run_sys_tests.py @@ -213,6 +213,13 @@ def run_sys_tests( rerun_existing_failures=rerun_existing_failures, extra_create_test_args=extra_create_test_args, ) + + running_ctsm_py_tests = ( + testfile == "/path/to/testfile" + or testlist in [["test1", "test2"], ["foo"]] + or suite_name == "my_suite" + ) + if suite_name: if not dry_run: _make_cs_status_for_suite(testroot, testid_base) @@ -225,16 +232,24 @@ def run_sys_tests( testroot=testroot, create_test_args=create_test_args, dry_run=dry_run, + running_ctsm_py_tests=running_ctsm_py_tests, ) else: if not dry_run: _make_cs_status_non_suite(testroot, testid_base) + running_ctsm_py_tests = testfile == "/path/to/testfile" if testfile: test_args = ["--testfile", os.path.abspath(testfile)] + if not running_ctsm_py_tests: + with open(test_args[1], "r") as testfile_abspath: + testname_list = testfile_abspath.readlines() elif testlist: test_args = testlist + testname_list = testlist else: raise RuntimeError("None of suite_name, testfile or testlist were provided") + if not running_ctsm_py_tests: + _check_py_env(testname_list) _run_create_test( cime_path=cime_path, test_args=test_args, @@ -668,9 +683,10 @@ def _run_test_suite( testroot, create_test_args, dry_run, + running_ctsm_py_tests, ): if not suite_compilers: - suite_compilers = _get_compilers_for_suite(suite_name, machine.name) + suite_compilers = _get_compilers_for_suite(suite_name, machine.name, running_ctsm_py_tests) for compiler in suite_compilers: test_args = [ "--xml-category", @@ -692,12 +708,65 @@ def _run_test_suite( ) -def _get_compilers_for_suite(suite_name, machine_name): +def _get_testmod_list(test_attributes, unique=False): + # Isolate testmods, producing a list like + # ["clm-test1mod1", "clm-test2mod1", "clm-test2mod2", ...] + # Handles test attributes passed in from run_sys_tests calls using -t, -f, or -s + + testmods = [] + for test_attribute in test_attributes: + for dot_split in test_attribute.split("."): + slash_replaced = dot_split.replace("/", "-") + for ddash_split in slash_replaced.split("--"): + if "clm-" in ddash_split and (ddash_split not in testmods or not unique): + testmods.append(ddash_split) + + return testmods + + +def _check_py_env(test_attributes): + err_msg = " can't be loaded. Do you need to activate the ctsm_pylib conda environment?" + # Suppress pylint import-outside-toplevel warning because (a) we only want to import + # this when certain tests are requested, and (b) the import needs to be in a try-except + # block to produce a nice error message. + # pylint: disable=import-outside-toplevel disable + # Suppress pylint unused-import warning because the import itself IS the use. + # pylint: disable=unused-import disable + # Suppress pylint import-error warning because the whole point here is to check + # whether import is possible. + # pylint: disable=import-error disable + + # Check requirements for FSURDATMODIFYCTSM, if needed + if any("FSURDATMODIFYCTSM" in t for t in test_attributes): + try: + import ctsm.modify_input_files.modify_fsurdat + except ModuleNotFoundError as err: + raise ModuleNotFoundError("modify_fsurdat" + err_msg) from err + + # Check that list for any testmods that use modify_fates_paramfile.py + testmods_to_check = ["clm-FatesColdTwoStream", "clm-FatesColdTwoStreamNoCompFixedBioGeo"] + testmods = _get_testmod_list(test_attributes) + if any(t in testmods_to_check for t in testmods): + # This bit is needed because it's outside the top-level python/ directory. + fates_dir = os.path.join( + os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, "src", "fates" + ) + sys.path.insert(1, fates_dir) + try: + import tools.modify_fates_paramfile + except ModuleNotFoundError as err: + raise ModuleNotFoundError("modify_fates_paramfile" + err_msg) from err + + +def _get_compilers_for_suite(suite_name, machine_name, running_ctsm_py_tests): test_data = get_tests_from_xml(xml_machine=machine_name, xml_category=suite_name) if not test_data: raise RuntimeError( "No tests found for suite {} on machine {}".format(suite_name, machine_name) ) + if not running_ctsm_py_tests: + _check_py_env([t["testname"] for t in test_data]) + _check_py_env([t["testmods"] for t in test_data if "testmods" in t.keys()]) compilers = sorted({one_test["compiler"] for one_test in test_data}) logger.info("Running with compilers: %s", compilers) return compilers diff --git a/tools/site_and_regional/modify_singlept_site_neon.py b/python/ctsm/site_and_regional/modify_singlept_site_neon.py similarity index 67% rename from tools/site_and_regional/modify_singlept_site_neon.py rename to python/ctsm/site_and_regional/modify_singlept_site_neon.py index 3610b0bb5c..ae1318e2f8 100755 --- a/tools/site_and_regional/modify_singlept_site_neon.py +++ b/python/ctsm/site_and_regional/modify_singlept_site_neon.py @@ -6,24 +6,21 @@ This script is for modifying surface dataset at neon sites using data available from the neon server. -After creating a single point surface data file from a global -surface data file using subset_data.py, use this script to +After creating a single point surface data file from a global +surface data file using subset_data.py, use this script to overwrite some fields with site-specific data for neon sites. This script will do the following: -- Download neon data for the specified site if it does not exist - in the specified directory : (i.e. ../../../neon_surffiles). +- Download neon data for the specified site if it does not exist + in the specified directory : (i.e. ../../../neon_surf_files). - Modify surface dataset with downloaded data. ------------------------------------------------------------------- -Instructions for running on Cheyenne/Casper: +Instructions for running using conda python environments: -load the following into your local environment - module load python - ncar_pylib +../../py_env_create +conda activate ctsm_py -To remove NPL from your environment on Cheyenne/Casper: - deactivate ------------------------------------------------------------------- To see the available options: ./modify_singlept_site_neon.py --help @@ -34,80 +31,34 @@ """ # TODO (NS) # --[] If subset file not found run subset_data.py -# --[] List of valid neon sites for all scripts come from one place. # --[] Download files only when available. # Import libraries from __future__ import print_function +import argparse +from datetime import date +from getpass import getuser +import glob +import logging import os import sys -import glob -import argparse import requests -import logging import numpy as np import pandas as pd import xarray as xr +from packaging import version -from datetime import date -from getpass import getuser - +from ctsm.path_utils import path_to_ctsm_root myname = getuser() # -- valid neon sites -valid_neon_sites = [ - "ABBY", - "BARR", - "BART", - "BLAN", - "BONA", - "CLBJ", - "CPER", - "DCFS", - "DEJU", - "DELA", - "DSNY", - "GRSM", - "GUAN", - "HARV", - "HEAL", - "JERC", - "JORN", - "KONA", - "KONZ", - "LAJA", - "LENO", - "MLBS", - "MOAB", - "NIWO", - "NOGP", - "OAES", - "ONAQ", - "ORNL", - "OSBS", - "PUUM", - "RMNP", - "SCBI", - "SERC", - "SJER", - "SOAP", - "SRER", - "STEI", - "STER", - "TALL", - "TEAK", - "TOOL", - "TREE", - "UKFS", - "UNDE", - "WOOD", - "WREF", - "YELL", -] +valid_neon_sites = glob.glob( + os.path.join(path_to_ctsm_root(), "cime_config", "usermods_dirs", "NEON", "[!d]*") +) def get_parser(): @@ -131,7 +82,7 @@ def get_parser(): parser.add_argument( "--surf_dir", help=""" - Directory of single point surface dataset. + Directory of single point surface dataset. [default: %(default)s] """, action="store", @@ -144,7 +95,7 @@ def get_parser(): "--out_dir", help=""" Directory to write updated single point surface dataset. - [default: %(default)s] + [default: %(default)s] """, action="store", dest="out_dir", @@ -152,18 +103,38 @@ def get_parser(): required=False, default="/glade/scratch/" + myname + "/single_point_neon_updated/", ) + parser.add_argument( + "--inputdata-dir", + help=""" + Directory containing standard input files from CESM input data such as the surf_soildepth_file. + [default: %(default)s] + """, + action="store", + dest="inputdatadir", + type=str, + required=False, + default="/glade/p/cesmdata/cseg/inputdata", + ) parser.add_argument( "-d", "--debug", help=""" - Debug mode will print more information. - [default: %(default)s] + Debug mode will print more information. + [default: %(default)s] """, action="store_true", dest="debug", default=False, ) + parser.add_argument( + "--16pft", + help="Modify 16-pft surface data files (e.g. for a FATES run)", + action="store_true", + dest="pft_16", + default=False, + ) + return parser @@ -171,7 +142,7 @@ def get_neon(neon_dir, site_name): """ Function for finding neon data files and download from neon server if the - file does not exits. + file does not exist. Args: neon_dir (str): local directory for downloading neon data. @@ -192,7 +163,7 @@ def get_neon(neon_dir, site_name): neon_file = os.path.join(neon_dir, site_name + "_surfaceData.csv") - # -- Download the file if it does not exits + # -- Download the file if it does not exist if os.path.isfile(neon_file): print("neon file for", site_name, "already exists! ") print("Skipping download from neon for", site_name, "...") @@ -207,18 +178,15 @@ def get_neon(neon_dir, site_name): ) response = requests.get(url) - with open(neon_file, "wb") as f: - f.write(response.content) + with open(neon_file, "wb") as a_file: + a_file.write(response.content) # -- Check if download status_code if response.status_code == 200: print("Download finished successfully for", site_name) elif response.status_code == 404: sys.exit( - "Data for this site " - + site_name - + " was not available on the neon server:" - + url + "Data for this site " + site_name + " was not available on the neon server:" + url ) print("Download exit status code: ", response.status_code) @@ -231,7 +199,7 @@ def get_neon(neon_dir, site_name): return neon_file -def find_surffile(surf_dir, site_name): +def find_surffile(surf_dir, site_name, pft_16): """ Function for finding and choosing surface file for a neon site. @@ -242,6 +210,7 @@ def find_surffile(surf_dir, site_name): Args: surf_dir (str): directory of single point surface data site_name (str): 4 letter neon site name + pft_16 (bool): if true, use 16-PFT version of surface data file Raises: Error if the surface data for the site is not created @@ -250,10 +219,13 @@ def find_surffile(surf_dir, site_name): surf_file (str): name of the surface dataset file """ - # sf_name = "surfdata_hist_16pfts_Irrig_CMIP6_simyr2000_"+site_name+"*.nc" - sf_name = "surfdata_*hist_78pfts_CMIP6_simyr2000_" + site_name + "*.nc" - print (os.path.join(surf_dir , sf_name)) - surf_file = sorted(glob.glob(os.path.join(surf_dir , sf_name))) + if pft_16: + sf_name = "surfdata_1x1_NEON_" + site_name + "*hist_16pfts_Irrig_CMIP6_simyr2000_*.nc" + else: + sf_name = "surfdata_1x1_NEON_" + site_name + "*hist_78pfts_CMIP6_simyr2000_*.nc" + + print(os.path.join(surf_dir, sf_name)) + surf_file = sorted(glob.glob(os.path.join(surf_dir, sf_name))) if len(surf_file) > 1: print("The following files found :", *surf_file, sep="\n- ") @@ -265,18 +237,22 @@ def find_surffile(surf_dir, site_name): surf_file = surf_file[0] else: sys.exit( - "Surface data for this site " + site_name + "was not found:" + surf_file, - ".", - "\n", - "Please run ./subset_data.py for this site.", + "Surface data for this site " + + str(site_name) + + " was not found:" + + str(surf_dir) + + str(sf_name) + + "." + + "\n" + + "Please run ./subset_data.py for this site." ) return surf_file -def find_soil_structure(surf_file): +def find_soil_structure(args, surf_file): """ Function for finding surface dataset soil - strucutre using surface data metadata. + structure using surface data metadata. In CLM surface data, soil layer information is in a file from surface data metadata @@ -284,7 +260,7 @@ def find_soil_structure(surf_file): This function finds this file for the surface dataset, read it, and find soil layers. - Args: + args: surf_file (str): single point surface data filename Raises: @@ -298,14 +274,12 @@ def find_soil_structure(surf_file): print("------------") print("surf_file : ", surf_file) - f1 = xr.open_dataset(surf_file) + f_1 = xr.open_dataset(surf_file) print("------------") - # print (f1.attrs["Soil_texture_raw_data_file_name"]) + # print (f_1.attrs["Soil_texture_raw_data_file_name"]) - clm_input_dir = "/glade/p/cesmdata/cseg/inputdata/lnd/clm2/rawdata/" - surf_soildepth_file = os.path.join( - clm_input_dir, f1.attrs["Soil_texture_raw_data_file_name"] - ) + clm_input_dir = os.path.join(args.inputdatadir, "lnd/clm2/rawdata/") + surf_soildepth_file = os.path.join(clm_input_dir, f_1.attrs["Soil_texture_raw_data_file_name"]) if os.path.exists(surf_soildepth_file): print( @@ -313,9 +287,9 @@ def find_soil_structure(surf_file): surf_soildepth_file, "for surface data soil structure information:", ) - f1_soildepth = xr.open_dataset(surf_soildepth_file) - print(f1_soildepth["DZSOI"]) - soil_bot = f1_soildepth["DZSOI"].values + f_1_soildepth = xr.open_dataset(surf_soildepth_file) + print(f_1_soildepth["DZSOI"]) + soil_bot = f_1_soildepth["DZSOI"].values # -- soil layer top soil_top = soil_bot[:-1] @@ -323,41 +297,40 @@ def find_soil_structure(surf_file): else: sys.exit( - "Cannot find soil structure file : " - + surf_soildepth_file - + "for the surface dataset." + "Cannot find soil structure file : " + surf_soildepth_file + "for the surface dataset." ) return soil_bot, soil_top -def update_metadata(nc, surf_file, neon_file, zb_flag): +def update_metadata(nc_file, surf_file, neon_file, zb_flag): """ Function for updating modified surface dataset - metadat for neon sites. + metadata for neon sites. Args: - nc (xr Dataset): netcdf file including updated neon surface data + nc_file (xr Dataset): netcdf file including updated neon surface data surf_file (str): single point surface data filename neon_file (str): filename of neon downloaded surface dataset + zb_flag (bool): update bedrock Returns: - nc (xr Dataset): netcdf file including updated neon surface data + nc_file (xr Dataset): netcdf file including updated neon surface data """ today = date.today() today_string = today.strftime("%Y-%m-%d") - nc.attrs["Updated_on"] = today_string - nc.attrs["Updated_by"] = myname - nc.attrs["Updated_with"] = os.path.abspath(__file__) - nc.attrs["Updated_from"] = surf_file - nc.attrs["Updated_using"] = neon_file + nc_file.attrs["Updated_on"] = today_string + nc_file.attrs["Updated_by"] = myname + nc_file.attrs["Updated_with"] = os.path.abspath(__file__) + nc_file.attrs["Updated_from"] = surf_file + nc_file.attrs["Updated_using"] = neon_file if zb_flag: - nc.attrs["Updated_fields"] = "PCT_CLAY, PCT_SAND, ORGANIC, zbedrock" + nc_file.attrs["Updated_fields"] = "PCT_CLAY, PCT_SAND, ORGANIC, zbedrock" else: - nc.attrs["Updated_fields"] = "PCT_CLAY, PCT_SAND, ORGANIC" + nc_file.attrs["Updated_fields"] = "PCT_CLAY, PCT_SAND, ORGANIC" - return nc + return nc_file def update_time_tag(fname_in): @@ -371,7 +344,7 @@ def update_time_tag(fname_in): fname_in (str) : file name with the old time tag Raises: - error if the file does not end with with + error if the file does not end with [._]cYYMMDD.nc or [._]YYMMDD.nc Returns: @@ -414,7 +387,7 @@ def sort_print_soil_layers(obs_bot, soil_bot): print("================================", "================================") - for index, row in depth_df.iterrows(): + for _, row in depth_df.iterrows(): if row["type"] == "obs": print("-------------", "{0:.3f}".format(row["depth"]), "------------") else: @@ -440,10 +413,9 @@ def check_neon_time(): download_file(url, listing_file) - df = pd.read_csv(listing_file) - df = df[df["object"].str.contains("_surfaceData.csv")] - # df=df.join(df['object'].str.split("/", expand=True)) - dict_out = dict(zip(df["object"], df["last_modified"])) + d_f = pd.read_csv(listing_file) + d_f = d_f[d_f["object"].str.contains("_surfaceData.csv")] + dict_out = dict(zip(d_f["object"], d_f["last_modified"])) print(dict_out) return dict_out @@ -460,8 +432,8 @@ def download_file(url, fname): try: response = requests.get(url) - with open(fname, "wb") as f: - f.write(response.content) + with open(fname, "wb") as a_file: + a_file.write(response.content) # -- Check if download status_code if response.status_code == 200: @@ -469,12 +441,12 @@ def download_file(url, fname): elif response.status_code == 404: print("File " + fname + "was not available on the neon server:" + url) except Exception as err: - print ('The server could not fulfill the request.') - print ('Something went wrong in downloading', fname) - print ('Error code:', err.code) + print("The server could not fulfill the request.") + print("Something went wrong in downloading", fname) + print("Error code:", err.code) -def fill_interpolate(f2, var, method): +def fill_interpolate(f_2, var, method): """ Function to interpolate a variable in a xarray dataset a specific method @@ -483,62 +455,65 @@ def fill_interpolate(f2, var, method): print("Filling in ", var, "with interpolation (method =" + method + ").") print("Variable before filling : ") - print(f2[var]) + print(f_2[var]) - tmp_df = pd.DataFrame(f2[var].values.ravel()) + tmp_df = pd.DataFrame(f_2[var].values.ravel()) tmp_df = tmp_df.interpolate(method=method, limit_direction="both") - # tmp_df = tmp_df.interpolate(method ='spline',order = 2, limit_direction ='both') - # tmp_df = tmp_df.interpolate(method="pad", limit=5, limit_direction = 'forward') tmp = tmp_df.to_numpy() - soil_levels = f2[var].size + soil_levels = f_2[var].size for soil_lev in range(soil_levels): - f2[var][soil_lev] = tmp[soil_lev].reshape(1, 1) + f_2[var][soil_lev] = tmp[soil_lev].reshape(1, 1) print("Variable after filling : ") - print(f2[var]) + print(f_2[var]) print("=====================================") def main(): - + """modify_singlept_site_neon main function""" args = get_parser().parse_args() # -- debugging option if args.debug: logging.basicConfig(level=logging.DEBUG) - file_time = check_neon_time() + # Check if pandas is a recent enough version + pdvers = pd.__version__ + if version.parse(pdvers) < version.parse("1.1.0"): + sys.exit( + """The pandas version in your python environment is too old, + update to a newer version of pandas (>=1.1.0): version=%s""", + pdvers, + ) + + # file_time = check_neon_time() # -- specify site from which to extract data site_name = args.site_name # -- Look for surface data surf_dir = args.surf_dir - surf_file = find_surffile(surf_dir, site_name) + surf_file = find_surffile(surf_dir, site_name, args.pft_16) # -- directory structure - current_dir = os.getcwd() - parent_dir = os.path.dirname(current_dir) - clone_dir = os.path.abspath(os.path.join(__file__, "../../..")) + clone_dir = os.path.abspath(os.path.join(__file__, "../../../..")) neon_dir = os.path.join(clone_dir, "neon_surffiles") - print("Present Directory", current_dir) - # -- download neon data if needed neon_file = get_neon(neon_dir, site_name) # -- Read neon data - df = pd.read_csv(neon_file) + d_f = pd.read_csv(neon_file) # -- Read surface dataset files print("surf_file:", surf_file) - f1 = xr.open_dataset(surf_file) + f_1 = xr.open_dataset(surf_file) # -- Find surface dataset soil depth information - soil_bot, soil_top = find_soil_structure(surf_file) + soil_bot, soil_top = find_soil_structure(args, surf_file) # -- Find surface dataset soil levels # TODO: how? NS uses metadata on file to find @@ -546,22 +521,15 @@ def main(): # better suggestion by WW to write dzsoi to neon surface dataset # This todo needs to go to the subset_data - # TODO Will: if I sum them up , are they 3.5? (m) YES - print("soil_top:", soil_top) - print("soil_bot:", soil_bot) - print("Sum of soil top depths :", sum(soil_top)) - print("Sum of soil bottom depths :", sum(soil_bot)) - soil_top = np.cumsum(soil_top) soil_bot = np.cumsum(soil_bot) soil_mid = 0.5 * (soil_bot - soil_top) + soil_top # print ("Cumulative sum of soil bottom depths :", sum(soil_bot)) - obs_top = df["biogeoTopDepth"] / 100 - obs_bot = df["biogeoBottomDepth"] / 100 + obs_bot = d_f["biogeoBottomDepth"] / 100 # -- Mapping surface dataset and neon soil levels - bins = df["biogeoTopDepth"] / 100 + bins = d_f["biogeoTopDepth"] / 100 bin_index = np.digitize(soil_mid, bins) - 1 """ @@ -589,41 +557,40 @@ def main(): """ # -- update fields with neon - f2 = f1 - soil_levels = f2["PCT_CLAY"].size + f_2 = f_1 + soil_levels = f_2["PCT_CLAY"].size for soil_lev in range(soil_levels): print("--------------------------") print("soil_lev:", soil_lev) - print(df["clayTotal"][bin_index[soil_lev]]) - f2["PCT_CLAY"][soil_lev] = df["clayTotal"][bin_index[soil_lev]] - f2["PCT_SAND"][soil_lev] = df["sandTotal"][bin_index[soil_lev]] + print(d_f["clayTotal"][bin_index[soil_lev]]) + f_2["PCT_CLAY"][soil_lev] = d_f["clayTotal"][bin_index[soil_lev]] + f_2["PCT_SAND"][soil_lev] = d_f["sandTotal"][bin_index[soil_lev]] - bulk_den = df["bulkDensExclCoarseFrag"][bin_index[soil_lev]] - carbon_tot = df["carbonTot"][bin_index[soil_lev]] - estimated_oc = df["estimatedOC"][bin_index[soil_lev]] + bulk_den = d_f["bulkDensExclCoarseFrag"][bin_index[soil_lev]] + carbon_tot = d_f["carbonTot"][bin_index[soil_lev]] + estimated_oc = d_f["estimatedOC"][bin_index[soil_lev]] # -- estimated_oc in neon data is rounded to the nearest integer. # -- Check to make sure the rounded oc is not higher than carbon_tot. # -- Use carbon_tot if estimated_oc is bigger than carbon_tot. - if estimated_oc > carbon_tot: - estimated_oc = carbon_tot + estimated_oc = min(estimated_oc, carbon_tot) layer_depth = ( - df["biogeoBottomDepth"][bin_index[soil_lev]] - - df["biogeoTopDepth"][bin_index[soil_lev]] + d_f["biogeoBottomDepth"][bin_index[soil_lev]] + - d_f["biogeoTopDepth"][bin_index[soil_lev]] ) - # f2["ORGANIC"][soil_lev] = estimated_oc * bulk_den / 0.58 + # f_2["ORGANIC"][soil_lev] = estimated_oc * bulk_den / 0.58 # -- after adding caco3 by NEON: # -- if caco3 exists: # -- inorganic = caco3/100.0869*12.0107 # -- organic = carbon_tot - inorganic # -- else: - # -- oranigc = estimated_oc * bulk_den /0.58 + # -- organic = estimated_oc * bulk_den /0.58 - caco3 = df["caco3Conc"][bin_index[soil_lev]] + caco3 = d_f["caco3Conc"][bin_index[soil_lev]] inorganic = caco3 / 100.0869 * 12.0107 print("inorganic:", inorganic) @@ -632,7 +599,7 @@ def main(): else: actual_oc = estimated_oc - f2["ORGANIC"][soil_lev] = actual_oc * bulk_den / 0.58 + f_2["ORGANIC"][soil_lev] = actual_oc * bulk_den / 0.58 print("~~~~~~~~~~~~~~~~~~~~~~~~") print("inorganic:") @@ -645,14 +612,14 @@ def main(): print("carbon_tot : ", carbon_tot) print("estimated_oc : ", estimated_oc) print("bulk_den : ", bulk_den) - print("organic :", f2["ORGANIC"][soil_lev].values) + print("organic :", f_2["ORGANIC"][soil_lev].values) print("--------------------------") # -- Interpolate missing values method = "linear" - fill_interpolate(f2, "PCT_CLAY", method) - fill_interpolate(f2, "PCT_SAND", method) - fill_interpolate(f2, "ORGANIC", method) + fill_interpolate(f_2, "PCT_CLAY", method) + fill_interpolate(f_2, "PCT_SAND", method) + fill_interpolate(f_2, "ORGANIC", method) # -- Update zbedrock if neon observation does not make it down to 2m depth rock_thresh = 2 @@ -661,7 +628,7 @@ def main(): if obs_bot.iloc[-1] < rock_thresh: print("zbedrock is updated.") - f2["zbedrock"].values[:, :] = obs_bot.iloc[-1] + f_2["zbedrock"].values[:, :] = obs_bot.iloc[-1] zb_flag = True sort_print_soil_layers(obs_bot, soil_bot) @@ -670,20 +637,18 @@ def main(): ag_sites = ["KONA", "STER"] if site_name in ag_sites: print("Updating PCT_NATVEG") - print("Original : ", f2.PCT_NATVEG.values) - f2.PCT_NATVEG.values = [[0.0]] - print("Updated : ", f2.PCT_NATVEG.values) + print("Original : ", f_2.PCT_NATVEG.values) + f_2.PCT_NATVEG.values = [[0.0]] + print("Updated : ", f_2.PCT_NATVEG.values) print("Updating PCT_CROP") - print("Original : ", f2.PCT_CROP.values) - f2.PCT_CROP.values = [[100.0]] - print("Updated : ", f2.PCT_CROP.values) + print("Original : ", f_2.PCT_CROP.values) + f_2.PCT_CROP.values = [[100.0]] + print("Updated : ", f_2.PCT_CROP.values) print("Updating PCT_NAT_PFT") - #print (f2.PCT_NAT_PFT) - print(f2.PCT_NAT_PFT.values[0]) - f2.PCT_NAT_PFT.values[0] = [[100.0]] - print(f2.PCT_NAT_PFT[0].values) + print(f_2.PCT_NAT_PFT.values[0]) + print(f_2.PCT_NAT_PFT[0].values) out_dir = args.out_dir @@ -695,18 +660,9 @@ def main(): wfile = out_dir + update_time_tag(surf_file) # -- update netcdf metadata - f2 = update_metadata(f2, surf_file, neon_file, zb_flag) - - print(f2.attrs) - f2.to_netcdf(path=wfile, mode="w", format="NETCDF3_64BIT") - - print( - "Successfully updated surface data file for neon site(" - + site_name - + "):\n - " - + wfile - ) + f_2 = update_metadata(f_2, surf_file, neon_file, zb_flag) + print(f_2.attrs) + f_2.to_netcdf(path=wfile, mode="w", format="NETCDF3_64BIT") -if __name__ == "__main__": - main() + print("Successfully updated surface data file for neon site(" + site_name + "):\n - " + wfile) diff --git a/python/ctsm/site_and_regional/neon_arg_parse.py b/python/ctsm/site_and_regional/neon_arg_parse.py new file mode 100644 index 0000000000..99f184dd62 --- /dev/null +++ b/python/ctsm/site_and_regional/neon_arg_parse.py @@ -0,0 +1,240 @@ +""" +Argument parser to use throughout run_neon.py +""" + +import argparse +import logging +import os +import sys + +# Get the ctsm util tools and then the cime tools. +_CTSM_PYTHON = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "python")) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position, import-error, unused-import, wrong-import-order +from ctsm import add_cime_to_path +from ctsm.utils import parse_isoduration +from CIME.utils import parse_args_and_handle_standard_logging_options +from CIME.utils import setup_standard_logging_options + + +def get_parser(args, description, valid_neon_sites): + """ + Get parser object for this script. + """ + parser = argparse.ArgumentParser( + description=description, formatter_class=argparse.RawDescriptionHelpFormatter + ) + + setup_standard_logging_options(parser) + + parser.print_usage = parser.print_help + + parser.add_argument( + "--neon-sites", + help="4-letter neon site code.", + action="store", + required=False, + choices=valid_neon_sites + ["all"], + dest="neon_sites", + default=["OSBS"], + nargs="+", + ) + + parser.add_argument( + "--base-case", + help=""" + Root Directory of base case build + [default: %(default)s] + """, + action="store", + dest="base_case_root", + type=str, + required=False, + default=None, + ) + + parser.add_argument( + "--output-root", + help=""" + Root output directory of cases + [default: %(default)s] + """, + action="store", + dest="output_root", + type=str, + required=False, + default="CIME_OUTPUT_ROOT as defined in cime", + ) + + parser.add_argument( + "--overwrite", + help=""" + overwrite existing case directories + [default: %(default)s] + """, + action="store_true", + dest="overwrite", + required=False, + default=False, + ) + + parser.add_argument( + "--setup-only", + help=""" + Only setup the requested cases, do not build or run + [default: %(default)s] + """, + action="store_true", + dest="setup_only", + required=False, + default=False, + ) + + parser.add_argument( + "--rerun", + help=""" + If the case exists but does not appear to be complete, restart it. + [default: %(default)s] + """, + action="store_true", + dest="rerun", + required=False, + default=False, + ) + + parser.add_argument( + "--no-batch", + help=""" + Run locally, do not use batch queueing system (if defined for Machine) + [default: %(default)s] + """, + action="store_true", + dest="no_batch", + required=False, + default=False, + ) + + parser.add_argument( + "--run-type", + help=""" + Type of run to do + [default: %(default)s] + """, + choices=["ad", "postad", "transient"], # , "sasu"], + default="transient", + ) + + parser.add_argument( + "--prism", + help=""" + Uses the PRISM reanaylsis precipitation data for the site instead of the NEON data + (only available over Continental US) + """, + action="store_true", + dest="prism", + required=False, + default=False, + ) + + parser.add_argument( + "--experiment", + help=""" + Appends the case name with string for model experiment + """, + action="store", + dest="experiment", + type=str, + required=False, + default=None, + ) + + parser.add_argument( + "--run-length", + help=""" + How long to run (modified ISO 8601 duration) + [default: %(default)s] + """, + required=False, + type=str, + default="0Y", + ) + + parser.add_argument( + "--run-from-postad", + help=""" + For transient runs only - should we start from the postad spinup or finidat? + By default start from finidat, if this flag is used the postad run must be available. + """, + action="store_true", + required=False, + default=False, + ) + parser.add_argument( + "--neon-version", + help=""" + Neon data version to use for this simulation. + [default: use the latest data available] + """, + action="store", + dest="user_version", + required=False, + type=str, + choices=["v1", "v2", "v3"], + ) + + args = parse_args_and_handle_standard_logging_options(args, parser) + + if "all" in args.neon_sites: + neon_sites = valid_neon_sites + else: + neon_sites = args.neon_sites + for site in neon_sites: + if site not in valid_neon_sites: + raise ValueError("Invalid site name {}".format(site)) + + if "CIME_OUTPUT_ROOT" in args.output_root: + args.output_root = None + + if args.run_length == "0Y": + if args.run_type == "ad": + run_length = "100Y" + elif args.run_type == "postad": + run_length = "100Y" + else: + # The transient run length is set by cdeps atm buildnml to + # the last date of the available tower data + # this value is not used + run_length = "4Y" + else: + run_length = args.run_length + + run_length = parse_isoduration(run_length) + + base_case_root = None + if args.base_case_root: + base_case_root = os.path.abspath(args.base_case_root) + if not os.path.exists(base_case_root): + raise ValueError("Base case root does not exist: {}".format(base_case_root)) + + # Reduce output level for this script unless --debug or + # --verbose is provided on the command line + if not args.debug and not args.verbose: + root_logger = logging.getLogger() + root_logger.setLevel(logging.WARN) + + return ( + neon_sites, + args.output_root, + args.run_type, + args.experiment, + args.prism, + args.overwrite, + run_length, + base_case_root, + args.run_from_postad, + args.setup_only, + args.no_batch, + args.rerun, + args.user_version, + ) diff --git a/python/ctsm/site_and_regional/neon_site.py b/python/ctsm/site_and_regional/neon_site.py new file mode 100755 index 0000000000..31ae78f5ad --- /dev/null +++ b/python/ctsm/site_and_regional/neon_site.py @@ -0,0 +1,394 @@ +""" +This module contains the NeonSite class and class functions which are used in run_neon.py +""" + +# Import libraries +import glob +import logging +import os +import re +import shutil +import sys +import time + +# Get the ctsm util tools and then the cime tools. +_CTSM_PYTHON = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "python")) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position, import-error, unused-import, wrong-import-order +from ctsm import add_cime_to_path +from ctsm.path_utils import path_to_ctsm_root + +from CIME import build +from CIME.case import Case +from CIME.utils import safe_copy, expect, symlink_force + +logger = logging.getLogger(__name__) + + +# pylint: disable=too-many-instance-attributes +class NeonSite: + """ + A class for encapsulating neon sites. + """ + + def __init__(self, name, start_year, end_year, start_month, end_month, finidat): + self.name = name + self.start_year = int(start_year) + self.end_year = int(end_year) + self.start_month = int(start_month) + self.end_month = int(end_month) + self.cesmroot = path_to_ctsm_root() + self.finidat = finidat + + def build_base_case( + self, cesmroot, output_root, res, compset, overwrite=False, setup_only=False + ): + """ + Function for building a base_case to clone. + To spend less time on building ctsm for the neon cases, + all the other cases are cloned from this case + + Args: + self: + The NeonSite object + base_root (str): + root of the base_case CIME + res (str): + base_case resolution or gridname + compset (str): + base case compset + overwrite (bool) : + Flag to overwrite the case if exists + """ + print("---- building a base case -------") + # pylint: disable=attribute-defined-outside-init + self.base_case_root = output_root + # pylint: enable=attribute-defined-outside-init + user_mods_dirs = [os.path.join(cesmroot, "cime_config", "usermods_dirs", "NEON", self.name)] + if not output_root: + output_root = os.getcwd() + case_path = os.path.join(output_root, self.name) + + logger.info("base_case_name : %s", self.name) + logger.info("user_mods_dir : %s", user_mods_dirs[0]) + + if overwrite and os.path.isdir(case_path): + print("Removing the existing case at: {}".format(case_path)) + shutil.rmtree(case_path) + + with Case(case_path, read_only=False) as case: + if not os.path.isdir(case_path): + print("---- creating a base case -------") + + case.create( + case_path, + cesmroot, + compset, + res, + run_unsupported=True, + answer="r", + output_root=output_root, + user_mods_dirs=user_mods_dirs, + driver="nuopc", + ) + + print("---- base case created ------") + + # --change any config for base_case: + # case.set_value("RUN_TYPE","startup") + print("---- base case setup ------") + case.case_setup() + else: + # For existing case check that the compset name is correct + existingcompname = case.get_value("COMPSET") + match = re.search("^HIST", existingcompname, flags=re.IGNORECASE) + if re.search("^HIST", compset, flags=re.IGNORECASE) is None: + expect( + match is None, + """Existing base case is a historical type and should not be + --rerun with the --overwrite option""", + ) + else: + expect( + match is not None, + """Existing base case should be a historical type and is not + --rerun with the --overwrite option""", + ) + # reset the case + case.case_setup(reset=True) + case_path = case.get_value("CASEROOT") + + if setup_only: + return case_path + + print("---- base case build ------") + print("--- This may take a while and you may see WARNING messages ---") + # always walk through the build process to make sure it's up to date. + initial_time = time.time() + build.case_build(case_path, case=case) + end_time = time.time() + total = end_time - initial_time + print("Time required to building the base case: {} s.".format(total)) + # update case_path to be the full path to the base case + return case_path + + # pylint: disable=no-self-use + def get_batch_query(self, case): + """ + Function for querying the batch queue query command for a case, depending on the + user's batch system. + + Args: + case: + case object + """ + + if case.get_value("BATCH_SYSTEM") == "none": + return "none" + return case.get_value("batch_query") + + # pylint: disable=too-many-statements + def run_case( + self, + base_case_root, + run_type, + prism, + run_length, + user_version, + overwrite=False, + setup_only=False, + no_batch=False, + rerun=False, + experiment=False, + ): + """ + Run case. + + Args: + self + base_case_root: str, opt + file path of base case + run_type: str, opt + transient, post_ad, or ad case, default transient + prism: bool, opt + if True, use PRISM precipitation, default False + run_length: str, opt + length of run, default '4Y' + user_version: str, opt + default 'latest' + overwrite: bool, opt + default False + setup_only: bool, opt + default False; if True, set up but do not run case + no_batch: bool, opt + default False + rerun: bool, opt + default False + experiment: str, opt + name of experiment, default False + """ + user_mods_dirs = [ + os.path.join(self.cesmroot, "cime_config", "usermods_dirs", "NEON", self.name) + ] + expect( + os.path.isdir(base_case_root), + "Error base case does not exist in {}".format(base_case_root), + ) + # -- if user gives a version: + if user_version: + version = user_version + else: + version = "latest" + + print("using this version:", version) + + if experiment is not None: + self.name = self.name + "." + experiment + case_root = os.path.abspath(os.path.join(base_case_root, "..", self.name + "." + run_type)) + + rundir = None + if os.path.isdir(case_root): + if overwrite: + print("---- removing the existing case -------") + shutil.rmtree(case_root) + elif rerun: + with Case(case_root, read_only=False) as case: + rundir = case.get_value("RUNDIR") + # For existing case check that the compset name is correct + existingcompname = case.get_value("COMPSET") + match = re.search("^HIST", existingcompname, flags=re.IGNORECASE) + # pylint: disable=undefined-variable + if re.search("^HIST", compset, flags=re.IGNORECASE) is None: + expect( + match is None, + """Existing base case is a historical type and should not be + --rerun with the --overwrite option""", + ) + # pylint: enable=undefined-variable + else: + expect( + match is not None, + """Existing base case should be a historical type and is not + --rerun with the --overwrite option""", + ) + if os.path.isfile(os.path.join(rundir, "ESMF_Profile.summary")): + print("Case {} appears to be complete, not rerunning.".format(case_root)) + elif not setup_only: + print("Resubmitting case {}".format(case_root)) + case.submit(no_batch=no_batch) + print("-----------------------------------") + print("Successfully submitted case!") + batch_query = self.get_batch_query(case) + if batch_query != "none": + print(f"Use {batch_query} to check its run status") + return + else: + logger.warning("Case already exists in %s, not overwritting", case_root) + return + + if run_type == "postad": + adcase_root = case_root.replace(".postad", ".ad") + if not os.path.isdir(adcase_root): + logger.warning("postad requested but no ad case found in %s", adcase_root) + return + + if not os.path.isdir(case_root): + # read_only = False should not be required here + with Case(base_case_root, read_only=False) as basecase: + print("---- cloning the base case in {}".format(case_root)) + # + # EBK: 11/05/2022 -- Note keeping the user_mods_dirs argument is important. Although + # it causes some of the user_nl_* files to have duplicated inputs. It also ensures + # that the shell_commands file is copied, as well as taking care of the DATM inputs. + # See https://github.com/ESCOMP/CTSM/pull/1872#pullrequestreview-1169407493 + # + basecase.create_clone(case_root, keepexe=True, user_mods_dirs=user_mods_dirs) + + with Case(case_root, read_only=False) as case: + if run_type != "transient": + # in order to avoid the complication of leap years, + # we always set the run_length in units of days. + case.set_value("STOP_OPTION", "ndays") + case.set_value("REST_OPTION", "end") + case.set_value("CONTINUE_RUN", False) + case.set_value("NEONVERSION", version) + if prism: + case.set_value("CLM_USRDAT_NAME", "NEON.PRISM") + + if run_type == "ad": + case.set_value("CLM_FORCE_COLDSTART", "on") + case.set_value("CLM_ACCELERATED_SPINUP", "on") + case.set_value("RUN_REFDATE", "0018-01-01") + case.set_value("RUN_STARTDATE", "0018-01-01") + case.set_value("RESUBMIT", 1) + case.set_value("STOP_N", run_length) + + else: + case.set_value("CLM_FORCE_COLDSTART", "off") + case.set_value("CLM_ACCELERATED_SPINUP", "off") + case.set_value("RUN_TYPE", "hybrid") + + if run_type == "postad": + self.set_ref_case(case) + case.set_value("STOP_N", run_length) + + # For transient cases STOP will be set in the user_mod_directory + if run_type == "transient": + if self.finidat: + case.set_value("RUN_TYPE", "startup") + else: + if not self.set_ref_case(case): + return + case.set_value("CALENDAR", "GREGORIAN") + case.set_value("RESUBMIT", 0) + case.set_value("STOP_OPTION", "nmonths") + + if not rundir: + rundir = case.get_value("RUNDIR") + + self.modify_user_nl(case_root, run_type, rundir) + + case.create_namelists() + # explicitly run check_input_data + case.check_all_input_data() + if not setup_only: + case.submit(no_batch=no_batch) + print("-----------------------------------") + print("Successfully submitted case!") + batch_query = self.get_batch_query(case) + if batch_query != "none": + print(f"Use {batch_query} to check its run status") + + def set_ref_case(self, case): + """ + Set an existing case as the reference case, eg for use with spinup. + """ + rundir = case.get_value("RUNDIR") + case_root = case.get_value("CASEROOT") + if case_root.endswith(".postad"): + ref_case_root = case_root.replace(".postad", ".ad") + root = ".ad" + else: + ref_case_root = case_root.replace(".transient", ".postad") + root = ".postad" + if not os.path.isdir(ref_case_root): + logger.warning( + "ERROR: spinup must be completed first, could not find directory %s", ref_case_root + ) + return False + + with Case(ref_case_root) as refcase: + refrundir = refcase.get_value("RUNDIR") + case.set_value("RUN_REFDIR", refrundir) + case.set_value("RUN_REFCASE", os.path.basename(ref_case_root)) + refdate = None + for reffile in glob.iglob(refrundir + "/{}{}.clm2.r.*.nc".format(self.name, root)): + m_searched = re.search(r"(\d\d\d\d-\d\d-\d\d)-\d\d\d\d\d.nc", reffile) + if m_searched: + refdate = m_searched.group(1) + symlink_force(reffile, os.path.join(rundir, os.path.basename(reffile))) + logger.info("Found refdate of %s", refdate) + if not refdate: + logger.warning("Could not find refcase for %s", case_root) + return False + + for rpfile in glob.iglob(refrundir + "/rpointer*"): + safe_copy(rpfile, rundir) + if not os.path.isdir(os.path.join(rundir, "inputdata")) and os.path.isdir( + os.path.join(refrundir, "inputdata") + ): + symlink_force(os.path.join(refrundir, "inputdata"), os.path.join(rundir, "inputdata")) + + case.set_value("RUN_REFDATE", refdate) + if case_root.endswith(".postad"): + case.set_value("RUN_STARTDATE", refdate) + # NOTE: if start options are set, RUN_STARTDATE should be modified here + return True + + def modify_user_nl(self, case_root, run_type, rundir): + """ + Modify user namelist. If transient, include finidat in user_nl; + Otherwise, adjust user_nl to include different mfilt, nhtfrq, and variables in hist_fincl1. + """ + user_nl_fname = os.path.join(case_root, "user_nl_clm") + user_nl_lines = None + if run_type == "transient": + if self.finidat: + user_nl_lines = [ + "finidat = '{}/inputdata/lnd/ctsm/initdata/{}'".format(rundir, self.finidat) + ] + else: + user_nl_lines = [ + "hist_fincl2 = ''", + "hist_mfilt = 20", + "hist_nhtfrq = -8760", + "hist_empty_htapes = .true.", + """hist_fincl1 = 'TOTECOSYSC', 'TOTECOSYSN', 'TOTSOMC', 'TOTSOMN', 'TOTVEGC', + 'TOTVEGN', 'TLAI', 'GPP', 'CPOOL', 'NPP', 'TWS', 'H2OSNO'""", + ] + + if user_nl_lines: + with open(user_nl_fname, "a") as nl_file: + for line in user_nl_lines: + nl_file.write("{}\n".format(line)) diff --git a/python/ctsm/site_and_regional/neon_surf_wrapper.py b/python/ctsm/site_and_regional/neon_surf_wrapper.py new file mode 100755 index 0000000000..5ec6183801 --- /dev/null +++ b/python/ctsm/site_and_regional/neon_surf_wrapper.py @@ -0,0 +1,227 @@ +#! /usr/bin/env python3 +""" +|------------------------------------------------------------------| +|--------------------- Instructions -----------------------------| +|------------------------------------------------------------------| +This script is a simple wrapper for neon sites that performs the +following: + 1) For neon sites, subset surface dataset from global dataset + (i.e. ./subset_data.py ) + 2) Download neon and update the created surface dataset + based on the downloaded neon data. + (i.e. modify_singlept_site_neon.py) + +Instructions for running using conda python environments: + +../../py_env_create +conda activate ctsm_pylib + +""" +# TODO +# Automatic downloading of missing files if they are missing +# -[ ] Download neon sites and dom pft file +# -[ ] Make sure verbose works for printing out commands running + +# Import libraries +from __future__ import print_function + +import os +import logging +import argparse +import subprocess +import tqdm +import pandas as pd + + +def get_parser(): + """ + Get parser object for this script. + """ + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter + ) + + parser.print_usage = parser.print_help + + parser.add_argument( + "-v", + "--verbose", + help="Verbose mode will print more information. ", + action="store_true", + dest="verbose", + default=False, + ) + + parser.add_argument( + "--16pft", + help="Create and/or modify 16-PFT surface datasets (e.g. for a FATES run) ", + action="store_true", + dest="pft_16", + default=False, + ) + + parser.add_argument( + "-m", + "--mixed", + help="Do not overwrite surface dataset to be just one dominant PFT at 100%", + action="store_true", + dest="mixed", + default=False, + ) + + return parser + + +def execute(command): + """ + Function for running a command on shell. + Args: + command (str): + command that we want to run. + Raises: + Error with the return code from shell. + """ + print("\n", " >> ", *command, "\n") + + try: + subprocess.check_call(command, stdout=open(os.devnull, "w"), stderr=subprocess.STDOUT) + + except subprocess.CalledProcessError as err: + print(err) + + +def main(): + """ + Loop through neon sites and execute subset and modify commands + """ + args = get_parser().parse_args() + + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + + neon_sites = pd.read_csv("neon_sites_dompft.csv") + + for _, row in tqdm.tqdm(neon_sites.iterrows()): + lat = row["Lat"] + lon = row["Lon"] + site = row["Site"] + pft = row["pft"] + clmsite = "1x1_NEON_" + site + print("Now processing site :", site) + + if args.mixed and args.pft_16: + # use surface dataset with 16 pfts, and don't overwrite with 100% 1 dominant PFT + # don't set crop flag + # don't set a dominant pft + subset_command = [ + "./subset_data", + "point", + "--lat", + str(lat), + "--lon", + str(lon), + "--site", + clmsite, + "--create-surface", + "--uniform-snowpack", + "--cap-saturation", + "--verbose", + "--overwrite", + ] + modify_command = [ + "./modify_singlept_site_neon", + "--neon_site", + site, + "--surf_dir", + "subset_data_single_point", + "--16pft", + ] + elif args.pft_16: + # use surface dataset with 16 pfts, but overwrite to 100% 1 dominant PFT + # don't set crop flag + # set dominant pft + subset_command = [ + "./subset_data", + "point", + "--lat", + str(lat), + "--lon", + str(lon), + "--site", + clmsite, + "--dompft", + str(pft), + "--create-surface", + "--uniform-snowpack", + "--cap-saturation", + "--verbose", + "--overwrite", + ] + modify_command = [ + "./modify_singlept_site_neon", + "--neon_site", + site, + "--surf_dir", + "subset_data_single_point", + "--16pft", + ] + elif args.mixed: + # use surface dataset with 78 pfts, and don't overwrite with 100% 1 dominant PFT + # NOTE: FATES will currently not run with a 78-PFT surface dataset + # set crop flag + # don't set dominant pft + subset_command = [ + "./subset_data", + "point", + "--lat", + str(lat), + "--lon", + str(lon), + "--site", + clmsite, + "--crop", + "--create-surface", + "--uniform-snowpack", + "--cap-saturation", + "--verbose", + "--overwrite", + ] + modify_command = [ + "./modify_singlept_site_neon", + "--neon_site", + site, + "--surf_dir", + "subset_data_single_point", + ] + else: + # use surface dataset with 78 pfts, and overwrite to 100% 1 dominant PFT + # NOTE: FATES will currently not run with a 78-PFT surface dataset + # set crop flag + # set dominant pft + subset_command = [ + "./subset_data", + "point", + "--lat", + str(lat), + "--lon", + str(lon), + "--site", + clmsite, + "--crop", + "--dompft", + str(pft), + "--create-surface", + "--uniform-snowpack", + "--cap-saturation", + "--verbose", + "--overwrite", + ] + modify_command = [ + "./modify_singlept_site_neon", + "--neon_site", + site, + "--surf_dir", + "subset_data_single_point", + ] + execute(subset_command) + execute(modify_command) diff --git a/python/ctsm/site_and_regional/regional_case.py b/python/ctsm/site_and_regional/regional_case.py index 1dc6a522cc..2a2dc66bce 100644 --- a/python/ctsm/site_and_regional/regional_case.py +++ b/python/ctsm/site_and_regional/regional_case.py @@ -145,7 +145,7 @@ def create_domain_at_reg(self, indir, file): f_in.close() f_out.close() - def create_surfdata_at_reg(self, indir, file, user_mods_dir): + def create_surfdata_at_reg(self, indir, file, user_mods_dir, specify_fsurf_out): """ Create surface data file for this RegionalCase class. """ @@ -154,7 +154,11 @@ def create_surfdata_at_reg(self, indir, file, user_mods_dir): # specify files fsurf_in = os.path.join(indir, file) - fsurf_out = add_tag_to_filename(fsurf_in, self.tag) + if specify_fsurf_out is None: + fsurf_out = add_tag_to_filename(fsurf_in, self.tag, replace_res=True) + else: + fsurf_out = specify_fsurf_out + logger.info("fsurf_in: %s", fsurf_in) logger.info("fsurf_out: %s", os.path.join(self.out_dir, fsurf_out)) @@ -194,7 +198,7 @@ def create_landuse_at_reg(self, indir, file, user_mods_dir): # specify files fluse_in = os.path.join(indir, file) - fluse_out = add_tag_to_filename(fluse_in, self.tag) + fluse_out = add_tag_to_filename(fluse_in, self.tag, replace_res=True) logger.info("fluse_in: %s", fluse_in) logger.info("fluse_out: %s", os.path.join(self.out_dir, fluse_out)) diff --git a/python/ctsm/site_and_regional/run_neon.py b/python/ctsm/site_and_regional/run_neon.py new file mode 100755 index 0000000000..72bf3fdfb4 --- /dev/null +++ b/python/ctsm/site_and_regional/run_neon.py @@ -0,0 +1,239 @@ +#! /usr/bin/env python3 + +""" +|------------------------------------------------------------------| +|--------------------- Instructions -----------------------------| +|------------------------------------------------------------------| +This is a wrapper script for running CTSM simulation for one or more +neon sites. + +This script is only for neon site and we will develop a more general +code later. + +This script first creates and builds a generic base case. +Next, it will clone the base_case for different neon sites and run +types to reduce the need to build ctsm everytime. + +This script will do the following: + 1) Create a generic base case for cloning. + 2) Make the case for the specific neon site(s). + 3) Make changes to the case, for: + a. AD spinup + b. post-AD spinup + c. transient + #--------------- + d. SASU or Matrix spinup + 4) Build and submit the case. + +------------------------------------------------------------------- +Instructions for running using conda python environments: + +../../py_env_create +conda activate ctsm_py + +------------------------------------------------------------------- +To see the available options: + ./run_neon.py --help +------------------------------------------------------------------- +""" +# TODO (NS) +# - [ ] +# - [ ] Case dependency and the ability to check case status +# - [ ] If Case dependency works we don't need finidat given explicilty for post-ad and transient. + +# - [ ] checkout_externals instead of using env varaiable +# - [ ] wget the fields available and run for those available + +# - [ ] Matrix spin-up if (SASU) Eric merged it in +# - [ ] Make sure both AD and SASU are not on at the same time + +# - [ ] Make sure CIME and other dependencies are checked out. + + +# Import libraries +import glob +import logging +import os +import sys +import pandas as pd + +# Get the ctsm util tools and then the cime tools. +_CTSM_PYTHON = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "python")) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position +from ctsm.path_utils import path_to_ctsm_root +from ctsm.download_utils import download_file +from ctsm.site_and_regional.neon_arg_parse import get_parser +from ctsm.site_and_regional.neon_site import NeonSite + +# pylint: disable=import-error, wildcard-import, wrong-import-order +from standard_script_setup import * + +logger = logging.getLogger(__name__) + + +def check_neon_listing(valid_neon_sites): + """ + A function to download and parse neon listing file. + """ + listing_file = "listing.csv" + url = "https://storage.neonscience.org/neon-ncar/listing.csv" + + download_file(url, listing_file) + available_list = parse_neon_listing(listing_file, valid_neon_sites) + return available_list + + +def parse_neon_listing(listing_file, valid_neon_sites): + """ + A function to parse neon listing file + and find neon sites with the dates + where data is available. + + Args: + listing_file (str): downloaded listing file + + Returns: + available_list : + list of neon_site objects that is found + on the downloaded listing file. + """ + + # pd.set_option("display.max_rows", None, "display.max_columns", None) + + available_list = [] + + listing_df = pd.read_csv(listing_file) + + # check for finidat files for transient run + finidatlist = listing_df[listing_df["object"].str.contains("lnd/ctsm")] + + # -- filter lines with atm/cdep + listing_df = listing_df[listing_df["object"].str.contains("atm/cdeps/")] + + # -- split the object str to extract site name + listing_df = listing_df["object"].str.split("/", expand=True) + + # -- groupby site name + grouped_df = listing_df.groupby(8) + for key, _ in grouped_df: + # -- check if it is a valid neon site + if any(key in x for x in valid_neon_sites): + site_name = key + tmp_df = grouped_df.get_group(key) + + # -- filter files only ending with YYYY-MM.nc + tmp_df = tmp_df[tmp_df[9].str.contains(r"\d\d\d\d-\d\d.nc")] + + # -- find all the data versions + # versions = tmp_df[7].unique() + # print ("all versions available for ", site_name,":", *versions) + latest_version = tmp_df[7].iloc[-1] + # print ("latests version available for ", site_name,":", latest_version) + + tmp_df = tmp_df[tmp_df[7].str.contains(latest_version)] + # -- remove .nc from the file names + tmp_df[9] = tmp_df[9].str.replace(".nc", "", regex=False) + + tmp_df2 = tmp_df[9].str.split("-", expand=True) + + # ignore any prefix in file name and just get year + tmp_df2[0] = tmp_df2[0].str.slice(-4) + + # -- figure out start_year and end_year + start_year = tmp_df2[0].iloc[0] + end_year = tmp_df2[0].iloc[-1] + + # -- figure out start_month and end_month + start_month = tmp_df2[1].iloc[0] + end_month = tmp_df2[1].iloc[-1] + + logger.debug("Valid neon site %s found!", site_name) + logger.debug("File version %s", latest_version) + logger.debug("start_year=%s", start_year) + logger.debug("end_year=%s", end_year) + logger.debug("start_month=%s", start_month) + logger.debug("end_month=%s", end_month) + finidat = None + for line in finidatlist["object"]: + if site_name in line: + finidat = line.split(",")[0].split("/")[-1] + + neon_site = NeonSite(site_name, start_year, end_year, start_month, end_month, finidat) + logger.debug(neon_site) + available_list.append(neon_site) + + return available_list + + +def main(description): + """ + Determine valid neon sites. Make an output directory if it does not exist. + Loop through requested sites and run CTSM at that site. + """ + cesmroot = path_to_ctsm_root() + # Get the list of supported neon sites from usermods + valid_neon_sites = glob.glob( + os.path.join(cesmroot, "cime_config", "usermods_dirs", "NEON", "[!d]*") + ) + valid_neon_sites = sorted([v.split("/")[-1] for v in valid_neon_sites]) + + ( + site_list, + output_root, + run_type, + experiment, + prism, + overwrite, + run_length, + base_case_root, + run_from_postad, + setup_only, + no_batch, + rerun, + user_version, + ) = get_parser(sys.argv, description, valid_neon_sites) + + if output_root: + logger.debug("output_root : %s", output_root) + if not os.path.exists(output_root): + os.makedirs(output_root) + + # -- check neon listing file for available data: + available_list = check_neon_listing(valid_neon_sites) + + # ================================= + # -- all neon sites can be cloned from one generic case + # -- so no need to define a base_case for every site. + + res = "CLM_USRDAT" + if run_type == "transient": + compset = "IHist1PtClm51Bgc" + else: + compset = "I1PtClm51Bgc" + + # -- Looping over neon sites + + for neon_site in available_list: + if neon_site.name in site_list: + if run_from_postad: + neon_site.finidat = None + if not base_case_root: + base_case_root = neon_site.build_base_case( + cesmroot, output_root, res, compset, overwrite, setup_only + ) + logger.info("-----------------------------------") + logger.info("Running CTSM for neon site : %s", neon_site.name) + neon_site.run_case( + base_case_root, + run_type, + prism, + run_length, + user_version, + overwrite, + setup_only, + no_batch, + rerun, + experiment, + ) diff --git a/python/ctsm/site_and_regional/single_point_case.py b/python/ctsm/site_and_regional/single_point_case.py index 31ab158706..456bebee91 100644 --- a/python/ctsm/site_and_regional/single_point_case.py +++ b/python/ctsm/site_and_regional/single_point_case.py @@ -52,6 +52,8 @@ class SinglePointCase(BaseCase): flag for creating user mods directories and files dom_pft : int dominant pft type for this single point (None if not specified) + evenly_split_cropland : bool + flag for splitting cropland evenly among all crop types pct_pft : list weight or percentage of each pft. num_pft : list @@ -105,6 +107,7 @@ def __init__( create_datm, create_user_mods, dom_pft, + evenly_split_cropland, pct_pft, num_pft, include_nonveg, @@ -125,6 +128,7 @@ def __init__( self.plon = plon self.site_name = site_name self.dom_pft = dom_pft + self.evenly_split_cropland = evenly_split_cropland self.pct_pft = pct_pft self.num_pft = num_pft self.include_nonveg = include_nonveg @@ -342,7 +346,7 @@ def create_landuse_at_point(self, indir, file, user_mods_dir): # specify files fluse_in = os.path.join(indir, file) - fluse_out = add_tag_to_filename(fluse_in, self.tag) + fluse_out = add_tag_to_filename(fluse_in, self.tag, replace_res=True) logger.info("fluse_in: %s", fluse_in) logger.info("fluse_out: %s", os.path.join(self.out_dir, fluse_out)) @@ -437,6 +441,9 @@ def modify_surfdata_atpoint(self, f_orig): f_mod["PCT_CROP"] = f_mod["PCT_CROP"] / tot_pct * 100 f_mod["PCT_NATVEG"] = f_mod["PCT_NATVEG"] / tot_pct * 100 + if self.evenly_split_cropland: + f_mod["PCT_CFT"][:, :, :] = 100.0 / f_mod["PCT_CFT"].shape[2] + else: logger.info( "You chose --include-nonveg --> \ @@ -450,7 +457,7 @@ def modify_surfdata_atpoint(self, f_orig): return f_mod - def create_surfdata_at_point(self, indir, file, user_mods_dir): + def create_surfdata_at_point(self, indir, file, user_mods_dir, specify_fsurf_out): """ Create surface data file at a single point. """ @@ -464,7 +471,10 @@ def create_surfdata_at_point(self, indir, file, user_mods_dir): # specify file fsurf_in = os.path.join(indir, file) - fsurf_out = add_tag_to_filename(fsurf_in, self.tag) + if specify_fsurf_out is None: + fsurf_out = add_tag_to_filename(fsurf_in, self.tag, replace_res=True) + else: + fsurf_out = specify_fsurf_out logger.info("fsurf_in: %s", fsurf_in) logger.info("fsurf_out: %s", os.path.join(self.out_dir, fsurf_out)) diff --git a/python/ctsm/subset_data.py b/python/ctsm/subset_data.py index 7a33d9c2fa..030cea2247 100644 --- a/python/ctsm/subset_data.py +++ b/python/ctsm/subset_data.py @@ -2,10 +2,10 @@ |------------------------------------------------------------------| |--------------------- Instructions -----------------------------| |------------------------------------------------------------------| -Instructions for running on Cheyenne/Casper: -load the following into your local environment - module load python - ncar_pylib +Instructions for running using conda python environments: + +../../py_env_create +conda activate ctsm_py ------------------------------------------------------------------- To see the available options for single point or regional cases: ./subset_data.py --help @@ -67,6 +67,7 @@ from ctsm.site_and_regional.regional_case import RegionalCase from ctsm.args_utils import plon_type, plat_type from ctsm.path_utils import path_to_ctsm_root +from ctsm.utils import abort # -- import ctsm logging flags from ctsm.ctsm_logging import ( @@ -75,7 +76,7 @@ process_logging_args, ) -DEFAULTS_FILE = "default_data.cfg" +DEFAULTS_CONFIG = "tools/site_and_regional/default_data.cfg" logger = logging.getLogger(__name__) @@ -148,6 +149,13 @@ def get_parser(): dest="cap_saturation", required=False, ) + pt_parser.add_argument( + "--evenly_split_cropland", + help="Introduce equal areas of all crops", + action="store_true", + dest="evenly_split_cropland", + required=False, + ) pt_parser.add_argument( "--dompft", help="Dominant PFT(s): if we set the grid to 100%% one or multiple PFTs \ @@ -308,12 +316,40 @@ def get_parser(): type=str, default="", ) + + subparser.add_argument( + "--out-surface", + help="Output surface dataset name \ + (if you want to override the default based on the current date). \n \ + (only valid if outputing a surface dataset)", + action="store", + dest="out_surface", + type=str, + ) + cesmroot = path_to_ctsm_root() + defaults_file = os.path.join(cesmroot, DEFAULTS_CONFIG) + subparser.add_argument( + "--cfg-file", + help="Default configure file to use for default filenames.", + action="store", + dest="config_file", + type=str, + default=defaults_file, + ) subparser.add_argument( "--overwrite", help="Flag to overwrite if the files already exists.", action="store_true", dest="overwrite", ) + subparser.add_argument( + "--inputdata-dir", + help="Top level path to the CESM inputdata directory.", + action="store", + dest="inputdatadir", + type=str, + default="defaults.cfg", + ) add_logging_args(subparser) # -- print help for both subparsers @@ -326,6 +362,64 @@ def get_parser(): return parser +def check_args(args): + """Check the command line arguments""" + # --------------------------------- # + # print help and exit when no option is chosen + if args.run_type not in ("point", "region"): + err_msg = textwrap.dedent( + """\ + \n ------------------------------------ + \n Must supply a positional argument: 'point' or 'region'. + """ + ) + raise argparse.ArgumentError(None, err_msg) + + if not any( + [ + args.create_surfdata, + args.create_domain, + args.create_landuse, + args.create_datm, + ] + ): + err_msg = textwrap.dedent( + """\ + \n ------------------------------------ + \n Must supply one of: + \n --create-surface \n --create-landuse \n --create-datm \n --create-domain \n + """ + ) + raise argparse.ArgumentError(None, err_msg) + + if not os.path.exists(args.config_file): + err_msg = textwrap.dedent( + """\ + \n ------------------------------------ + \n Entered default config file does not exist" + """ + ) + raise argparse.ArgumentError(None, err_msg) + + if args.out_surface and not args.create_surfdata: + err_msg = textwrap.dedent( + """\ + \n ------------------------------------ + \n out-surface option is given without the --create-surface option" + """ + ) + raise argparse.ArgumentError(None, err_msg) + + if args.out_surface and os.path.exists(args.out_surface) and not args.overwrite: + err_msg = textwrap.dedent( + """\ + \n ------------------------------------ + \n out-surface filename exists and the overwrite option was not also selected" + """ + ) + raise argparse.ArgumentError(None, err_msg) + + def setup_user_mods(user_mods_dir, cesmroot): """ Sets up the user mods files and directories @@ -381,10 +475,19 @@ def setup_files(args, defaults, cesmroot): if args.create_user_mods: setup_user_mods(args.user_mods_dir, cesmroot) + if args.inputdatadir == "defaults.cfg": + clmforcingindir = defaults.get("main", "clmforcingindir") + else: + clmforcingindir = args.inputdatadir + + if not os.path.isdir(clmforcingindir): + logger.info("clmforcingindir does not exist: %s", clmforcingindir) + abort("inputdata directory does not exist") + # DATM data datm_type = "datm_gswp3" dir_output_datm = "datmdata" - dir_input_datm = defaults.get(datm_type, "dir") + dir_input_datm = os.path.join(clmforcingindir, defaults.get(datm_type, "dir")) if args.create_datm: if not os.path.isdir(os.path.join(args.out_dir, dir_output_datm)): os.mkdir(os.path.join(args.out_dir, dir_output_datm)) @@ -396,19 +499,24 @@ def setup_files(args, defaults, cesmroot): fsurf_in = defaults.get("surfdat", "surfdat_" + num_pft + "pft") fluse_in = defaults.get("landuse", "landuse_" + num_pft + "pft") + if args.out_surface: + fsurf_out = args.out_surface + else: + fsurf_out = None file_dict = { - "main_dir": defaults.get("main", "clmforcingindir"), + "main_dir": clmforcingindir, "fdomain_in": defaults.get("domain", "file"), "fsurf_dir": os.path.join( - defaults.get("main", "clmforcingindir"), + clmforcingindir, os.path.join(defaults.get("surfdat", "dir")), ), "fluse_dir": os.path.join( - defaults.get("main", "clmforcingindir"), + clmforcingindir, os.path.join(defaults.get("landuse", "dir")), ), "fsurf_in": fsurf_in, + "fsurf_out": fsurf_out, "fluse_in": fluse_in, "datm_tuple": DatmFiles( dir_input_datm, @@ -450,6 +558,7 @@ def subset_point(args, file_dict: dict): create_datm=args.create_datm, create_user_mods=args.create_user_mods, dom_pft=args.dom_pft, + evenly_split_cropland=args.evenly_split_cropland, pct_pft=args.pct_pft, num_pft=num_pft, include_nonveg=args.include_nonveg, @@ -468,7 +577,10 @@ def subset_point(args, file_dict: dict): # -- Create CTSM surface data file if single_point.create_surfdata: single_point.create_surfdata_at_point( - file_dict["fsurf_dir"], file_dict["fsurf_in"], args.user_mods_dir + file_dict["fsurf_dir"], + file_dict["fsurf_in"], + args.user_mods_dir, + specify_fsurf_out=file_dict["fsurf_out"], ) # -- Create CTSM transient landuse data file @@ -528,7 +640,10 @@ def subset_region(args, file_dict: dict): # -- Create CTSM surface data file if region.create_surfdata: region.create_surfdata_at_reg( - file_dict["fsurf_dir"], file_dict["fsurf_in"], args.user_mods_dir + file_dict["fsurf_dir"], + file_dict["fsurf_in"], + args.user_mods_dir, + specify_fsurf_out=file_dict["fsurf_out"], ) # -- Create CTSM transient landuse data file @@ -552,34 +667,7 @@ def main(): parser = get_parser() args = parser.parse_args() - # --------------------------------- # - # print help and exit when no option is chosen - if args.run_type != "point" and args.run_type != "region": - err_msg = textwrap.dedent( - """\ - \n ------------------------------------ - \n Must supply a positional argument: 'point' or 'region'. - """ - ) - raise parser.error(err_msg) - - if not any( - [ - args.create_surfdata, - args.create_domain, - args.create_landuse, - args.create_datm, - ] - ): - err_msg = textwrap.dedent( - """\ - \n ------------------------------------ - \n Must supply one of: - \n --create-surface \n --create-landuse \n --create-datm \n --create-domain \n - """ - ) - raise parser.error(err_msg) - + check_args(args) # --------------------------------- # # process logging args (i.e. debug and verbose) process_logging_args(args) @@ -588,7 +676,7 @@ def main(): # parse defaults file cesmroot = path_to_ctsm_root() defaults = configparser.ConfigParser() - defaults.read(os.path.join(cesmroot, "tools/site_and_regional", DEFAULTS_FILE)) + defaults.read(args.config_file) # --------------------------------- # myname = getuser() diff --git a/python/ctsm/test/test_sys_fsurdat_modifier.py b/python/ctsm/test/test_sys_fsurdat_modifier.py index 6ee00604be..1a5045c14d 100755 --- a/python/ctsm/test/test_sys_fsurdat_modifier.py +++ b/python/ctsm/test/test_sys_fsurdat_modifier.py @@ -10,12 +10,15 @@ import unittest import tempfile import shutil +import sys import xarray as xr +import numpy as np from ctsm.path_utils import path_to_ctsm_root from ctsm import unit_testing from ctsm.modify_input_files.fsurdat_modifier import fsurdat_modifier +from ctsm.modify_input_files.fsurdat_modifier import fsurdat_modifier_arg_process # Allow test names that pylint doesn't like; otherwise hard to make them # readable @@ -39,6 +42,7 @@ def setUp(self): path_to_ctsm_root(), "tools/modify_input_files/modify_fsurdat_template.cfg" ) testinputs_path = os.path.join(path_to_ctsm_root(), "python/ctsm/test/testinputs") + self._testinputs_path = testinputs_path self._fsurdat_in = os.path.join( testinputs_path, "surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc", @@ -53,6 +57,287 @@ def tearDown(self): """ shutil.rmtree(self._tempdir, ignore_errors=True) + def test_no_files_given_fail(self): + """ + Test that if no input or output files are given that it will gracefully fail + """ + self._cfg_file_path = os.path.join( + self._testinputs_path, "modify_fsurdat_short_nofiles.cfg" + ) + sys.argv = ["fsurdat_modifier", self._cfg_file_path] + parser = fsurdat_modifier_arg_process() + with self.assertRaisesRegex(SystemExit, "must contain item 'fsurdat_in'"): + fsurdat_modifier(parser) + + def test_short_config(self): + """ + Test that a short config file works + """ + self._cfg_file_path = os.path.join(self._testinputs_path, "modify_fsurdat_short.cfg") + sys.argv = ["fsurdat_modifier", self._cfg_file_path] + parser = fsurdat_modifier_arg_process() + fsurdat_out = ( + "ctsm/test/testinputs/surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214_out.nc" + ) + if os.path.exists(fsurdat_out): + os.remove(fsurdat_out) + fsurdat_modifier(parser) + # Run it again with the overwrite option so that it will overwrite the file just created + sys.argv = ["fsurdat_modifier", self._cfg_file_path, "--overwrite"] + parser = fsurdat_modifier_arg_process() + fsurdat_modifier(parser) + # Cleanup + os.remove(fsurdat_out) + + def test_short_infile_both_cmdline_and_cfg(self): + """ + Test that a graceful fail happens when the infile + is given both in the command line and the config file + """ + self._cfg_file_path = os.path.join(self._testinputs_path, "modify_fsurdat_short.cfg") + sys.argv = [ + "fsurdat_modifier", + self._cfg_file_path, + "-i", + "specify_fsurdat_in_on_cmd_line.nc", + ] + parser = fsurdat_modifier_arg_process() + with self.assertRaisesRegex( + SystemExit, + "fsurdat_in is specified in both the command line and the config file, pick one", + ): + fsurdat_modifier(parser) + + def test_short_outfile_both_cmdline_and_cfg(self): + """ + Test that a graceful fail happens when the outfile is given + both in the command line and the config file + """ + self._cfg_file_path = os.path.join(self._testinputs_path, "modify_fsurdat_short.cfg") + sys.argv = [ + "fsurdat_modifier", + self._cfg_file_path, + "-o", + "specify_fsurdat_out_on_cmd_line.nc", + ] + parser = fsurdat_modifier_arg_process() + with self.assertRaisesRegex( + SystemExit, + "fsurdat_out is specified in both the command line and the config file, pick one", + ): + fsurdat_modifier(parser) + + def test_opt_sections(self): + """ + Test that a simple file with the optional sections works + """ + self._cfg_file_path = os.path.join(self._testinputs_path, "modify_fsurdat_opt_sections.cfg") + outfile = os.path.join( + self._tempdir, + "surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214_output_urban.nc", + ) + sys.argv = [ + "fsurdat_modifier", + self._cfg_file_path, + "-i", + os.path.join( + self._testinputs_path, "surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc" + ), + "-o", + outfile, + ] + parser = fsurdat_modifier_arg_process() + fsurdat_modifier(parser) + # Read the resultant output file and make sure the fields are changed as expected + fsurdat_out_data = xr.open_dataset(outfile) + zero0d = np.zeros((5, 5)) + one0d = np.ones((5, 5)) + pct_urban = np.array( + [ + [ + [100.0, 100.0, 100.0, 100.0, 100.0], + [100.0, 100.0, 100.0, 100.0, 100.0], + [100.0, 100.0, 100.0, 100.0, 100.0], + [100.0, 100.0, 100.0, 100.0, 100.0], + [100.0, 100.0, 100.0, 100.0, 100.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.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.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.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0], + ], + ] + ) + lev2_two = np.empty((2, 3, 5, 5)) + lev2_two[0, :, :, :] = 200.0 + lev2_two[1, :, :, :] = 100.0 + lev2_five = np.empty((5, 3, 5, 5)) + lev2_five[0, :, :, :] = 1.0 + lev2_five[1, :, :, :] = 2.0 + lev2_five[2, :, :, :] = 3.0 + lev2_five[3, :, :, :] = 4.0 + lev2_five[4, :, :, :] = 5.0 + lev1 = np.array( + [ + [ + [200.0, 200.0, 200.0, 200.0, 200.0], + [200.0, 200.0, 200.0, 200.0, 200.0], + [200.0, 200.0, 200.0, 200.0, 200.0], + [200.0, 200.0, 200.0, 200.0, 200.0], + [200.0, 200.0, 200.0, 200.0, 200.0], + ], + [ + [150.0, 150.0, 150.0, 150.0, 150.0], + [150.0, 150.0, 150.0, 150.0, 150.0], + [150.0, 150.0, 150.0, 150.0, 150.0], + [150.0, 150.0, 150.0, 150.0, 150.0], + [150.0, 150.0, 150.0, 150.0, 150.0], + ], + [ + [100.0, 100.0, 100.0, 100.0, 100.0], + [100.0, 100.0, 100.0, 100.0, 100.0], + [100.0, 100.0, 100.0, 100.0, 100.0], + [100.0, 100.0, 100.0, 100.0, 100.0], + [100.0, 100.0, 100.0, 100.0, 100.0], + ], + ] + ) + np.testing.assert_array_equal(fsurdat_out_data.PCT_NATVEG, zero0d) + np.testing.assert_array_equal(fsurdat_out_data.PCT_CROP, zero0d) + np.testing.assert_array_equal(fsurdat_out_data.PCT_LAKE, zero0d) + np.testing.assert_array_equal(fsurdat_out_data.PCT_WETLAND, zero0d) + np.testing.assert_array_equal(fsurdat_out_data.PCT_GLACIER, zero0d) + np.testing.assert_array_equal(fsurdat_out_data.PCT_URBAN, pct_urban) + np.testing.assert_array_equal(fsurdat_out_data.LAKEDEPTH, one0d * 200.0) + np.testing.assert_array_equal(fsurdat_out_data.T_BUILDING_MIN, lev1) + np.testing.assert_array_equal(fsurdat_out_data.ALB_ROOF_DIR, lev2_two) + np.testing.assert_array_equal(fsurdat_out_data.TK_ROOF, lev2_five) + + def test_evenly_split_cropland(self): + """ + Test that evenly splitting cropland works + """ + self._create_config_file_evenlysplitcrop() + sys.argv = [ + "fsurdat_modifier", + self._cfg_file_path, + ] + parser = fsurdat_modifier_arg_process() + fsurdat_modifier(parser) + # Read the resultant output file and make sure the fields are changed as expected + fsurdat_in_data = xr.open_dataset(self._fsurdat_in) + fsurdat_out_data = xr.open_dataset(self._fsurdat_out) + Ncrops = fsurdat_out_data.dims["cft"] + pct_cft = np.full_like(fsurdat_out_data.PCT_CFT, 100 / Ncrops) + np.testing.assert_array_equal(fsurdat_in_data.PCT_NATVEG, fsurdat_out_data.PCT_NATVEG) + np.testing.assert_array_equal(fsurdat_in_data.PCT_CROP, fsurdat_out_data.PCT_CROP) + np.testing.assert_array_equal(fsurdat_in_data.PCT_LAKE, fsurdat_out_data.PCT_LAKE) + np.testing.assert_array_equal(fsurdat_in_data.PCT_WETLAND, fsurdat_out_data.PCT_WETLAND) + np.testing.assert_array_equal(fsurdat_in_data.PCT_GLACIER, fsurdat_out_data.PCT_GLACIER) + np.testing.assert_array_equal(fsurdat_in_data.PCT_URBAN, fsurdat_out_data.PCT_URBAN) + np.testing.assert_array_equal(fsurdat_out_data.PCT_CFT, pct_cft) + + def test_1x1_mexicocity(self): + """ + Test that the mexicocity file is handled correctly + """ + self._cfg_file_path = os.path.join( + self._testinputs_path, "modify_fsurdat_1x1mexicocity.cfg" + ) + expectfile = os.path.join( + self._testinputs_path, + "surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206_modified.nc", + ) + outfile = os.path.join( + self._tempdir, + "surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206_modified.nc", + ) + infile = os.path.join( + self._testinputs_path, + "surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206.nc", + ) + sys.argv = [ + "fsurdat_modifier", + self._cfg_file_path, + "-i", + infile, + "-o", + outfile, + ] + parser = fsurdat_modifier_arg_process() + fsurdat_modifier(parser) + + # Read the resultant output file and make sure the fields are changed as expected + fsurdat_out_data = xr.open_dataset(outfile) + fsurdat_inp_data = xr.open_dataset(infile) + fsurdat_exp_data = xr.open_dataset(expectfile) + + self.assertFalse(fsurdat_out_data.equals(fsurdat_inp_data)) + # assert that fsurdat_out equals fsurdat_out_baseline + self.assertTrue(fsurdat_out_data.equals(fsurdat_exp_data)) + + def test_cfg_file_DNE_fail(self): + """ + Test that if the config file does not exist that it gracefully fails + """ + self._cfg_file_path = os.path.join(self._tempdir, "FILE_DOES_NOT_EXIST.cfg") + sys.argv = ["fsurdat_modifier", self._cfg_file_path] + with self.assertRaisesRegex(SystemExit, "Config file does NOT exist"): + fsurdat_modifier_arg_process() + + def test_input_fsurdat_DNE_fail(self): + """ + Test that if the input fsurdat file does not exist that it gracefully fails + """ + self._cfg_file_path = os.path.join( + self._testinputs_path, "modify_fsurdat_short_nofiles.cfg" + ) + sys.argv = ["fsurdat_modifier", self._cfg_file_path, "-i", "FILE_DOES_NOT_EXIST.nc"] + parser = fsurdat_modifier_arg_process() + with self.assertRaisesRegex(SystemExit, "Input fsurdat_in file does NOT exist"): + fsurdat_modifier(parser) + + def test_output_fsurdat_EXISTS_fail(self): + """ + Test that if the output fsurdat file does exist that it gracefully fails + without --overwrite option + """ + self._cfg_file_path = os.path.join( + self._testinputs_path, "modify_fsurdat_short_nofiles.cfg" + ) + sys.argv = [ + "fsurdat_modifier", + self._cfg_file_path, + "-i", + self._cfg_file_path, + "-o", + self._cfg_file_path, + ] + parser = fsurdat_modifier_arg_process() + with self.assertRaisesRegex(SystemExit, "Output file already exists"): + fsurdat_modifier(parser) + + def test_cfg_file_empty_fail(self): + """ + Test that if the config file is empty it gracefully fails + """ + self._cfg_file_path = os.path.join(self._tempdir, "EMPTY_FILE.cfg") + fil = open(self._cfg_file_path, "w") + fil.close() + sys.argv = ["fsurdat_modifier", self._cfg_file_path] + parser = fsurdat_modifier_arg_process() + with self.assertRaisesRegex(SystemExit, "Config file does not have the expected section"): + fsurdat_modifier(parser) + def test_minimalInfo(self): """ This test specifies a minimal amount of information @@ -62,7 +347,9 @@ def test_minimalInfo(self): self._create_config_file_minimal() # run the fsurdat_modifier tool - fsurdat_modifier(self._cfg_file_path) + sys.argv = ["fsurdat_modifier", self._cfg_file_path] + parser = fsurdat_modifier_arg_process() + fsurdat_modifier(parser) # the critical piece of this test is that the above command # doesn't generate errors; however, we also do some assertions below @@ -80,7 +367,9 @@ def test_crop(self): self._create_config_file_crop() # run the fsurdat_modifier tool - fsurdat_modifier(self._cfg_file_path) + sys.argv = ["fsurdat_modifier", self._cfg_file_path] + parser = fsurdat_modifier_arg_process() + fsurdat_modifier(parser) # the critical piece of this test is that the above command # doesn't generate errors; however, we also do some assertions below @@ -105,7 +394,12 @@ def test_allInfo(self): self._create_config_file_complete() # run the fsurdat_modifier tool - fsurdat_modifier(self._cfg_file_path) + sys.argv = [ + "fsurdat_modifier", + self._cfg_file_path, + ] + parser = fsurdat_modifier_arg_process() + fsurdat_modifier(parser) # the critical piece of this test is that the above command # doesn't generate errors; however, we also do some assertions below @@ -136,6 +430,23 @@ def _create_config_file_minimal(self): line = f"fsurdat_out = {self._fsurdat_out}" cfg_out.write(line) + def _create_config_file_evenlysplitcrop(self): + """ + Open the new and the template .cfg files + Loop line by line through the template .cfg file + When string matches, replace that line's content + """ + with open(self._cfg_file_path, "w", encoding="utf-8") as cfg_out: + with open(self._cfg_template_path, "r", encoding="utf-8") as cfg_in: + for line in cfg_in: + if re.match(r" *evenly_split_cropland *=", line): + line = "evenly_split_cropland = True" + elif re.match(r" *fsurdat_in *=", line): + line = f"fsurdat_in = {self._fsurdat_in}" + elif re.match(r" *fsurdat_out *=", line): + line = f"fsurdat_out = {self._fsurdat_out}" + cfg_out.write(line) + def _create_config_file_crop(self): """ Open the new and the template .cfg files @@ -159,6 +470,8 @@ def _create_config_file_crop(self): line = "lnd_lon_2 = 300\n" elif re.match(r" *dom_pft *=", line): line = "dom_pft = 15" + elif re.match(r" *evenly_split_cropland *=", line): + line = "evenly_split_cropland = False" elif re.match(r" *lai *=", line): line = "lai = 0 1 2 3 4 5 5 4 3 2 1 0\n" elif re.match(r" *sai *=", line): @@ -194,6 +507,8 @@ def _create_config_file_complete(self): line = "lnd_lon_2 = 300\n" elif re.match(r" *dom_pft *=", line): line = "dom_pft = 1" + elif re.match(r" *evenly_split_cropland *=", line): + line = "evenly_split_cropland = False" elif re.match(r" *lai *=", line): line = "lai = 0 1 2 3 4 5 5 4 3 2 1 0\n" elif re.match(r" *sai *=", line): diff --git a/python/ctsm/test/test_sys_lilac_build_ctsm.py b/python/ctsm/test/test_sys_lilac_build_ctsm.py index f1c5e22f8f..d773749bf7 100755 --- a/python/ctsm/test/test_sys_lilac_build_ctsm.py +++ b/python/ctsm/test/test_sys_lilac_build_ctsm.py @@ -27,6 +27,7 @@ class TestSysBuildCtsm(unittest.TestCase): def setUp(self): self._tempdir = tempfile.mkdtemp() + self.assertTrue(os.path.isdir(self._tempdir)) # Hack around a check in CIME: As of https://github.com/ESMCI/cime/pull/4228, If # NCAR_HOST is in the environment, CIME checks if the machine you're running on is @@ -68,6 +69,7 @@ def test_buildSetup_userDefinedMachine_minimalInfo(self): gmake_j=8, no_pnetcdf=True, ) + self.assertTrue(os.path.isdir(build_dir)) # the critical piece of this test is that the above command doesn't generate any # errors; however we also do some assertions below @@ -87,6 +89,7 @@ def test_buildSetup_userDefinedMachine_allInfo(self): build_dir = os.path.join(self._tempdir, "ctsm_build") inputdata_path = os.path.realpath(os.path.join(self._tempdir, "my_inputdata")) os.makedirs(inputdata_path) + self.assertTrue(os.path.isdir(inputdata_path)) build_ctsm( cime_path=_CIME_PATH, build_dir=build_dir, @@ -107,6 +110,7 @@ def test_buildSetup_userDefinedMachine_allInfo(self): build_with_openmp=True, inputdata_path=os.path.join(self._tempdir, "my_inputdata"), ) + self.assertTrue(os.path.isdir(build_dir)) # the critical piece of this test is that the above command doesn't generate any # errors; however we also do some assertions below diff --git a/python/ctsm/test/test_sys_mesh_modifier.py b/python/ctsm/test/test_sys_mesh_modifier.py index 0889a505ba..da59368391 100755 --- a/python/ctsm/test/test_sys_mesh_modifier.py +++ b/python/ctsm/test/test_sys_mesh_modifier.py @@ -57,6 +57,7 @@ def setUp(self): metadata_file = os.path.join(self._tempdir, "metadata.nc") configure_path = os.path.join(path_to_cime(), "CIME/scripts/configure") + self._previous_dir = os.getcwd() os.chdir(self._tempdir) # cd to tempdir # Run configure to generate .env_mach_specific.sh @@ -111,7 +112,7 @@ def tearDown(self): """ Remove temporary directory """ - os.getcwd() # cd back to the original working directory + os.chdir(self._previous_dir) # cd back to the original working directory shutil.rmtree(self._tempdir, ignore_errors=True) def test_allInfo(self): diff --git a/python/ctsm/test/test_sys_modify_singlept_site_neon.py b/python/ctsm/test/test_sys_modify_singlept_site_neon.py new file mode 100755 index 0000000000..74362be4cd --- /dev/null +++ b/python/ctsm/test/test_sys_modify_singlept_site_neon.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +""" +System tests for modify_singlept_site_neon.py +""" + +import os +import unittest +import tempfile +import shutil +import sys + +from ctsm.path_utils import path_to_ctsm_root +from ctsm import unit_testing +from ctsm.site_and_regional.modify_singlept_site_neon import main + +# Allow test names that pylint doesn't like; otherwise hard to make them +# readable +# pylint: disable=invalid-name + + +class TestSysModifySingleptSiteNeon(unittest.TestCase): + """System tests for modify_singlept_site_neon""" + + def setUp(self): + """ + Make /_tempdir for use by these tests. + Check tempdir for history files + """ + self._tempdir = tempfile.mkdtemp() + testinputs_path = os.path.join(path_to_ctsm_root(), "python/ctsm/test/testinputs") + self._cfg_file_path = os.path.join( + testinputs_path, "modify_singlept_site_neon_opt_sections.cfg" + ) + + def tearDown(self): + """ + Remove temporary directory + """ + shutil.rmtree(self._tempdir, ignore_errors=True) + + def test_modify_site(self): + """ + Test modifying a singple point site. + This test currently checks that the run fails due to dir structure + + TODO: The primary items to test here are the following: + 1) Fields are overwritten with site-specific data for neon sites + 2) Downloaded data is used in surface dataset + 3) Check specific fields listed in update_metadata for correct output + 4) Check that a netcdf with correct formatting is created + """ + sys.argv = [ + "modify_singlept_site_neon", + "--neon_site", + path_to_ctsm_root() + "/ctsm/cime_config/usermods_dirs/NEON/ABBY", + ] + # TODO: the above requires a full path instead of site name + # because of how run_neon is configured. + # This needs to be fixed in run_neon. + with self.assertRaises(SystemExit): + print( + """This should currently fail due to directory structure in run_neon + and the directory structure listed in sys.argv""" + ) + main() + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_sys_regrid_ggcmi_shdates.py b/python/ctsm/test/test_sys_regrid_ggcmi_shdates.py new file mode 100755 index 0000000000..7521ef09a5 --- /dev/null +++ b/python/ctsm/test/test_sys_regrid_ggcmi_shdates.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +"""System tests for regrid_ggcmi_shdates.py + +""" + +import os +import re + +import unittest +import tempfile +import shutil +import sys + +import xarray as xr +import numpy as np + +# -- add python/ctsm to path (needed if we want to run test stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + + +from ctsm.path_utils import path_to_ctsm_root +from ctsm import unit_testing +from ctsm.crop_calendars.regrid_ggcmi_shdates import regrid_ggcmi_shdates +from ctsm.crop_calendars.regrid_ggcmi_shdates import regrid_ggcmi_shdates_arg_process + +# Allow test names that pylint doesn't like; otherwise hard to make them +# readable +# pylint: disable=invalid-name + + +class TestRegridGgcmiShdates(unittest.TestCase): + """System tests for regrid_ggcmi_shdates""" + + def setUp(self): + # Where in the /testinputs directory are the raw crop calendar file(s)? + testinputs_path = os.path.join(path_to_ctsm_root(), "python", "ctsm", "test", "testinputs") + testinputs_cc_path = os.path.join(testinputs_path, "cropcals") + self._testinputs_cc_path = testinputs_cc_path + + # Make /_tempdir for use by these tests. + self._tempdir = tempfile.mkdtemp() + + # Obtain path for the directory being created in /_tempdir + self._regridded_cropcals = os.path.join(self._tempdir, "regridded_cropcals") + + # What extension do the raw crop calendar file(s) have? + self._extension = ".nc4" + + # Which crop(s) should we test? (comma-separated string) + self._crop_list = "swh_rf" + + # What is the complete set of input arguments (including script name)? + regrid_template_file = os.path.join( + testinputs_path, "surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc" + ) + self._function_call_list = [ + "regrid_ggcmi_shdates", + "-i", + testinputs_cc_path, + "-x", + ".nc4", + "-o", + self._regridded_cropcals, + "-rr", + "5x5amazon", + "-rt", + regrid_template_file, + "--crop-list", + "swh_rf", + ] + + def tearDown(self): + """ + Remove temporary directory + """ + shutil.rmtree(self._tempdir, ignore_errors=True) + + def test_regrid_ggcmi_shdates(self): + + # Call script + sys.argv = self._function_call_list + args = regrid_ggcmi_shdates_arg_process() + regrid_ggcmi_shdates( + args.regrid_resolution, + args.regrid_template_file, + args.regrid_input_directory, + args.regrid_output_directory, + args.regrid_extension, + args.crop_list, + ) + + # Read output file + regrid_out_file = os.path.join( + self._regridded_cropcals, + "swh_rf_ggcmi_crop_calendar_phase3_v1.01_nninterp-5x5amazon.nc4", + ) + regrid_out_ds = xr.open_dataset(regrid_out_file) + + # Check sowing dates + expected_sow_dates = np.array( + [ + [120, 120, 120, 120, 120], + [120, 120, 120, 120, 120], + [120, 120, 120, 120, 120], + [330, 335, 335, 120, 120], + [325, 335, 335, 335, 120], + ] + ) + np.testing.assert_array_equal(expected_sow_dates, regrid_out_ds["planting_day"].values) + + # Check maturity dates + expected_mat_dates = np.array( + [ + [221, 221, 221, 221, 221], + [221, 221, 221, 221, 221], + [221, 221, 221, 221, 221], + [153, 128, 128, 221, 221], + [163, 128, 128, 128, 221], + ] + ) + np.testing.assert_array_equal(expected_mat_dates, regrid_out_ds["maturity_day"].values) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_sys_run_neon.py b/python/ctsm/test/test_sys_run_neon.py new file mode 100755 index 0000000000..f4c417ea51 --- /dev/null +++ b/python/ctsm/test/test_sys_run_neon.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +"""System tests for run_neon + +""" + +import glob +import os +import unittest +import tempfile +import shutil +import sys + +from ctsm import unit_testing +from ctsm.site_and_regional.run_neon import main +from ctsm.path_utils import path_to_ctsm_root + +# Allow test names that pylint doesn't like; otherwise hard to make them +# readable +# pylint: disable=invalid-name + + +class TestSysRunNeon(unittest.TestCase): + """System tests for run_neon""" + + def setUp(self): + """ + Make /_tempdir for use by these tests. + Check tempdir for history files + """ + self._previous_dir = os.getcwd() + self._tempdir = tempfile.mkdtemp() + os.chdir(self._tempdir) # cd to tempdir + + def tearDown(self): + """ + Remove temporary directory + """ + os.chdir(self._previous_dir) + shutil.rmtree(self._tempdir, ignore_errors=True) + + def test_one_site(self): + """ + This test specifies a site to run + Run the tool, check that file structure is set up correctly + """ + + # run the run_neon tool + sys.argv = [ + os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_neon"), + "--neon-sites", + "BART", + "--setup-only", + "--output-root", + self._tempdir, + ] + main("") + + # assert that BART directories were created during setup + self.assertTrue("BART" in glob.glob(self._tempdir + "/BART*")[0]) + + # TODO: Would also be useful to test the following items: + # It might be good to ensure the log files are working as expected? + # Test running transient, ad and post ad cases. + # Test use of base case root. + # Test for using prism? + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_config_utils.py b/python/ctsm/test/test_unit_config_utils.py new file mode 100644 index 0000000000..c9ee23bac3 --- /dev/null +++ b/python/ctsm/test/test_unit_config_utils.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +"""Unit tests for config_utils +""" + +import unittest + +from configparser import ConfigParser + +from ctsm import unit_testing +from ctsm.config_utils import lon_range_0_to_360, get_config_value_or_array + +# Allow test names that pylint doesn't like; otherwise hard to make them +# readable +# pylint: disable=invalid-name + +# pylint: disable=protected-access + + +class TestConfigUtils(unittest.TestCase): + """Tests of config_utils""" + + # Allow these to be set outside of the __init__ method + # pylint: disable=attribute-defined-outside-init + def setUp(self): + """Setup for testing""" + self.config = ConfigParser() + self.section = "main" + self.file_path = "path_to_file" + self.config[self.section] = {} + + def test_negative_lon(self): + """Test lon_range_0_to_360 for a negative longitude""" + lon = -180.0 + lon_new = lon_range_0_to_360(lon) + self.assertEqual(lon_new, 180.0, "lon not as expected") + + def test_negative2_lon(self): + """Test lon_range_0_to_360 for a negative longitude""" + lon = -5.0 + lon_new = lon_range_0_to_360(lon) + self.assertEqual(lon_new, 355.0, "lon not as expected") + + def test_regular_lon(self): + """Test lon_range_0_to_360 for a regular longitude""" + lon = 22.567 + lon_new = lon_range_0_to_360(lon) + self.assertEqual(lon_new, lon, "lon not as expected") + + def test_lon_out_of_range(self): + """Test lon_range_0_to_360 for longitude out of range""" + lon = 361.0 + with self.assertRaisesRegex(SystemExit, "lon_in needs to be in the range 0 to 360"): + lon_range_0_to_360(lon) + + def test_lon_out_of_range_negative(self): + """Test lon_range_0_to_360 for longitude out of range""" + lon = -181.0 + with self.assertRaisesRegex(SystemExit, "lon_in needs to be in the range 0 to 360"): + lon_range_0_to_360(lon) + + def test_config_value_or_array_single_value(self): + """Simple test of get_config_value_or_array""" + item = "single_value_thing" + # Test on a string, float and integer + self.config.set(self.section, item, "one-thing") + value = get_config_value_or_array(self.config, self.section, item) + self.assertEqual(value, "one-thing", "Value as expected") + self.config.set(self.section, item, "100.") + value = get_config_value_or_array(self.config, self.section, item) + self.assertEqual(value, "100.", "Value as expected") + self.config.set(self.section, item, "100") + value = get_config_value_or_array(self.config, self.section, item) + self.assertEqual(value, "100", "Value as expected") + # Run over again, with an explicit conversion + self.config.set(self.section, item, "one-thing") + value = get_config_value_or_array(self.config, self.section, item, convert_to_type=str) + self.assertEqual(value, "one-thing", "Value as expected") + self.config.set(self.section, item, "100.") + value = get_config_value_or_array(self.config, self.section, item, convert_to_type=float) + self.assertEqual(value, 100.0, "Value as expected") + self.config.set(self.section, item, "100") + value = get_config_value_or_array(self.config, self.section, item, convert_to_type=int) + self.assertEqual(value, 100, "Value as expected") + + def test_config_value_or_array_for_list(self): + """Simple test of get_config_value_or_array for a list""" + item = "three_things" + # Test on a string, float and integer + mystr = "one two three" + mystrlist = ["one", "two", "three"] + myfloat = "1. 2. 3." + myfloatlist = [1.0, 2.0, 3.0] + myint = "1 2 3" + myintlist = [1, 2, 3] + self.config.set(self.section, item, mystr) + value = get_config_value_or_array(self.config, self.section, item, convert_to_type=str) + self.assertEqual(value, mystrlist, "List as expected") + self.assertEqual(len(value), 3, "List size as expected") + self.config.set(self.section, item, myfloat) + value = get_config_value_or_array(self.config, self.section, item, convert_to_type=float) + self.assertEqual(value, myfloatlist, "Value as expected") + self.assertEqual(len(value), 3, "List size as expected") + self.config.set(self.section, item, myint) + value = get_config_value_or_array(self.config, self.section, item, convert_to_type=int) + self.assertEqual(value, myintlist, "Value as expected") + self.assertEqual(len(value), 3, "List size as expected") + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_fsurdat_modifier.py b/python/ctsm/test/test_unit_fsurdat_modifier.py new file mode 100755 index 0000000000..166924903b --- /dev/null +++ b/python/ctsm/test/test_unit_fsurdat_modifier.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python3 + +""" +Unit tests for fsurdat_modifier subroutines: +""" + +import unittest +import os +import sys +import shutil + +import tempfile +from configparser import ConfigParser +import xarray as xr + +from ctsm import unit_testing +from ctsm.path_utils import path_to_ctsm_root +from ctsm.modify_input_files.fsurdat_modifier import fsurdat_modifier_arg_process +from ctsm.modify_input_files.fsurdat_modifier import read_cfg_subgrid +from ctsm.modify_input_files.fsurdat_modifier import read_cfg_option_control +from ctsm.modify_input_files.fsurdat_modifier import read_cfg_var_list +from ctsm.modify_input_files.fsurdat_modifier import check_no_subgrid_section +from ctsm.modify_input_files.fsurdat_modifier import check_no_varlist_section +from ctsm.modify_input_files.modify_fsurdat import ModifyFsurdat + +# Allow test names that pylint doesn't like; otherwise hard to make them +# readable +# pylint: disable=invalid-name + +# pylint: disable=protected-access + + +# Allow as many public methods as needed... +# pylint: disable=too-many-public-methods +# Allow all the instance attributes that we need +# pylint: disable=too-many-instance-attributes +class TestFSurdatModifier(unittest.TestCase): + """Tests the fsurdat_modifier subroutines""" + + def setUp(self): + """Setup for trying out the methods""" + testinputs_path = os.path.join(path_to_ctsm_root(), "python/ctsm/test/testinputs") + self._cfg_file_path = os.path.join(testinputs_path, "modify_fsurdat_opt_sections.cfg") + self._testinputs_path = testinputs_path + self._fsurdat_in = os.path.join( + testinputs_path, + "surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc", + ) + self._tempdir = tempfile.mkdtemp() + self._fsurdat_in = os.path.join( + testinputs_path, + "surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc", + ) + self._fsurdat_out = os.path.join(self._tempdir, "fsurdat_out.nc") + sys.argv = [ + "fsurdat_modifier", + self._cfg_file_path, + "-i", + self._fsurdat_in, + "-o", + self._fsurdat_out, + ] + parser = fsurdat_modifier_arg_process() + self.cfg_path = str(parser.cfg_path) + self.config = ConfigParser() + self.config.read(self.cfg_path) + my_data = xr.open_dataset(self._fsurdat_in) + self.modify_fsurdat = ModifyFsurdat( + my_data=my_data, + lon_1=0.0, + lon_2=360.0, + lat_1=90.0, + lat_2=90.0, + landmask_file=None, + lat_dimname=None, + lon_dimname=None, + ) + + def tearDown(self): + """ + Remove temporary directory + """ + shutil.rmtree(self._tempdir, ignore_errors=True) + + def test_subgrid_and_idealized_fails(self): + """test that subgrid and idealized fails gracefully""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "True") + self.config.set(section, "include_nonveg", "False") + self.config.set(section, "process_subgrid_section", "True") + self.config.set(section, "dom_pft", "UNSET") + with self.assertRaisesRegex( + SystemExit, + "idealized AND process_subgrid_section can NOT both be on, pick one or the other", + ): + read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + + def test_dompft_and_splitcropland_fails(self): + """test that setting dompft crop with evenly_split_cropland True fails gracefully""" + section = "modify_fsurdat_basic_options" + crop_pft = max(self.modify_fsurdat.file.natpft.values) + 1 + self.config.set(section, "dom_pft", str(crop_pft)) + self.config.set(section, "evenly_split_cropland", "True") + with self.assertRaisesRegex( + SystemExit, + "dom_pft must not be set to a crop PFT when evenly_split_cropland is True", + ): + read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + + def test_optional_only_true_and_false(self): + """test that optional settings can only be true or false""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "dom_pft", "1") + varlist = ( + "idealized", + "include_nonveg", + "process_subgrid_section", + "process_var_list_section", + ) + for var in varlist: + self.config.set(section, var, "True") + self.config.set(section, "idealized", "False") + read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + for var in varlist: + self.config.set(section, var, "False") + read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + self.config.set(section, "dom_pft", "UNSET") + read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + varlist = ( + "idealized", + "evenly_split_cropland", + ) + for var in varlist: + orig_value = self.config.get(section, var) + self.config.set(section, var, "Thing") + with self.assertRaisesRegex( + SystemExit, "Non-boolean value found for .cfg file variable: " + var + ): + read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + self.config.set(section, var, orig_value) + + def test_read_subgrid(self): + """test a simple read of subgrid""" + read_cfg_subgrid(self.config, self.cfg_path) + + def test_read_subgrid_allglacier(self): + """test a read of subgrid that's for all glacier""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "0. 0. 0.") + self.config.set(section, "pct_lake", "0.") + self.config.set(section, "pct_wetland", "0.") + self.config.set(section, "pct_glacier", "100.") + self.config.set(section, "pct_natveg", "0.") + self.config.set(section, "pct_crop", "0.") + read_cfg_subgrid(self.config, self.cfg_path) + + def test_read_subgrid_allspecial(self): + """test a read of subgrid that's all special landunits""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "0. 0. 0.") + self.config.set(section, "pct_lake", "25.") + self.config.set(section, "pct_wetland", "35.") + self.config.set(section, "pct_glacier", "40.") + self.config.set(section, "pct_natveg", "0.") + self.config.set(section, "pct_crop", "0.") + read_cfg_subgrid(self.config, self.cfg_path) + + def test_read_subgrid_allurban(self): + """test a read of subgrid that's all urban""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "100.0 0.0 0.0") + self.config.set(section, "pct_lake", "0.") + self.config.set(section, "pct_wetland", "0.") + self.config.set(section, "pct_glacier", "0.") + self.config.set(section, "pct_natveg", "0.") + self.config.set(section, "pct_crop", "0.") + read_cfg_subgrid(self.config, self.cfg_path) + + def test_read_subgrid_split_cropland(self): + """ + test a read of subgrid that's 50/50 natural and + cropland, with cropland split evenly among + crop types + """ + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + self.config.set(section, "evenly_split_cropland", "True") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "0.0 0.0 0.0") + self.config.set(section, "pct_lake", "0.") + self.config.set(section, "pct_wetland", "0.") + self.config.set(section, "pct_glacier", "0.") + self.config.set(section, "pct_natveg", "50.") + self.config.set(section, "pct_crop", "50.") + read_cfg_subgrid(self.config, self.cfg_path) + + def test_read_var_list(self): + """test a simple read of var_list""" + read_cfg_var_list(self.config, idealized=True) + + def test_subgrid_outofrange(self): + """test a read of subgrid that's out of range""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "101. 0. 0.") + with self.assertRaisesRegex(SystemExit, "is out of range of 0 to 100 ="): + read_cfg_subgrid(self.config, self.cfg_path) + + def test_subgrid_pct_urban_toosmall(self): + """test a read of subgrid for PCT_URBAN that's an array too small""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "100. 0.") + with self.assertRaisesRegex( + SystemExit, "PCT_URBAN is not a list of the expected size of 3" + ): + read_cfg_subgrid(self.config, self.cfg_path) + + def test_subgrid_pct_urban_toobig(self): + """test a read of subgrid for PCT_URBAN that's an array too big""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "100. 0. 0. 0.") + with self.assertRaisesRegex( + SystemExit, "PCT_URBAN is not a list of the expected size of 3" + ): + read_cfg_subgrid(self.config, self.cfg_path) + + def test_subgrid_pct_urban_singlevalue(self): + """test a read of subgrid for PCT_URBAN that's a single value""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "100.") + with self.assertRaisesRegex( + SystemExit, "PCT_URBAN is not a list of the expected size of 3" + ): + read_cfg_subgrid(self.config, self.cfg_path) + + def test_subgrid_notsumtohundred(self): + """test a read of subgrid that's doesn't sum to a hundred""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "0. 0. 0.") + self.config.set(section, "pct_lake", "0.") + self.config.set(section, "pct_wetland", "0.") + self.config.set(section, "pct_glacier", "0.") + self.config.set(section, "pct_natveg", "0.") + self.config.set(section, "pct_crop", "0.") + with self.assertRaisesRegex( + SystemExit, "PCT fractions in subgrid section do NOT sum to a hundred as they should" + ): + read_cfg_subgrid(self.config, self.cfg_path) + + def test_subgrid_badvar(self): + """test a read of subgrid for a variable thats not in the list""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "badvariable", "100.") + with self.assertRaisesRegex(SystemExit, "is not a valid variable name. Valid vars ="): + read_cfg_subgrid(self.config, self.cfg_path) + + def test_varlist_varinidealized(self): + """test a read of varlist for a variable thats in the idealized list, + when idealized is on""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "True") + section = "modify_fsurdat_variable_list" + self.config.set(section, "PCT_SAND", "100.") + with self.assertRaisesRegex( + SystemExit, + "is a special variable handled in the idealized section." + + " This should NOT be handled in the variable list section. Special idealized vars =", + ): + read_cfg_var_list(self.config, idealized=True) + + def test_varlist_varinsubgrid(self): + """test a read of varlist for a variable thats in the subgrid list""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + section = "modify_fsurdat_variable_list" + self.config.set(section, "PCT_GLACIER", "100.") + with self.assertRaisesRegex( + SystemExit, + "is a variable handled in the subgrid section." + + " This should NOT be handled in the variable list section. Subgrid vars =", + ): + read_cfg_var_list(self.config, idealized=False) + + def test_varlist_monthlyvar(self): + """test a read of varlist for a variable thats one of the monthly + variables handled in the dom_pft section""" + section = "modify_fsurdat_variable_list" + self.config.set(section, "MONTHLY_LAI", "100.") + with self.assertRaisesRegex( + SystemExit, + "is a variable handled as part of the dom_pft handling." + + " This should NOT be handled in the variable list section." + + " Monthly vars handled this way =", + ): + read_cfg_var_list(self.config, idealized=False) + + def test_subgrid_remove(self): + """test a read of subgrid when it's section has been removed""" + section = "modify_fsurdat_subgrid_fractions" + self.config.remove_section(section) + with self.assertRaisesRegex(SystemExit, "Config file does not have the expected section"): + read_cfg_subgrid(self.config, self.cfg_path) + + def test_subgrid_not_thereifoff(self): + """test that a graceful error happens if subgrid section is off, + but it appears in the file""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "process_subgrid_section", "False") + with self.assertRaisesRegex(SystemExit, "Config file does have a section"): + check_no_subgrid_section(self.config) + + def test_varlist_not_thereifoff(self): + """test that a graceful error happens if varlist section is off, + but it appears in the file""" + section = "modify_fsurdat_basic_options" + self.config.set(section, "process_var_list_section", "False") + with self.assertRaisesRegex(SystemExit, "Config file does have a section"): + check_no_varlist_section(self.config) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_iso_utils.py b/python/ctsm/test/test_unit_iso_utils.py new file mode 100755 index 0000000000..c58beef52e --- /dev/null +++ b/python/ctsm/test/test_unit_iso_utils.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +"""Unit tests for the iso functions in utils +""" + +import unittest + +from ctsm import unit_testing +from ctsm.utils import parse_isoduration, get_isosplit + +# Allow names that pylint doesn't like, because otherwise I find it hard +# to make readable unit test names +# pylint: disable=invalid-name + + +class TestIsoUtils(unittest.TestCase): + """Tests of iso functions in utils""" + + def test_iso_split_for_Year(self): + """ + Tests the get_isosplit function for a strings with Years + """ + iso_string = "0Y" + self.assertEqual(get_isosplit(iso_string, "Y"), ("0", "")) + iso_string = "1Y" + self.assertEqual(get_isosplit(iso_string, "Y"), ("1", "")) + iso_string = "4Y" + self.assertEqual(get_isosplit(iso_string, "Y"), ("4", "")) + iso_string = "100Y" + self.assertEqual(get_isosplit(iso_string, "Y"), ("100", "")) + iso_string = "999999Y" + self.assertEqual(get_isosplit(iso_string, "Y"), ("999999", "")) + + def test_parse_isoduration_for_Years(self): + """ + Tests the parse_isoduration function for iso strings with Years + """ + days_in_year = 365 + iso_string = "0Y" + self.assertEqual(parse_isoduration(iso_string), 0) + iso_string = "1Y" + self.assertEqual(parse_isoduration(iso_string), days_in_year) + iso_string = "4Y" + self.assertEqual(parse_isoduration(iso_string), 4 * days_in_year) + iso_string = "100Y" + self.assertEqual(parse_isoduration(iso_string), 100 * days_in_year) + iso_string = "999999Y" + self.assertEqual(parse_isoduration(iso_string), 999999 * days_in_year) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_modify_fsurdat.py b/python/ctsm/test/test_unit_modify_fsurdat.py index e53175b9b7..a075035b73 100755 --- a/python/ctsm/test/test_unit_modify_fsurdat.py +++ b/python/ctsm/test/test_unit_modify_fsurdat.py @@ -19,87 +19,102 @@ # pylint: disable=protected-access +## Too many instant variables as part of the class (too many self. in the SetUp) +# pylint: disable=too-many-instance-attributes + class TestModifyFsurdat(unittest.TestCase): """Tests the setvar_lev functions and the - _get_rectangle function + _get_rectangle, check_varlist, and set_varlist methods """ - def test_setvarLev(self): - """ - Tests that setvar_lev0, setvar_lev1, and setvar_lev2 update values of - variables within a rectangle defined by user-specified - lon_1, lon_2, lat_1, lat_2 - """ + def setUp(self): # get longxy, latixy that would normally come from an fsurdat file # self._get_longxy_latixy will convert -180 to 180 to 0-360 longitudes # get cols, rows also - min_lon = 2 # expects min_lon < max_lon - min_lat = 3 # expects min_lat < max_lat - longxy, latixy, cols, rows = self._get_longxy_latixy( - _min_lon=min_lon, _max_lon=10, _min_lat=min_lat, _max_lat=12 + self.min_lon = 2 # expects min_lon < max_lon + self.min_lat = 3 # expects min_lat < max_lat + longxy, latixy, self.cols, self.rows = self._get_longxy_latixy( + _min_lon=self.min_lon, _max_lon=10, _min_lat=self.min_lat, _max_lat=12 ) # get not_rectangle from user-defined lon_1, lon_2, lat_1, lat_2 - lon_1 = 3 - lon_2 = 5 # lon_1 < lon_2 - lat_1 = 5 - lat_2 = 7 # lat_1 < lat_2 + self.lon_1 = 3 + self.lon_2 = 5 # lon_1 < lon_2 + self.lat_1 = 5 + self.lat_2 = 7 # lat_1 < lat_2 # create xarray dataset containing lev0, lev1, and lev2 variables; # the fsurdat_modify tool reads variables like this from fsurdat file - var_1d = np.arange(cols) - var_lev2 = var_1d * np.ones((rows, cols, rows, cols)) - var_lev1 = var_1d * np.ones((cols, rows, cols)) + var_1d = np.arange(self.cols) + var_lev0 = var_1d * np.ones((self.rows, self.cols)) + var_lev1 = var_1d * np.ones((self.cols, self.rows, self.cols)) + var_lev2 = var_1d * np.ones((self.rows, self.cols, self.rows, self.cols)) + var_lev3 = var_1d * np.ones((self.cols, self.rows, self.cols, self.rows, self.cols)) my_data = xr.Dataset( data_vars=dict( LONGXY=(["x", "y"], longxy), # use LONGXY as var_lev0 LATIXY=(["x", "y"], latixy), # __init__ expects LONGXY, LATIXY + urbdens=(["numurbl"], var_1d), # numurbl needs to be dimension + var_lev0=(["x", "y"], var_lev0), var_lev1=(["w", "x", "y"], var_lev1), var_lev2=(["v", "w", "x", "y"], var_lev2), + var_lev3=(["z", "v", "w", "x", "y"], var_lev3), + VAR_LEV0_UPPERCASE=(["x", "y"], var_lev0), + VAR_LEV1_UPPERCASE=(["w", "x", "y"], var_lev1), + VAR_LEV2_UPPERCASE=(["v", "w", "x", "y"], var_lev2), + VAR_LEV3_UPPERCASE=(["z", "v", "w", "x", "y"], var_lev3), ) ) # create ModifyFsurdat object - modify_fsurdat = ModifyFsurdat( + self.modify_fsurdat = ModifyFsurdat( my_data=my_data, - lon_1=lon_1, - lon_2=lon_2, - lat_1=lat_1, - lat_2=lat_2, + lon_1=self.lon_1, + lon_2=self.lon_2, + lat_1=self.lat_1, + lat_2=self.lat_2, landmask_file=None, lat_dimname=None, lon_dimname=None, ) + def test_setvarLev(self): + """ + Tests that setvar_lev0, setvar_lev1, and setvar_lev2 update values of + variables within a rectangle defined by user-specified + lon_1, lon_2, lat_1, lat_2 + """ + # initialize and then modify the comparison matrices - comp_lev0 = modify_fsurdat.file.LONGXY - comp_lev1 = modify_fsurdat.file.var_lev1 - comp_lev2 = modify_fsurdat.file.var_lev2 + comp_lev0 = self.modify_fsurdat.file.LONGXY + comp_lev1 = self.modify_fsurdat.file.var_lev1 + comp_lev2 = self.modify_fsurdat.file.var_lev2 val_for_rectangle = 1.5 comp_lev0[ - lat_1 - min_lat : lat_2 - min_lat + 1, lon_1 - min_lon : lon_2 - min_lon + 1 + self.lat_1 - self.min_lat : self.lat_2 - self.min_lat + 1, + self.lon_1 - self.min_lon : self.lon_2 - self.min_lon + 1, ] = val_for_rectangle comp_lev1[ ..., - lat_1 - min_lat : lat_2 - min_lat + 1, - lon_1 - min_lon : lon_2 - min_lon + 1, + self.lat_1 - self.min_lat : self.lat_2 - self.min_lat + 1, + self.lon_1 - self.min_lon : self.lon_2 - self.min_lon + 1, ] = val_for_rectangle comp_lev2[ ..., - lat_1 - min_lat : lat_2 - min_lat + 1, - lon_1 - min_lon : lon_2 - min_lon + 1, + self.lat_1 - self.min_lat : self.lat_2 - self.min_lat + 1, + self.lon_1 - self.min_lon : self.lon_2 - self.min_lon + 1, ] = val_for_rectangle # test setvar - modify_fsurdat.setvar_lev0("LONGXY", val_for_rectangle) - np.testing.assert_array_equal(modify_fsurdat.file.LONGXY, comp_lev0) + self.modify_fsurdat.setvar_lev0("LONGXY", val_for_rectangle) + np.testing.assert_array_equal(self.modify_fsurdat.file.LONGXY, comp_lev0) - modify_fsurdat.setvar_lev1("var_lev1", val_for_rectangle, cols - 1) - np.testing.assert_array_equal(modify_fsurdat.file.var_lev1, comp_lev1) + self.modify_fsurdat.setvar_lev1("var_lev1", val_for_rectangle, self.cols - 1) + np.testing.assert_array_equal(self.modify_fsurdat.file.var_lev1, comp_lev1) - modify_fsurdat.setvar_lev2("var_lev2", val_for_rectangle, cols - 1, rows - 1) - np.testing.assert_array_equal(modify_fsurdat.file.var_lev2, comp_lev2) + self.modify_fsurdat.setvar_lev2("var_lev2", val_for_rectangle, self.cols - 1, self.rows - 1) + np.testing.assert_array_equal(self.modify_fsurdat.file.var_lev2, comp_lev2) def test_getNotRectangle_lon1leLon2Lat1leLat2(self): """ @@ -359,6 +374,85 @@ def test_getNotRectangle_latsOutOfBounds(self): latixy=latixy, ) + def test_check_varlist_lists(self): + """Test the check_varlist method for list for dimensions that works""" + lev1list = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] + lev2list = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] + settings = {"var_lev1": lev1list, "var_lev2": lev2list} + settings_new = self.modify_fsurdat.check_varlist(settings) + self.assertEqual( + settings_new, settings, "list of variable settings not identical as expected" + ) + + def test_check_varlist_lists_wrongsizes(self): + """Test the check_varlist method for lists to gracefully fail when the sizes are wrong""" + lev1list = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0] + settings = {"var_lev1": lev1list} + with self.assertRaisesRegex( + SystemExit, "Variable var_lev1 is of the wrong size. It should be" + ): + self.modify_fsurdat.check_varlist(settings) + + def test_get_numurb_dens(self): + """Check that get num urban density types is correct""" + self.assertEqual( + self.modify_fsurdat.get_urb_dens(), + 9, + "Default number of urban density types is correct", + ) + + def test_check_varlist_uppercase(self): + """Test the check_varlist method for all the dimensions that + works with allowuppercase option""" + vallist = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] + vallist2 = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] + expected = { + "VAR_LEV0_UPPERCASE": 100.0, + "VAR_LEV1_UPPERCASE": vallist, + "VAR_LEV2_UPPERCASE": vallist2, + } + settings = { + "var_lev0_uppercase": 100.0, + "var_lev1_uppercase": vallist, + "var_lev2_uppercase": vallist2, + } + settings_new = self.modify_fsurdat.check_varlist(settings, allow_uppercase_vars=True) + self.assertEqual( + expected, + settings_new, + "list of variable settings not converted to uppercase as expected", + ) + + def test_check_varlist_badvar(self): + """Test the check_varlist method for a variable not on the file""" + settings = {"badvar": 100.0} + with self.assertRaisesRegex(SystemExit, "Variable badvar is NOT in the file"): + self.modify_fsurdat.check_varlist(settings) + + def test_check_varlist_badvar_uppercase(self): + """Test the check_varlist method for a variable not on the file with allow uppercase""" + settings = {"badvar": 100.0} + with self.assertRaisesRegex(SystemExit, "Variable BADVAR is NOT in the file"): + self.modify_fsurdat.check_varlist(settings, allow_uppercase_vars=True) + + def test_set_varlist_toohighdim(self): + """Test the set_varlist method for a variable of too high a dimension""" + settings = {"var_lev3": 100.0} + with self.assertRaisesRegex( + SystemExit, "Variable var_lev3 is a higher dimension than currently allowed" + ): + self.modify_fsurdat.set_varlist(settings) + + def test_set_varlist_toohighdim_uppercase(self): + """Test the set_varlist method for a variable of too high a dimension in uppercase""" + settings = {"var_lev3_uppercase": 100.0} + with self.assertRaisesRegex( + SystemExit, + "For higher dimensional vars, the variable needs to be expressed as a " + + "list of values of the dimension size = 9 for variable=VAR_LEV3_UPPERCASE", + ): + self.modify_fsurdat.check_varlist(settings, allow_uppercase_vars=True) + def _get_longxy_latixy(self, _min_lon, _max_lon, _min_lat, _max_lat): """ Return longxy, latixy, cols, rows diff --git a/python/ctsm/test/test_unit_modify_singlept_site_neon.py b/python/ctsm/test/test_unit_modify_singlept_site_neon.py new file mode 100755 index 0000000000..ecd96357b3 --- /dev/null +++ b/python/ctsm/test/test_unit_modify_singlept_site_neon.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +""" +Unit tests for modify_singlept_site_neon + +You can run this by: + python -m unittest test_unit_modify_singlept_site_neon.py +""" + +import os +import shutil +import sys +import tempfile +import unittest +from datetime import date +import xarray as xr + +# -- add python/ctsm to path (needed if we want to run the test stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.path_utils import path_to_ctsm_root + +# pylint: disable=wrong-import-position +from ctsm import unit_testing +from ctsm.site_and_regional.modify_singlept_site_neon import ( + get_neon, + find_surffile, + update_metadata, + update_time_tag, + check_neon_time, +) + +# pylint: disable=invalid-name + + +class TestModifySingleptSiteNeon(unittest.TestCase): + """ + Basic class for testing modify_singlept_site_neon.py. + """ + + def setUp(self): + """ + Make /_tempdir for use by these tests. + Check tempdir for history files + """ + self._tempdir = tempfile.mkdtemp() + + def tearDown(self): + """ + Remove temporary directory + """ + shutil.rmtree(self._tempdir, ignore_errors=True) + + def test_get_neon(self): + """ + Test to see if neon data for valid site name is found + """ + site_name = "ABBY" + neon_dir = self._tempdir + file = get_neon(neon_dir, site_name) + self.assertEqual(file.split("/")[-1][:4], "ABBY", "CSV file did not download as expected") + + def test_get_neon_false_site(self): + """ + Test to see if neon data for invalid site name is found + """ + site_name = "INVALID_SITE" + neon_dir = self._tempdir + with self.assertRaises(SystemExit): + get_neon(neon_dir, site_name) + + def test_find_surffile(self): + """ + Test that surface file does not exist in tempdir and raises system exit error + """ + surf_dir = self._tempdir + site_name = "BART" + pft_16 = True + with self.assertRaises(SystemExit): + find_surffile(surf_dir, site_name, pft_16) + + def test_find_soil_structure(self): + """ + Test to ensure that correct attributes are found for find_soil_structure. + soil_texture_raw_data_file_name should be found, and test should go through sysexit. + """ + surf_file_name = "surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206.nc" + surf_file = os.path.join( + path_to_ctsm_root(), + "python/ctsm/test/testinputs/", + surf_file_name, + ) + f1 = xr.open_dataset(surf_file) + self.assertEqual( + f1.attrs["Soil_texture_raw_data_file_name"], + "mksrf_soitex.10level.c010119.nc", + "did not retrieve expected surface soil texture filename from surf file", + ) + + def test_update_metadata(self): + """ + Test to ensure that the file was updated today. + """ + surf_file = "surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206.nc" + neon_file = "dummy_neon_file.nc" + zb_flag = True + f1 = xr.open_dataset( + os.path.join(path_to_ctsm_root(), "python/ctsm/test/testinputs/") + surf_file + ) + f2 = update_metadata(f1, surf_file, neon_file, zb_flag) + today = date.today() + today_string = today.strftime("%Y-%m-%d") + self.assertEqual(f2.attrs["Updated_on"], today_string, "File was not updated as expected") + + def test_update_time_tag(self): + """ + Test that file ending is updated + """ + self.assertEqual( + update_time_tag("test_YYMMDD.nc")[-9:-3], + date.today().strftime("%y%m%d"), + "File ending not as expected", + ) + + def test_check_neon_time(self): + """ + Test that dictionary containing last modified information is correctly downloaded + """ + previous_dir = os.getcwd() + os.chdir(self._tempdir) # cd to tempdir + last_abby_download = check_neon_time()[ + "https://storage.neonscience.org/neon-ncar/NEON/surf_files/v1/ABBY_surfaceData.csv" + ] + self.assertEqual( + len(last_abby_download), + 19, + "last ABBY download has unexpected date format or does not exist", + ) + # Note: this checks that data is not pulled from before 2021; + # we may want to update this occassionally, + # but in any case it confirms that the oldest data is not found + self.assertGreater( + int(last_abby_download[:4]), 2021, "ABBY download is older than expected" + ) + # change back to previous dir once listing.csv file is created in tempdir and test complete + os.chdir(previous_dir) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_neon_arg_parse.py b/python/ctsm/test/test_unit_neon_arg_parse.py new file mode 100755 index 0000000000..7bae337709 --- /dev/null +++ b/python/ctsm/test/test_unit_neon_arg_parse.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +""" +Unit tests for neon_arg_parse + +You can run this by: + python -m unittest test_unit_neon_arg_parse.py +""" + +import unittest +import tempfile +import shutil +import os +import sys +import glob + +# -- add python/ctsm to path (needed if we want to run the test stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position +from ctsm import unit_testing +from ctsm.site_and_regional.neon_arg_parse import get_parser +from ctsm.path_utils import path_to_ctsm_root + +# pylint: disable=invalid-name + + +class Test_neon_arg_parse(unittest.TestCase): + """ + Basic class for testing neon_arg_parse.py. + """ + + def setUp(self): + """ + Make /_tempdir for use by these tests. + """ + self._tempdir = tempfile.mkdtemp() + + def tearDown(self): + """ + Remove temporary directory + """ + shutil.rmtree(self._tempdir, ignore_errors=True) + + def test_function(self): + """ + Test that neon_arg_parse is properly reading arguments... + """ + sys.argv = [ + "neon_arg_parse", + "--neon-sites", + "ABBY", + "--experiment", + "test", + "--run-type", + "ad", + ] + description = "" + cesmroot = path_to_ctsm_root() + valid_neon_sites = glob.glob( + os.path.join(cesmroot, "cime_config", "usermods_dirs", "NEON", "[!d]*") + ) + valid_neon_sites = sorted([v.split("/")[-1] for v in valid_neon_sites]) + parsed_arguments = get_parser(sys.argv, description, valid_neon_sites) + + self.assertEqual(parsed_arguments[0][0], "ABBY", "arguments not processed as expected") + self.assertEqual(parsed_arguments[3], "test", "arguments not processed as expected") + self.assertEqual(parsed_arguments[4], False, "arguments not processed as expected") + self.assertEqual(parsed_arguments[2], "ad", "arguments not processed as expected") + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_neon_site.py b/python/ctsm/test/test_unit_neon_site.py new file mode 100755 index 0000000000..4828718272 --- /dev/null +++ b/python/ctsm/test/test_unit_neon_site.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +""" +Unit tests for NeonSite + +You can run this by: + python -m unittest test_unit_neon_site.py +""" + +import unittest +import tempfile +import shutil +import os +import glob +import sys + +# -- add python/ctsm to path (needed if we want to run the test stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position +from ctsm import unit_testing +from ctsm.site_and_regional.neon_site import NeonSite + +# pylint: disable=invalid-name + + +class TestNeonSite(unittest.TestCase): + """ + Basic class for testing NeonSite.py. + """ + + def setUp(self): + """ + Make /_tempdir for use by these tests. + """ + self._tempdir = tempfile.mkdtemp() + + def tearDown(self): + """ + Remove temporary directory + """ + shutil.rmtree(self._tempdir, ignore_errors=True) + + def test_modify_user_nl_transient(self): + """ + Test that modify_user_nl is correctly adding lines to namelist for transient cases + """ + # NeonSite parameters: + name = "ABBY" + start_year = 2020 + end_year = 2021 + start_month = 1 + end_month = 12 + # finidat = None + finidat = "dummy_finidat" + + # modify_user_nl parameters: + case_root = self._tempdir + run_type = "transient" + rundir = "" + + # create NeonSite object and update namelist + NeonSite(name, start_year, end_year, start_month, end_month, finidat).modify_user_nl( + case_root, run_type, rundir + ) + + # gather file contents for test + new_nl_file = open(glob.glob(case_root + "/*")[0], "r") + lines_read = new_nl_file.readlines()[0] + new_nl_file.close() + + # assertion + self.assertEqual( + lines_read, + "finidat = '/inputdata/lnd/ctsm/initdata/dummy_finidat'\n", + "transient case has unexpected nl", + ) + + def test_modify_user_nl_ad(self): + """ + Test that modify_user_nl is correctly adding lines to namelist for ad cases + """ + # NeonSite parameters: + name = "ABBY" + start_year = 2020 + end_year = 2021 + start_month = 1 + end_month = 12 + # finidat = None + finidat = "dummy_finidat" + + # modify_user_nl parameters: + case_root = self._tempdir + run_type = "ad" + rundir = "" + + # create NeonSite object and update namelist + NeonSite(name, start_year, end_year, start_month, end_month, finidat).modify_user_nl( + case_root, run_type, rundir + ) + + # gather file contents for test + new_nl_file = open(glob.glob(case_root + "/*")[0], "r") + lines_read = new_nl_file.readlines()[1] + new_nl_file.close() + + # assertion + self.assertEqual(lines_read, "hist_mfilt = 20\n", "ad case has unexpected nl") + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_neon_surf_wrapper.py b/python/ctsm/test/test_unit_neon_surf_wrapper.py new file mode 100755 index 0000000000..443af2079b --- /dev/null +++ b/python/ctsm/test/test_unit_neon_surf_wrapper.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +""" +Unit tests for neon_surf_wrapper + +You can run this by: + python -m unittest test_unit_neon_surf_wrapper.py +""" + +import unittest +import os +import sys + +# -- add python/ctsm to path (needed if we want to run the test stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position +from ctsm import unit_testing +from ctsm.site_and_regional.neon_surf_wrapper import get_parser + +# pylint: disable=invalid-name + + +class TestNeonSurfWrapper(unittest.TestCase): + """ + Basic class for testing neon_surf_wrapper.py. + """ + + def test_parser(self): + """ + Test that parser has same defaults as expected + """ + + self.assertEqual(get_parser().argument_default, None, "Parser not working as expected") + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_run_neon.py b/python/ctsm/test/test_unit_run_neon.py new file mode 100755 index 0000000000..a35608e249 --- /dev/null +++ b/python/ctsm/test/test_unit_run_neon.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +""" +Unit tests for run_neon + +You can run this by: + python -m unittest test_unit_run_neon.py +""" + +import unittest +import tempfile +import shutil +import os +import sys + +# -- add python/ctsm to path (needed if we want to run the test stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position +from ctsm import unit_testing +from ctsm.site_and_regional.run_neon import check_neon_listing + +# pylint: disable=invalid-name + + +class TestRunNeon(unittest.TestCase): + """ + Basic class for testing run_neon.py. + """ + + def setUp(self): + """ + Make /_tempdir for use by these tests. + """ + self._tempdir = tempfile.mkdtemp() + + def tearDown(self): + """ + Remove temporary directory + """ + shutil.rmtree(self._tempdir, ignore_errors=True) + + def test_check_neon_listing(self): + """ + Test that neon listing is available for valid sites + """ + valid_neon_sites = ["ABBY", "BART"] + previous_dir = os.getcwd() + os.chdir(self._tempdir) # cd to tempdir + available_list = check_neon_listing(valid_neon_sites) + self.assertEqual( + available_list[0].name, "ABBY", "available list of actual sites not as expected" + ) + self.assertEqual( + available_list[1].name, "BART", "available list of actual sites not as expected" + ) + # change to previous dir once listing.csv file is created in tempdir and test complete + os.chdir(previous_dir) + + def test_check_neon_listing_misspelled(self): + """ + Test that neon listing is not available for invalid sites + """ + valid_neon_sites = ["INVALID_SITE1", "INVALID_SITE2"] + previous_dir = os.getcwd() + os.chdir(self._tempdir) # cd to tempdir + available_list = check_neon_listing(valid_neon_sites) + self.assertEqual( + available_list, [], "available list of incorrect dummy site not as expected" + ) + # change to previous dir once listing.csv file is created in tempdir and test complete + os.chdir(previous_dir) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_run_sys_tests.py b/python/ctsm/test/test_unit_run_sys_tests.py index ee5197d76f..65ec1df5a5 100755 --- a/python/ctsm/test/test_unit_run_sys_tests.py +++ b/python/ctsm/test/test_unit_run_sys_tests.py @@ -16,7 +16,7 @@ from ctsm import add_cime_to_path # pylint: disable=unused-import from ctsm import unit_testing -from ctsm.run_sys_tests import run_sys_tests +from ctsm.run_sys_tests import run_sys_tests, _get_testmod_list from ctsm.machine_defaults import MACHINE_DEFAULTS from ctsm.machine import create_machine from ctsm.joblauncher.job_launcher_factory import JOB_LAUNCHER_FAKE @@ -269,6 +269,57 @@ def test_withDryRun_nothingDone(self): self.assertEqual(os.listdir(self._scratch), []) self.assertEqual(machine.job_launcher.get_commands(), []) + def test_getTestmodList_suite(self): + """Ensure that _get_testmod_list() works correctly with suite-style input""" + input = [ + "clm/default", + "clm/default", + "clm/crop", + "clm/cropMonthlyOutput", + ] + target = [ + "clm-default", + "clm-default", + "clm-crop", + "clm-cropMonthlyOutput", + ] + output = _get_testmod_list(input, unique=False) + self.assertEqual(output, target) + + def test_getTestmodList_suite_unique(self): + """Ensure that _get_testmod_list() works correctly with unique=True""" + input = [ + "clm/default", + "clm/default", + "clm/crop", + "clm/cropMonthlyOutput", + ] + target = [ + "clm-default", + "clm-crop", + "clm-cropMonthlyOutput", + ] + + output = _get_testmod_list(input, unique=True) + self.assertEqual(output, target) + + def test_getTestmodList_testname(self): + """Ensure that _get_testmod_list() works correctly with full test name(s) specified""" + input = [ + "ERS_D_Ld15.f45_f45_mg37.I2000Clm50FatesRs.izumi_nag.clm-crop", + "ERS_D_Ld15.f45_f45_mg37.I2000Clm50FatesRs.izumi_nag.clm-default", + ] + target = ["clm-crop", "clm-default"] + output = _get_testmod_list(input) + self.assertEqual(output, target) + + def test_getTestmodList_twomods(self): + """Ensure that _get_testmod_list() works correctly with full test name(s) specified and two mods in one test""" + input = ["ERS_D_Ld15.f45_f45_mg37.I2000Clm50FatesRs.izumi_nag.clm-default--clm-crop"] + target = ["clm-default", "clm-crop"] + output = _get_testmod_list(input) + self.assertEqual(output, target) + if __name__ == "__main__": unit_testing.setup_for_tests() diff --git a/python/ctsm/test/test_unit_singlept_data.py b/python/ctsm/test/test_unit_singlept_data.py index 570207fa26..6fdc3109d9 100755 --- a/python/ctsm/test/test_unit_singlept_data.py +++ b/python/ctsm/test/test_unit_singlept_data.py @@ -36,6 +36,7 @@ class TestSinglePointCase(unittest.TestCase): create_datm = True create_user_mods = True dom_pft = [8] + evenly_split_cropland = False pct_pft = None num_pft = 16 include_nonveg = False @@ -58,6 +59,7 @@ def test_create_tag_noname(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -84,6 +86,7 @@ def test_create_tag_name(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -111,6 +114,7 @@ def test_check_dom_pft_too_big(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -138,6 +142,7 @@ def test_check_dom_pft_too_small(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -165,6 +170,7 @@ def test_check_dom_pft_numpft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -193,6 +199,7 @@ def test_check_dom_pft_mixed_range(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -223,6 +230,7 @@ def test_check_nonveg_nodompft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -254,6 +262,7 @@ def test_check_pct_pft_notsamenumbers(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -284,6 +293,7 @@ def test_check_pct_pft_sum_not1(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -314,6 +324,7 @@ def test_check_pct_pft_fraction_topct(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, diff --git a/python/ctsm/test/test_unit_singlept_data_surfdata.py b/python/ctsm/test/test_unit_singlept_data_surfdata.py index 9623975452..0052e796d1 100755 --- a/python/ctsm/test/test_unit_singlept_data_surfdata.py +++ b/python/ctsm/test/test_unit_singlept_data_surfdata.py @@ -44,6 +44,7 @@ class TestSinglePointCaseSurfaceNoCrop(unittest.TestCase): create_datm = True create_user_mods = True dom_pft = [8] + evenly_split_cropland = False pct_pft = None num_pft = 16 include_nonveg = False @@ -155,6 +156,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_pctnatpft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -187,6 +189,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_pctnatveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -215,6 +218,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_pctcrop(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -243,6 +247,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_glacier(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -272,6 +277,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_wetland(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -301,6 +307,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_lake(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -330,6 +337,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_unisnow(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -360,6 +368,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_capsat(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -390,6 +399,7 @@ def test_modify_surfdata_atpoint_nocrop_multipft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -426,6 +436,7 @@ def test_modify_surfdata_atpoint_nocrop_urban_nononveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -460,6 +471,7 @@ def test_modify_surfdata_atpoint_nocrop_urban_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -497,6 +509,7 @@ def test_modify_surfdata_atpoint_nocrop_wetland_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -527,6 +540,7 @@ def test_modify_surfdata_atpoint_nocrop_nopft_zero_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -560,6 +574,7 @@ def test_modify_surfdata_atpoint_nocrop_nopft_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -594,6 +609,7 @@ class TestSinglePointCaseSurfaceCrop(unittest.TestCase): create_datm = True create_user_mods = True dom_pft = [17] + evenly_split_cropland = False pct_pft = None num_pft = 78 include_nonveg = False @@ -705,6 +721,7 @@ def test_modify_surfdata_atpoint_crop_1pft_pctnatpft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -737,6 +754,7 @@ def test_modify_surfdata_atpoint_crop_1pft_pctnatveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -765,6 +783,7 @@ def test_modify_surfdata_atpoint_crop_1pft_pctcrop(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -793,6 +812,7 @@ def test_modify_surfdata_atpoint_crop_1pft_glacier(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -822,6 +842,7 @@ def test_modify_surfdata_atpoint_crop_1pft_wetland(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -851,6 +872,7 @@ def test_modify_surfdata_atpoint_crop_1pft_lake(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -880,6 +902,7 @@ def test_modify_surfdata_atpoint_crop_1pft_unisnow(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -909,6 +932,7 @@ def test_modify_surfdata_atpoint_crop_1pft_capsat(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -939,6 +963,7 @@ def test_modify_surfdata_atpoint_crop_multipft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -973,6 +998,7 @@ def test_modify_surfdata_atpoint_crop_urban_nononveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -1007,6 +1033,7 @@ def test_modify_surfdata_atpoint_crop_urban_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -1044,6 +1071,7 @@ def test_modify_surfdata_atpoint_crop_lake_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -1074,6 +1102,7 @@ def test_modify_surfdata_atpoint_crop_nopft_zero_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -1107,6 +1136,7 @@ def test_modify_surfdata_atpoint_crop_nopft_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, diff --git a/python/ctsm/test/test_unit_subset_data.py b/python/ctsm/test/test_unit_subset_data.py new file mode 100755 index 0000000000..b8ea5b06f2 --- /dev/null +++ b/python/ctsm/test/test_unit_subset_data.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +""" +Unit tests for subset_data + +You can run this by: + python -m unittest test_unit_subset_data.py +""" + +import unittest +import configparser +import argparse +import os +import sys + +# -- add python/ctsm to path (needed if we want to run the test stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position +from ctsm import unit_testing +from ctsm.subset_data import get_parser, setup_files, check_args +from ctsm.path_utils import path_to_ctsm_root + +# pylint: disable=invalid-name + + +class TestSubsetData(unittest.TestCase): + """ + Basic class for testing SubsetData class in subset_data.py. + """ + + def setUp(self): + sys.argv = ["subset_data", "point", "--create-surface"] + DEFAULTS_FILE = os.path.join(os.getcwd(), "ctsm/test/testinputs/default_data.cfg") + self.parser = get_parser() + self.args = self.parser.parse_args() + self.cesmroot = path_to_ctsm_root() + self.defaults = configparser.ConfigParser() + self.defaults.read(os.path.join(self.cesmroot, "tools/site_and_regional", DEFAULTS_FILE)) + + def test_inputdata_setup_files_basic(self): + """ + Test + """ + check_args(self.args) + files = setup_files(self.args, self.defaults, self.cesmroot) + self.assertEqual( + files["fsurf_in"], + "surfdata_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc", + "fsurf_in filename not whats expected", + ) + self.assertEqual( + files["fsurf_out"], + None, + "fsurf_out filename not whats expected", + ) + self.assertEqual( + files["main_dir"], + "/glade/campaign/cesm/cesmdata/cseg/inputdata", + "main_dir directory not whats expected", + ) + + def test_inputdata_setup_files_inputdata_dne(self): + """ + Test that inputdata directory does not exist + """ + check_args(self.args) + self.defaults.set("main", "clmforcingindir", "/zztop") + with self.assertRaisesRegex(SystemExit, "inputdata directory does not exist"): + setup_files(self.args, self.defaults, self.cesmroot) + + def test_check_args_nooutput(self): + """ + Test that check args aborts when no-output is asked for + """ + sys.argv = ["subset_data", "point"] + self.args = self.parser.parse_args() + with self.assertRaisesRegex(argparse.ArgumentError, "Must supply one of"): + check_args(self.args) + + def test_check_args_notype(self): + """ + Test that check args aborts when no type is asked for + """ + sys.argv = ["subset_data"] + self.args = self.parser.parse_args() + with self.assertRaisesRegex(argparse.ArgumentError, "Must supply a positional argument:"): + check_args(self.args) + + def test_check_args_badconfig(self): + """ + Test that check args aborts when a config file is entered that doesn't exist + """ + sys.argv = ["subset_data", "point", "--create-surface", "--cfg-file", "zztop"] + self.args = self.parser.parse_args() + with self.assertRaisesRegex( + argparse.ArgumentError, "Entered default config file does not exist" + ): + check_args(self.args) + + def test_check_args_outsurfdat_provided(self): + """ + Test that check args allows an output surface dataset to be specified + when create-surface is on + """ + sys.argv = ["subset_data", "point", "--create-surface", "--out-surface", "outputsurface.nc"] + self.args = self.parser.parse_args() + check_args(self.args) + files = setup_files(self.args, self.defaults, self.cesmroot) + self.assertEqual( + files["fsurf_out"], + "outputsurface.nc", + "fsurf_out filename not whats expected", + ) + + def test_check_args_outsurfdat_fails_without_create_surface(self): + """ + Test that check args does not allow an output surface dataset to be specified + when create-surface is not on + """ + sys.argv = ["subset_data", "point", "--create-landuse", "--out-surface", "outputsurface.nc"] + self.args = self.parser.parse_args() + with self.assertRaisesRegex( + argparse.ArgumentError, + "out-surface option is given without the --create-surface option", + ): + check_args(self.args) + + def test_check_args_outsurfdat_fails_without_overwrite(self): + """ + Test that check args does not allow an output surface dataset to be specified + for an existing dataset without the overwrite option + """ + outfile = os.path.join( + os.getcwd(), + "ctsm/test/testinputs/", + "surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206.nc", + ) + self.assertTrue(os.path.exists(outfile), str(outfile) + " outfile should exist") + + sys.argv = ["subset_data", "point", "--create-surface", "--out-surface", outfile] + self.args = self.parser.parse_args() + with self.assertRaisesRegex( + argparse.ArgumentError, + "out-surface filename exists and the overwrite option was not also selected", + ): + check_args(self.args) + + def test_inputdata_setup_files_bad_inputdata_arg(self): + """ + Test that inputdata directory provided on command line does not exist if it's bad + """ + check_args(self.args) + self.args.inputdatadir = "/zztop" + with self.assertRaisesRegex(SystemExit, "inputdata directory does not exist"): + setup_files(self.args, self.defaults, self.cesmroot) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_utils_add_tag.py b/python/ctsm/test/test_unit_utils_add_tag.py new file mode 100755 index 0000000000..bef69f6154 --- /dev/null +++ b/python/ctsm/test/test_unit_utils_add_tag.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 + +"""Unit tests for add_tag_to_filename +""" + +import unittest + +from unittest.mock import patch +from datetime import date +from ctsm import unit_testing + +from ctsm import utils + +# Allow names that pylint doesn't like, because otherwise I find it hard +# to make readable unit test names +# pylint: disable=invalid-name + + +class TestUtilsAddTag(unittest.TestCase): + """Tests of utils: add_tag_to_filename""" + + @staticmethod + def _fake_today(): + """Set the fake date to Halloween""" + return date(year=2022, month=10, day=31) + + def testSimple(self): + """Simple test of surface dataset name""" + + fsurf_in = "surfdata_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr2000_c221105.nc" + with patch("ctsm.utils.date") as mock_date: + mock_date.today.side_effect = self._fake_today + + fsurf_out = utils.add_tag_to_filename(fsurf_in, "tag") + fsurf_out2 = utils.add_tag_to_filename(fsurf_in, "tag", replace_res=True) + + expect_fsurf = "surfdata_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr2000_tag_c221031.nc" + self.assertEqual(expect_fsurf, fsurf_out, "Expect filenames to be as expected") + expect_fsurf2 = "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc" + self.assertEqual(expect_fsurf2, fsurf_out2, "Expect filenames to be as expected") + + def testSimpleLanduse(self): + """Simple test of landuse dataset name""" + + landuse_in = "landuse.timeseries_0.9x1.25_hist_78pfts_CMIP6_simyr1850-2015_c190214.nc" + with patch("ctsm.utils.date") as mock_date: + mock_date.today.side_effect = self._fake_today + + landuse_out = utils.add_tag_to_filename(landuse_in, "tag") + landuse_out2 = utils.add_tag_to_filename(landuse_in, "tag", replace_res=True) + + expect_landuse = ( + "landuse.timeseries_0.9x1.25_hist_78pfts_CMIP6_simyr1850-2015_tag_c221031.nc" + ) + self.assertEqual(expect_landuse, landuse_out, "Expect filenames to be as expected") + expect_landuse2 = "landuse.timeseries_tag_hist_78pfts_CMIP6_simyr1850-2015_c221031.nc" + self.assertEqual(expect_landuse2, landuse_out2, "Expect filenames to be as expected") + + def testSimpleDatmDomain(self): + """Simple test of datm domain dataset name""" + + file_in = "domain.lnd.360x720_gswp3.0v1.c170606.nc" + with patch("ctsm.utils.date") as mock_date: + mock_date.today.side_effect = self._fake_today + + file_out = utils.add_tag_to_filename(file_in, "tag") + + expect_filename = "domain.lnd.360x720_gswp3.0v1_tag_c221031.nc" + self.assertEqual(expect_filename, file_out, "Expect filenames to be as expected") + + def testSimpleDomain(self): + """Simple test of domain dataset name""" + + file_in = "domain.lnd.fv0.9x1.25_gx1v7.151020.nc" + with patch("ctsm.utils.date") as mock_date: + mock_date.today.side_effect = self._fake_today + + file_out = utils.add_tag_to_filename(file_in, "tag") + + expect_filename = "domain.lnd.fv0.9x1.25_gx1v7_tag_c221031.nc" + self.assertEqual(expect_filename, file_out, "Expect filenames to be as expected") + + def testSurfReplaceListDomain(self): + """Simple test of list of surface dataset name with replace_res option""" + + files_in = [ + "surfdata_48x96_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc", + "surfdata_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc", + "surfdata_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc", + "surfdata_1.9x2.5_hist_16pfts_Irrig_CMIP6_simyr2000_c190304.nc", + "surfdata_1.9x2.5_hist_16pfts_Irrig_CMIP6_simyr2000_c190304.nc", + "surfdata_4x5_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc", + "surfdata_10x15_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc", + "surfdata_10x15_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc", + "surfdata_0.125nldas2_hist_16pfts_Irrig_CMIP6_simyr2005_c190412.nc", + "surfdata_64x128_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc", + "surfdata_0.9x1.25_hist_78pfts_CMIP6_simyr2000_c190214.nc", + "surfdata_1.9x2.5_hist_78pfts_CMIP6_simyr2000_c190304.nc", + "surfdata_0.125x0.125_hist_78pfts_CMIP6_simyr2005_c190624.nc", + "surfdata_10x15_hist_78pfts_CMIP6_simyr2000_c190214.nc", + "surfdata_4x5_hist_78pfts_CMIP6_simyr2000_c190214.nc", + "surfdata_1.9x2.5_hist_16pfts_Irrig_CMIP6_simyr1850_c190304.nc", + "surfdata_10x15_hist_16pfts_Irrig_CMIP6_simyr1850_c190214.nc", + "surfdata_4x5_hist_16pfts_Irrig_CMIP6_simyr1850_c190214.nc", + "surfdata_48x96_hist_78pfts_CMIP6_simyr1850_c190214.nc", + "surfdata_0.9x1.25_hist_78pfts_CMIP6_simyr1850_c190214.nc", + "surfdata_1.9x2.5_hist_78pfts_CMIP6_simyr1850_c190304.nc", + "surfdata_10x15_hist_78pfts_CMIP6_simyr1850_c190214.nc", + "surfdata_4x5_hist_78pfts_CMIP6_simyr1850_c190214.nc", + "surfdata_ne0np4.ARCTICGRIS.ne30x8_hist_78pfts_CMIP6_simyr2000_c200426.nc", + "surfdata_C96_hist_78pfts_CMIP6_simyr1850_c200317.nc", + "surfdata_C96_hist_78pfts_CMIP6_simyr1850_c20221108.nc", + "surfdata_0.9x1.25_hist_16pfts_nourb_CMIP6_simyrPtVg_c181114.nc", + ] + expect_filenames = [ + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2005_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr2005_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_16pfts_Irrig_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr2000_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_78pfts_CMIP6_simyr1850_c221031.nc", + "surfdata_tag_hist_16pfts_nourb_CMIP6_simyrPtVg_c221031.nc", + ] + self.assertEqual( + len(files_in), len(expect_filenames), "length of arrays does not match as expected" + ) + for i, file_in in enumerate(files_in): + + with patch("ctsm.utils.date") as mock_date: + mock_date.today.side_effect = self._fake_today + + file_out = utils.add_tag_to_filename(file_in, "tag", replace_res=True) + + self.assertEqual(expect_filenames[i], file_out, "Expect filenames to be as expected") + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_utils_import_coord.py b/python/ctsm/test/test_unit_utils_import_coord.py new file mode 100755 index 0000000000..b7ec8f90ec --- /dev/null +++ b/python/ctsm/test/test_unit_utils_import_coord.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +""" +Unit tests for utils.py functions related to importing coordinate variables +""" + +import unittest +import os +import sys +import shutil + +import tempfile +import xarray as xr +import numpy as np + +# -- add python/ctsm to path (needed if we want to run test stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm import unit_testing +from ctsm.path_utils import path_to_ctsm_root +from ctsm.ctsm_pylib_dependent_utils import import_coord_1d, import_coord_2d + +# Allow test names that pylint doesn't like; otherwise hard to make them +# readable +# pylint: disable=invalid-name + +# pylint: disable=protected-access + + +# Allow as many public methods as needed... +# pylint: disable=too-many-public-methods +# Allow all the instance attributes that we need +# pylint: disable=too-many-instance-attributes +class TestUtilsImportCoord(unittest.TestCase): + # Tests the importcoord* subroutines from utils.py + + def setUp(self): + """Setup for trying out the methods""" + testinputs_path = os.path.join(path_to_ctsm_root(), "python/ctsm/test/testinputs") + self._testinputs_path = testinputs_path + self._tempdir = tempfile.mkdtemp() + + self._1d_lonlat_file = os.path.join( + self._testinputs_path, "cropcals", "swh_rf_ggcmi_crop_calendar_phase3_v1.01.nc4" + ) + self._2d_lonlat_file = os.path.join( + self._testinputs_path, + "surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214_modified.nc", + ) + + def tearDown(self): + """ + Remove temporary directory + """ + shutil.rmtree(self._tempdir, ignore_errors=True) + + def test_importcoord1d(self): + ds = xr.open_dataset(self._1d_lonlat_file) + lat, Nlat = import_coord_1d(ds, "lat") + np.testing.assert_equal(Nlat, 360) + np.testing.assert_array_equal(lat.values[:4], [89.75, 89.25, 88.75, 88.25]) + np.testing.assert_array_equal(lat.values[-4:], [-88.25, -88.75, -89.25, -89.75]) + + def test_importcoord1d_attrs(self): + ds = xr.open_dataset(self._1d_lonlat_file) + lat, _ = import_coord_1d(ds, "lat") + # Unlike import_coord_2d, import_coord_1d doesn't rename the long name. + expected_attributes = { + "long_name": ds["lat"].attrs["long_name"], + "units": "degrees_north", + } + self.assertDictEqual(lat.attrs, expected_attributes) + + def test_importcoord1d_too_many_dims(self): + ds = xr.open_dataset(self._2d_lonlat_file) + with self.assertRaisesRegex( + SystemExit, + "Expected 1 dimension for LATIXY; found 2: \('lsmlat', 'lsmlon'\)", + ): + import_coord_1d(ds, "LATIXY") + + def test_importcoord2d(self): + ds = xr.open_dataset(self._2d_lonlat_file) + lat, _ = import_coord_2d(ds, "lat", "LATIXY") + expected_values = np.array([-13.9, -11.7, -9.5, -7.3, -5.1]).astype(np.float32) + np.testing.assert_array_equal(lat.values, expected_values) + + def test_importcoord2d_attrs(self): + ds = xr.open_dataset(self._2d_lonlat_file) + lat, _ = import_coord_2d(ds, "lat", "LATIXY") + expected_attributes = { + "long_name": "coordinate latitude", + "units": "degrees_north", + } + self.assertDictEqual(lat.attrs, expected_attributes) + + def test_importcoord2d_rename_dim(self): + ds = xr.open_dataset(self._2d_lonlat_file) + lat, _ = import_coord_2d(ds, "lat", "LATIXY") + self.assertTupleEqual(lat.dims, ("lat",)) + + def test_importcoord2d_no_dim_contains_coordName(self): + ds = xr.open_dataset(self._2d_lonlat_file) + ds = ds.rename({"lsmlat": "abc"}) + with self.assertRaisesRegex( + SystemExit, + "ERROR: Expected 1 dimension name containing lat; found 0: \[\]", + ): + import_coord_2d(ds, "lat", "LATIXY") + + def test_importcoord2d_1_dim_containing(self): + ds = xr.open_dataset(self._2d_lonlat_file) + ds = ds.rename({"lsmlon": "lsmlat2"}) + with self.assertRaisesRegex( + SystemExit, + "Expected 1 dimension name containing lat; found 2: \['lsmlat', 'lsmlat2'\]", + ): + import_coord_2d(ds, "lat", "LATIXY") + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/testinputs/README.md b/python/ctsm/test/testinputs/README.md index 45451b53e1..ef8953d20e 100644 --- a/python/ctsm/test/testinputs/README.md +++ b/python/ctsm/test/testinputs/README.md @@ -6,7 +6,8 @@ Installing Git LFS on your machine is a two-step process; step (1) needs to be done once per machine, and step (2) needs to be done once per user: 1. Install the Git LFS tool: Follow the instructions on the [Git LFS page](https://git-lfs.github.com/) for installing Git LFS on your platform. - - On cheyenne, Git LFS is already available as long as you are using a git + - On derecho the system default version of git already has Git LFS installed. + - On cheyenne and casper, Git LFS is already available as long as you are using a git module rather than the default system-level git. So just make sure that you are always using git via a git module (`module load git`). - On a Mac using homebrew, this can be done with `brew install git-lfs`. diff --git a/python/ctsm/test/testinputs/cropcals/swh_rf_ggcmi_crop_calendar_phase3_v1.01.nc4 b/python/ctsm/test/testinputs/cropcals/swh_rf_ggcmi_crop_calendar_phase3_v1.01.nc4 new file mode 100644 index 0000000000..985210eaea Binary files /dev/null and b/python/ctsm/test/testinputs/cropcals/swh_rf_ggcmi_crop_calendar_phase3_v1.01.nc4 differ diff --git a/python/ctsm/test/testinputs/default_data.cfg b/python/ctsm/test/testinputs/default_data.cfg new file mode 100644 index 0000000000..0425aba133 --- /dev/null +++ b/python/ctsm/test/testinputs/default_data.cfg @@ -0,0 +1,28 @@ +[main] +clmforcingindir = /glade/campaign/cesm/cesmdata/cseg/inputdata + +[datm_gswp3] +dir = atm/datm7/atm_forcing.datm7.GSWP3.0.5d.v1.c170516 +domain = domain.lnd.360x720_gswp3.0v1.c170606.nc +solardir = Solar +precdir = Precip +tpqwdir = TPHWL +solartag = clmforc.GSWP3.c2011.0.5x0.5.Solr. +prectag = clmforc.GSWP3.c2011.0.5x0.5.Prec. +tpqwtag = clmforc.GSWP3.c2011.0.5x0.5.TPQWL. +solarname = CLMGSWP3v1.Solar +precname = CLMGSWP3v1.Precip +tpqwname = CLMGSWP3v1.TPQW + +[surfdat] +dir = lnd/clm2/surfdata_map/release-clm5.0.18 +surfdat_16pft = surfdata_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc +surfdat_78pft = surfdata_0.9x1.25_hist_78pfts_CMIP6_simyr2000_c190214.nc + +[landuse] +dir = lnd/clm2/surfdata_map/release-clm5.0.18 +landuse_16pft = landuse.timeseries_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr1850-2015_c190214.nc +landuse_78pft = landuse.timeseries_0.9x1.25_hist_78pfts_CMIP6_simyr1850-2015_c190214.nc + +[domain] +file = share/domains/domain.lnd.fv0.9x1.25_gx1v7.151020.nc diff --git a/python/ctsm/test/testinputs/modify_fsurdat_1x1mexicocity.cfg b/python/ctsm/test/testinputs/modify_fsurdat_1x1mexicocity.cfg new file mode 100644 index 0000000000..0d8a751f32 --- /dev/null +++ b/python/ctsm/test/testinputs/modify_fsurdat_1x1mexicocity.cfg @@ -0,0 +1,85 @@ +[modify_fsurdat_basic_options] + +idealized = False +process_subgrid_section = True +process_var_list_section = True +include_nonveg = True + +landmask_file = UNSET + +lat_dimname = lsmlat +lon_dimname = lsmlon + +dom_pft = UNSET +evenly_split_cropland = False + +lai = UNSET +sai = UNSET +hgt_top = UNSET +hgt_bot = UNSET +soil_color = UNSET +std_elev = UNSET +max_sat_area = UNSET + +lnd_lat_1 = -90 +lnd_lat_2 = 90 +lnd_lon_1 = 0 +lnd_lon_2 = 360 + +# Section for subgrid_fractions +[modify_fsurdat_subgrid_fractions] +# If subgrid_fractions = True this section will be enabled + +# NOTE: PCT_URBAN must be a list of three floats that sum to the total urban area +PCT_URBAN = 100.0 0.0 0.0 +PCT_CROP = 0.0 +PCT_NATVEG= 0.0 +PCT_GLACIER= 0.0 +PCT_WETLAND= 0.0 +PCT_LAKE = 0.0 + +# Section with a list of variables to prcoess +[modify_fsurdat_variable_list] +# If variable_list = True this section will be enabled +# Can't specify PFT as they are in dom_pft +# Add variables on the file and assign a new value +# can't specify soil_color, max_sat_area + +# Variables on numurbl which is 3 +CANYON_HWR = 1.18 1.18 1.18 +EM_IMPROAD = 0.95 0.95 0.95 +EM_PERROAD = 0.95 0.95 0.95 +EM_ROOF = 0.9 0.9 0.9 +EM_WALL = 0.85 0.85 0.85 +HT_ROOF = 18.8 18.8 18.8 +THICK_ROOF = 0.185 0.185 0.185 +THICK_WALL = 0.45 0.45 0.45 +T_BUILDING_MIN = 200.0 200.0 200.0 +WIND_HGT_CANYON = 9.4 9.4 9.4 +WTLUNIT_ROOF = 0.55 0.55 0.55 +WTROAD_PERV = 0.04 0.04 0.04 +# NOTE: This variable is integer rather than float +NLEV_IMPROAD = 5 5 5 + +# Variables on numrad which is 2 +ALB_IMPROAD_DIR = 0.08 0.08 +ALB_IMPROAD_DIF = 0.08 0.08 +ALB_PERROAD_DIR = 0.08 0.08 +ALB_PERROAD_DIF = 0.08 0.08 +ALB_ROOF_DIR = 0.2 0.2 +ALB_ROOF_DIF = 0.2 0.2 +ALB_WALL_DIR = 0.25 0.25 +ALB_WALL_DIF = 0.25 0.25 + +# Variabls on nlevurb which is 5 +TK_ROOF = 0.20 0.93 0.93 0.03 0.16 +TK_WALL = 0.88 0.88 0.88 0.88 0.88 +TK_IMPROAD = 0.82 0.82 2.10 2.10 2.10 +CV_ROOF = 1760000.0 1500000.0 1500000.0 250000.0 870000.0 +CV_WALL = 1540000.0 1540000.0 1540000.0 1540000.0 1540000.0 +CV_IMPROAD = 1740000.0 1740000.0 2000000.0 2000000.0 2000000.0 + +# Natural and Crop PFT's don't really need to be set, since they have zero area, but +# it looks better to do so +PCT_NAT_PFT = 100. 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 +PCT_CFT = 100. 0.0 diff --git a/python/ctsm/test/testinputs/modify_fsurdat_opt_sections.cfg b/python/ctsm/test/testinputs/modify_fsurdat_opt_sections.cfg new file mode 100644 index 0000000000..b1fcf8a2e1 --- /dev/null +++ b/python/ctsm/test/testinputs/modify_fsurdat_opt_sections.cfg @@ -0,0 +1,52 @@ +[modify_fsurdat_basic_options] + +idealized = False +process_subgrid_section = True +process_var_list_section = True +include_nonveg = True + +landmask_file = UNSET + +lat_dimname = lsmlat +lon_dimname = lsmlon + +dom_pft = UNSET +evenly_split_cropland = False + +lai = UNSET +sai = UNSET +hgt_top = UNSET +hgt_bot = UNSET +soil_color = UNSET +std_elev = UNSET +max_sat_area = UNSET + +lnd_lat_1 = -90 +lnd_lat_2 = 90 +lnd_lon_1 = 0 +lnd_lon_2 = 360 + +# Section for subgrid_fractions +[modify_fsurdat_subgrid_fractions] +# Set to 100% urban for the test +PCT_NATVEG = 0.0 +PCT_CROP = 0.0 +PCT_LAKE = 0.0 +PCT_GLACIER = 0.0 +PCT_WETLAND = 0.0 +# NOTE: PCT_URBAN must be a list of three floats that sum to the total urban area +PCT_URBAN = 100.0 0.0 0.0 + + +# Section with a list of variables to prcoess +[modify_fsurdat_variable_list] +# Set lake-depth to 200 for the test +LAKEDEPTH = 200.00 + +# Set soem urban multidimensional variables to 200 for the test +# If given as a single value set all to the value, if as list set by dimension and if array as an array +CANYON_HWR = 200.00 150.0 100. +HT_ROOF = 200.0 150.0 100. +T_BUILDING_MIN = 200 150.0 100. +ALB_ROOF_DIR = 200. 100. +TK_ROOF = 1. 2. 3. 4. 5. diff --git a/python/ctsm/test/testinputs/modify_fsurdat_short.cfg b/python/ctsm/test/testinputs/modify_fsurdat_short.cfg new file mode 100644 index 0000000000..38b88795e8 --- /dev/null +++ b/python/ctsm/test/testinputs/modify_fsurdat_short.cfg @@ -0,0 +1,30 @@ +[modify_fsurdat_basic_options] + +fsurdat_in = ctsm/test/testinputs/surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc +fsurdat_out = ctsm/test/testinputs/surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214_out.nc + +idealized = False +process_subgrid_section = False +process_var_list_section = False +include_nonveg = False + +landmask_file = UNSET + +lat_dimname = lsmlat +lon_dimname = lsmlon + +dom_pft = UNSET +evenly_split_cropland = False + +lai = UNSET +sai = UNSET +hgt_top = UNSET +hgt_bot = UNSET +soil_color = UNSET +std_elev = UNSET +max_sat_area = UNSET + +lnd_lat_1 = -90 +lnd_lat_2 = 90 +lnd_lon_1 = 0 +lnd_lon_2 = 360 diff --git a/python/ctsm/test/testinputs/modify_fsurdat_short_nofiles.cfg b/python/ctsm/test/testinputs/modify_fsurdat_short_nofiles.cfg new file mode 100644 index 0000000000..3e5aada24b --- /dev/null +++ b/python/ctsm/test/testinputs/modify_fsurdat_short_nofiles.cfg @@ -0,0 +1,11 @@ +[modify_fsurdat_basic_options] + +idealized = False +process_subgrid_section = False +process_var_list_section = False +include_nonveg = False + +lnd_lat_1 = -90 +lnd_lat_2 = 90 +lnd_lon_1 = 0 +lnd_lon_2 = 360 diff --git a/python/ctsm/test/testinputs/surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206.nc b/python/ctsm/test/testinputs/surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206.nc new file mode 100644 index 0000000000..4bf009ebe6 --- /dev/null +++ b/python/ctsm/test/testinputs/surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef672e3ab2c237fd6f76afdd1dfa7d01263c01bd94bef2a8f37acfba3a9b6e36 +size 27300 diff --git a/python/ctsm/test/testinputs/surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206_modified.nc b/python/ctsm/test/testinputs/surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206_modified.nc new file mode 100644 index 0000000000..80a66c286f --- /dev/null +++ b/python/ctsm/test/testinputs/surfdata_1x1_mexicocityMEX_hist_16pfts_Irrig_CMIP6_simyr2000_c221206_modified.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cdfc14c3f7e9efa4f0acc4caca22b616e09e04b2713ab1424d695492ac52327c +size 27772 diff --git a/python/ctsm/utils.py b/python/ctsm/utils.py index 2851a3b619..8578ea860c 100644 --- a/python/ctsm/utils.py +++ b/python/ctsm/utils.py @@ -4,9 +4,10 @@ import os import sys import string +import re import pdb -from datetime import date +from datetime import date, timedelta from getpass import getuser from ctsm.git_utils import get_ctsm_git_short_hash @@ -43,21 +44,29 @@ def fill_template_file(path_to_template, path_to_final, substitutions): final_file.write(final_file_contents) -def add_tag_to_filename(filename, tag): +def add_tag_to_filename(filename, tag, replace_res=False): """ Add a tag and replace timetag of a filename Expects file to end with [._]cYYMMDD.nc or [._]YYMMDD.nc + or with 4-digit years YYYYMMDD. Add the tag to just before that ending part and change the ending part to the current time tag. + if replace_res is True, then replace the resolution + part of the filename. Expects the file to start with + [a-z.]_ and then the resolution. + Parameters ---------- filename (str) : file name tag (str) : string of a tag to be added to the end of filename + (or to replace the resolution part of the filename) Raises ------ Error: When it cannot find . and _ in the filename. + Error: When it's asked to replace the resolution and + can't figure out where that is in the filename. Returns ------ @@ -69,11 +78,27 @@ def add_tag_to_filename(filename, tag): if basename[cend] == "c": cend = cend - 1 if (basename[cend] != ".") and (basename[cend] != "_"): - err_msg = "Trouble figuring out where to add tag to filename: " + filename - abort(err_msg) + # Check if date stirng at end includes a 4 digit year + cend = -12 + if basename[cend] == "c": + cend = cend - 1 + if (basename[cend] != ".") and (basename[cend] != "_"): + err_msg = "Trouble figuring out where to add tag to filename: " + filename + abort(err_msg) today = date.today() today_string = today.strftime("%y%m%d") - fname_out = basename[:cend] + "_" + tag + "_c" + today_string + ".nc" + if not replace_res: + fname_out = basename[:cend] + "_" + tag + "_c" + today_string + ".nc" + else: + match = re.fullmatch(r"([a-z.]+)_([Cfvnenp0-9x.crunldasA-Z]+)_(.+?)", basename[:cend]) + if match is not None: + fname_out = ( + match.group(1) + "_" + tag + "_" + match.group(3) + "_c" + today_string + ".nc" + ) + else: + abort( + "Trouble figuring out where to replace the resolution in the filename: " + filename + ) return fname_out @@ -164,3 +189,33 @@ def write_output(file, file_in, file_out, file_type): file.to_netcdf(path=file_out, mode="w", format="NETCDF3_64BIT") logger.info("Successfully created: %s", file_out) file.close() + + +def get_isosplit(iso_string, split): + """ + Split a string (iso_string) by the character sent in from split + Returns the number for that character split + Only used by parse_isoduration + """ + if split in iso_string: + num, iso_string = iso_string.split(split) + else: + num = 0 + return num, iso_string + + +def parse_isoduration(iso_string): + """ + simple ISO 8601 duration parser, does not account for leap years and assumes 30 day months + """ + # Remove prefix + iso_string = iso_string.split("P")[-1] + + # Step through letter dividers + years, iso_string = get_isosplit(iso_string, "Y") + months, iso_string = get_isosplit(iso_string, "M") + days, iso_string = get_isosplit(iso_string, "D") + + # Convert all to timedelta + delta_t = timedelta(days=int(days) + 365 * int(years) + 30 * int(months)) + return int(delta_t.total_seconds() / 86400) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 395c2e4868..27d85d464f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,7 +27,7 @@ set (drv_sources_needed_base ) extract_sources("${drv_sources_needed_base}" "${drv_sources}" drv_sources_needed) -# Add CLM source directories (these add their own test directories) +# Add CLM source directories add_subdirectory(${CLM_ROOT}/src/utils clm_utils) add_subdirectory(${CLM_ROOT}/src/biogeochem clm_biogeochem) add_subdirectory(${CLM_ROOT}/src/soilbiogeochem clm_soilbiogeochem) diff --git a/src/biogeochem/CNAllocationMod.F90 b/src/biogeochem/CNAllocationMod.F90 index ff8131c9f7..8d38ca4b87 100644 --- a/src/biogeochem/CNAllocationMod.F90 +++ b/src/biogeochem/CNAllocationMod.F90 @@ -398,7 +398,9 @@ subroutine calc_crop_allocation_fractions(bounds, num_pcropp, filter_pcropp, & ! allocation coefficients should be irrelevant because crops have no ! live carbon pools aleaf(p) = 1._r8 + aleafi(p) = 1._r8 astem(p) = 0._r8 + astemi(p) = 0._r8 aroot(p) = 0._r8 do k = 1, nrepr arepr(p,k) = 0._r8 @@ -413,7 +415,9 @@ subroutine calc_crop_allocation_fractions(bounds, num_pcropp, filter_pcropp, & ! allocation coefficients should be irrelevant because crops have no ! live carbon pools aleaf(p) = 1._r8 + aleafi(p) = 1._r8 astem(p) = 0._r8 + astemi(p) = 0._r8 aroot(p) = 0._r8 do k = 1, nrepr arepr(p,k) = 0._r8 diff --git a/src/biogeochem/CNAnnualUpdateMod.F90 b/src/biogeochem/CNAnnualUpdateMod.F90 index 682898259a..956042db63 100644 --- a/src/biogeochem/CNAnnualUpdateMod.F90 +++ b/src/biogeochem/CNAnnualUpdateMod.F90 @@ -10,6 +10,7 @@ module CNAnnualUpdateMod use CNvegStateType , only : cnveg_state_type use PatchType , only : patch use filterColMod , only : filter_col_type, col_filter_from_filter_and_logical_array + use ColumnType , only : col ! implicit none private @@ -21,7 +22,7 @@ module CNAnnualUpdateMod contains !----------------------------------------------------------------------- - subroutine CNAnnualUpdate(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & + subroutine CNAnnualUpdate(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & cnveg_state_inst, cnveg_carbonflux_inst) ! ! !DESCRIPTION: @@ -34,10 +35,10 @@ subroutine CNAnnualUpdate(bounds, num_soilc, filter_soilc, num_soilp, filter_soi ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of bgc soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for bgc soil columns + integer , intent(in) :: num_bgc_vegp ! number of bgc veg patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc veg patches type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst ! @@ -53,22 +54,25 @@ subroutine CNAnnualUpdate(bounds, num_soilc, filter_soilc, num_soilp, filter_soi dt = get_step_size_real() secspyear = get_curr_days_per_year() * secspday - do fc = 1,num_soilc - c = filter_soilc(fc) - cnveg_state_inst%annsum_counter_col(c) = cnveg_state_inst%annsum_counter_col(c) + dt - if (cnveg_state_inst%annsum_counter_col(c) >= secspyear) then - end_of_year(c) = .true. - cnveg_state_inst%annsum_counter_col(c) = 0._r8 - else - end_of_year(c) = .false. + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) + if(.not.col%is_fates(c))then + cnveg_state_inst%annsum_counter_col(c) = cnveg_state_inst%annsum_counter_col(c) + dt + if (cnveg_state_inst%annsum_counter_col(c) >= secspyear) then + end_of_year(c) = .true. + cnveg_state_inst%annsum_counter_col(c) = 0._r8 + else + end_of_year(c) = .false. + end if end if end do + - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) c = patch%column(p) - if (end_of_year(c)) then + if (end_of_year(c) .and. .not.col%is_fates(c)) then ! update annual plant ndemand accumulator cnveg_state_inst%annsum_potential_gpp_patch(p) = cnveg_state_inst%tempsum_potential_gpp_patch(p) @@ -94,20 +98,22 @@ subroutine CNAnnualUpdate(bounds, num_soilc, filter_soilc, num_soilp, filter_soi end do ! Get column-level averages, just for the columns that have reached their personal end-of-year - filter_endofyear_c = col_filter_from_filter_and_logical_array( & - bounds = bounds, & - num_orig = num_soilc, & - filter_orig = filter_soilc, & - logical_col = end_of_year(bounds%begc:bounds%endc)) - - call p2c(bounds, filter_endofyear_c%num, filter_endofyear_c%indices, & - cnveg_carbonflux_inst%annsum_npp_patch(bounds%begp:bounds%endp), & - cnveg_carbonflux_inst%annsum_npp_col(bounds%begc:bounds%endc)) - - call p2c(bounds, filter_endofyear_c%num, filter_endofyear_c%indices, & - cnveg_state_inst%annavg_t2m_patch(bounds%begp:bounds%endp), & - cnveg_state_inst%annavg_t2m_col(bounds%begc:bounds%endc)) - + if(num_bgc_vegp>0)then + filter_endofyear_c = col_filter_from_filter_and_logical_array( & + bounds = bounds, & + num_orig = num_bgc_soilc, & + filter_orig = filter_bgc_soilc, & + logical_col = end_of_year(bounds%begc:bounds%endc)) + + call p2c(bounds, filter_endofyear_c%num, filter_endofyear_c%indices, & + cnveg_carbonflux_inst%annsum_npp_patch(bounds%begp:bounds%endp), & + cnveg_carbonflux_inst%annsum_npp_col(bounds%begc:bounds%endc)) + + call p2c(bounds, filter_endofyear_c%num, filter_endofyear_c%indices, & + cnveg_state_inst%annavg_t2m_patch(bounds%begp:bounds%endp), & + cnveg_state_inst%annavg_t2m_col(bounds%begc:bounds%endc)) + end if + end subroutine CNAnnualUpdate end module CNAnnualUpdateMod diff --git a/src/biogeochem/CNBalanceCheckMod.F90 b/src/biogeochem/CNBalanceCheckMod.F90 index b038536a5c..8801efdf72 100644 --- a/src/biogeochem/CNBalanceCheckMod.F90 +++ b/src/biogeochem/CNBalanceCheckMod.F90 @@ -10,19 +10,23 @@ module CNBalanceCheckMod use shr_log_mod , only : errMsg => shr_log_errMsg use decompMod , only : bounds_type, subgrid_level_gridcell, subgrid_level_column use abortutils , only : endrun - use clm_varctl , only : iulog, use_nitrif_denitrif + use clm_varctl , only : iulog, use_nitrif_denitrif, use_fates_bgc use clm_time_manager , only : get_step_size_real use CNVegNitrogenFluxType , only : cnveg_nitrogenflux_type use CNVegNitrogenStateType , only : cnveg_nitrogenstate_type use CNVegCarbonFluxType , only : cnveg_carbonflux_type use CNVegCarbonStateType , only : cnveg_carbonstate_type + use SoilBiogeochemCarbonStateType , only : soilbiogeochem_carbonstate_type + use SoilBiogeochemNitrogenStateType , only : soilbiogeochem_nitrogenstate_type use SoilBiogeochemNitrogenfluxType , only : soilbiogeochem_nitrogenflux_type use SoilBiogeochemCarbonfluxType , only : soilbiogeochem_carbonflux_type use CNProductsMod , only : cn_products_type use ColumnType , only : col use GridcellType , only : grc use CNSharedParamsMod , only : use_fun - + use CLMFatesInterfaceMod , only : hlm_fates_interface_type + use clm_varpar , only : nlevdecomp + ! implicit none private @@ -100,7 +104,7 @@ end subroutine InitAllocate !----------------------------------------------------------------------- subroutine BeginCNGridcellBalance(this, bounds, cnveg_carbonflux_inst, & - cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & + soilbiogeochem_carbonstate_inst, soilbiogeochem_nitrogenstate_inst, & c_products_inst, n_products_inst) ! ! !DESCRIPTION: @@ -113,26 +117,27 @@ subroutine BeginCNGridcellBalance(this, bounds, cnveg_carbonflux_inst, & ! !USES: ! ! !ARGUMENTS: - class(cn_balance_type) , intent(inout) :: this - type(bounds_type) , intent(in) :: bounds - type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst - type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst - type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst - type(cn_products_type) , intent(in) :: c_products_inst - type(cn_products_type) , intent(in) :: n_products_inst + class(cn_balance_type) , intent(inout) :: this + type(bounds_type) , intent(in) :: bounds + type(soilbiogeochem_carbonstate_type), intent(in) :: soilbiogeochem_carbonstate_inst + type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst + type(soilbiogeochem_nitrogenstate_type) , intent(in) :: soilbiogeochem_nitrogenstate_inst + type(cn_products_type) , intent(in) :: c_products_inst + type(cn_products_type) , intent(in) :: n_products_inst ! ! !LOCAL VARIABLES: integer :: g integer :: begg, endg real(r8) :: hrv_xsmrpool_amount_left_to_dribble(bounds%begg:bounds%endg) + real(r8) :: gru_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg) real(r8) :: dwt_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg) !----------------------------------------------------------------------- associate( & begcb => this%begcb_grc , & ! Output: [real(r8) (:)] (gC/m2) gridcell carbon mass, beginning of time step begnb => this%begnb_grc , & ! Output: [real(r8) (:)] (gN/m2) gridcell nitrogen mass, beginning of time step - totc => cnveg_carbonstate_inst%totc_grc , & ! Input: [real(r8) (:)] (gC/m2) total gridcell carbon, incl veg and cpool - totn => cnveg_nitrogenstate_inst%totn_grc, & ! Input: [real(r8) (:)] (gN/m2) total gridcell nitrogen, incl veg + totc => soilbiogeochem_carbonstate_inst%totc_grc , & ! Input: [real(r8) (:)] (gC/m2) total gridcell carbon, incl veg and cpool + totn => soilbiogeochem_nitrogenstate_inst%totn_grc, & ! Input: [real(r8) (:)] (gN/m2) total gridcell nitrogen, incl veg c_cropprod1 => c_products_inst%cropprod1_grc , & ! Input: [real(r8) (:)] (gC/m2) carbon in crop products n_cropprod1 => n_products_inst%cropprod1_grc , & ! Input: [real(r8) (:)] (gC/m2) nitrogen in crop products c_tot_woodprod => c_products_inst%tot_woodprod_grc , & ! Input: [real(r8) (:)] (gC/m2) total carbon in wood products @@ -140,15 +145,24 @@ subroutine BeginCNGridcellBalance(this, bounds, cnveg_carbonflux_inst, & ) begg = bounds%begg; endg = bounds%endg - - call cnveg_carbonflux_inst%hrv_xsmrpool_to_atm_dribbler%get_amount_left_to_dribble_beg( & + + if(.not.use_fates_bgc)then + call cnveg_carbonflux_inst%hrv_xsmrpool_to_atm_dribbler%get_amount_left_to_dribble_beg( & bounds, hrv_xsmrpool_amount_left_to_dribble(bounds%begg:bounds%endg)) - call cnveg_carbonflux_inst%dwt_conv_cflux_dribbler%get_amount_left_to_dribble_beg( & - bounds, dwt_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg)) + call cnveg_carbonflux_inst%dwt_conv_cflux_dribbler%get_amount_left_to_dribble_beg( & + bounds, dwt_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg)) + call cnveg_carbonflux_inst%gru_conv_cflux_dribbler%get_amount_left_to_dribble_beg( & + bounds, gru_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg)) + else + hrv_xsmrpool_amount_left_to_dribble(bounds%begg:bounds%endg) = 0._r8 + dwt_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg) = 0._r8 + gru_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg) = 0._r8 + end if do g = begg, endg begcb(g) = totc(g) + c_tot_woodprod(g) + c_cropprod1(g) + & hrv_xsmrpool_amount_left_to_dribble(g) + & + gru_conv_cflux_amount_left_to_dribble(g) + & dwt_conv_cflux_amount_left_to_dribble(g) begnb(g) = totn(g) + n_tot_woodprod(g) + n_cropprod1(g) end do @@ -159,7 +173,7 @@ end subroutine BeginCNGridcellBalance !----------------------------------------------------------------------- subroutine BeginCNColumnBalance(this, bounds, num_soilc, filter_soilc, & - cnveg_carbonstate_inst, cnveg_nitrogenstate_inst) + soilbiogeochem_carbonstate_inst,soilbiogeochem_nitrogenstate_inst) ! ! !DESCRIPTION: ! Calculate beginning column-level carbon/nitrogen balance, for mass conservation check @@ -173,8 +187,8 @@ subroutine BeginCNColumnBalance(this, bounds, num_soilc, filter_soilc, & type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilc ! number of soil columns filter integer , intent(in) :: filter_soilc(:) ! filter for soil columns - type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst - type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst + type(soilbiogeochem_carbonstate_type), intent(in) :: soilbiogeochem_carbonstate_inst + type(soilbiogeochem_nitrogenstate_type), intent(in) :: soilbiogeochem_nitrogenstate_inst ! ! !LOCAL VARIABLES: integer :: fc,c @@ -183,14 +197,16 @@ subroutine BeginCNColumnBalance(this, bounds, num_soilc, filter_soilc, & associate( & col_begcb => this%begcb_col , & ! Output: [real(r8) (:)] (gC/m2) column carbon mass, beginning of time step col_begnb => this%begnb_col , & ! Output: [real(r8) (:)] (gN/m2) column nitrogen mass, beginning of time step - totcolc => cnveg_carbonstate_inst%totc_col , & ! Input: [real(r8) (:)] (gC/m2) total column carbon, incl veg and cpool - totcoln => cnveg_nitrogenstate_inst%totn_col & ! Input: [real(r8) (:)] (gN/m2) total column nitrogen, incl veg + totcolc => soilbiogeochem_carbonstate_inst%totc_col , & ! Input: [real(r8) (:)] (gC/m2) total column carbon, incl veg and cpool + totcoln => soilbiogeochem_nitrogenstate_inst%totn_col & ! Input: [real(r8) (:)] (gN/m2) total column nitrogen, incl veg ) do fc = 1,num_soilc c = filter_soilc(fc) + col_begcb(c) = totcolc(c) col_begnb(c) = totcoln(c) + end do end associate @@ -199,14 +215,20 @@ end subroutine BeginCNColumnBalance !----------------------------------------------------------------------- subroutine CBalanceCheck(this, bounds, num_soilc, filter_soilc, & - soilbiogeochem_carbonflux_inst, cnveg_carbonflux_inst, & - cnveg_carbonstate_inst, c_products_inst) + soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, c_products_inst, & + clm_fates) ! ! !USES: use subgridAveMod, only: c2g + ! ! !DESCRIPTION: ! Perform carbon mass conservation check for column and patch + ! + ! Note on FATES: On fates colums, there is no vegetation biomass + ! and no gpp flux. There is a litter input flux. + ! ! !ARGUMENTS: class(cn_balance_type) , intent(inout) :: this @@ -214,13 +236,18 @@ subroutine CBalanceCheck(this, bounds, num_soilc, filter_soilc, & integer , intent(in) :: num_soilc ! number of soil columns in filter integer , intent(in) :: filter_soilc(:) ! filter for soil columns type(soilbiogeochem_carbonflux_type) , intent(in) :: soilbiogeochem_carbonflux_inst + type(soilbiogeochem_carbonstate_type), intent(inout) :: soilbiogeochem_carbonstate_inst type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst type(cn_products_type) , intent(in) :: c_products_inst + type(hlm_fates_interface_type) , intent(inout) :: clm_fates + ! ! !LOCAL VARIABLES: - integer :: c, g, err_index ! indices + integer :: c, g, err_index ! indices + integer :: s ! fates site index (follows c) integer :: fc ! lake filter indices + integer :: ic ! index of the current clump logical :: err_found ! error flag real(r8) :: dt ! radiation time step (seconds) real(r8) :: col_cinputs, grc_cinputs @@ -229,13 +256,15 @@ subroutine CBalanceCheck(this, bounds, num_soilc, filter_soilc, & real(r8) :: grc_errcb(bounds%begg:bounds%endg) real(r8) :: som_c_leached_grc(bounds%begg:bounds%endg) real(r8) :: hrv_xsmrpool_amount_left_to_dribble(bounds%begg:bounds%endg) + real(r8) :: gru_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg) real(r8) :: dwt_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg) + !----------------------------------------------------------------------- associate( & grc_begcb => this%begcb_grc , & ! Input: [real(r8) (:) ] (gC/m2) gridcell-level carbon mass, beginning of time step grc_endcb => this%endcb_grc , & ! Output: [real(r8) (:) ] (gC/m2) gridcell-level carbon mass, end of time step - totgrcc => cnveg_carbonstate_inst%totc_grc , & ! Input: [real(r8) (:)] (gC/m2) total gridcell carbon, incl veg and cpool + totgrcc => soilbiogeochem_carbonstate_inst%totc_grc , & ! Output: [real(r8) (:)] (gC/m2) total gridcell carbon, incl veg and cpool nbp_grc => cnveg_carbonflux_inst%nbp_grc , & ! Input: [real(r8) (:) ] (gC/m2/s) net biome production (positive for sink) cropprod1_grc => c_products_inst%cropprod1_grc , & ! Input: [real(r8) (:)] (gC/m2) carbon in crop products tot_woodprod_grc => c_products_inst%tot_woodprod_grc , & ! Input: [real(r8) (:)] (gC/m2) total carbon in wood products @@ -244,43 +273,70 @@ subroutine CBalanceCheck(this, bounds, num_soilc, filter_soilc, & col_begcb => this%begcb_col , & ! Input: [real(r8) (:) ] (gC/m2) carbon mass, beginning of time step col_endcb => this%endcb_col , & ! Output: [real(r8) (:) ] (gC/m2) carbon mass, end of time step wood_harvestc => cnveg_carbonflux_inst%wood_harvestc_col , & ! Input: [real(r8) (:) ] (gC/m2/s) wood harvest (to product pools) + gru_conv_cflux => cnveg_carbonflux_inst%gru_conv_cflux_col , & ! Input: [real(r8) (:) ] (gC/m2/s) wood harvest (to product pools) + gru_wood_productc_gain => cnveg_carbonflux_inst%gru_wood_productc_gain_col , & ! Input: [real(r8) (:) ] (gC/m2/s) wood harvest (to product pools) crop_harvestc_to_cropprodc => cnveg_carbonflux_inst%crop_harvestc_to_cropprodc_col , & ! Input: [real(r8) (:) ] (gC/m2/s) crop harvest C to 1-year crop product pool gpp => cnveg_carbonflux_inst%gpp_col , & ! Input: [real(r8) (:) ] (gC/m2/s) gross primary production er => cnveg_carbonflux_inst%er_col , & ! Input: [real(r8) (:) ] (gC/m2/s) total ecosystem respiration, autotrophic + heterotrophic col_fire_closs => cnveg_carbonflux_inst%fire_closs_col , & ! Input: [real(r8) (:) ] (gC/m2/s) total column-level fire C loss col_hrv_xsmrpool_to_atm => cnveg_carbonflux_inst%hrv_xsmrpool_to_atm_col , & ! Input: [real(r8) (:) ] (gC/m2/s) excess MR pool harvest mortality - col_xsmrpool_to_atm => cnveg_carbonflux_inst%xsmrpool_to_atm_col , & ! Input: [real(r8) (:) ] (gC/m2/s) excess MR pool crop harvest loss to atm + col_xsmrpool_to_atm => cnveg_carbonflux_inst%xsmrpool_to_atm_col , & ! Input: [real(r8) (:) ] (gC/m2/s) excess MR pool crop harvest loss to atm som_c_leached => soilbiogeochem_carbonflux_inst%som_c_leached_col , & ! Input: [real(r8) (:) ] (gC/m2/s) total SOM C loss from vertical transport - totcolc => cnveg_carbonstate_inst%totc_col & ! Input: [real(r8) (:) ] (gC/m2) total column carbon, incl veg and cpool + totcolc => soilbiogeochem_carbonstate_inst%totc_col , & ! Input: [real(r8) (:) ] (gC/m2) total column carbon, incl veg and cpool + fates_litter_flux => soilbiogeochem_carbonflux_inst%fates_litter_flux & ! Total carbon litter flux from FATES to CLM [gC/m2/s] ) ! set time steps dt = get_step_size_real() + ! clump index + ic = bounds%clump_index + err_found = .false. do fc = 1,num_soilc c = filter_soilc(fc) ! calculate the total column-level carbon storage, for mass conservation check + ! for bigleaf, totcolc includes soil and all of the veg c pools including cpool, xfer, etc + ! for fates, totcolc only includes soil and non-fates litter carbon, + ! see soibiogeochem_carbonstate_inst%summary for calculations col_endcb(c) = totcolc(c) + + + if( col%is_fates(c) ) then + + s = clm_fates%f2hmap(ic)%hsites(c) + + col_cinputs = fates_litter_flux(c) + + ! calculate total column-level outputs + ! fates has already exported burn losses and fluxes to the atm + ! So they are irrelevant here + ! (gC/m2/s) total heterotrophic respiration + col_coutputs = soilbiogeochem_carbonflux_inst%hr_col(c) - ! calculate total column-level inputs - col_cinputs = gpp(c) - - ! calculate total column-level outputs - ! er = ar + hr, col_fire_closs includes patch-level fire losses - col_coutputs = er(c) + col_fire_closs(c) + col_hrv_xsmrpool_to_atm(c) + & - col_xsmrpool_to_atm(c) - - ! Fluxes to product pools are included in column-level outputs: the product - ! pools are not included in totcolc, so are outside the system with respect to - ! these balance checks. (However, the dwt flux to product pools is NOT included, - ! since col_begcb is initialized after the dynamic area adjustments - i.e., - ! after the dwt term has already been taken out.) - col_coutputs = col_coutputs + & - wood_harvestc(c) + & - crop_harvestc_to_cropprodc(c) + else + + ! calculate total column-level inputs + col_cinputs = gpp(c) + + ! calculate total column-level outputs + ! er = ar + hr, col_fire_closs includes patch-level fire losses + col_coutputs = er(c) + col_fire_closs(c) + col_hrv_xsmrpool_to_atm(c) + & + col_xsmrpool_to_atm(c) + gru_conv_cflux(c) + + ! Fluxes to product pools are included in column-level outputs: the product + ! pools are not included in totcolc, so are outside the system with respect to + ! these balance checks. (However, the dwt flux to product pools is NOT included, + ! since col_begcb is initialized after the dynamic area adjustments - i.e., + ! after the dwt term has already been taken out.) + col_coutputs = col_coutputs + & + wood_harvestc(c) + & + gru_wood_productc_gain(c) + & + crop_harvestc_to_cropprodc(c) + + end if ! subtract leaching flux col_coutputs = col_coutputs - som_c_leached(c) @@ -303,19 +359,28 @@ subroutine CBalanceCheck(this, bounds, num_soilc, filter_soilc, & if (err_found) then c = err_index write(iulog,*)'column cbalance error = ', col_errcb(c), c + write(iulog,*)'is fates column? = ', col%is_fates(c) write(iulog,*)'Latdeg,Londeg=',grc%latdeg(col%gridcell(c)),grc%londeg(col%gridcell(c)) write(iulog,*)'begcb = ',col_begcb(c) write(iulog,*)'endcb = ',col_endcb(c) write(iulog,*)'delta store = ',col_endcb(c)-col_begcb(c) write(iulog,*)'--- Inputs ---' - write(iulog,*)'gpp = ',gpp(c)*dt + if( col%is_fates(c) ) then + write(iulog,*)'fates litter_flux = ',fates_litter_flux(c)*dt + else + write(iulog,*)'gpp = ',gpp(c)*dt + end if write(iulog,*)'--- Outputs ---' - write(iulog,*)'er = ',er(c)*dt - write(iulog,*)'col_fire_closs = ',col_fire_closs(c)*dt - write(iulog,*)'col_hrv_xsmrpool_to_atm = ',col_hrv_xsmrpool_to_atm(c)*dt - write(iulog,*)'col_xsmrpool_to_atm = ',col_xsmrpool_to_atm(c)*dt - write(iulog,*)'wood_harvestc = ',wood_harvestc(c)*dt - write(iulog,*)'crop_harvestc_to_cropprodc = ', crop_harvestc_to_cropprodc(c)*dt + if( .not.col%is_fates(c) ) then + write(iulog,*)'er = ',er(c)*dt + write(iulog,*)'col_fire_closs = ',col_fire_closs(c)*dt + write(iulog,*)'col_hrv_xsmrpool_to_atm = ',col_hrv_xsmrpool_to_atm(c)*dt + write(iulog,*)'col_xsmrpool_to_atm = ',col_xsmrpool_to_atm(c)*dt + write(iulog,*)'wood_harvestc = ',wood_harvestc(c)*dt + write(iulog,*)'crop_harvestc_to_cropprodc = ', crop_harvestc_to_cropprodc(c)*dt + else + write(iulog,*)'hr = ',soilbiogeochem_carbonflux_inst%hr_col(c)*dt + end if write(iulog,*)'-1*som_c_leached = ',som_c_leached(c)*dt call endrun(subgrid_index=c, subgrid_level=subgrid_level_column, msg=errMsg(sourcefile, __LINE__)) end if @@ -344,28 +409,45 @@ subroutine CBalanceCheck(this, bounds, num_soilc, filter_soilc, & ! We account for the latter fluxes as inputs below; the same ! fluxes have entered the pools earlier in the timestep. For true ! conservation we would need to add a flux out of npp into seed. - call cnveg_carbonflux_inst%hrv_xsmrpool_to_atm_dribbler%get_amount_left_to_dribble_end( & - bounds, hrv_xsmrpool_amount_left_to_dribble(bounds%begg:bounds%endg)) - call cnveg_carbonflux_inst%dwt_conv_cflux_dribbler%get_amount_left_to_dribble_end( & - bounds, dwt_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg)) - grc_endcb(g) = totgrcc(g) + tot_woodprod_grc(g) + cropprod1_grc(g) + & - hrv_xsmrpool_amount_left_to_dribble(g) + & - dwt_conv_cflux_amount_left_to_dribble(g) - - ! calculate total gridcell-level inputs - ! slevis notes: - ! nbp_grc = nep_grc - fire_closs_grc - hrv_xsmrpool_to_atm_dribbled_grc - dwt_conv_cflux_dribbled_grc - product_closs_grc - grc_cinputs = nbp_grc(g) + & - dwt_seedc_to_leaf_grc(g) + dwt_seedc_to_deadstem_grc(g) - - ! calculate total gridcell-level outputs - grc_coutputs = - som_c_leached_grc(g) - - ! calculate the total gridcell-level carbon balance error - ! for this time step - grc_errcb(g) = (grc_cinputs - grc_coutputs) * dt - & - (grc_endcb(g) - grc_begcb(g)) + if(.not.use_fates_bgc)then + call cnveg_carbonflux_inst%hrv_xsmrpool_to_atm_dribbler%get_amount_left_to_dribble_end( & + bounds, hrv_xsmrpool_amount_left_to_dribble(bounds%begg:bounds%endg)) + call cnveg_carbonflux_inst%dwt_conv_cflux_dribbler%get_amount_left_to_dribble_end( & + bounds, dwt_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg)) + call cnveg_carbonflux_inst%gru_conv_cflux_dribbler%get_amount_left_to_dribble_end( & + bounds, gru_conv_cflux_amount_left_to_dribble(bounds%begg:bounds%endg)) + + grc_endcb(g) = totgrcc(g) + tot_woodprod_grc(g) + cropprod1_grc(g) + & + hrv_xsmrpool_amount_left_to_dribble(g) + & + gru_conv_cflux_amount_left_to_dribble(g) + & + dwt_conv_cflux_amount_left_to_dribble(g) + + ! calculate total gridcell-level inputs + ! slevis notes: + ! nbp_grc = nep_grc - fire_closs_grc - hrv_xsmrpool_to_atm_dribbled_grc - & + ! dwt_conv_cflux_dribbled_grc - gru_conv_cflux_dribbled_grc - product_closs_grc + grc_cinputs = nbp_grc(g) + & + dwt_seedc_to_leaf_grc(g) + dwt_seedc_to_deadstem_grc(g) + + ! calculate total gridcell-level outputs + grc_coutputs = - som_c_leached_grc(g) + + ! calculate the total gridcell-level carbon balance error + ! for this time step + grc_errcb(g) = (grc_cinputs - grc_coutputs) * dt - & + (grc_endcb(g) - grc_begcb(g)) + + else + + ! Totally punt on this for now. We just don't track these gridscale variables yet (RGK) + grc_cinputs = 0._r8 + grc_endcb(g) = grc_begcb(g) + grc_coutputs = 0._r8 + grc_errcb(g) = 0._r8 + + end if + ! check for significant errors if (abs(grc_errcb(g)) > this%cerror) then err_found = .true. @@ -398,8 +480,9 @@ end subroutine CBalanceCheck !----------------------------------------------------------------------- subroutine NBalanceCheck(this, bounds, num_soilc, filter_soilc, & - soilbiogeochem_nitrogenflux_inst, cnveg_nitrogenflux_inst, & - cnveg_nitrogenstate_inst, n_products_inst, atm2lnd_inst) + soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst, & + cnveg_nitrogenflux_inst, & + cnveg_nitrogenstate_inst, n_products_inst, atm2lnd_inst, clm_fates) ! ! !DESCRIPTION: ! Perform nitrogen mass conservation check @@ -415,17 +498,21 @@ subroutine NBalanceCheck(this, bounds, num_soilc, filter_soilc, & integer , intent(in) :: num_soilc ! number of soil columns in filter integer , intent(in) :: filter_soilc (:) ! filter for soil columns type(soilbiogeochem_nitrogenflux_type) , intent(in) :: soilbiogeochem_nitrogenflux_inst + type(soilbiogeochem_nitrogenstate_type) , intent(inout) :: soilbiogeochem_nitrogenstate_inst type(cnveg_nitrogenflux_type) , intent(in) :: cnveg_nitrogenflux_inst type(cnveg_nitrogenstate_type) , intent(inout) :: cnveg_nitrogenstate_inst type(cn_products_type) , intent(in) :: n_products_inst type(atm2lnd_type) , intent(in) :: atm2lnd_inst + type(hlm_fates_interface_type) , intent(inout) :: clm_fates + ! ! !LOCAL VARIABLES: - integer :: c,err_index,j ! indices - integer :: g ! gridcell index - integer :: fc ! lake filter indices - logical :: err_found ! error flag - real(r8):: dt ! radiation time step (seconds) + integer :: c,err_index,j,s ! indices + integer :: ic ! index of clump + integer :: g ! gridcell index + integer :: fc ! lake filter indices + logical :: err_found ! error flag + real(r8):: dt ! radiation time step (seconds) real(r8):: col_ninputs(bounds%begc:bounds%endc) real(r8):: col_noutputs(bounds%begc:bounds%endc) real(r8):: col_errnb(bounds%begc:bounds%endc) @@ -441,7 +528,7 @@ subroutine NBalanceCheck(this, bounds, num_soilc, filter_soilc, & associate( & grc_begnb => this%begnb_grc , & ! Input: [real(r8) (:) ] (gN/m2) gridcell nitrogen mass, beginning of time step grc_endnb => this%endnb_grc , & ! Output: [real(r8) (:) ] (gN/m2) gridcell nitrogen mass, end of time step - totgrcn => cnveg_nitrogenstate_inst%totn_grc , & ! Input: [real(r8) (:) ] (gN/m2) total gridcell nitrogen, incl veg + totgrcn => soilbiogeochem_nitrogenstate_inst%totn_grc , & ! Input: [real(r8) (:) ] (gN/m2) total gridcell nitrogen, incl veg cropprod1_grc => n_products_inst%cropprod1_grc , & ! Input: [real(r8) (:)] (gN/m2) nitrogen in crop products product_loss_grc => n_products_inst%product_loss_grc , & ! Input: [real(r8) (:)] (gN/m2) losses from wood & crop products tot_woodprod_grc => n_products_inst%tot_woodprod_grc , & ! Input: [real(r8) (:)] (gN/m2) total nitrogen in wood products @@ -465,11 +552,18 @@ subroutine NBalanceCheck(this, bounds, num_soilc, filter_soilc, & col_fire_nloss => cnveg_nitrogenflux_inst%fire_nloss_col , & ! Input: [real(r8) (:) ] (gN/m2/s) total column-level fire N loss wood_harvestn => cnveg_nitrogenflux_inst%wood_harvestn_col , & ! Input: [real(r8) (:) ] (gN/m2/s) wood harvest (to product pools) + gru_conv_nflux_grc => cnveg_nitrogenflux_inst%gru_conv_nflux_grc , & ! Input: [real(r8) (:) ] (gC/m2/s) wood harvest (to product pools) summed to the gridcell level + gru_conv_nflux => cnveg_nitrogenflux_inst%gru_conv_nflux_col , & ! Input: [real(r8) (:) ] (gC/m2/s) wood harvest (to product pools) + gru_wood_productn_gain => cnveg_nitrogenflux_inst%gru_wood_productn_gain_col , & ! Input: [real(r8) (:) ] (gC/m2/s) wood harvest (to product pools) + gru_wood_productn_gain_grc => cnveg_nitrogenflux_inst%gru_wood_productn_gain_grc, & ! Input: [real(r8) (:) ] (gC/m2/s) wood harvest (to product pools) summed to the gridcell level crop_harvestn_to_cropprodn => cnveg_nitrogenflux_inst%crop_harvestn_to_cropprodn_col , & ! Input: [real(r8) (:) ] (gN/m2/s) crop harvest N to 1-year crop product pool - totcoln => cnveg_nitrogenstate_inst%totn_col & ! Input: [real(r8) (:) ] (gN/m2) total column nitrogen, incl veg + totcoln => soilbiogeochem_nitrogenstate_inst%totn_col , & ! Input: [real(r8) (:) ] (gN/m2) total column nitrogen, incl veg + sminn_to_plant => soilbiogeochem_nitrogenflux_inst%sminn_to_plant_col, & + fates_litter_flux => soilbiogeochem_nitrogenflux_inst%fates_litter_flux & ! Total nitrogen litter flux from FATES to CLM [gN/m2/s] ) + ! set time steps dt = get_step_size_real() @@ -477,6 +571,9 @@ subroutine NBalanceCheck(this, bounds, num_soilc, filter_soilc, & col_ninputs_partial(:) = 0._r8 col_noutputs_partial(:) = 0._r8 + ! clump index + ic = bounds%clump_index + err_found = .false. do fc = 1,num_soilc c=filter_soilc(fc) @@ -486,6 +583,11 @@ subroutine NBalanceCheck(this, bounds, num_soilc, filter_soilc, & ! calculate total column-level inputs col_ninputs(c) = ndep_to_sminn(c) + nfix_to_sminn(c) + supplement_to_sminn(c) + + ! If using fates, pass in the decomposition flux + if( col%is_fates(c) ) then + col_ninputs(c) = col_ninputs(c) + fates_litter_flux(c) + end if if(use_fun)then col_ninputs(c) = col_ninputs(c) + ffix_to_sminn(c) ! for FUN, free living fixation is a seprate flux. RF. @@ -496,18 +598,30 @@ subroutine NBalanceCheck(this, bounds, num_soilc, filter_soilc, & end if col_ninputs_partial(c) = col_ninputs(c) - + ! calculate total column-level outputs - col_noutputs(c) = denit(c) + col_fire_nloss(c) - ! Fluxes to product pools are included in column-level outputs: the product - ! pools are not included in totcoln, so are outside the system with respect to - ! these balance checks. (However, the dwt flux to product pools is NOT included, - ! since col_begnb is initialized after the dynamic area adjustments - i.e., - ! after the dwt term has already been taken out.) - col_noutputs(c) = col_noutputs(c) + & - wood_harvestn(c) + & - crop_harvestn_to_cropprodn(c) + col_noutputs(c) = denit(c) + + if( .not.col%is_fates(c) ) then + + col_noutputs(c) = col_noutputs(c) + col_fire_nloss(c) + gru_conv_nflux(c) + + ! Fluxes to product pools are included in column-level outputs: the product + ! pools are not included in totcoln, so are outside the system with respect to + ! these balance checks. (However, the dwt flux to product pools is NOT included, + ! since col_begnb is initialized after the dynamic area adjustments - i.e., + ! after the dwt term has already been taken out.) + col_noutputs(c) = col_noutputs(c) + & + wood_harvestn(c) + & + gru_wood_productn_gain(c) + & + crop_harvestn_to_cropprodn(c) + else + + ! If we are using fates, remove plant uptake + col_noutputs(c) = col_noutputs(c) + sminn_to_plant(c) + + end if if (.not. use_nitrif_denitrif) then col_noutputs(c) = col_noutputs(c) + sminn_leached(c) @@ -518,11 +632,15 @@ subroutine NBalanceCheck(this, bounds, num_soilc, filter_soilc, & end if col_noutputs(c) = col_noutputs(c) - som_n_leached(c) + + col_noutputs_partial(c) = col_noutputs(c) - col_noutputs_partial(c) = col_noutputs(c) - & - wood_harvestn(c) - & - crop_harvestn_to_cropprodn(c) - + if( .not.col%is_fates(c) ) then + col_noutputs_partial(c) = col_noutputs_partial(c) - & + wood_harvestn(c) - & + crop_harvestn_to_cropprodn(c) + end if + ! calculate the total column-level nitrogen balance error for this time step col_errnb(c) = (col_ninputs(c) - col_noutputs(c))*dt - & (col_endnb(c) - col_begnb(c)) @@ -550,87 +668,103 @@ subroutine NBalanceCheck(this, bounds, num_soilc, filter_soilc, & write(iulog,*)'input mass = ',col_ninputs(c)*dt write(iulog,*)'output mass = ',col_noutputs(c)*dt write(iulog,*)'net flux = ',(col_ninputs(c)-col_noutputs(c))*dt - write(iulog,*)'inputs,ffix,nfix,ndep = ',ffix_to_sminn(c)*dt,nfix_to_sminn(c)*dt,ndep_to_sminn(c)*dt - write(iulog,*)'outputs,lch,roff,dnit = ',smin_no3_leached(c)*dt, smin_no3_runoff(c)*dt,f_n2o_nit(c)*dt + if(col%is_fates(c))then + write(iulog,*)'inputs,ndep,nfix,suppn= ',ndep_to_sminn(c)*dt,nfix_to_sminn(c)*dt,supplement_to_sminn(c)*dt + else + write(iulog,*)'inputs,ffix,nfix,ndep = ',ffix_to_sminn(c)*dt,nfix_to_sminn(c)*dt,ndep_to_sminn(c)*dt + end if + if(col%is_fates(c))then + write(iulog,*)'outputs,lch,roff,dnit,plnt = ',smin_no3_leached(c)*dt, smin_no3_runoff(c)*dt,f_n2o_nit(c)*dt,sminn_to_plant(c)*dt + else + write(iulog,*)'outputs,lch,roff,dnit = ',smin_no3_leached(c)*dt, smin_no3_runoff(c)*dt,f_n2o_nit(c)*dt + end if call endrun(subgrid_index=c, subgrid_level=subgrid_level_column, msg=errMsg(sourcefile, __LINE__)) end if - ! Repeat error check at the gridcell level - call c2g( bounds = bounds, & - carr = totcoln(bounds%begc:bounds%endc), & - garr = totgrcn(bounds%begg:bounds%endg), & - c2l_scale_type = 'unity', & - l2g_scale_type = 'unity') - call c2g( bounds = bounds, & - carr = col_ninputs_partial(bounds%begc:bounds%endc), & - garr = grc_ninputs_partial(bounds%begg:bounds%endg), & - c2l_scale_type = 'unity', & - l2g_scale_type = 'unity') - call c2g( bounds = bounds, & - carr = col_noutputs_partial(bounds%begc:bounds%endc), & - garr = grc_noutputs_partial(bounds%begg:bounds%endg), & - c2l_scale_type = 'unity', & - l2g_scale_type = 'unity') - - err_found = .false. - do g = bounds%begg, bounds%endg - ! calculate the total gridcell-level nitrogen storage, for mass conservation check - ! Notes: - ! Not including seedn_grc in grc_begnb and grc_endnb because - ! seedn_grc forms out of thin air, for now, and equals - ! -1 * (dwt_seedn_to_leaf_grc(g) + dwt_seedn_to_deadstem_grc(g)) - ! We account for the latter fluxes as inputs below; the same - ! fluxes have entered the pools earlier in the timestep. For true - ! conservation we would need to add a flux out of nfix into seed. - grc_endnb(g) = totgrcn(g) + tot_woodprod_grc(g) + cropprod1_grc(g) - - ! calculate total gridcell-level inputs - grc_ninputs(g) = grc_ninputs_partial(g) + & - dwt_seedn_to_leaf_grc(g) + & - dwt_seedn_to_deadstem_grc(g) - - ! calculate total gridcell-level outputs - grc_noutputs(g) = grc_noutputs_partial(g) + & - dwt_conv_nflux_grc(g) + & - product_loss_grc(g) - - ! calculate the total gridcell-level nitrogen balance error for this time step - grc_errnb(g) = (grc_ninputs(g) - grc_noutputs(g)) * dt - & - (grc_endnb(g) - grc_begnb(g)) - - if (abs(grc_errnb(g)) > this%nerror) then - err_found = .true. - err_index = g - end if - - if (abs(grc_errnb(g)) > this%nwarning) then - write(iulog,*) 'nbalance warning at g =', g, grc_errnb(g), grc_endnb(g) + if_notfates: if(.not.use_fates_bgc)then + + ! Repeat error check at the gridcell level + call c2g( bounds = bounds, & + carr = totcoln(bounds%begc:bounds%endc), & + garr = totgrcn(bounds%begg:bounds%endg), & + c2l_scale_type = 'unity', & + l2g_scale_type = 'unity') + call c2g( bounds = bounds, & + carr = col_ninputs_partial(bounds%begc:bounds%endc), & + garr = grc_ninputs_partial(bounds%begg:bounds%endg), & + c2l_scale_type = 'unity', & + l2g_scale_type = 'unity') + call c2g( bounds = bounds, & + carr = col_noutputs_partial(bounds%begc:bounds%endc), & + garr = grc_noutputs_partial(bounds%begg:bounds%endg), & + c2l_scale_type = 'unity', & + l2g_scale_type = 'unity') + + err_found = .false. + do g = bounds%begg, bounds%endg + ! calculate the total gridcell-level nitrogen storage, for mass conservation check + ! Notes: + ! Not including seedn_grc in grc_begnb and grc_endnb because + ! seedn_grc forms out of thin air, for now, and equals + ! -1 * (dwt_seedn_to_leaf_grc(g) + dwt_seedn_to_deadstem_grc(g)) + ! We account for the latter fluxes as inputs below; the same + ! fluxes have entered the pools earlier in the timestep. For true + ! conservation we would need to add a flux out of nfix into seed. + grc_endnb(g) = totgrcn(g) + tot_woodprod_grc(g) + cropprod1_grc(g) + + ! calculate total gridcell-level inputs + grc_ninputs(g) = grc_ninputs_partial(g) + & + dwt_seedn_to_leaf_grc(g) + & + dwt_seedn_to_deadstem_grc(g) + + ! calculate total gridcell-level outputs + grc_noutputs(g) = grc_noutputs_partial(g) + & + dwt_conv_nflux_grc(g) + & + product_loss_grc(g) - & + ! Subtract the next one because it is present in + ! grc_noutputs_partial but not needed at the + ! gridcell level + gru_wood_productn_gain_grc(g) + + ! calculate the total gridcell-level nitrogen balance error for this time step + grc_errnb(g) = (grc_ninputs(g) - grc_noutputs(g)) * dt - & + (grc_endnb(g) - grc_begnb(g)) + + if (abs(grc_errnb(g)) > this%nerror) then + err_found = .true. + err_index = g + end if + + if (abs(grc_errnb(g)) > this%nwarning) then + write(iulog,*) 'nbalance warning at g =', g, grc_errnb(g), grc_endnb(g) + end if + end do + if (err_found) then + g = err_index + write(iulog,*) 'gridcell nbalance error =', grc_errnb(g), g + write(iulog,*) 'latdeg, londeg =', grc%latdeg(g), grc%londeg(g) + write(iulog,*) 'begnb =', grc_begnb(g) + write(iulog,*) 'endnb =', grc_endnb(g) + write(iulog,*) 'delta store =', grc_endnb(g) - grc_begnb(g) + write(iulog,*) 'input mass =', grc_ninputs(g) * dt + write(iulog,*) 'output mass =', grc_noutputs(g) * dt + write(iulog,*) 'net flux =', (grc_ninputs(g) - grc_noutputs(g)) * dt + write(iulog,*) '--- Inputs ---' + write(iulog,*) 'grc_ninputs_partial =', grc_ninputs_partial(g) * dt + write(iulog,*) 'dwt_seedn_to_leaf_grc =', dwt_seedn_to_leaf_grc(g) * dt + write(iulog,*) 'dwt_seedn_to_deadstem_grc =', dwt_seedn_to_deadstem_grc(g) * dt + write(iulog,*) '--- Outputs ---' + write(iulog,*) 'grc_noutputs_partial =', grc_noutputs_partial(g) * dt + write(iulog,*) 'dwt_conv_nflux_grc =', dwt_conv_nflux_grc(g) * dt + write(iulog,*) '-gru_wood_productn_gain_grc =', -gru_wood_productn_gain_grc(g) * dt + write(iulog,*) 'product_loss_grc =', product_loss_grc(g) * dt + call endrun(subgrid_index=g, subgrid_level=subgrid_level_gridcell, msg=errMsg(sourcefile, __LINE__)) end if - end do - - if (err_found) then - g = err_index - write(iulog,*) 'gridcell nbalance error =', grc_errnb(g), g - write(iulog,*) 'latdeg, londeg =', grc%latdeg(g), grc%londeg(g) - write(iulog,*) 'begnb =', grc_begnb(g) - write(iulog,*) 'endnb =', grc_endnb(g) - write(iulog,*) 'delta store =', grc_endnb(g) - grc_begnb(g) - write(iulog,*) 'input mass =', grc_ninputs(g) * dt - write(iulog,*) 'output mass =', grc_noutputs(g) * dt - write(iulog,*) 'net flux =', (grc_ninputs(g) - grc_noutputs(g)) * dt - write(iulog,*) '--- Inputs ---' - write(iulog,*) 'grc_ninputs_partial =', grc_ninputs_partial(g) * dt - write(iulog,*) 'dwt_seedn_to_leaf_grc =', dwt_seedn_to_leaf_grc(g) * dt - write(iulog,*) 'dwt_seedn_to_deadstem_grc =', dwt_seedn_to_deadstem_grc(g) * dt - write(iulog,*) '--- Outputs ---' - write(iulog,*) 'grc_noutputs_partial =', grc_noutputs_partial(g) * dt - write(iulog,*) 'dwt_conv_nflux_grc =', dwt_conv_nflux_grc(g) * dt - write(iulog,*) 'product_loss_grc =', product_loss_grc(g) * dt - call endrun(subgrid_index=g, subgrid_level=subgrid_level_gridcell, msg=errMsg(sourcefile, __LINE__)) - end if + + end if if_notfates end associate - + end subroutine NBalanceCheck end module CNBalanceCheckMod diff --git a/src/biogeochem/CNCIsoFluxMod.F90 b/src/biogeochem/CNCIsoFluxMod.F90 index 817c35b766..fbe4cd927f 100644 --- a/src/biogeochem/CNCIsoFluxMod.F90 +++ b/src/biogeochem/CNCIsoFluxMod.F90 @@ -8,7 +8,6 @@ module CNCIsoFluxMod use shr_kind_mod , only : r8 => shr_kind_r8 use shr_log_mod , only : errMsg => shr_log_errMsg use clm_varpar , only : ndecomp_cascade_transitions, nlevdecomp, ndecomp_pools - use clm_varpar , only : max_patch_per_col, maxsoil_patches use clm_varpar , only : i_litr_min, i_litr_max, i_met_lit use abortutils , only : endrun use pftconMod , only : pftcon @@ -31,12 +30,14 @@ module CNCIsoFluxMod public :: CIsoFlux1 public :: CIsoFlux2 public :: CIsoFlux2h + public :: CIsoFlux2g public :: CIsoFlux3 ! ! !PRIVATE MEMBER FUNCTIONS: private :: CNCIsoLitterToColumn private :: CNCIsoGapPftToColumn private :: CNCIsoHarvestPftToColumn + private :: CNCIsoGrossUnrepPftToColumn private :: CIsoFluxCalc1d private :: CIsoFluxCalc2dFlux private :: CIsoFluxCalc2dBoth @@ -83,7 +84,7 @@ subroutine CIsoFlux1(num_soilc, filter_soilc, num_soilp, filter_soilp, & character(len=*) , intent(in) :: isotope ! 'c13' or 'c14' ! ! !LOCAL VARIABLES: - integer :: fp,pi,l,fc,cc,j,k,p + integer :: fp,l,fc,cc,j,k,p integer :: cdp !----------------------------------------------------------------------- @@ -426,6 +427,16 @@ subroutine CIsoFlux1(num_soilc, filter_soilc, num_soilp, filter_soilp, & iso_cnveg_cs%livestemc_patch , cnveg_cs%livestemc_patch, & num_soilp , filter_soilp, 1._r8, 0, isotope) + call CIsoFluxCalc(& + iso_cnveg_cf%leafc_to_removedresiduec_patch , cnveg_cf%leafc_to_removedresiduec_patch, & + iso_cnveg_cs%leafc_patch , cnveg_cs%leafc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%livestemc_to_removedresiduec_patch, cnveg_cf%livestemc_to_removedresiduec_patch, & + iso_cnveg_cs%livestemc_patch , cnveg_cs%livestemc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + call CIsoFluxCalc(& iso_cnveg_cf%repr_grainc_to_seed_patch , cnveg_cf%repr_grainc_to_seed_patch, & iso_cnveg_cs%reproductivec_patch , cnveg_cs%reproductivec_patch, & @@ -495,7 +506,9 @@ subroutine CIsoFlux1(num_soilc, filter_soilc, num_soilp, filter_soilp, & p = filter_soilp(fp) iso_cnveg_cf%crop_harvestc_to_cropprodc_patch(p) = & iso_cnveg_cf%leafc_to_biofuelc_patch(p) + & - iso_cnveg_cf%livestemc_to_biofuelc_patch(p) + iso_cnveg_cf%livestemc_to_biofuelc_patch(p) + & + iso_cnveg_cf%leafc_to_removedresiduec_patch(p) + & + iso_cnveg_cf%livestemc_to_removedresiduec_patch(p) end do if (use_grainproduct) then @@ -533,7 +546,7 @@ subroutine CIsoFlux1(num_soilc, filter_soilc, num_soilp, filter_soilp, & ! For later clean-up, it would be possible to generalize this function to operate on a single ! patch-to-column flux. - call CNCIsoLitterToColumn(num_soilc, filter_soilc, soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) + call CNCIsoLitterToColumn(num_soilp, filter_soilp, soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) ! column-level non-mortality fluxes @@ -576,7 +589,7 @@ subroutine CIsoFlux1(num_soilc, filter_soilc, num_soilp, filter_soilp, & end subroutine CIsoFlux1 !----------------------------------------------------------------------- - subroutine CIsoFlux2(num_soilc, filter_soilc, num_soilp , filter_soilp, & + subroutine CIsoFlux2(num_soilp, filter_soilp, & soilbiogeochem_state_inst, & cnveg_carbonflux_inst, cnveg_carbonstate_inst, & iso_cnveg_carbonflux_inst, iso_cnveg_carbonstate_inst, isotope) @@ -585,8 +598,6 @@ subroutine CIsoFlux2(num_soilc, filter_soilc, num_soilp , filter_soilp, & ! On the radiation time step, set the carbon isotopic fluxes for gap mortality ! ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst @@ -598,7 +609,6 @@ subroutine CIsoFlux2(num_soilc, filter_soilc, num_soilp , filter_soilp, & ! ! !LOCAL VARIABLES: - integer :: fp,pi !----------------------------------------------------------------------- associate( & @@ -711,14 +721,14 @@ subroutine CIsoFlux2(num_soilc, filter_soilc, num_soilp , filter_soilp, & ! call routine to shift patch-level gap mortality fluxes to column , for isotopes ! the non-isotope version of this routine is in CNGapMortalityMod.F90. - call CNCIsoGapPftToColumn(num_soilc, filter_soilc, soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) + call CNCIsoGapPftToColumn(num_soilp, filter_soilp, soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) end associate end subroutine CIsoFlux2 !----------------------------------------------------------------------- - subroutine CIsoFlux2h(num_soilc , filter_soilc, num_soilp , filter_soilp, & + subroutine CIsoFlux2h(num_soilp, filter_soilp, & soilbiogeochem_state_inst, & cnveg_carbonflux_inst, cnveg_carbonstate_inst, & iso_cnveg_carbonflux_inst, iso_cnveg_carbonstate_inst, isotope) @@ -727,8 +737,6 @@ subroutine CIsoFlux2h(num_soilc , filter_soilc, num_soilp , filter_soilp, & ! set the carbon isotopic fluxes for harvest mortality ! ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst @@ -857,14 +865,163 @@ subroutine CIsoFlux2h(num_soilc , filter_soilc, num_soilp , filter_soilp, & ! call routine to shift patch-level gap mortality fluxes to column, ! for isotopes the non-isotope version of this routine is in CNGapMortalityMod.F90. - call CNCIsoHarvestPftToColumn(num_soilc, filter_soilc, soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) + call CNCIsoHarvestPftToColumn(num_soilp, filter_soilp, soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) end associate end subroutine CIsoFlux2h !----------------------------------------------------------------------- - subroutine CIsoFlux3(num_soilc , filter_soilc, num_soilp , filter_soilp, & + subroutine CIsoFlux2g(num_soilp, filter_soilp, & + soilbiogeochem_state_inst, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, & + iso_cnveg_carbonflux_inst, iso_cnveg_carbonstate_inst, isotope) + ! + ! !DESCRIPTION: + ! set the carbon isotopic fluxes for gross unrepresented landcover change mortality + ! + ! !ARGUMENTS: + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! filter for soil patches + type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst + type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst + type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst + type(cnveg_carbonflux_type) , intent(inout) :: iso_cnveg_carbonflux_inst + type(cnveg_carbonstate_type) , intent(in) :: iso_cnveg_carbonstate_inst + character(len=*) , intent(in) :: isotope ! 'c13' or 'c14' + + !----------------------------------------------------------------------- + + associate( & + cnveg_cf => cnveg_carbonflux_inst , & + cnveg_cs => cnveg_carbonstate_inst , & + iso_cnveg_cf => iso_cnveg_carbonflux_inst , & + iso_cnveg_cs => iso_cnveg_carbonstate_inst & + ) + + ! patch-level gap mortality fluxes + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_leafc_to_litter_patch , cnveg_cf%gru_leafc_to_litter_patch, & + iso_cnveg_cs%leafc_patch , cnveg_cs%leafc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_leafc_storage_to_atm_patch , cnveg_cf%gru_leafc_storage_to_atm_patch, & + iso_cnveg_cs%leafc_storage_patch , cnveg_cs%leafc_storage_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_leafc_xfer_to_atm_patch , cnveg_cf%gru_leafc_xfer_to_atm_patch, & + iso_cnveg_cs%leafc_xfer_patch , cnveg_cs%leafc_xfer_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_frootc_to_litter_patch , cnveg_cf%gru_frootc_to_litter_patch, & + iso_cnveg_cs%frootc_patch , cnveg_cs%frootc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_frootc_storage_to_atm_patch , cnveg_cf%gru_frootc_storage_to_atm_patch, & + iso_cnveg_cs%frootc_storage_patch , cnveg_cs%frootc_storage_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_frootc_xfer_to_atm_patch , cnveg_cf%gru_frootc_xfer_to_atm_patch, & + iso_cnveg_cs%frootc_xfer_patch , cnveg_cs%frootc_xfer_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_livestemc_to_atm_patch , cnveg_cf%gru_livestemc_to_atm_patch, & + iso_cnveg_cs%livestemc_patch , cnveg_cs%livestemc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_livestemc_storage_to_atm_patch , cnveg_cf%gru_livestemc_storage_to_atm_patch, & + iso_cnveg_cs%livestemc_storage_patch , cnveg_cs%livestemc_storage_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_livestemc_xfer_to_atm_patch , cnveg_cf%gru_livestemc_xfer_to_atm_patch, & + iso_cnveg_cs%livestemc_xfer_patch , cnveg_cs%livestemc_xfer_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_deadstemc_to_atm_patch , cnveg_cf%gru_deadstemc_to_atm_patch, & + iso_cnveg_cs%deadstemc_patch , cnveg_cs%deadstemc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_wood_productc_gain_patch , cnveg_cf%gru_wood_productc_gain_patch, & + iso_cnveg_cs%deadstemc_patch , cnveg_cs%deadstemc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_deadstemc_storage_to_atm_patch , cnveg_cf%gru_deadstemc_storage_to_atm_patch, & + iso_cnveg_cs%deadstemc_storage_patch , cnveg_cs%deadstemc_storage_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_deadstemc_xfer_to_atm_patch , cnveg_cf%gru_deadstemc_xfer_to_atm_patch, & + iso_cnveg_cs%deadstemc_xfer_patch , cnveg_cs%deadstemc_xfer_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_livecrootc_to_litter_patch , cnveg_cf%gru_livecrootc_to_litter_patch, & + iso_cnveg_cs%livecrootc_patch , cnveg_cs%livecrootc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_livecrootc_storage_to_atm_patch , cnveg_cf%gru_livecrootc_storage_to_atm_patch, & + iso_cnveg_cs%livecrootc_storage_patch , cnveg_cs%livecrootc_storage_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_livecrootc_xfer_to_atm_patch , cnveg_cf%gru_livecrootc_xfer_to_atm_patch, & + iso_cnveg_cs%livecrootc_xfer_patch , cnveg_cs%livecrootc_xfer_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_deadcrootc_to_litter_patch , cnveg_cf%gru_deadcrootc_to_litter_patch, & + iso_cnveg_cs%deadcrootc_patch , cnveg_cs%deadcrootc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_deadcrootc_storage_to_atm_patch , cnveg_cf%gru_deadcrootc_storage_to_atm_patch, & + iso_cnveg_cs%deadcrootc_storage_patch , cnveg_cs%deadcrootc_storage_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_deadcrootc_xfer_to_atm_patch , cnveg_cf%gru_deadcrootc_xfer_to_atm_patch, & + iso_cnveg_cs%deadcrootc_xfer_patch , cnveg_cs%deadcrootc_xfer_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_gresp_storage_to_atm_patch , cnveg_cf%gru_gresp_storage_to_atm_patch, & + iso_cnveg_cs%gresp_storage_patch , cnveg_cs%gresp_storage_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_gresp_xfer_to_atm_patch , cnveg_cf%gru_gresp_xfer_to_atm_patch, & + iso_cnveg_cs%gresp_xfer_patch , cnveg_cs%gresp_xfer_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + call CIsoFluxCalc(& + iso_cnveg_cf%gru_xsmrpool_to_atm_patch , cnveg_cf%gru_xsmrpool_to_atm_patch, & + iso_cnveg_cs%totvegc_patch , cnveg_cs%totvegc_patch, & + num_soilp , filter_soilp, 1._r8, 0, isotope) + + ! call routine to shift patch-level gap mortality fluxes to column, + ! for isotopes the non-isotope version of this routine is in CNGapMortalityMod.F90. + + call CNCIsoGrossUnrepPftToColumn(num_soilp, filter_soilp, soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) + + end associate + + end subroutine CIsoFlux2g + + !----------------------------------------------------------------------- + subroutine CIsoFlux3(num_soilp, filter_soilp, & soilbiogeochem_state_inst , soilbiogeochem_carbonstate_inst, & cnveg_carbonflux_inst, cnveg_carbonstate_inst, & iso_cnveg_carbonflux_inst, iso_cnveg_carbonstate_inst, & @@ -874,8 +1031,6 @@ subroutine CIsoFlux3(num_soilc , filter_soilc, num_soilp , filter_soilp, & ! On the radiation time step, set the carbon isotopic fluxes for fire mortality ! ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst @@ -888,7 +1043,7 @@ subroutine CIsoFlux3(num_soilc , filter_soilc, num_soilp , filter_soilp, & character(len=*) , intent(in) :: isotope ! 'c13' or 'c14' ! ! !LOCAL VARIABLES: - integer :: pi,pp,l,fc,cc,j,i + integer :: fp,pp,l,cc,j,i !----------------------------------------------------------------------- associate( & @@ -1123,32 +1278,22 @@ subroutine CIsoFlux3(num_soilc , filter_soilc, num_soilp , filter_soilp, & ! calculate the column-level flux of deadstem and deadcrootc to cwdc as the result of fire mortality. - do pi = 1,max_patch_per_col - do fc = 1,num_soilc - cc = filter_soilc(fc) - if ( pi <= col%npatches(cc) ) then - pp = col%patchi(cc) + pi - 1 - if (patch%active(pp)) then - do j = 1, nlevdecomp - iso_cnveg_cf%fire_mortality_c_to_cwdc_col(cc,j) = & - iso_cnveg_cf%fire_mortality_c_to_cwdc_col(cc,j) + & - (iso_cnveg_cf%m_deadstemc_to_litter_fire_patch(pp) + & - iso_cnveg_cf%m_livestemc_to_litter_fire_patch(pp)) * & - patch%wtcol(pp) * stem_prof(pp,j) - iso_cnveg_cf%fire_mortality_c_to_cwdc_col(cc,j) = & - iso_cnveg_cf%fire_mortality_c_to_cwdc_col(cc,j) + & - (iso_cnveg_cf%m_deadcrootc_to_litter_fire_patch(pp) + & - iso_cnveg_cf%m_livecrootc_to_litter_fire_patch(pp)) * & - patch%wtcol(pp) * croot_prof(pp,j) - end do - end if - end if + do fp = 1,num_soilp + pp = filter_soilp(fp) + cc = patch%column(pp) + do j = 1, nlevdecomp + iso_cnveg_cf%fire_mortality_c_to_cwdc_col(cc,j) = & + iso_cnveg_cf%fire_mortality_c_to_cwdc_col(cc,j) + & + (iso_cnveg_cf%m_deadstemc_to_litter_fire_patch(pp) + & + iso_cnveg_cf%m_livestemc_to_litter_fire_patch(pp)) * & + patch%wtcol(pp) * stem_prof(pp,j) + iso_cnveg_cf%fire_mortality_c_to_cwdc_col(cc,j) = & + iso_cnveg_cf%fire_mortality_c_to_cwdc_col(cc,j) + & + (iso_cnveg_cf%m_deadcrootc_to_litter_fire_patch(pp) + & + iso_cnveg_cf%m_livecrootc_to_litter_fire_patch(pp)) * & + patch%wtcol(pp) * croot_prof(pp,j) end do - end do - - do fc = 1,num_soilc - cc = filter_soilc(fc) do j = 1, nlevdecomp do l = 1, ndecomp_pools if ( soilbiogeochem_cs%decomp_cpools_vr_col(cc,j,l) /= 0._r8) then @@ -1163,54 +1308,47 @@ subroutine CIsoFlux3(num_soilc , filter_soilc, num_soilp , filter_soilp, & end do end do - - do pi = 1,max_patch_per_col - do fc = 1,num_soilc - cc = filter_soilc(fc) - if ( pi <= col%npatches(cc) ) then - pp = col%patchi(cc) + pi - 1 - if (patch%active(pp)) then - do j = 1, nlevdecomp - iso_cnveg_cf%m_c_to_litr_fire_col(cc,j,i_met_lit) = & - iso_cnveg_cf%m_c_to_litr_fire_col(cc,j,i_met_lit) + & - ((iso_cnveg_cf%m_leafc_to_litter_fire_patch(pp) * lf_f(ivt(pp),i_met_lit) & - +iso_cnveg_cf%m_leafc_storage_to_litter_fire_patch(pp) + & - iso_cnveg_cf%m_leafc_xfer_to_litter_fire_patch(pp) + & - iso_cnveg_cf%m_gresp_storage_to_litter_fire_patch(pp) & - +iso_cnveg_cf%m_gresp_xfer_to_litter_fire_patch(pp))*leaf_prof(pp,j) + & - (iso_cnveg_cf%m_frootc_to_litter_fire_patch(pp) * fr_f(ivt(pp),i_met_lit) & - +iso_cnveg_cf%m_frootc_storage_to_litter_fire_patch(pp) + & - iso_cnveg_cf%m_frootc_xfer_to_litter_fire_patch(pp))*froot_prof(pp,j) & - +(iso_cnveg_cf%m_livestemc_storage_to_litter_fire_patch(pp) + & - iso_cnveg_cf%m_livestemc_xfer_to_litter_fire_patch(pp) & - +iso_cnveg_cf%m_deadstemc_storage_to_litter_fire_patch(pp) + & - iso_cnveg_cf%m_deadstemc_xfer_to_litter_fire_patch(pp))* stem_prof(pp,j)& - +(iso_cnveg_cf%m_livecrootc_storage_to_litter_fire_patch(pp) + & - iso_cnveg_cf%m_livecrootc_xfer_to_litter_fire_patch(pp) & - +iso_cnveg_cf%m_deadcrootc_storage_to_litter_fire_patch(pp) + & - iso_cnveg_cf%m_deadcrootc_xfer_to_litter_fire_patch(pp))* croot_prof(pp,j)) * patch%wtcol(pp) - - ! Here metabolic litter is treated differently than other - ! types of litter, so it remains outside this litter loop, - ! in the line above - do i = i_met_lit+1, i_litr_max - iso_cnveg_cf%m_c_to_litr_fire_col(cc,j,i) = & - iso_cnveg_cf%m_c_to_litr_fire_col(cc,j,i) + & - (iso_cnveg_cf%m_leafc_to_litter_fire_patch(pp) * lf_f(ivt(pp),i) * leaf_prof(pp,j) + & - iso_cnveg_cf%m_frootc_to_litter_fire_patch(pp) * fr_f(ivt(pp),i) * froot_prof(pp,j)) * patch%wtcol(pp) - end do - end do - end if - end if + do fp = 1,num_soilp + pp = filter_soilp(fp) + cc = patch%column(pp) + do j = 1, nlevdecomp + iso_cnveg_cf%m_c_to_litr_fire_col(cc,j,i_met_lit) = & + iso_cnveg_cf%m_c_to_litr_fire_col(cc,j,i_met_lit) + & + ((iso_cnveg_cf%m_leafc_to_litter_fire_patch(pp) * lf_f(ivt(pp),i_met_lit) & + +iso_cnveg_cf%m_leafc_storage_to_litter_fire_patch(pp) + & + iso_cnveg_cf%m_leafc_xfer_to_litter_fire_patch(pp) + & + iso_cnveg_cf%m_gresp_storage_to_litter_fire_patch(pp) & + +iso_cnveg_cf%m_gresp_xfer_to_litter_fire_patch(pp))*leaf_prof(pp,j) + & + (iso_cnveg_cf%m_frootc_to_litter_fire_patch(pp) * fr_f(ivt(pp),i_met_lit) & + +iso_cnveg_cf%m_frootc_storage_to_litter_fire_patch(pp) + & + iso_cnveg_cf%m_frootc_xfer_to_litter_fire_patch(pp))*froot_prof(pp,j) & + +(iso_cnveg_cf%m_livestemc_storage_to_litter_fire_patch(pp) + & + iso_cnveg_cf%m_livestemc_xfer_to_litter_fire_patch(pp) & + +iso_cnveg_cf%m_deadstemc_storage_to_litter_fire_patch(pp) + & + iso_cnveg_cf%m_deadstemc_xfer_to_litter_fire_patch(pp))* stem_prof(pp,j)& + +(iso_cnveg_cf%m_livecrootc_storage_to_litter_fire_patch(pp) + & + iso_cnveg_cf%m_livecrootc_xfer_to_litter_fire_patch(pp) & + +iso_cnveg_cf%m_deadcrootc_storage_to_litter_fire_patch(pp) + & + iso_cnveg_cf%m_deadcrootc_xfer_to_litter_fire_patch(pp))* croot_prof(pp,j)) * patch%wtcol(pp) + + ! Here metabolic litter is treated differently than other + ! types of litter, so it remains outside this litter loop, + ! in the line above + do i = i_met_lit+1, i_litr_max + iso_cnveg_cf%m_c_to_litr_fire_col(cc,j,i) = & + iso_cnveg_cf%m_c_to_litr_fire_col(cc,j,i) + & + (iso_cnveg_cf%m_leafc_to_litter_fire_patch(pp) * lf_f(ivt(pp),i) * leaf_prof(pp,j) + & + iso_cnveg_cf%m_frootc_to_litter_fire_patch(pp) * fr_f(ivt(pp),i) * froot_prof(pp,j)) * patch%wtcol(pp) + end do end do - end do + end do end associate end subroutine CIsoFlux3 !----------------------------------------------------------------------- - subroutine CNCIsoLitterToColumn (num_soilc, filter_soilc, & + subroutine CNCIsoLitterToColumn (num_soilp, filter_soilp, & soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) ! ! !DESCRIPTION: @@ -1224,13 +1362,13 @@ subroutine CNCIsoLitterToColumn (num_soilc, filter_soilc, & !DML ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! filter for soil patches type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst type(cnveg_carbonflux_type) , intent(inout) :: iso_cnveg_carbonflux_inst ! ! !LOCAL VARIABLES: - integer :: fc,c,pi,p,k,j,i + integer :: fp,c,p,k,j,i !----------------------------------------------------------------------- associate( & @@ -1252,59 +1390,50 @@ subroutine CNCIsoLitterToColumn (num_soilc, filter_soilc, & ) do j = 1, nlevdecomp - do pi = 1,max_patch_per_col - do fc = 1,num_soilc - c = filter_soilc(fc) - - if ( pi <= col%npatches(c) ) then - p = col%patchi(c) + pi - 1 - if (patch%active(p)) then - do i = i_litr_min, i_litr_max - phenology_c_to_litr_c(c,j,i) = & - phenology_c_to_litr_c(c,j,i) + & - ! leaf litter carbon fluxes - leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & - ! fine root litter carbon fluxes - frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) - end do + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) + + do i = i_litr_min, i_litr_max + phenology_c_to_litr_c(c,j,i) = & + phenology_c_to_litr_c(c,j,i) + & + ! leaf litter carbon fluxes + leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & + ! fine root litter carbon fluxes + frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + end do !DML - if (ivt(p) >= npcropmin) then ! add livestemc to litter - ! stem litter carbon fluxes - do i = i_litr_min, i_litr_max - phenology_c_to_litr_c(c,j,i) = & - phenology_c_to_litr_c(c,j,i) + & - livestemc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - end do - - if (.not. use_grainproduct) then - ! grain litter carbon fluxes - do i = i_litr_min, i_litr_max - do k = repr_grain_min, repr_grain_max - phenology_c_to_litr_c(c,j,i) = & - phenology_c_to_litr_c(c,j,i) + & - repr_grainc_to_food(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - end do - end do - end if - - ! reproductive structure litter carbon fluxes - do i = i_litr_min, i_litr_max - do k = repr_structure_min, repr_structure_max - phenology_c_to_litr_c(c,j,i) = & - phenology_c_to_litr_c(c,j,i) + & - repr_structurec_to_litter(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - end do - end do - - end if -!DML - end if + if (ivt(p) >= npcropmin) then ! add livestemc to litter + ! stem litter carbon fluxes + do i = i_litr_min, i_litr_max + phenology_c_to_litr_c(c,j,i) = & + phenology_c_to_litr_c(c,j,i) + & + livestemc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + end do + + if (.not. use_grainproduct) then + ! grain litter carbon fluxes + do i = i_litr_min, i_litr_max + do k = repr_grain_min, repr_grain_max + phenology_c_to_litr_c(c,j,i) = & + phenology_c_to_litr_c(c,j,i) + & + repr_grainc_to_food(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + end do + end do end if - end do + ! reproductive structure litter carbon fluxes + do i = i_litr_min, i_litr_max + do k = repr_structure_min, repr_structure_max + phenology_c_to_litr_c(c,j,i) = & + phenology_c_to_litr_c(c,j,i) + & + repr_structurec_to_litter(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + end do + end do + end if + !DML end do - end do end associate @@ -1312,7 +1441,7 @@ subroutine CNCIsoLitterToColumn (num_soilc, filter_soilc, & end subroutine CNCIsoLitterToColumn !----------------------------------------------------------------------- - subroutine CNCIsoGapPftToColumn (num_soilc, filter_soilc, & + subroutine CNCIsoGapPftToColumn (num_soilp, filter_soilp, & soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) ! ! !DESCRIPTION: @@ -1320,13 +1449,13 @@ subroutine CNCIsoGapPftToColumn (num_soilc, filter_soilc, & ! to the column level and assign them to the three litter pools (+ cwd pool) ! ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! soil column filter + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! soil patch filter type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst type(cnveg_carbonflux_type) , intent(inout) :: iso_cnveg_carbonflux_inst ! ! !LOCAL VARIABLES: - integer :: fc,c,pi,p,j,i ! indices + integer :: fp,c,p,j,i ! indices !----------------------------------------------------------------------- associate( & @@ -1367,72 +1496,63 @@ subroutine CNCIsoGapPftToColumn (num_soilc, filter_soilc, & ) do j = 1, nlevdecomp - do pi = 1,maxsoil_patches - do fc = 1,num_soilc - c = filter_soilc(fc) - - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 - - if (patch%active(p)) then - - do i = i_litr_min, i_litr_max - ! leaf gap mortality carbon fluxes - gap_mortality_c_to_litr_c(c,j,i) = & - gap_mortality_c_to_litr_c(c,j,i) + & - m_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - ! fine root gap mortality carbon fluxes - gap_mortality_c_to_litr_c(c,j,i) = & - gap_mortality_c_to_litr_c(c,j,i) + & - m_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) - end do - - ! wood gap mortality carbon fluxes - gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & - m_livestemc_to_litter(p) * wtcol(p) * stem_prof(p,j) - gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & - m_deadstemc_to_litter(p) * wtcol(p) * stem_prof(p,j) - gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & - m_livecrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) - gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & - m_deadcrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) - - ! Metabolic litter is treated differently than other types - ! of litter, so it gets this additional line after the - ! most recent loop over all litter types - gap_mortality_c_to_litr_c(c,j,i_met_lit) = & - gap_mortality_c_to_litr_c(c,j,i_met_lit) + & - ! storage gap mortality carbon fluxes - m_leafc_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - m_frootc_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - m_livestemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - m_deadstemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - m_livecrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - m_deadcrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - m_gresp_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - ! transfer gap mortality carbon fluxes - m_leafc_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - m_frootc_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - m_livestemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - m_deadstemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - m_livecrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - m_deadcrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - m_gresp_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) - - end if - end if - + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) + + do i = i_litr_min, i_litr_max + ! leaf gap mortality carbon fluxes + gap_mortality_c_to_litr_c(c,j,i) = & + gap_mortality_c_to_litr_c(c,j,i) + & + m_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + ! fine root gap mortality carbon fluxes + gap_mortality_c_to_litr_c(c,j,i) = & + gap_mortality_c_to_litr_c(c,j,i) + & + m_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) end do + ! wood gap mortality carbon fluxes + gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & + m_livestemc_to_litter(p) * wtcol(p) * stem_prof(p,j) + gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & + m_deadstemc_to_litter(p) * wtcol(p) * stem_prof(p,j) + gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & + m_livecrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & + m_deadcrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + + ! Metabolic litter is treated differently than other types + ! of litter, so it gets this additional line after the + ! most recent loop over all litter types + gap_mortality_c_to_litr_c(c,j,i_met_lit) = & + gap_mortality_c_to_litr_c(c,j,i_met_lit) + & + ! storage gap mortality carbon fluxes + m_leafc_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + m_frootc_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + m_livestemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + m_deadstemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + m_livecrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + m_deadcrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + m_gresp_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + ! transfer gap mortality carbon fluxes + m_leafc_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + m_frootc_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + m_livestemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + m_deadstemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + m_livecrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + m_deadcrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + m_gresp_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + end do end do + end associate end subroutine CNCIsoGapPftToColumn !----------------------------------------------------------------------- - subroutine CNCIsoHarvestPftToColumn (num_soilc, filter_soilc, & + subroutine CNCIsoHarvestPftToColumn (num_soilp, filter_soilp, & soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) ! ! !DESCRIPTION: @@ -1440,13 +1560,13 @@ subroutine CNCIsoHarvestPftToColumn (num_soilc, filter_soilc, & ! to the column level and assign them to the litter, cwd, and wood product pools ! ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! soil column filter + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! soil patch filter type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst type(cnveg_carbonflux_type) , intent(inout) :: iso_cnveg_carbonflux_inst ! ! !LOCAL VARIABLES: - integer :: fc,c,pi,p,j,i ! indices + integer :: fp,c,p,j,i ! indices !----------------------------------------------------------------------- associate( & @@ -1487,82 +1607,160 @@ subroutine CNCIsoHarvestPftToColumn (num_soilc, filter_soilc, & ) do j = 1, nlevdecomp - do pi = 1,maxsoil_patches - do fc = 1,num_soilc - c = filter_soilc(fc) + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 - - if (patch%active(p)) then - - do i = i_litr_min, i_litr_max - ! leaf harvest mortality carbon fluxes - harvest_c_to_litr_c(c,j,i) = & - harvest_c_to_litr_c(c,j,i) + & - hrv_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - - ! fine root harvest mortality carbon fluxes - harvest_c_to_litr_c(c,j,i) = & - harvest_c_to_litr_c(c,j,i) + & - hrv_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) - end do - - ! wood harvest mortality carbon fluxes - harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & - hrv_livestemc_to_litter(p) * wtcol(p) * stem_prof(p,j) - harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & - hrv_livecrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) - harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & - hrv_deadcrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) - - ! Metabolic litter is treated differently than other types - ! of litter, so it gets this additional line after the - ! most recent loop over all litter types - harvest_c_to_litr_c(c,j,i_met_lit) = & - harvest_c_to_litr_c(c,j,i_met_lit) + & - ! storage harvest mortality carbon fluxes - hrv_leafc_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - hrv_frootc_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - hrv_livestemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_deadstemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_livecrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_deadcrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_gresp_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - ! transfer harvest mortality carbon fluxes - hrv_leafc_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - hrv_frootc_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - hrv_livestemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_deadstemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_livecrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_deadcrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_gresp_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) - end if - end if - + do i = i_litr_min, i_litr_max + ! leaf harvest mortality carbon fluxes + harvest_c_to_litr_c(c,j,i) = & + harvest_c_to_litr_c(c,j,i) + & + hrv_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + + ! fine root harvest mortality carbon fluxes + harvest_c_to_litr_c(c,j,i) = & + harvest_c_to_litr_c(c,j,i) + & + hrv_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) end do + ! wood harvest mortality carbon fluxes + harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & + hrv_livestemc_to_litter(p) * wtcol(p) * stem_prof(p,j) + harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & + hrv_livecrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & + hrv_deadcrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + + ! Metabolic litter is treated differently than other types + ! of litter, so it gets this additional line after the + ! most recent loop over all litter types + harvest_c_to_litr_c(c,j,i_met_lit) = & + harvest_c_to_litr_c(c,j,i_met_lit) + & + ! storage harvest mortality carbon fluxes + hrv_leafc_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + hrv_frootc_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + hrv_livestemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_deadstemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_livecrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_deadcrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_gresp_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + ! transfer harvest mortality carbon fluxes + hrv_leafc_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + hrv_frootc_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + hrv_livestemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_deadstemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_livecrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_deadcrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_gresp_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + end do end do - do pi = 1,maxsoil_patches - do fc = 1,num_soilc - c = filter_soilc(fc) - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 - - if (patch%active(p)) then - cwood_harvestc(c) = cwood_harvestc(c) + & - pwood_harvestc(p) * wtcol(p) - end if - end if - end do + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) + cwood_harvestc(c) = cwood_harvestc(c) + & + pwood_harvestc(p) * wtcol(p) end do end associate end subroutine CNCIsoHarvestPftToColumn + !----------------------------------------------------------------------- + subroutine CNCIsoGrossUnrepPftToColumn (num_soilp, filter_soilp, & + soilbiogeochem_state_inst, iso_cnveg_carbonflux_inst) + ! + ! !DESCRIPTION: + ! gather all patch-level gross unrepresented landcover change mortality fluxes + ! to the column level and assign them to the litter, cwd, and wood product pools + ! + ! !ARGUMENTS: + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! soil patch filter + type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst + type(cnveg_carbonflux_type) , intent(inout) :: iso_cnveg_carbonflux_inst + ! + ! !LOCAL VARIABLES: + integer :: fp,c,p,j,i ! indices + !----------------------------------------------------------------------- + + associate( & + ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type + wtcol => patch%wtcol , & ! Input: [real(r8) (:) ] patch weight relative to column (0-1) + + lf_f => pftcon%lf_f , & ! Input: leaf litter fractions + fr_f => pftcon%fr_f , & ! Input: fine root litter fractions + + leaf_prof => soilbiogeochem_state_inst%leaf_prof_patch , & ! Input: [real(r8) (:,:) ] (1/m) profile of leaves + froot_prof => soilbiogeochem_state_inst%froot_prof_patch , & ! Input: [real(r8) (:,:) ] (1/m) profile of fine roots + croot_prof => soilbiogeochem_state_inst%croot_prof_patch , & ! Input: [real(r8) (:,:) ] (1/m) profile of coarse roots + stem_prof => soilbiogeochem_state_inst%stem_prof_patch , & ! Input: [real(r8) (:,:) ] (1/m) profile of stems + + gru_leafc_to_litter => iso_cnveg_carbonflux_inst%gru_leafc_to_litter_patch , & ! Input: [real(r8) (:) ] + gru_frootc_to_litter => iso_cnveg_carbonflux_inst%gru_frootc_to_litter_patch , & ! Input: [real(r8) (:) ] + gru_livestemc_to_atm => iso_cnveg_carbonflux_inst%gru_livestemc_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_deadstemc_to_atm => iso_cnveg_carbonflux_inst%gru_deadstemc_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_wood_productc_gain => iso_cnveg_carbonflux_inst%gru_wood_productc_gain_patch , & ! Input: [real(r8) (:) ] + gru_livecrootc_to_litter => iso_cnveg_carbonflux_inst%gru_livecrootc_to_litter_patch , & ! Input: [real(r8) (:) ] + gru_deadcrootc_to_litter => iso_cnveg_carbonflux_inst%gru_deadcrootc_to_litter_patch , & ! Input: [real(r8) (:) ] + gru_leafc_storage_to_atm => iso_cnveg_carbonflux_inst%gru_leafc_storage_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_frootc_storage_to_atm => iso_cnveg_carbonflux_inst%gru_frootc_storage_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_livestemc_storage_to_atm => iso_cnveg_carbonflux_inst%gru_livestemc_storage_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_deadstemc_storage_to_atm => iso_cnveg_carbonflux_inst%gru_deadstemc_storage_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_livecrootc_storage_to_atm => iso_cnveg_carbonflux_inst%gru_livecrootc_storage_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_deadcrootc_storage_to_atm => iso_cnveg_carbonflux_inst%gru_deadcrootc_storage_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_gresp_storage_to_atm => iso_cnveg_carbonflux_inst%gru_gresp_storage_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_leafc_xfer_to_atm => iso_cnveg_carbonflux_inst%gru_leafc_xfer_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_frootc_xfer_to_atm => iso_cnveg_carbonflux_inst%gru_frootc_xfer_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_livestemc_xfer_to_atm => iso_cnveg_carbonflux_inst%gru_livestemc_xfer_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_deadstemc_xfer_to_atm => iso_cnveg_carbonflux_inst%gru_deadstemc_xfer_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_livecrootc_xfer_to_atm => iso_cnveg_carbonflux_inst%gru_livecrootc_xfer_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_deadcrootc_xfer_to_atm => iso_cnveg_carbonflux_inst%gru_deadcrootc_xfer_to_atm_patch , & ! Input: [real(r8) (:) ] + gru_gresp_xfer_to_atm => iso_cnveg_carbonflux_inst%gru_gresp_xfer_to_atm_patch , & ! Input: [real(r8) (:) ] + cwood_harvestc => iso_cnveg_carbonflux_inst%wood_harvestc_col , & ! Output: [real(r8) (:) ] + gru_c_to_litr_c => iso_cnveg_carbonflux_inst%gru_c_to_litr_c_col , & ! Output: [real(r8) (:,:,:) ] C fluxes associated with gross unrepresented landcover change to litter pools (gC/m3/s) + gru_c_to_cwdc_c => iso_cnveg_carbonflux_inst%gru_c_to_cwdc_col , & ! Output: [real(r8) (:,:) ] C fluxes associated with harvest to CWD pool (gC/m3/s) + gru_wood_productc_gain_c => iso_cnveg_carbonflux_inst%gru_wood_productc_gain_col & ! Input: [real(r8) (:) ] + ) + + do j = 1, nlevdecomp + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) + + do i = i_litr_min, i_litr_max + gru_c_to_litr_c(c,j,i) = & + gru_c_to_litr_c(c,j,i) + & + ! leaf gross unrepresented landcover change mortality carbon fluxes + gru_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & + ! fine root gross unrepresented landcover change mortality carbon fluxes + gru_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + end do + + ! coarse root gross unrepresented landcover change mortality carbon fluxes + gru_c_to_cwdc_c(c,j) = gru_c_to_cwdc_c(c,j) + & + gru_livecrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + gru_c_to_cwdc_c(c,j) = gru_c_to_cwdc_c(c,j) + & + gru_deadcrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + + end do + end do + + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) + + ! wood gross unrepresented landcover change mortality carbon fluxes to product pools + gru_wood_productc_gain_c(c) = gru_wood_productc_gain_c(c) + & + gru_wood_productc_gain(p) * wtcol(p) + + end do + + end associate + + end subroutine CNCIsoGrossUnrepPftToColumn + !----------------------------------------------------------------------- subroutine CIsoFluxCalc1d(& ciso_flux, ctot_flux, & diff --git a/src/biogeochem/CNCStateUpdate1Mod.F90 b/src/biogeochem/CNCStateUpdate1Mod.F90 index 843754f3cd..5e38de2676 100644 --- a/src/biogeochem/CNCStateUpdate1Mod.F90 +++ b/src/biogeochem/CNCStateUpdate1Mod.F90 @@ -18,13 +18,16 @@ module CNCStateUpdate1Mod use CNVegCarbonStateType , only : cnveg_carbonstate_type use CNVegCarbonFluxType , only : cnveg_carbonflux_type use CropType , only : crop_type - use CropReprPoolsMod , only : nrepr, repr_grain_min, repr_grain_max, repr_structure_min, repr_structure_max + use CropReprPoolsMod , only : nrepr, repr_grain_min, repr_grain_max + use CropReprPoolsMod , only : repr_structure_min, repr_structure_max use SoilBiogeochemDecompCascadeConType , only : decomp_cascade_con, use_soil_matrixcn use SoilBiogeochemCarbonFluxType , only : soilbiogeochem_carbonflux_type use SoilBiogeochemCarbonStateType , only : soilbiogeochem_carbonstate_type use PatchType , only : patch - use clm_varctl , only : use_fates, use_cn, iulog, use_fates_sp use CNSharedParamsMod , only : use_matrixcn + use CLMFatesInterfaceMod , only : hlm_fates_interface_type + use ColumnType , only : col + ! implicit none private @@ -43,6 +46,7 @@ subroutine CStateUpdateDynPatch(bounds, num_soilc_with_inactive, filter_soilc_wi ! ! !DESCRIPTION: ! Update carbon states based on fluxes from dyn_cnbal_patch + ! This routine is not called with FATES active. ! ! !ARGUMENTS: type(bounds_type), intent(in) :: bounds @@ -71,26 +75,23 @@ subroutine CStateUpdateDynPatch(bounds, num_soilc_with_inactive, filter_soilc_wi dt = get_step_size_real() - if (.not. use_fates) then - do j = 1,nlevdecomp - do fc = 1, num_soilc_with_inactive - c = filter_soilc_with_inactive(fc) - do i = i_litr_min, i_litr_max - cs_soil%decomp_cpools_vr_col(c,j,i) = & - cs_soil%decomp_cpools_vr_col(c,j,i) + & - cf_veg%dwt_frootc_to_litr_c_col(c,j,i) * dt - end do - cs_soil%decomp_cpools_vr_col(c,j,i_cwd) = cs_soil%decomp_cpools_vr_col(c,j,i_cwd) + & - ( cf_veg%dwt_livecrootc_to_cwdc_col(c,j) + cf_veg%dwt_deadcrootc_to_cwdc_col(c,j) ) * dt + do j = 1,nlevdecomp + do fc = 1, num_soilc_with_inactive + c = filter_soilc_with_inactive(fc) + do i = i_litr_min, i_litr_max + cs_soil%decomp_cpools_vr_col(c,j,i) = & + cs_soil%decomp_cpools_vr_col(c,j,i) + & + cf_veg%dwt_frootc_to_litr_c_col(c,j,i) * dt end do + cs_soil%decomp_cpools_vr_col(c,j,i_cwd) = cs_soil%decomp_cpools_vr_col(c,j,i_cwd) + & + ( cf_veg%dwt_livecrootc_to_cwdc_col(c,j) + cf_veg%dwt_deadcrootc_to_cwdc_col(c,j) ) * dt end do + end do - do g = bounds%begg, bounds%endg - cs_veg%seedc_grc(g) = cs_veg%seedc_grc(g) - cf_veg%dwt_seedc_to_leaf_grc(g) * dt - cs_veg%seedc_grc(g) = cs_veg%seedc_grc(g) - cf_veg%dwt_seedc_to_deadstem_grc(g) * dt - end do - - end if + do g = bounds%begg, bounds%endg + cs_veg%seedc_grc(g) = cs_veg%seedc_grc(g) - cf_veg%dwt_seedc_to_leaf_grc(g) * dt + cs_veg%seedc_grc(g) = cs_veg%seedc_grc(g) - cf_veg%dwt_seedc_to_deadstem_grc(g) * dt + end do end associate @@ -123,8 +124,6 @@ subroutine CStateUpdate0(num_soilp, filter_soilp, & ! set time steps dt = get_step_size_real() - - ! gross photosynthesis fluxes do fp = 1,num_soilp p = filter_soilp(fp) @@ -140,7 +139,8 @@ end subroutine CStateUpdate0 !----------------------------------------------------------------------- subroutine CStateUpdate1( num_soilc, filter_soilc, num_soilp, filter_soilp, & crop_inst, cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - soilbiogeochem_carbonflux_inst, dribble_crophrv_xsmrpool_2atm) + soilbiogeochem_carbonflux_inst, dribble_crophrv_xsmrpool_2atm, & + clm_fates, clump_index) ! ! !DESCRIPTION: ! On the radiation time step, update all the prognostic carbon state @@ -157,6 +157,8 @@ subroutine CStateUpdate1( num_soilc, filter_soilc, num_soilp, filter_soilp, & type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst type(soilbiogeochem_carbonflux_type) , intent(inout) :: soilbiogeochem_carbonflux_inst logical , intent(in) :: dribble_crophrv_xsmrpool_2atm + type(hlm_fates_interface_type) , intent(inout) :: clm_fates + integer , intent(in) :: clump_index ! ! !LOCAL VARIABLES: integer :: c,p,j,k,l,i ! indices @@ -186,12 +188,23 @@ subroutine CStateUpdate1( num_soilc, filter_soilc, num_soilp, filter_soilp, & dt = get_step_size_real() ! Below is the input into the soil biogeochemistry model - - ! plant to litter fluxes - if (.not. use_fates) then - do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + + fc_loop: do fc = 1,num_soilc + c = filter_soilc(fc) + + fates_if: if( col%is_fates(c) ) then + + ! If this is a fates column, then we ask fates for the + ! litter fluxes, the following routine simply copies + ! prepared litter c flux boundary conditions into + ! cf_soil%decomp_cpools_sourcesink_col + + call clm_fates%UpdateCLitterfluxes(cf_soil,clump_index,c) + + else + + do j = 1,nlevdecomp + ! ! State update without the matrix solution ! @@ -206,35 +219,40 @@ subroutine CStateUpdate1( num_soilc, filter_soilc, num_soilp, filter_soilp, & ! terms have been moved to CStateUpdateDynPatch. I think this is zeroed every ! time step, but to be safe, I'm explicitly setting it to zero here. cf_soil%decomp_cpools_sourcesink_col(c,j,i_cwd) = 0._r8 - ! - ! For the matrix solution the actual state update comes after the matrix - ! multiply in SoilMatrix, but the matrix needs to be setup with - ! the equivalent of above. Those changes can be here or in the - ! native subroutines dealing with that field - ! + ! + ! For the matrix solution the actual state update comes after the matrix + ! multiply in SoilMatrix, but the matrix needs to be setup with + ! the equivalent of above. Those changes can be here or in the + ! native subroutines dealing with that field + ! else ! phenology and dynamic land cover fluxes end if end do - end do - else if ( .not. use_fates_sp ) then !use_fates - ! here add all fates litterfall and CWD breakdown to litter fluxes + + end if fates_if + + end do fc_loop + + + ! litter and SOM HR fluxes + do k = 1, ndecomp_cascade_transitions do j = 1,nlevdecomp do fc = 1,num_soilc c = filter_soilc(fc) - ! TODO(wjs, 2017-01-02) Should some portion or all of the following fluxes - ! be moved to the updates in CStateUpdateDynPatch? - do i = i_litr_min, i_litr_max - cf_soil%decomp_cpools_sourcesink_col(c,j,i) = & - cf_soil%FATES_c_to_litr_c_col(c,j,i) * dt - end do + ! + ! State update without the matrix solution + ! + if (.not. use_soil_matrixcn) then + cf_soil%decomp_cpools_sourcesink_col(c,j,cascade_donor_pool(k)) = & + cf_soil%decomp_cpools_sourcesink_col(c,j,cascade_donor_pool(k)) & + - ( cf_soil%decomp_cascade_hr_vr_col(c,j,k) + cf_soil%decomp_cascade_ctransfer_vr_col(c,j,k)) *dt + end if !not use_soil_matrixcn end do end do - endif - - if ( .not. use_fates_sp ) then !use_fates - ! litter and SOM HR fluxes - do k = 1, ndecomp_cascade_transitions + end do + do k = 1, ndecomp_cascade_transitions + if ( cascade_receiver_pool(k) /= 0 ) then ! skip terminal transitions do j = 1,nlevdecomp do fc = 1,num_soilc c = filter_soilc(fc) @@ -242,34 +260,16 @@ subroutine CStateUpdate1( num_soilc, filter_soilc, num_soilp, filter_soilp, & ! State update without the matrix solution ! if (.not. use_soil_matrixcn) then - cf_soil%decomp_cpools_sourcesink_col(c,j,cascade_donor_pool(k)) = & - cf_soil%decomp_cpools_sourcesink_col(c,j,cascade_donor_pool(k)) & - - ( cf_soil%decomp_cascade_hr_vr_col(c,j,k) + cf_soil%decomp_cascade_ctransfer_vr_col(c,j,k)) *dt - end if !not use_soil_matrixcn - end do - end do - end do - do k = 1, ndecomp_cascade_transitions - if ( cascade_receiver_pool(k) /= 0 ) then ! skip terminal transitions - do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) - ! - ! State update without the matrix solution - ! - if (.not. use_soil_matrixcn) then - cf_soil%decomp_cpools_sourcesink_col(c,j,cascade_receiver_pool(k)) = & + cf_soil%decomp_cpools_sourcesink_col(c,j,cascade_receiver_pool(k)) = & cf_soil%decomp_cpools_sourcesink_col(c,j,cascade_receiver_pool(k)) & + cf_soil%decomp_cascade_ctransfer_vr_col(c,j,k)*dt - end if !not use_soil_matrixcn - end do + end if !not use_soil_matrixcn end do - end if - end do - end if + end do + end if + end do - if (.not. use_fates) then -ptch: do fp = 1,num_soilp + soilpatch_loop: do fp = 1,num_soilp p = filter_soilp(fp) c = patch%column(p) @@ -320,8 +320,10 @@ subroutine CStateUpdate1( num_soilc, filter_soilc, num_soilp, filter_soilp, & end if if (ivt(p) >= npcropmin) then ! skip 2 generic crops cs_veg%livestemc_patch(p) = cs_veg%livestemc_patch(p) - cf_veg%livestemc_to_litter_patch(p)*dt - cs_veg%livestemc_patch(p) = cs_veg%livestemc_patch(p) - cf_veg%livestemc_to_biofuelc_patch(p)*dt - cs_veg%leafc_patch(p) = cs_veg%leafc_patch(p) - cf_veg%leafc_to_biofuelc_patch(p)*dt + cs_veg%livestemc_patch(p) = cs_veg%livestemc_patch(p) - & + (cf_veg%livestemc_to_biofuelc_patch(p) + cf_veg%livestemc_to_removedresiduec_patch(p))*dt + cs_veg%leafc_patch(p) = cs_veg%leafc_patch(p) - & + (cf_veg%leafc_to_biofuelc_patch(p) + cf_veg%leafc_to_removedresiduec_patch(p))*dt cs_veg%cropseedc_deficit_patch(p) = cs_veg%cropseedc_deficit_patch(p) & - cf_veg%crop_seedc_to_leaf_patch(p) * dt do k = repr_grain_min, repr_grain_max @@ -674,8 +676,7 @@ subroutine CStateUpdate1( num_soilc, filter_soilc, num_soilp, filter_soilp, & end if end if - end do ptch ! end of patch loop - end if ! end of NOT fates + end do soilpatch_loop ! end of patch loop end associate diff --git a/src/biogeochem/CNCStateUpdate2Mod.F90 b/src/biogeochem/CNCStateUpdate2Mod.F90 index 41c69cc118..6ecc4893e3 100644 --- a/src/biogeochem/CNCStateUpdate2Mod.F90 +++ b/src/biogeochem/CNCStateUpdate2Mod.F90 @@ -26,6 +26,7 @@ module CNCStateUpdate2Mod ! !PUBLIC MEMBER FUNCTIONS: public:: CStateUpdate2 public:: CStateUpdate2h + public:: CStateUpdate2g !----------------------------------------------------------------------- contains @@ -324,4 +325,116 @@ subroutine CStateUpdate2h(num_soilc, filter_soilc, num_soilp, filter_soilp, & end subroutine CStateUpdate2h + !----------------------------------------------------------------------- + subroutine CStateUpdate2g(num_soilc, filter_soilc, num_soilp, filter_soilp, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, soilbiogeochem_carbonstate_inst) + ! + ! !DESCRIPTION: + ! Update all the prognostic carbon state + ! variables affected by gross unrepresented landcover change mortality fluxes + ! + ! !ARGUMENTS: + integer , intent(in) :: num_soilc ! number of soil columns in filter + integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! filter for soil patches + type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst + type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + type(soilbiogeochem_carbonstate_type) , intent(inout) :: soilbiogeochem_carbonstate_inst + ! + ! !LOCAL VARIABLES: + integer :: c,p,j,k,l,i ! indices + integer :: fp,fc ! lake filter indices + real(r8):: dt ! radiation time step (seconds) + !----------------------------------------------------------------------- + + associate( & + cf_veg => cnveg_carbonflux_inst , & + cs_veg => cnveg_carbonstate_inst , & + cs_soil => soilbiogeochem_carbonstate_inst & + ) + + ! set time steps + dt = get_step_size_real() + + ! column level carbon fluxes from gross unrepresented landcover change mortality + do j = 1, nlevdecomp + do fc = 1,num_soilc + c = filter_soilc(fc) + + ! column gross unrepresented landcover change fluxes + do i = i_litr_min, i_litr_max + cs_soil%decomp_cpools_vr_col(c,j,i) = & + cs_soil%decomp_cpools_vr_col(c,j,i) + cf_veg%gru_c_to_litr_c_col(c,j,i) * dt + end do + cs_soil%decomp_cpools_vr_col(c,j,i_cwd) = & + cs_soil%decomp_cpools_vr_col(c,j,i_cwd) + cf_veg%gru_c_to_cwdc_col(c,j) * dt + + ! wood to product pools - states updated in CNProducts + end do + end do + + ! patch loop + do fp = 1,num_soilp + p = filter_soilp(fp) + + ! patch-level carbon fluxes from gross unrepresented landcover change mortality + ! displayed pools + cs_veg%leafc_patch(p) = cs_veg%leafc_patch(p) & + - cf_veg%gru_leafc_to_litter_patch(p) * dt + cs_veg%frootc_patch(p) = cs_veg%frootc_patch(p) & + - cf_veg%gru_frootc_to_litter_patch(p) * dt + cs_veg%livestemc_patch(p) = cs_veg%livestemc_patch(p) & + - cf_veg%gru_livestemc_to_atm_patch(p) * dt + cs_veg%deadstemc_patch(p) = cs_veg%deadstemc_patch(p) & + - cf_veg%gru_deadstemc_to_atm_patch(p) * dt + cs_veg%deadstemc_patch(p) = cs_veg%deadstemc_patch(p) & + - cf_veg%gru_wood_productc_gain_patch(p) * dt + cs_veg%livecrootc_patch(p) = cs_veg%livecrootc_patch(p) & + - cf_veg%gru_livecrootc_to_litter_patch(p) * dt + cs_veg%deadcrootc_patch(p) = cs_veg%deadcrootc_patch(p) & + - cf_veg%gru_deadcrootc_to_litter_patch(p) * dt + + ! xsmrpool + cs_veg%xsmrpool_patch(p) = cs_veg%xsmrpool_patch(p) & + - cf_veg%gru_xsmrpool_to_atm_patch(p) * dt + + ! storage pools + cs_veg%leafc_storage_patch(p) = cs_veg%leafc_storage_patch(p) & + - cf_veg%gru_leafc_storage_to_atm_patch(p) * dt + cs_veg%frootc_storage_patch(p) = cs_veg%frootc_storage_patch(p) & + - cf_veg%gru_frootc_storage_to_atm_patch(p) * dt + cs_veg%livestemc_storage_patch(p) = cs_veg%livestemc_storage_patch(p) & + - cf_veg%gru_livestemc_storage_to_atm_patch(p) * dt + cs_veg%deadstemc_storage_patch(p) = cs_veg%deadstemc_storage_patch(p) & + - cf_veg%gru_deadstemc_storage_to_atm_patch(p) * dt + cs_veg%livecrootc_storage_patch(p) = cs_veg%livecrootc_storage_patch(p) & + - cf_veg%gru_livecrootc_storage_to_atm_patch(p) * dt + cs_veg%deadcrootc_storage_patch(p) = cs_veg%deadcrootc_storage_patch(p) & + - cf_veg%gru_deadcrootc_storage_to_atm_patch(p) * dt + cs_veg%gresp_storage_patch(p) = cs_veg%gresp_storage_patch(p) & + - cf_veg%gru_gresp_storage_to_atm_patch(p) * dt + + ! transfer pools + cs_veg%leafc_xfer_patch(p) = cs_veg%leafc_xfer_patch(p) & + - cf_veg%gru_leafc_xfer_to_atm_patch(p) * dt + cs_veg%frootc_xfer_patch(p) = cs_veg%frootc_xfer_patch(p) & + - cf_veg%gru_frootc_xfer_to_atm_patch(p) * dt + cs_veg%livestemc_xfer_patch(p) = cs_veg%livestemc_xfer_patch(p) & + - cf_veg%gru_livestemc_xfer_to_atm_patch(p) * dt + cs_veg%deadstemc_xfer_patch(p) = cs_veg%deadstemc_xfer_patch(p) & + - cf_veg%gru_deadstemc_xfer_to_atm_patch(p) * dt + cs_veg%livecrootc_xfer_patch(p) = cs_veg%livecrootc_xfer_patch(p) & + - cf_veg%gru_livecrootc_xfer_to_atm_patch(p) * dt + cs_veg%deadcrootc_xfer_patch(p) = cs_veg%deadcrootc_xfer_patch(p) & + - cf_veg%gru_deadcrootc_xfer_to_atm_patch(p) * dt + cs_veg%gresp_xfer_patch(p) = cs_veg%gresp_xfer_patch(p) & + - cf_veg%gru_gresp_xfer_to_atm_patch(p) * dt + + end do ! end of patch loop + + end associate + + end subroutine CStateUpdate2g + end module CNCStateUpdate2Mod diff --git a/src/biogeochem/CNDriverMod.F90 b/src/biogeochem/CNDriverMod.F90 index fee8752d2c..bee506c8cb 100644 --- a/src/biogeochem/CNDriverMod.F90 +++ b/src/biogeochem/CNDriverMod.F90 @@ -6,12 +6,12 @@ module CNDriverMod ! ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 - use clm_varctl , only : use_c13, use_c14, use_fates, use_dynroot - use dynSubgridControlMod , only : get_do_harvest + use clm_varctl , only : use_c13, use_c14, use_fates, use_fates_bgc, use_dynroot + use dynSubgridControlMod , only : get_do_harvest, get_do_grossunrep use decompMod , only : bounds_type use perf_mod , only : t_startf, t_stopf use clm_varctl , only : use_nitrif_denitrif, use_nguardrail - use clm_varctl , only : iulog, use_crop, use_crop_agsys + use clm_varctl , only : iulog, use_crop, use_crop_agsys, use_cn use SoilBiogeochemDecompCascadeConType, only : mimics_decomp, century_decomp, decomp_method use CNSharedParamsMod , only : use_fun use CNVegStateType , only : cnveg_state_type @@ -43,7 +43,7 @@ module CNDriverMod use ActiveLayerMod , only : active_layer_type use SoilWaterRetentionCurveMod , only : soil_water_retention_curve_type use CLMFatesInterfaceMod , only : hlm_fates_interface_type - use CropReprPoolsMod , only : nrepr + use CropReprPoolsMod , only : nrepr ! ! !PUBLIC TYPES: implicit none @@ -77,14 +77,15 @@ subroutine CNDriverInit(bounds, NLFilename, cnfire_method) class(fire_method_type) , intent(inout) :: cnfire_method !----------------------------------------------------------------------- call SoilBiogeochemCompetitionInit(bounds) - call CNPhenologyInit(bounds) - call cnfire_method%FireInit(bounds, NLFilename) - + if(use_cn)then + call CNPhenologyInit(bounds) + call cnfire_method%FireInit(bounds, NLFilename) + end if end subroutine CNDriverInit !----------------------------------------------------------------------- subroutine CNDriverNoLeaching(bounds, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & num_pcropp, filter_pcropp, num_soilnopcropp, filter_soilnopcropp, & num_actfirec, filter_actfirec, num_actfirep, filter_actfirep, & num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & @@ -125,16 +126,17 @@ subroutine CNDriverNoLeaching(bounds, use CNPhenologyMod , only: CNPhenology use CNGRespMod , only: CNGResp use FireMethodType , only: fire_method_type - use CNCIsoFluxMod , only: CIsoFlux1, CIsoFlux2, CIsoFlux2h, CIsoFlux3 + use CNCIsoFluxMod , only: CIsoFlux1, CIsoFlux2, CIsoFlux2h, CIsoFlux2g, CIsoFlux3 use CNC14DecayMod , only: C14Decay use CNCStateUpdate1Mod , only: CStateUpdate1,CStateUpdate0 - use CNCStateUpdate2Mod , only: CStateUpdate2, CStateUpdate2h + use CNCStateUpdate2Mod , only: CStateUpdate2, CStateUpdate2h, CStateUpdate2g use CNCStateUpdate3Mod , only: CStateUpdate3 use CNNStateUpdate1Mod , only: NStateUpdate1 - use CNNStateUpdate2Mod , only: NStateUpdate2, NStateUpdate2h + use CNNStateUpdate2Mod , only: NStateUpdate2, NStateUpdate2h, NStateUpdate2g use CNGapMortalityMod , only: CNGapMortality use CNSharedParamsMod , only: use_fun use dynHarvestMod , only: CNHarvest + use dynGrossUnrepMod , only: CNGrossUnrep use SoilBiogeochemDecompCascadeMIMICSMod, only: decomp_rates_mimics use SoilBiogeochemDecompCascadeBGCMod , only: decomp_rate_constants_bgc use SoilBiogeochemCompetitionMod , only: SoilBiogeochemCompetition @@ -150,10 +152,10 @@ subroutine CNDriverNoLeaching(bounds, ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_vegp ! number of veg patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for veg patches integer , intent(out) :: num_actfirep ! number of soil patches on fire in filter integer , intent(out) :: filter_actfirep(:) ! filter for soil patches on fire integer , intent(out) :: num_actfirec ! number of soil columns on fire in filter @@ -240,58 +242,59 @@ subroutine CNDriverNoLeaching(bounds, ! -------------------------------------------------- ! zero the column-level C and N fluxes ! -------------------------------------------------- - - call t_startf('CNZero') + call t_startf('CNZero') ! COMPILER_BUG(wjs, 2014-11-29, pgi 14.7) Without this, the filter is full of garbage ! in some situations call t_startf('CNZero-soilbgc-cflux') - dummy_to_make_pgi_happy = ubound(filter_soilc, 1) + dummy_to_make_pgi_happy = ubound(filter_bgc_soilc, 1) call soilbiogeochem_carbonflux_inst%SetValues( & - num_soilc, filter_soilc, 0._r8) + num_bgc_soilc, filter_bgc_soilc, 0._r8) if ( use_c13 ) then call c13_soilbiogeochem_carbonflux_inst%SetValues( & - num_soilc, filter_soilc, 0._r8) + num_bgc_soilc, filter_bgc_soilc, 0._r8) end if if ( use_c14 ) then call c14_soilbiogeochem_carbonflux_inst%SetValues( & - num_soilc, filter_soilc, 0._r8) + num_bgc_soilc, filter_bgc_soilc, 0._r8) end if call t_stopf('CNZero-soilbgc-cflux') - call t_startf('CNZero-vegbgc-cflux') - call cnveg_carbonflux_inst%SetValues( & - nvegcpool,& - num_soilp, filter_soilp, 0._r8, & - num_soilc, filter_soilc, 0._r8) - if ( use_c13 ) then - call c13_cnveg_carbonflux_inst%SetValues( & + if(num_bgc_vegp>0)then + call t_startf('CNZero-vegbgc-cflux') + call cnveg_carbonflux_inst%SetValues( & nvegcpool,& - num_soilp, filter_soilp, 0._r8, & - num_soilc, filter_soilc, 0._r8) - end if - if ( use_c14 ) then - call c14_cnveg_carbonflux_inst%SetValues( & - nvegcpool,& - num_soilp, filter_soilp, 0._r8, & - num_soilc, filter_soilc, 0._r8) + num_bgc_vegp, filter_bgc_vegp, 0._r8, & + num_bgc_soilc, filter_bgc_soilc, 0._r8) + if ( use_c13 ) then + call c13_cnveg_carbonflux_inst%SetValues( & + nvegcpool,& + num_bgc_vegp, filter_bgc_vegp, 0._r8, & + num_bgc_soilc, filter_bgc_soilc, 0._r8) + end if + if ( use_c14 ) then + call c14_cnveg_carbonflux_inst%SetValues( & + nvegcpool,& + num_bgc_vegp, filter_bgc_vegp, 0._r8, & + num_bgc_soilc, filter_bgc_soilc, 0._r8) + end if + call t_stopf('CNZero-vegbgc-cflux') + + call t_startf('CNZero-vegbgc-nflux') + call cnveg_nitrogenflux_inst%SetValues( & + nvegnpool, & + num_bgc_vegp, filter_bgc_vegp, 0._r8, & + num_bgc_soilc, filter_bgc_soilc, 0._r8) end if - call t_stopf('CNZero-vegbgc-cflux') - - call t_startf('CNZero-vegbgc-nflux') - call cnveg_nitrogenflux_inst%SetValues( & - nvegnpool, & - num_soilp, filter_soilp, 0._r8, & - num_soilc, filter_soilc, 0._r8) - + call t_stopf('CNZero-vegbgc-nflux') call t_startf('CNZero-soilbgc-nflux') call soilbiogeochem_nitrogenflux_inst%SetValues( & - num_soilc, filter_soilc, 0._r8) + num_bgc_soilc, filter_bgc_soilc, 0._r8) call t_stopf('CNZero-soilbgc-nflux') call t_stopf('CNZero') - + ! -------------------------------------------------- ! Nitrogen Deposition, Fixation and Respiration ! -------------------------------------------------- @@ -303,30 +306,31 @@ subroutine CNDriverNoLeaching(bounds, if(use_fun)then call t_startf('CNFLivFixation') - call CNFreeLivingFixation( num_soilc, filter_soilc, & + call CNFreeLivingFixation( num_bgc_soilc, filter_bgc_soilc, & waterfluxbulk_inst, soilbiogeochem_nitrogenflux_inst) call t_stopf('CNFLivFixation') else call t_startf('CNFixation') - call CNNFixation( num_soilc, filter_soilc, & - cnveg_carbonflux_inst, soilbiogeochem_nitrogenflux_inst) + call CNNFixation( num_bgc_soilc, filter_bgc_soilc, & + cnveg_carbonflux_inst, soilbiogeochem_nitrogenflux_inst, & + clm_fates, bounds%clump_index) call t_stopf('CNFixation') end if if (use_crop) then - call CNNFert(bounds, num_soilc,filter_soilc, & + call CNNFert(bounds, num_bgc_soilc,filter_bgc_soilc, & cnveg_nitrogenflux_inst, soilbiogeochem_nitrogenflux_inst) if (.not. use_fun) then ! if FUN is active, then soy fixation handled by FUN - call CNSoyfix (bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & + call CNSoyfix (bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & waterdiagnosticbulk_inst, crop_inst, cnveg_state_inst, cnveg_nitrogenflux_inst , & soilbiogeochem_state_inst, soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst) end if end if call t_startf('CNMResp') - call CNMResp(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & + call CNMResp(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & canopystate_inst, soilstate_inst, temperature_inst, photosyns_inst, & cnveg_carbonflux_inst, cnveg_nitrogenstate_inst) call t_stopf('CNMResp') @@ -338,19 +342,21 @@ subroutine CNDriverNoLeaching(bounds, call t_startf('SoilBiogeochem') call t_startf('DecompRate') if (decomp_method == century_decomp) then - call decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & - soilstate_inst, temperature_inst, ch4_inst, soilbiogeochem_carbonflux_inst) + call decomp_rate_constants_bgc(bounds, num_bgc_soilc, filter_bgc_soilc, & + soilstate_inst, temperature_inst, ch4_inst, soilbiogeochem_carbonflux_inst, & + cnveg_state_inst%idop_patch) else if (decomp_method == mimics_decomp) then - call decomp_rates_mimics(bounds, num_soilc, filter_soilc, & - num_soilp, filter_soilp, clm_fates, & + call decomp_rates_mimics(bounds, num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, clm_fates, & soilstate_inst, temperature_inst, cnveg_carbonflux_inst, ch4_inst, & - soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst) + soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & + cnveg_state_inst%idop_patch) end if call t_stopf('DecompRate') call t_startf('SoilBiogeochemPotential') ! calculate potential decomp rates and total immobilization demand (previously inlined in CNDecompAlloc) - call SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & + call SoilBiogeochemPotential (bounds, num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_state_inst, soilbiogeochem_carbonstate_inst, soilbiogeochem_carbonflux_inst, & soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & cn_decomp_pools=cn_decomp_pools(begc:endc,1:nlevdecomp,1:ndecomp_pools), & @@ -360,13 +366,14 @@ subroutine CNDriverNoLeaching(bounds, p_decomp_npool_to_din=p_decomp_npool_to_din(begc:endc,1:nlevdecomp,1:ndecomp_cascade_transitions)) call t_stopf('SoilBiogeochemPotential') - ! calculate vertical profiles for distributing soil and litter C and N (previously subroutine decomp_vertprofiles called from CNDecompAlloc) - call SoilBiogeochemVerticalProfile(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & + ! calculate vertical profiles for distributing soil and litter C and N + ! (previously subroutine decomp_vertprofiles called from CNDecompAlloc) + call SoilBiogeochemVerticalProfile(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & active_layer_inst, soilstate_inst,soilbiogeochem_state_inst) ! calculate nitrification and denitrification rates (previously subroutine nitrif_denitrif called from CNDecompAlloc) if (use_nitrif_denitrif) then - call SoilBiogeochemNitrifDenitrif(bounds, num_soilc, filter_soilc, & + call SoilBiogeochemNitrifDenitrif(bounds, num_bgc_soilc, filter_bgc_soilc, & soilstate_inst, waterstatebulk_inst, temperature_inst, ch4_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst) end if @@ -388,43 +395,45 @@ subroutine CNDriverNoLeaching(bounds, ! do_nutrient_competition should be modified, but that modification should not significantly change ! the current interface. - !RF: moved ths call to before nutrient_demand, so that croplive didn't change half way through crop N cycle. - if ( use_fun ) then - call t_startf('CNPhenology_phase1') - call CNPhenology (bounds, num_soilc, filter_soilc, num_soilp, & - filter_soilp, num_pcropp, filter_pcropp, & - waterdiagnosticbulk_inst, wateratm2lndbulk_inst, temperature_inst, atm2lnd_inst, & - crop_inst, canopystate_inst, soilstate_inst, dgvs_inst, & - cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst, & - leaf_prof_patch=soilbiogeochem_state_inst%leaf_prof_patch(begp:endp,1:nlevdecomp_full), & - froot_prof_patch=soilbiogeochem_state_inst%froot_prof_patch(begp:endp,1:nlevdecomp_full), & - phase=1) - call t_stopf('CNPhenology_phase1') - - call t_startf('CNFUNInit') - call CNFUNInit(bounds,cnveg_state_inst,cnveg_carbonstate_inst,cnveg_nitrogenstate_inst) - call t_stopf('CNFUNInit') - - end if - - call t_startf('cnalloc') - call calc_gpp_mr_availc( & - bounds, num_soilp, filter_soilp, & - crop_inst, photosyns_inst, canopystate_inst, & - cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst) - - if (.not. use_crop_agsys) then - call calc_crop_allocation_fractions(bounds, num_pcropp, filter_pcropp, & - crop_inst, cnveg_state_inst) - end if - - call calc_allometry(num_soilp, filter_soilp, & - cnveg_carbonflux_inst, cnveg_state_inst) - call t_stopf('cnalloc') - + !RF: moved ths call to before nutrient_demand, so that croplive didn't change half way through crop N cycle. + if(num_bgc_vegp>0)then + if ( use_fun) then + call t_startf('CNPhenology_phase1') + call CNPhenology (bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, & + filter_bgc_vegp, num_pcropp, filter_pcropp, & + waterdiagnosticbulk_inst, wateratm2lndbulk_inst, temperature_inst, atm2lnd_inst, & + crop_inst, canopystate_inst, soilstate_inst, dgvs_inst, & + cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & + cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & + c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst, & + leaf_prof_patch=soilbiogeochem_state_inst%leaf_prof_patch(begp:endp,1:nlevdecomp_full), & + froot_prof_patch=soilbiogeochem_state_inst%froot_prof_patch(begp:endp,1:nlevdecomp_full), & + phase=1) + call t_stopf('CNPhenology_phase1') + + call t_startf('CNFUNInit') + call CNFUNInit(bounds,cnveg_state_inst,cnveg_carbonstate_inst,cnveg_nitrogenstate_inst) + call t_stopf('CNFUNInit') + + end if + + call t_startf('cnalloc') + call calc_gpp_mr_availc( & + bounds, num_bgc_vegp, filter_bgc_vegp, & + crop_inst, photosyns_inst, canopystate_inst, & + cnveg_carbonstate_inst, cnveg_carbonflux_inst, & + c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst) + + if (.not. use_crop_agsys) then + call calc_crop_allocation_fractions(bounds, num_pcropp, filter_pcropp, & + crop_inst, cnveg_state_inst) + end if + + call calc_allometry(num_bgc_vegp, filter_bgc_vegp, & + cnveg_carbonflux_inst, cnveg_state_inst) + call t_stopf('cnalloc') + end if + call t_startf('calc_plant_nutrient_demand') ! We always call calc_plant_nutrient_demand for natural veg patches, but only call ! it for crop patches if NOT running with AgSys (since AgSys calculates the relevant @@ -450,16 +459,23 @@ subroutine CNDriverNoLeaching(bounds, ! get the column-averaged plant_ndemand (needed for following call to SoilBiogeochemCompetition) - call p2c(bounds, num_soilc, filter_soilc, & - cnveg_nitrogenflux_inst%plant_ndemand_patch(begp:endp), & - soilbiogeochem_state_inst%plant_ndemand_col(begc:endc)) + if(num_bgc_vegp>0)then + call p2c(bounds, num_bgc_soilc, filter_bgc_soilc, & + cnveg_nitrogenflux_inst%plant_ndemand_patch(begp:endp), & + soilbiogeochem_state_inst%plant_ndemand_col(begc:endc)) + else + ! With FATES N coupling, we will have a call to fill + ! this in on the filter_bgc_soilc + soilbiogeochem_state_inst%plant_ndemand_col(begc:endc) = 0._r8 + end if + call t_stopf('calc_plant_nutrient_demand') ! resolve plant/heterotroph competition for mineral N call t_startf('soilbiogeochemcompetition') - call SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, filter_soilp, & + call SoilBiogeochemCompetition (bounds, num_bgc_soilc, filter_bgc_soilc,num_bgc_vegp, filter_bgc_vegp, & p_decomp_cn_gain, pmnf_decomp_cascade, waterstatebulk_inst, & waterfluxbulk_inst,temperature_inst,soilstate_inst,cnveg_state_inst, & cnveg_carbonstate_inst ,& @@ -474,7 +490,7 @@ subroutine CNDriverNoLeaching(bounds, call t_startf('calc_plant_nutrient_competition') call nutrient_competition_method%calc_plant_nutrient_competition ( & - bounds, num_soilp, filter_soilp, & + bounds, num_bgc_vegp, filter_bgc_vegp, & cnveg_state_inst, crop_inst, canopystate_inst, & cnveg_carbonstate_inst, cnveg_carbonflux_inst, & c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & @@ -494,7 +510,7 @@ subroutine CNDriverNoLeaching(bounds, call t_startf('SoilBiogeochemDecomp') - call SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, & + call SoilBiogeochemDecomp (bounds, num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_state_inst, soilbiogeochem_carbonstate_inst, soilbiogeochem_carbonflux_inst, & soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & cn_decomp_pools=cn_decomp_pools(begc:endc,1:nlevdecomp,1:ndecomp_pools), & @@ -510,12 +526,22 @@ subroutine CNDriverNoLeaching(bounds, ! CNphenology needs to be called after above calls, since it depends on current ! time-step fluxes to new growth on the lastlitterfall timestep in deciduous systems - - call t_startf('CNPhenology') - - if ( .not. use_fun ) then - call CNPhenology (bounds, num_soilc, filter_soilc, num_soilp, & - filter_soilp, num_pcropp, filter_pcropp, & + if(num_bgc_vegp>0)then + call t_startf('CNPhenology') + if ( .not. use_fun ) then + call CNPhenology (bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, & + filter_bgc_vegp, num_pcropp, filter_pcropp, & + waterdiagnosticbulk_inst, wateratm2lndbulk_inst, temperature_inst, atm2lnd_inst, & + crop_inst, canopystate_inst, soilstate_inst, dgvs_inst, & + cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & + cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & + c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst, & + leaf_prof_patch=soilbiogeochem_state_inst%leaf_prof_patch(begp:endp,1:nlevdecomp_full), & + froot_prof_patch=soilbiogeochem_state_inst%froot_prof_patch(begp:endp,1:nlevdecomp_full), & + phase=1) + end if + call CNPhenology (bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, & + filter_bgc_vegp, num_pcropp, filter_pcropp, & waterdiagnosticbulk_inst, wateratm2lndbulk_inst, temperature_inst, atm2lnd_inst, & crop_inst, canopystate_inst, soilstate_inst, dgvs_inst, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & @@ -523,28 +549,17 @@ subroutine CNDriverNoLeaching(bounds, c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst, & leaf_prof_patch=soilbiogeochem_state_inst%leaf_prof_patch(begp:endp,1:nlevdecomp_full), & froot_prof_patch=soilbiogeochem_state_inst%froot_prof_patch(begp:endp,1:nlevdecomp_full), & - phase=1) + phase=2) + + call t_stopf('CNPhenology') end if - call CNPhenology (bounds, num_soilc, filter_soilc, num_soilp, & - filter_soilp, num_pcropp, filter_pcropp, & - waterdiagnosticbulk_inst, wateratm2lndbulk_inst, temperature_inst, atm2lnd_inst, & - crop_inst, canopystate_inst, soilstate_inst, dgvs_inst, & - cnveg_state_inst, cnveg_carbonstate_inst, cnveg_carbonflux_inst, & - cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst, & - leaf_prof_patch=soilbiogeochem_state_inst%leaf_prof_patch(begp:endp,1:nlevdecomp_full), & - froot_prof_patch=soilbiogeochem_state_inst%froot_prof_patch(begp:endp,1:nlevdecomp_full), & - phase=2) - - call t_stopf('CNPhenology') - !-------------------------------------------- ! Growth respiration !-------------------------------------------- call t_startf('CNGResp') - call CNGResp(num_soilp, filter_soilp,& + call CNGResp(num_bgc_vegp, filter_bgc_vegp,& cnveg_carbonflux_inst, canopystate_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst) call t_stopf('CNGResp') @@ -556,7 +571,7 @@ subroutine CNDriverNoLeaching(bounds, if( use_dynroot ) then call t_startf('CNRootDyn') - call CNRootDyn(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & + call CNRootDyn(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, cnveg_carbonflux_inst, & cnveg_state_inst, crop_inst, soilstate_inst, soilbiogeochem_nitrogenstate_inst) @@ -572,24 +587,24 @@ subroutine CNDriverNoLeaching(bounds, call t_startf('CNUpdate0') - call CStateUpdate0(num_soilp, filter_soilp, & + call CStateUpdate0(num_bgc_vegp, filter_bgc_vegp, & cnveg_carbonflux_inst, cnveg_carbonstate_inst) if ( use_c13 ) then - call CStateUpdate0(num_soilp, filter_soilp, & + call CStateUpdate0(num_bgc_vegp, filter_bgc_vegp, & c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst) end if if ( use_c14 ) then - call CStateUpdate0(num_soilp, filter_soilp, & + call CStateUpdate0(num_bgc_vegp, filter_bgc_vegp, & c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst) end if call t_stopf('CNUpdate0') - if ( use_nguardrail ) then + if ( use_nguardrail .and. num_bgc_vegp>0 ) then call t_startf('CNPrecisionControl') - call CNPrecisionControl(bounds, num_soilp, filter_soilp, & + call CNPrecisionControl(bounds, num_bgc_vegp, filter_bgc_vegp, & cnveg_carbonstate_inst, c13_cnveg_carbonstate_inst, & c14_cnveg_carbonstate_inst, cnveg_nitrogenstate_inst) call t_stopf('CNPrecisionControl') @@ -606,7 +621,7 @@ subroutine CNDriverNoLeaching(bounds, ! Set the carbon isotopic flux variables (except for gap-phase mortality and fire fluxes) if ( use_c13 ) then - call CIsoFlux1(num_soilc, filter_soilc, num_soilp, filter_soilp, & + call CIsoFlux1(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & soilbiogeochem_state_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & cnveg_carbonflux_inst, cnveg_carbonstate_inst, & @@ -615,7 +630,7 @@ subroutine CNDriverNoLeaching(bounds, isotope='c13') end if if ( use_c14 ) then - call CIsoFlux1(num_soilc, filter_soilc, num_soilp, filter_soilp, & + call CIsoFlux1(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & soilbiogeochem_state_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & cnveg_carbonflux_inst, cnveg_carbonstate_inst, & @@ -625,36 +640,40 @@ subroutine CNDriverNoLeaching(bounds, end if ! Update all prognostic carbon state variables (except for gap-phase mortality and fire fluxes) - call CStateUpdate1( num_soilc, filter_soilc, num_soilp, filter_soilp, & + call CStateUpdate1( num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & crop_inst, cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - soilbiogeochem_carbonflux_inst, dribble_crophrv_xsmrpool_2atm) + soilbiogeochem_carbonflux_inst, dribble_crophrv_xsmrpool_2atm, & + clm_fates, bounds%clump_index) if ( use_c13 ) then - call CStateUpdate1(num_soilc, filter_soilc, num_soilp, filter_soilp, & + call CStateUpdate1(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & crop_inst, c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, & - c13_soilbiogeochem_carbonflux_inst, dribble_crophrv_xsmrpool_2atm) + c13_soilbiogeochem_carbonflux_inst, dribble_crophrv_xsmrpool_2atm, & + clm_fates, bounds%clump_index) end if if ( use_c14 ) then - call CStateUpdate1(num_soilc, filter_soilc, num_soilp, filter_soilp, & + call CStateUpdate1(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & crop_inst, c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, & - c14_soilbiogeochem_carbonflux_inst, dribble_crophrv_xsmrpool_2atm) + c14_soilbiogeochem_carbonflux_inst, dribble_crophrv_xsmrpool_2atm, & + clm_fates, bounds%clump_index) end if ! Update all prognostic nitrogen state variables (except for gap-phase mortality and fire fluxes) - call NStateUpdate1(num_soilc, filter_soilc, num_soilp, filter_soilp, & - cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst) + call NStateUpdate1(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & + clm_fates, bounds%clump_index) call t_stopf('CNUpdate1') - if ( use_nguardrail ) then + if ( use_nguardrail .and. num_bgc_vegp>0 ) then call t_startf('CNPrecisionControl') - call CNPrecisionControl(bounds, num_soilp, filter_soilp, & + call CNPrecisionControl(bounds, num_bgc_vegp, filter_bgc_vegp, & cnveg_carbonstate_inst, c13_cnveg_carbonstate_inst, & c14_cnveg_carbonstate_inst, cnveg_nitrogenstate_inst) call t_stopf('CNPrecisionControl') end if call t_startf('SoilBiogeochemStateUpdate1') - call SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & + call SoilBiogeochemNStateUpdate1(num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_state_inst, soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst) call t_stopf('SoilBiogeochemStateUpdate1') @@ -665,7 +684,7 @@ subroutine CNDriverNoLeaching(bounds, call t_startf('SoilBiogeochemLittVertTransp') - call SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & + call SoilBiogeochemLittVertTransp(bounds, num_bgc_soilc, filter_bgc_soilc, & active_layer_inst, soilbiogeochem_state_inst, & soilbiogeochem_carbonstate_inst, soilbiogeochem_carbonflux_inst, & c13_soilbiogeochem_carbonstate_inst, c13_soilbiogeochem_carbonflux_inst, & @@ -678,237 +697,317 @@ subroutine CNDriverNoLeaching(bounds, ! Calculate the gap mortality carbon and nitrogen fluxes !-------------------------------------------- - call t_startf('CNGapMortality') + if_bgc_vegp1: if(num_bgc_vegp>0)then - call CNGapMortality (bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & - dgvs_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & - cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, canopystate_inst, & - !cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & - leaf_prof_patch=soilbiogeochem_state_inst%leaf_prof_patch(begp:endp, 1:nlevdecomp_full), & - froot_prof_patch=soilbiogeochem_state_inst%froot_prof_patch(begp:endp, 1:nlevdecomp_full), & - croot_prof_patch=soilbiogeochem_state_inst%croot_prof_patch(begp:endp, 1:nlevdecomp_full), & - stem_prof_patch=soilbiogeochem_state_inst%stem_prof_patch(begp:endp, 1:nlevdecomp_full)) + call t_startf('CNGapMortality') - call t_stopf('CNGapMortality') + call CNGapMortality (bounds, num_bgc_vegp, filter_bgc_vegp, & + dgvs_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & + cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, canopystate_inst, & + leaf_prof_patch=soilbiogeochem_state_inst%leaf_prof_patch(begp:endp, 1:nlevdecomp_full), & + froot_prof_patch=soilbiogeochem_state_inst%froot_prof_patch(begp:endp, 1:nlevdecomp_full), & + croot_prof_patch=soilbiogeochem_state_inst%croot_prof_patch(begp:endp, 1:nlevdecomp_full), & + stem_prof_patch=soilbiogeochem_state_inst%stem_prof_patch(begp:endp, 1:nlevdecomp_full)) - !-------------------------------------------------------------------------- - ! Update2 (gap mortality) - ! The state updates are still called for the matrix solution (use_matrixn - ! and use_soil_matrixcn) but most of the state updates are done after - ! the matrix multiply in VegMatrix and SoilMatrix. - !-------------------------------------------------------------------------- + call t_stopf('CNGapMortality') - call t_startf('CNUpdate2') + !-------------------------------------------------------------------------- + ! Update2 (gap mortality) + ! The state updates are still called for the matrix solution (use_matrixn + ! and use_soil_matrixcn) but most of the state updates are done after + ! the matrix multiply in VegMatrix and SoilMatrix. + !-------------------------------------------------------------------------- - ! Set the carbon isotopic fluxes for gap mortality - if ( use_c13 ) then - call CIsoFlux2(num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_state_inst, cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - iso_cnveg_carbonflux_inst=c13_cnveg_carbonflux_inst, & - iso_cnveg_carbonstate_inst=c13_cnveg_carbonstate_inst, & - isotope='c13') - end if - if ( use_c14 ) then - call CIsoFlux2(num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_state_inst, cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - iso_cnveg_carbonflux_inst=c14_cnveg_carbonflux_inst, & - iso_cnveg_carbonstate_inst=c14_cnveg_carbonstate_inst, & - isotope='c14') - end if + call t_startf('CNUpdate2') - ! Update all the prognostic carbon state variables affected by gap-phase mortality fluxes - call CStateUpdate2(num_soilc, filter_soilc, num_soilp, filter_soilp, & - cnveg_carbonflux_inst, cnveg_carbonstate_inst, soilbiogeochem_carbonstate_inst, & - soilbiogeochem_carbonflux_inst) - if ( use_c13 ) then - call CStateUpdate2(num_soilc, filter_soilc, num_soilp, filter_soilp, & + ! Set the carbon isotopic fluxes for gap mortality + if ( use_c13 ) then + call CIsoFlux2(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst, cnveg_carbonflux_inst, cnveg_carbonstate_inst, & + iso_cnveg_carbonflux_inst=c13_cnveg_carbonflux_inst, & + iso_cnveg_carbonstate_inst=c13_cnveg_carbonstate_inst, & + isotope='c13') + end if + if ( use_c14 ) then + call CIsoFlux2(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst, cnveg_carbonflux_inst, cnveg_carbonstate_inst, & + iso_cnveg_carbonflux_inst=c14_cnveg_carbonflux_inst, & + iso_cnveg_carbonstate_inst=c14_cnveg_carbonstate_inst, & + isotope='c14') + end if + + ! Update all the prognostic carbon state variables affected by gap-phase mortality fluxes + call CStateUpdate2(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, soilbiogeochem_carbonstate_inst, & + soilbiogeochem_carbonflux_inst) + if ( use_c13 ) then + call CStateUpdate2(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst, & + c13_soilbiogeochem_carbonflux_inst) + end if + if ( use_c14 ) then + call CStateUpdate2(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, c14_soilbiogeochem_carbonstate_inst, & + c14_soilbiogeochem_carbonflux_inst) + end if + + ! Update all the prognostic nitrogen state variables affected by gap-phase mortality fluxes + call NStateUpdate2(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst,soilbiogeochem_nitrogenstate_inst, & + soilbiogeochem_nitrogenflux_inst) + + !-------------------------------------------------------------------------- + ! Update2h (harvest) + ! The state updates are still called for the matrix solution (use_matrixn + ! and use_soil_matrixcn) but most of the state updates are done after + ! the matrix multiply in VegMatrix and SoilMatrix. + !-------------------------------------------------------------------------- + + ! Set harvest mortality routine + if (get_do_harvest()) then + call CNHarvest(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & + cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) + end if + + if ( use_c13 ) then + call CIsoFlux2h(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, & + c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, & + isotope='c13') + end if + if ( use_c14 ) then + call CIsoFlux2h(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, & + c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, & + isotope='c14') + end if + + call CStateUpdate2h( num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, soilbiogeochem_carbonstate_inst, & + soilbiogeochem_carbonflux_inst) + if ( use_c13 ) then + call CStateUpdate2h(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst, & c13_soilbiogeochem_carbonflux_inst) - end if - if ( use_c14 ) then - call CStateUpdate2(num_soilc, filter_soilc, num_soilp, filter_soilp, & - c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, c14_soilbiogeochem_carbonstate_inst, & - c14_soilbiogeochem_carbonflux_inst) - end if + end if + if ( use_c14 ) then + call CStateUpdate2h(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, c14_soilbiogeochem_carbonstate_inst, & + c14_soilbiogeochem_carbonflux_inst) + end if - ! Update all the prognostic nitrogen state variables affected by gap-phase mortality fluxes - call NStateUpdate2(num_soilc, filter_soilc, num_soilp, filter_soilp, & - cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst,soilbiogeochem_nitrogenstate_inst, & - soilbiogeochem_nitrogenflux_inst) + call NStateUpdate2h(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenstate_inst, & + soilbiogeochem_nitrogenflux_inst) - !-------------------------------------------------------------------------- - ! Update2h (harvest) - ! The state updates are still called for the matrix solution (use_matrixn - ! and use_soil_matrixcn) but most of the state updates are done after - ! the matrix multiply in VegMatrix and SoilMatrix. - !-------------------------------------------------------------------------- + !-------------------------------------------- + ! Update2g (gross unrepresented landcover change) + !-------------------------------------------- - ! Set harvest mortality routine - if (get_do_harvest()) then - call CNHarvest(num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & - cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) - end if + ! Set gross unrepresented landcover change mortality routine + if (get_do_grossunrep()) then + call CNGrossUnrep(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & + cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) + end if - if ( use_c13 ) then - call CIsoFlux2h(num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_state_inst, & - cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, & - isotope='c13') - end if - if ( use_c14 ) then - call CIsoFlux2h(num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_state_inst, & - cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, & - isotope='c14') - end if + if ( use_c13 ) then + call CIsoFlux2g(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, & + c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, & + isotope='c13') + end if + if ( use_c14 ) then + call CIsoFlux2g(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, & + c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, & + isotope='c14') + end if - call CStateUpdate2h( num_soilc, filter_soilc, num_soilp, filter_soilp, & - cnveg_carbonflux_inst, cnveg_carbonstate_inst, soilbiogeochem_carbonstate_inst, & - soilbiogeochem_carbonflux_inst) - if ( use_c13 ) then - call CStateUpdate2h(num_soilc, filter_soilc, num_soilp, filter_soilp, & - c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst, & - c13_soilbiogeochem_carbonflux_inst) - end if - if ( use_c14 ) then - call CStateUpdate2h(num_soilc, filter_soilc, num_soilp, filter_soilp, & - c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, c14_soilbiogeochem_carbonstate_inst, & - c14_soilbiogeochem_carbonflux_inst) - end if + call CStateUpdate2g( num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, soilbiogeochem_carbonstate_inst) + if ( use_c13 ) then + call CStateUpdate2g(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst) + end if + if ( use_c14 ) then + call CStateUpdate2g(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, c14_soilbiogeochem_carbonstate_inst) + end if - call NStateUpdate2h(num_soilc, filter_soilc, num_soilp, filter_soilp, & - cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenstate_inst, & - soilbiogeochem_nitrogenflux_inst) - call t_stopf('CNUpdate2') + call NStateUpdate2g(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenstate_inst) + + call t_stopf('CNUpdate2') + + end if if_bgc_vegp1 + + if ( use_nguardrail .and. num_bgc_vegp>0 ) then - if ( use_nguardrail ) then call t_startf('CNPrecisionControl') - call CNPrecisionControl(bounds, num_soilp, filter_soilp, & + call CNPrecisionControl(bounds, num_bgc_vegp, filter_bgc_vegp, & cnveg_carbonstate_inst, c13_cnveg_carbonstate_inst, & c14_cnveg_carbonstate_inst, cnveg_nitrogenstate_inst) call t_stopf('CNPrecisionControl') + end if + !-------------------------------------------- ! Calculate loss fluxes from wood products pools ! and update product pool state variables !-------------------------------------------- call t_startf('CNWoodProducts') - call c_products_inst%UpdateProducts(bounds, & - num_soilp, filter_soilp, & - dwt_wood_product_gain_patch = cnveg_carbonflux_inst%dwt_wood_productc_gain_patch(begp:endp), & - wood_harvest_patch = cnveg_carbonflux_inst%wood_harvestc_patch(begp:endp), & - dwt_crop_product_gain_patch = cnveg_carbonflux_inst%dwt_crop_productc_gain_patch(begp:endp), & - crop_harvest_to_cropprod_patch = cnveg_carbonflux_inst%crop_harvestc_to_cropprodc_patch(begp:endp)) - call t_stopf('CNWoodProducts') - - if (use_c13) then - call c13_products_inst%UpdateProducts(bounds, & - num_soilp, filter_soilp, & - dwt_wood_product_gain_patch = c13_cnveg_carbonflux_inst%dwt_wood_productc_gain_patch(begp:endp), & - wood_harvest_patch = c13_cnveg_carbonflux_inst%wood_harvestc_patch(begp:endp), & - dwt_crop_product_gain_patch = c13_cnveg_carbonflux_inst%dwt_crop_productc_gain_patch(begp:endp), & - crop_harvest_to_cropprod_patch = c13_cnveg_carbonflux_inst%crop_harvestc_to_cropprodc_patch(begp:endp)) + + call c_products_inst%SetValues(bounds,0._r8) + if (use_c13) call c13_products_inst%SetValues(bounds,0._r8) + if (use_c14) call c14_products_inst%SetValues(bounds,0._r8) + call n_products_inst%SetValues(bounds,0._r8) + + if(use_fates_bgc) then + call clm_fates%wrap_WoodProducts(bounds, num_bgc_soilc, filter_bgc_soilc, c_products_inst, n_products_inst) end if - if (use_c14) then - call c14_products_inst%UpdateProducts(bounds, & - num_soilp, filter_soilp, & - dwt_wood_product_gain_patch = c14_cnveg_carbonflux_inst%dwt_wood_productc_gain_patch(begp:endp), & - wood_harvest_patch = c14_cnveg_carbonflux_inst%wood_harvestc_patch(begp:endp), & - dwt_crop_product_gain_patch = c14_cnveg_carbonflux_inst%dwt_crop_productc_gain_patch(begp:endp), & - crop_harvest_to_cropprod_patch = c14_cnveg_carbonflux_inst%crop_harvestc_to_cropprodc_patch(begp:endp)) - end if + if_bgc_vegp2: if(num_bgc_vegp>0)then + call c_products_inst%UpdateProducts(bounds, & + num_bgc_vegp, filter_bgc_vegp, & + dwt_wood_product_gain_patch = cnveg_carbonflux_inst%dwt_wood_productc_gain_patch(begp:endp), & + gru_wood_product_gain_patch = cnveg_carbonflux_inst%gru_wood_productc_gain_patch(begp:endp), & + wood_harvest_patch = cnveg_carbonflux_inst%wood_harvestc_patch(begp:endp), & + dwt_crop_product_gain_patch = cnveg_carbonflux_inst%dwt_crop_productc_gain_patch(begp:endp), & + crop_harvest_to_cropprod_patch = cnveg_carbonflux_inst%crop_harvestc_to_cropprodc_patch(begp:endp)) + + if (use_c13) then + call c13_products_inst%UpdateProducts(bounds, & + num_bgc_vegp, filter_bgc_vegp, & + dwt_wood_product_gain_patch = c13_cnveg_carbonflux_inst%dwt_wood_productc_gain_patch(begp:endp), & + gru_wood_product_gain_patch = c13_cnveg_carbonflux_inst%gru_wood_productc_gain_patch(begp:endp), & + wood_harvest_patch = c13_cnveg_carbonflux_inst%wood_harvestc_patch(begp:endp), & + dwt_crop_product_gain_patch = c13_cnveg_carbonflux_inst%dwt_crop_productc_gain_patch(begp:endp), & + crop_harvest_to_cropprod_patch = c13_cnveg_carbonflux_inst%crop_harvestc_to_cropprodc_patch(begp:endp)) + end if + + if (use_c14) then + call c14_products_inst%UpdateProducts(bounds, & + num_bgc_vegp, filter_bgc_vegp, & + dwt_wood_product_gain_patch = c14_cnveg_carbonflux_inst%dwt_wood_productc_gain_patch(begp:endp), & + gru_wood_product_gain_patch = c14_cnveg_carbonflux_inst%gru_wood_productc_gain_patch(begp:endp), & + wood_harvest_patch = c14_cnveg_carbonflux_inst%wood_harvestc_patch(begp:endp), & + dwt_crop_product_gain_patch = c14_cnveg_carbonflux_inst%dwt_crop_productc_gain_patch(begp:endp), & + crop_harvest_to_cropprod_patch = c14_cnveg_carbonflux_inst%crop_harvestc_to_cropprodc_patch(begp:endp)) + end if - call n_products_inst%UpdateProducts(bounds, & - num_soilp, filter_soilp, & - dwt_wood_product_gain_patch = cnveg_nitrogenflux_inst%dwt_wood_productn_gain_patch(begp:endp), & - wood_harvest_patch = cnveg_nitrogenflux_inst%wood_harvestn_patch(begp:endp), & - dwt_crop_product_gain_patch = cnveg_nitrogenflux_inst%dwt_crop_productn_gain_patch(begp:endp), & - crop_harvest_to_cropprod_patch = cnveg_nitrogenflux_inst%crop_harvestn_to_cropprodn_patch(begp:endp)) + call n_products_inst%UpdateProducts(bounds, & + num_bgc_vegp, filter_bgc_vegp, & + dwt_wood_product_gain_patch = cnveg_nitrogenflux_inst%dwt_wood_productn_gain_patch(begp:endp), & + gru_wood_product_gain_patch = cnveg_nitrogenflux_inst%gru_wood_productn_gain_patch(begp:endp), & + wood_harvest_patch = cnveg_nitrogenflux_inst%wood_harvestn_patch(begp:endp), & + dwt_crop_product_gain_patch = cnveg_nitrogenflux_inst%dwt_crop_productn_gain_patch(begp:endp), & + crop_harvest_to_cropprod_patch = cnveg_nitrogenflux_inst%crop_harvestn_to_cropprodn_patch(begp:endp)) + + end if if_bgc_vegp2 + + call c_products_inst%ComputeProductSummaryVars(bounds) + if (use_c13) call c13_products_inst%ComputeProductSummaryVars(bounds) + if (use_c14) call c14_products_inst%ComputeProductSummaryVars(bounds) + call n_products_inst%ComputeProductSummaryVars(bounds) + + call c_products_inst%ComputeSummaryVars(bounds) + if (use_c13) call c13_products_inst%ComputeSummaryVars(bounds) + if (use_c14) call c14_products_inst%ComputeSummaryVars(bounds) + call n_products_inst%ComputeSummaryVars(bounds) + + call t_stopf('CNWoodProducts') + !-------------------------------------------- ! Calculate fire area and fluxes !-------------------------------------------- - call t_startf('CNFire') - call cnfire_method%CNFireArea(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & - num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & - atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, waterdiagnosticbulk_inst, wateratm2lndbulk_inst, & - waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & - cnveg_state_inst, cnveg_carbonstate_inst, & - totlitc_col=soilbiogeochem_carbonstate_inst%totlitc_col(begc:endc), & - decomp_cpools_vr_col=soilbiogeochem_carbonstate_inst%decomp_cpools_vr_col(begc:endc,1:nlevdecomp_full,1:ndecomp_pools), & - t_soi17cm_col=temperature_inst%t_soi17cm_col(begc:endc)) - - call cnfire_method%CNFireFluxes(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & - num_actfirec, filter_actfirec, num_actfirep, filter_actfirep, & - dgvs_inst, cnveg_state_inst, & - cnveg_carbonstate_inst, cnveg_carbonflux_inst, cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & - soilbiogeochem_carbonflux_inst, & - leaf_prof_patch=soilbiogeochem_state_inst%leaf_prof_patch(begp:endp, 1:nlevdecomp_full), & - froot_prof_patch=soilbiogeochem_state_inst%froot_prof_patch(begp:endp, 1:nlevdecomp_full), & - croot_prof_patch=soilbiogeochem_state_inst%croot_prof_patch(begp:endp, 1:nlevdecomp_full), & - stem_prof_patch=soilbiogeochem_state_inst%stem_prof_patch(begp:endp, 1:nlevdecomp_full), & - totsomc_col=soilbiogeochem_carbonstate_inst%totsomc_col(begc:endc), & - decomp_cpools_vr_col=soilbiogeochem_carbonstate_inst%decomp_cpools_vr_col(begc:endc,1:nlevdecomp_full,1:ndecomp_pools), & - decomp_npools_vr_col=soilbiogeochem_nitrogenstate_inst%decomp_npools_vr_col(begc:endc,1:nlevdecomp_full,1:ndecomp_pools), & - somc_fire_col=soilbiogeochem_carbonflux_inst%somc_fire_col(begc:endc)) - call t_stopf('CNFire') - - - !-------------------------------------------------------------------------- - ! Update3 - ! The state updates are still called for the matrix solution (use_matrixn - ! and use_soil_matrixcn) but most of the state updates are done after - ! the matrix multiply in VegMatrix and SoilMatrix. - !-------------------------------------------------------------------------- - - call t_startf('CNUpdate3') - if ( use_c13 ) then - call CIsoFlux3(num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_state_inst , soilbiogeochem_carbonstate_inst, & - cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, & - c13_soilbiogeochem_carbonstate_inst, & - isotope='c13') - end if - if ( use_c14 ) then - call CIsoFlux3(num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_state_inst , soilbiogeochem_carbonstate_inst, & - cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, & - c14_soilbiogeochem_carbonstate_inst, & - isotope='c14') - end if + if_bgc_vegp3: if(num_bgc_vegp>0)then + + call t_startf('CNFire') + call cnfire_method%CNFireArea(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + num_exposedvegp, filter_exposedvegp, num_noexposedvegp, filter_noexposedvegp, & + atm2lnd_inst, energyflux_inst, saturated_excess_runoff_inst, waterdiagnosticbulk_inst, wateratm2lndbulk_inst, & + waterstatebulk_inst, soilstate_inst, soil_water_retention_curve, & + cnveg_state_inst, cnveg_carbonstate_inst, & + totlitc_col=soilbiogeochem_carbonstate_inst%totlitc_col(begc:endc), & + decomp_cpools_vr_col=soilbiogeochem_carbonstate_inst%decomp_cpools_vr_col(begc:endc,1:nlevdecomp_full,1:ndecomp_pools), & + t_soi17cm_col=temperature_inst%t_soi17cm_col(begc:endc)) + + call cnfire_method%CNFireFluxes(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + num_actfirec, filter_actfirec, num_actfirep, filter_actfirep, & + dgvs_inst, cnveg_state_inst, & + cnveg_carbonstate_inst, cnveg_carbonflux_inst, cnveg_nitrogenstate_inst, cnveg_nitrogenflux_inst, & + soilbiogeochem_carbonflux_inst, & + leaf_prof_patch=soilbiogeochem_state_inst%leaf_prof_patch(begp:endp, 1:nlevdecomp_full), & + froot_prof_patch=soilbiogeochem_state_inst%froot_prof_patch(begp:endp, 1:nlevdecomp_full), & + croot_prof_patch=soilbiogeochem_state_inst%croot_prof_patch(begp:endp, 1:nlevdecomp_full), & + stem_prof_patch=soilbiogeochem_state_inst%stem_prof_patch(begp:endp, 1:nlevdecomp_full), & + totsomc_col=soilbiogeochem_carbonstate_inst%totsomc_col(begc:endc), & + decomp_cpools_vr_col=soilbiogeochem_carbonstate_inst%decomp_cpools_vr_col(begc:endc,1:nlevdecomp_full,1:ndecomp_pools), & + decomp_npools_vr_col=soilbiogeochem_nitrogenstate_inst%decomp_npools_vr_col(begc:endc,1:nlevdecomp_full,1:ndecomp_pools), & + somc_fire_col=soilbiogeochem_carbonflux_inst%somc_fire_col(begc:endc)) + call t_stopf('CNFire') + + + !-------------------------------------------------------------------------- + ! Update3 + ! The state updates are still called for the matrix solution (use_matrixn + ! and use_soil_matrixcn) but most of the state updates are done after + ! the matrix multiply in VegMatrix and SoilMatrix. + !-------------------------------------------------------------------------- + + call t_startf('CNUpdate3') + if ( use_c13 ) then + call CIsoFlux3(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst , soilbiogeochem_carbonstate_inst, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, & + c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, & + c13_soilbiogeochem_carbonstate_inst, & + isotope='c13') + end if + if ( use_c14 ) then + call CIsoFlux3(num_bgc_vegp, filter_bgc_vegp, & + soilbiogeochem_state_inst , soilbiogeochem_carbonstate_inst, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, & + c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, & + c14_soilbiogeochem_carbonstate_inst, & + isotope='c14') + end if - call CStateUpdate3( num_soilc, filter_soilc, num_soilp, filter_soilp, & - cnveg_carbonflux_inst, cnveg_carbonstate_inst, soilbiogeochem_carbonstate_inst, & - soilbiogeochem_carbonflux_inst) + call CStateUpdate3( num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + cnveg_carbonflux_inst, cnveg_carbonstate_inst, soilbiogeochem_carbonstate_inst, & + soilbiogeochem_carbonflux_inst) - if ( use_c13 ) then - call CStateUpdate3( num_soilc, filter_soilc, num_soilp, filter_soilp, & - c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst, & - c13_soilbiogeochem_carbonflux_inst) - end if + if ( use_c13 ) then + call CStateUpdate3( num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + c13_cnveg_carbonflux_inst, c13_cnveg_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst, & + c13_soilbiogeochem_carbonflux_inst) + end if - if ( use_c14 ) then - call CStateUpdate3( num_soilc, filter_soilc, num_soilp, filter_soilp, & - c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, c14_soilbiogeochem_carbonstate_inst, & - c14_soilbiogeochem_carbonflux_inst) + if ( use_c14 ) then + call CStateUpdate3( num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + c14_cnveg_carbonflux_inst, c14_cnveg_carbonstate_inst, c14_soilbiogeochem_carbonstate_inst, & + c14_soilbiogeochem_carbonflux_inst) - call C14Decay(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & - c14_cnveg_carbonstate_inst, c14_soilbiogeochem_carbonstate_inst, & - c14_cnveg_carbonflux_inst, c14_soilbiogeochem_carbonflux_inst) - end if - call t_stopf('CNUpdate3') + call C14Decay(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + c14_cnveg_carbonstate_inst, c14_soilbiogeochem_carbonstate_inst, & + c14_cnveg_carbonflux_inst, c14_soilbiogeochem_carbonflux_inst) + end if + call t_stopf('CNUpdate3') - if ( use_nguardrail ) then + end if if_bgc_vegp3 + + if ( use_nguardrail .and. num_bgc_vegp>0 ) then call t_startf('CNPrecisionControl') - call CNPrecisionControl(bounds, num_soilp, filter_soilp, & + call CNPrecisionControl(bounds, num_bgc_vegp, filter_bgc_vegp, & cnveg_carbonstate_inst, c13_cnveg_carbonstate_inst, & c14_cnveg_carbonstate_inst, cnveg_nitrogenstate_inst) call t_stopf('CNPrecisionControl') @@ -920,7 +1019,7 @@ end subroutine CNDriverNoLeaching !----------------------------------------------------------------------- subroutine CNDriverLeaching(bounds, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & num_actfirec, filter_actfirec, num_actfirep, filter_actfirep,& waterstatebulk_inst, waterfluxbulk_inst, & soilstate_inst, cnveg_state_inst, & @@ -940,16 +1039,17 @@ subroutine CNDriverLeaching(bounds, & ! !USES: use SoilBiogeochemNLeachingMod, only: SoilBiogeochemNLeaching use CNNStateUpdate3Mod , only: NStateUpdate3 - use clm_time_manager , only : is_first_step_of_this_run_segment,is_beg_curr_year,is_end_curr_year,get_curr_date - use CNSharedParamsMod , only : use_matrixcn + use CNNStateUpdate3Mod , only: NStateUpdateLeaching + use clm_time_manager , only: is_first_step_of_this_run_segment,is_beg_curr_year,is_end_curr_year,get_curr_date + use CNSharedParamsMod , only: use_matrixcn use SoilBiogeochemDecompCascadeConType , only : use_soil_matrixcn ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_vegp ! number of soil patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for soil patches integer , intent(in) :: num_actfirec ! number of soil columns on fire in filter integer , intent(in) :: filter_actfirec(:) ! filter for soil columns on fire integer , intent(in) :: num_actfirep ! number of soil patches on fire in filter @@ -981,21 +1081,26 @@ subroutine CNDriverLeaching(bounds, & ! Mineral nitrogen dynamics (deposition, fixation, leaching) call t_startf('SoilBiogeochemNLeaching') - call SoilBiogeochemNLeaching(bounds, num_soilc, filter_soilc, & + call SoilBiogeochemNLeaching(bounds, num_bgc_soilc, filter_bgc_soilc, & waterstatebulk_inst, waterfluxbulk_inst, soilbiogeochem_nitrogenstate_inst, & soilbiogeochem_nitrogenflux_inst) - call t_stopf('SoilBiogeochemNLeaching') - - ! Nitrogen state variable update, mortality fluxes. - - call t_startf('NUpdate3') - - call NStateUpdate3(num_soilc, filter_soilc, num_soilp, filter_soilp, & - cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, & + call NStateUpdateLeaching(num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst) + call t_stopf('SoilBiogeochemNLeaching') - call t_stopf('NUpdate3') + + + + ! Nitrogen state variable update, mortality fluxes. + if(num_bgc_vegp>0)then + call t_startf('NUpdate3') + call NStateUpdate3(num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, & + soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst) + call t_stopf('NUpdate3') + end if + !-------------------------------------------------------------------------- ! Solve the matrix solution and do the state update for matrix solution as ! part of that @@ -1017,7 +1122,7 @@ end subroutine CNDriverLeaching !----------------------------------------------------------------------- subroutine CNDriverSummarizeStates(bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & cnveg_carbonstate_inst, c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst, & cnveg_nitrogenstate_inst, & soilbiogeochem_carbonstate_inst, & @@ -1034,10 +1139,10 @@ subroutine CNDriverSummarizeStates(bounds, num_allc, filter_allc, & type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_allc ! number of columns in allc filter integer , intent(in) :: filter_allc(:) ! filter for all active columns - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_vegp ! number of soil patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for soil patches type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst type(cnveg_carbonstate_type) , intent(inout) :: c13_cnveg_carbonstate_inst type(cnveg_carbonstate_type) , intent(inout) :: c14_cnveg_carbonstate_inst @@ -1058,53 +1163,46 @@ subroutine CNDriverSummarizeStates(bounds, num_allc, filter_allc, & call t_startf('CNsum') ! ---------------------------------------------- - ! soilbiogeochem carbon/nitrogen state summary + ! cnveg carbon/nitrogen state summary ! ---------------------------------------------- + call cnveg_carbonstate_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp) - call soilbiogeochem_carbonstate_inst%summary(bounds, num_allc, filter_allc) if ( use_c13 ) then - call c13_soilbiogeochem_carbonstate_inst%summary(bounds, num_allc, filter_allc) + call c13_cnveg_carbonstate_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp) end if + if ( use_c14 ) then - call c14_soilbiogeochem_carbonstate_inst%summary(bounds, num_allc, filter_allc) + call c14_cnveg_carbonstate_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp) end if - call soilbiogeochem_nitrogenstate_inst%summary(bounds, num_allc, filter_allc) ! ---------------------------------------------- - ! cnveg carbon/nitrogen state summary + ! soilbiogeochem carbon/nitrogen state summary + ! RGK 02-23: soilbiogeochem summary now depends on + ! cnveg summary, swapped call order ! ---------------------------------------------- - call cnveg_carbonstate_inst%Summary(bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_cwdc_col=soilbiogeochem_carbonstate_inst%cwdc_col(begc:endc), & - soilbiogeochem_totlitc_col=soilbiogeochem_carbonstate_inst%totlitc_col(begc:endc), & - soilbiogeochem_totmicc_col=soilbiogeochem_carbonstate_inst%totmicc_col(begc:endc), & - soilbiogeochem_totsomc_col=soilbiogeochem_carbonstate_inst%totsomc_col(begc:endc), & - soilbiogeochem_ctrunc_col=soilbiogeochem_carbonstate_inst%ctrunc_col(begc:endc)) - + call soilbiogeochem_carbonstate_inst%summary(bounds, num_allc, filter_allc, & + num_bgc_soilc, filter_bgc_soilc, cnveg_carbonstate_inst) if ( use_c13 ) then - call c13_cnveg_carbonstate_inst%Summary(bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_cwdc_col=c13_soilbiogeochem_carbonstate_inst%cwdc_col(begc:endc), & - soilbiogeochem_totlitc_col=c13_soilbiogeochem_carbonstate_inst%totlitc_col(begc:endc), & - soilbiogeochem_totmicc_col=c13_soilbiogeochem_carbonstate_inst%totmicc_col(begc:endc), & - soilbiogeochem_totsomc_col=c13_soilbiogeochem_carbonstate_inst%totsomc_col(begc:endc), & - soilbiogeochem_ctrunc_col=c13_soilbiogeochem_carbonstate_inst%ctrunc_col(begc:endc)) + call c13_soilbiogeochem_carbonstate_inst%summary(bounds, num_allc, filter_allc, & + num_bgc_soilc, filter_bgc_soilc, c13_cnveg_carbonstate_inst) end if - if ( use_c14 ) then - call c14_cnveg_carbonstate_inst%Summary(bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_cwdc_col=c14_soilbiogeochem_carbonstate_inst%cwdc_col(begc:endc), & - soilbiogeochem_totlitc_col=c14_soilbiogeochem_carbonstate_inst%totlitc_col(begc:endc), & - soilbiogeochem_totmicc_col=c14_soilbiogeochem_carbonstate_inst%totmicc_col(begc:endc), & - soilbiogeochem_totsomc_col=c14_soilbiogeochem_carbonstate_inst%totsomc_col(begc:endc), & - soilbiogeochem_ctrunc_col=c14_soilbiogeochem_carbonstate_inst%ctrunc_col(begc:endc)) + call c14_soilbiogeochem_carbonstate_inst%summary(bounds, num_allc, filter_allc, & + num_bgc_soilc, filter_bgc_soilc, c14_cnveg_carbonstate_inst) end if + + + ! RGK 02-23: This call will be moved to after cnveg nitr summary when we + ! couple in FATES N + + + call cnveg_nitrogenstate_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp) - call cnveg_nitrogenstate_inst%Summary(bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_nitrogenstate_inst) + call soilbiogeochem_nitrogenstate_inst%summary(bounds, num_allc, filter_allc, & + num_bgc_soilc, filter_bgc_soilc, cnveg_nitrogenstate_inst) + call t_stopf('CNsum') @@ -1112,7 +1210,7 @@ end subroutine CNDriverSummarizeStates !----------------------------------------------------------------------- subroutine CNDriverSummarizeFluxes(bounds, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & cnveg_carbonflux_inst, c13_cnveg_carbonflux_inst, c14_cnveg_carbonflux_inst, & cnveg_nitrogenflux_inst, & c_products_inst, c13_products_inst, c14_products_inst, & @@ -1133,10 +1231,10 @@ subroutine CNDriverSummarizeFluxes(bounds, & ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_vegp ! number of soil patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for soil patches type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst type(cnveg_carbonflux_type) , intent(inout) :: c13_cnveg_carbonflux_inst type(cnveg_carbonflux_type) , intent(inout) :: c14_cnveg_carbonflux_inst @@ -1171,72 +1269,74 @@ subroutine CNDriverSummarizeFluxes(bounds, & ! soilbiogeochem carbon/nitrogen flux summary ! ---------------------------------------------- - call soilbiogeochem_carbonflux_inst%Summary(bounds, num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + call soilbiogeochem_carbonflux_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & soilbiogeochem_carbonflux_inst%decomp_cascade_ctransfer_col(begc:endc,1:ndecomp_cascade_transitions), & soilbiogeochem_carbonstate_inst%cwdc_col(begc:endc), & soilbiogeochem_nitrogenstate_inst%cwdn_col(begc:endc), & - leafc_to_litter_patch=cnveg_carbonflux_inst%leafc_to_litter_patch(begp:endp), & - frootc_to_litter_patch=cnveg_carbonflux_inst%frootc_to_litter_patch(begp:endp)) + leafc_to_litter_patch=cnveg_carbonflux_inst%leafc_to_litter_patch, & + frootc_to_litter_patch=cnveg_carbonflux_inst%frootc_to_litter_patch) if ( use_c13 ) then - call c13_soilbiogeochem_carbonflux_inst%Summary(bounds, num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + call c13_soilbiogeochem_carbonflux_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & c13_soilbiogeochem_carbonflux_inst%decomp_cascade_ctransfer_col(begc:endc,1:ndecomp_cascade_transitions), & c13_soilbiogeochem_carbonstate_inst%cwdc_col(begc:endc), & soilbiogeochem_nitrogenstate_inst%cwdn_col(begc:endc), & - leafc_to_litter_patch=c13_cnveg_carbonflux_inst%leafc_to_litter_patch(begp:endp), & - frootc_to_litter_patch=c13_cnveg_carbonflux_inst%frootc_to_litter_patch(begp:endp)) + leafc_to_litter_patch=c13_cnveg_carbonflux_inst%leafc_to_litter_patch, & + frootc_to_litter_patch=c13_cnveg_carbonflux_inst%frootc_to_litter_patch) end if if ( use_c14 ) then - call c14_soilbiogeochem_carbonflux_inst%Summary(bounds, num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + call c14_soilbiogeochem_carbonflux_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & c14_soilbiogeochem_carbonflux_inst%decomp_cascade_ctransfer_col(begc:endc,1:ndecomp_cascade_transitions), & c14_soilbiogeochem_carbonstate_inst%cwdc_col(begc:endc), & soilbiogeochem_nitrogenstate_inst%cwdn_col(begc:endc), & - leafc_to_litter_patch=c14_cnveg_carbonflux_inst%leafc_to_litter_patch(begp:endp), & - frootc_to_litter_patch=c14_cnveg_carbonflux_inst%frootc_to_litter_patch(begp:endp)) + leafc_to_litter_patch=c14_cnveg_carbonflux_inst%leafc_to_litter_patch, & + frootc_to_litter_patch=c14_cnveg_carbonflux_inst%frootc_to_litter_patch) end if - call soilbiogeochem_nitrogenflux_inst%Summary(bounds, num_soilc, filter_soilc) + call soilbiogeochem_nitrogenflux_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc) ! ---------------------------------------------- ! cnveg carbon/nitrogen flux summary ! ---------------------------------------------- - call t_startf('CNvegCflux_summary') - call cnveg_carbonflux_inst%Summary(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & - isotope='bulk', & - soilbiogeochem_hr_col=soilbiogeochem_carbonflux_inst%hr_col(begc:endc), & - soilbiogeochem_cwdhr_col=soilbiogeochem_carbonflux_inst%cwdhr_col(begc:endc), & - soilbiogeochem_lithr_col=soilbiogeochem_carbonflux_inst%lithr_col(begc:endc), & - soilbiogeochem_decomp_cascade_ctransfer_col=& - soilbiogeochem_carbonflux_inst%decomp_cascade_ctransfer_col(begc:endc,1:ndecomp_cascade_transitions), & - product_closs_grc=c_products_inst%product_loss_grc(begg:endg)) - - if ( use_c13 ) then - call c13_cnveg_carbonflux_inst%Summary(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & - isotope='c13', & - soilbiogeochem_hr_col=c13_soilbiogeochem_carbonflux_inst%hr_col(begc:endc), & - soilbiogeochem_cwdhr_col=c13_soilbiogeochem_carbonflux_inst%cwdhr_col(begc:endc), & - soilbiogeochem_lithr_col=c13_soilbiogeochem_carbonflux_inst%lithr_col(begc:endc), & - soilbiogeochem_decomp_cascade_ctransfer_col=& - c13_soilbiogeochem_carbonflux_inst%decomp_cascade_ctransfer_col(begc:endc,1:ndecomp_cascade_transitions), & - product_closs_grc=c13_products_inst%product_loss_grc(begg:endg)) - end if - - if ( use_c14 ) then - call c14_cnveg_carbonflux_inst%Summary(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & - isotope='c14', & - soilbiogeochem_hr_col=c14_soilbiogeochem_carbonflux_inst%hr_col(begc:endc), & - soilbiogeochem_cwdhr_col=c14_soilbiogeochem_carbonflux_inst%cwdhr_col(begc:endc), & - soilbiogeochem_lithr_col=c14_soilbiogeochem_carbonflux_inst%lithr_col(begc:endc), & + if_bgc_vegp: if(num_bgc_vegp>0) then + call t_startf('CNvegCflux_summary') + call cnveg_carbonflux_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + isotope='bulk', & + soilbiogeochem_hr_col=soilbiogeochem_carbonflux_inst%hr_col(begc:endc), & + soilbiogeochem_cwdhr_col=soilbiogeochem_carbonflux_inst%cwdhr_col(begc:endc), & + soilbiogeochem_lithr_col=soilbiogeochem_carbonflux_inst%lithr_col(begc:endc), & soilbiogeochem_decomp_cascade_ctransfer_col=& - c14_soilbiogeochem_carbonflux_inst%decomp_cascade_ctransfer_col(begc:endc,1:ndecomp_cascade_transitions), & - product_closs_grc=c14_products_inst%product_loss_grc(begg:endg)) - end if - call t_stopf('CNvegCflux_summary') - - call cnveg_nitrogenflux_inst%Summary(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp) + soilbiogeochem_carbonflux_inst%decomp_cascade_ctransfer_col(begc:endc,1:ndecomp_cascade_transitions), & + product_closs_grc=c_products_inst%product_loss_grc(begg:endg)) + + if ( use_c13 ) then + call c13_cnveg_carbonflux_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + isotope='c13', & + soilbiogeochem_hr_col=c13_soilbiogeochem_carbonflux_inst%hr_col(begc:endc), & + soilbiogeochem_cwdhr_col=c13_soilbiogeochem_carbonflux_inst%cwdhr_col(begc:endc), & + soilbiogeochem_lithr_col=c13_soilbiogeochem_carbonflux_inst%lithr_col(begc:endc), & + soilbiogeochem_decomp_cascade_ctransfer_col=& + c13_soilbiogeochem_carbonflux_inst%decomp_cascade_ctransfer_col(begc:endc,1:ndecomp_cascade_transitions), & + product_closs_grc=c13_products_inst%product_loss_grc(begg:endg)) + end if + + if ( use_c14 ) then + call c14_cnveg_carbonflux_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & + isotope='c14', & + soilbiogeochem_hr_col=c14_soilbiogeochem_carbonflux_inst%hr_col(begc:endc), & + soilbiogeochem_cwdhr_col=c14_soilbiogeochem_carbonflux_inst%cwdhr_col(begc:endc), & + soilbiogeochem_lithr_col=c14_soilbiogeochem_carbonflux_inst%lithr_col(begc:endc), & + soilbiogeochem_decomp_cascade_ctransfer_col=& + c14_soilbiogeochem_carbonflux_inst%decomp_cascade_ctransfer_col(begc:endc,1:ndecomp_cascade_transitions), & + product_closs_grc=c14_products_inst%product_loss_grc(begg:endg)) + end if + call t_stopf('CNvegCflux_summary') + call cnveg_nitrogenflux_inst%Summary(bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp) + end if if_bgc_vegp + call t_stopf('CNsum') end subroutine CNDriverSummarizeFluxes diff --git a/src/biogeochem/CNFireEmissionsMod.F90 b/src/biogeochem/CNFireEmissionsMod.F90 index 645f074a7d..5a15e138d5 100644 --- a/src/biogeochem/CNFireEmissionsMod.F90 +++ b/src/biogeochem/CNFireEmissionsMod.F90 @@ -185,7 +185,7 @@ subroutine InitHistory(this, bounds) end subroutine InitHistory !----------------------------------------------------------------------- - subroutine CNFireEmisUpdate(bounds, num_soilp, filter_soilp, cnveg_cf_inst, cnveg_cs_inst, fireemis_inst ) + subroutine CNFireEmisUpdate(bounds, num_bgc_vegp, filter_bgc_vegp, cnveg_cf_inst, cnveg_cs_inst, fireemis_inst ) use CNVegcarbonfluxType, only : cnveg_carbonflux_type use CNVegCarbonStateType, only : cnveg_carbonstate_type @@ -194,8 +194,8 @@ subroutine CNFireEmisUpdate(bounds, num_soilp, filter_soilp, cnveg_cf_inst, cnve !ARGUMENTS: type(bounds_type), intent(in) :: bounds - integer, intent(in) :: num_soilp ! number of soil pfts in filter - integer, intent(in) :: filter_soilp(:) ! filter for soil pfts + integer, intent(in) :: num_bgc_vegp ! number of bgc veg patches + integer, intent(in) :: filter_bgc_vegp(:) ! filter for bgc veg patches type(cnveg_carbonflux_type), intent(in) :: cnveg_cf_inst type(cnveg_carbonstate_type),intent(in) :: cnveg_cs_inst type(fireemis_type), intent(inout) :: fireemis_inst @@ -235,8 +235,8 @@ subroutine CNFireEmisUpdate(bounds, num_soilp, filter_soilp, cnveg_cf_inst, cnve ! Begin loop over points !_______________________________________________________________________________ - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) g = patch%gridcell(p) c = patch%column(p) diff --git a/src/biogeochem/CNGapMortalityMod.F90 b/src/biogeochem/CNGapMortalityMod.F90 index 91c937f655..aa53317fb3 100644 --- a/src/biogeochem/CNGapMortalityMod.F90 +++ b/src/biogeochem/CNGapMortalityMod.F90 @@ -82,7 +82,7 @@ subroutine readParams ( ncid ) end subroutine readParams !----------------------------------------------------------------------- - subroutine CNGapMortality (bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & + subroutine CNGapMortality (bounds, num_soilp, filter_soilp, & dgvs_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst,& cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, canopystate_inst, & leaf_prof_patch, froot_prof_patch, croot_prof_patch, stem_prof_patch) @@ -99,8 +99,6 @@ subroutine CNGapMortality (bounds, num_soilc, filter_soilc, num_soilp, filter_so ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! column filter for soil points integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! patch filter for soil points type(dgvs_type) , intent(inout) :: dgvs_inst @@ -306,19 +304,19 @@ subroutine CNGapMortality (bounds, num_soilc, filter_soilc, num_soilp, filter_so ! gather all patch-level litterfall fluxes to the column ! for litter C and N inputs - call CNGap_PatchToColumn(bounds, num_soilc, filter_soilc, & + call CNGap_PatchToColumn(bounds, num_soilp, filter_soilp, & cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & leaf_prof_patch(bounds%begp:bounds%endp, 1:nlevdecomp_full), & froot_prof_patch(bounds%begp:bounds%endp, 1:nlevdecomp_full), & croot_prof_patch(bounds%begp:bounds%endp, 1:nlevdecomp_full), & stem_prof_patch(bounds%begp:bounds%endp, 1:nlevdecomp_full)) - + end associate end subroutine CNGapMortality !----------------------------------------------------------------------- - subroutine CNGap_PatchToColumn (bounds, num_soilc, filter_soilc, & + subroutine CNGap_PatchToColumn (bounds, num_soilp, filter_soilp, & cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & leaf_prof_patch, froot_prof_patch, croot_prof_patch, stem_prof_patch) ! @@ -331,8 +329,8 @@ subroutine CNGap_PatchToColumn (bounds, num_soilc, filter_soilc, & ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! soil column filter + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! soil patch filter type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst real(r8) , intent(in) :: leaf_prof_patch(bounds%begp:,1:) @@ -341,7 +339,7 @@ subroutine CNGap_PatchToColumn (bounds, num_soilc, filter_soilc, & real(r8) , intent(in) :: stem_prof_patch(bounds%begp:,1:) ! ! !LOCAL VARIABLES: - integer :: fc,c,pi,p,j,i ! indices + integer :: fp,c,p,j,i ! indices !----------------------------------------------------------------------- SHR_ASSERT_ALL_FL((ubound(leaf_prof_patch) == (/bounds%endp,nlevdecomp_full/)), sourcefile, __LINE__) @@ -408,84 +406,75 @@ subroutine CNGap_PatchToColumn (bounds, num_soilc, filter_soilc, & ) do j = 1,nlevdecomp - do pi = 1,maxsoil_patches - do fc = 1,num_soilc - c = filter_soilc(fc) - - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 - - if (patch%active(p)) then - - do i = i_litr_min, i_litr_max - gap_mortality_c_to_litr_c(c,j,i) = & - gap_mortality_c_to_litr_c(c,j,i) + & - ! leaf gap mortality carbon fluxes - m_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & - ! fine root gap mortality carbon fluxes - m_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) - end do - - ! wood gap mortality carbon fluxes - gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & - (m_livestemc_to_litter(p) + m_deadstemc_to_litter(p)) * wtcol(p) * stem_prof(p,j) - gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & - (m_livecrootc_to_litter(p) + m_deadcrootc_to_litter(p)) * wtcol(p) * croot_prof(p,j) - - ! storage gap mortality carbon fluxes - ! Metabolic litter is treated differently than other types - ! of litter, so it gets this additional line after the - ! most recent loop over all litter types - gap_mortality_c_to_litr_c(c,j,i_met_lit) = & - gap_mortality_c_to_litr_c(c,j,i_met_lit) + & - (m_leafc_storage_to_litter(p) + m_gresp_storage_to_litter(p)) * wtcol(p) * leaf_prof(p,j) + & - m_frootc_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - (m_livestemc_storage_to_litter(p) + m_deadstemc_storage_to_litter(p)) * wtcol(p) * stem_prof(p,j) + & - (m_livecrootc_storage_to_litter(p) + m_deadcrootc_storage_to_litter(p)) * wtcol(p) * croot_prof(p,j) + & - - ! transfer gap mortality carbon fluxes - (m_leafc_xfer_to_litter(p) + m_gresp_xfer_to_litter(p)) * wtcol(p) * leaf_prof(p,j) + & - m_frootc_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - (m_livestemc_xfer_to_litter(p) + m_deadstemc_xfer_to_litter(p)) * wtcol(p) * stem_prof(p,j) + & - (m_livecrootc_xfer_to_litter(p) + m_deadcrootc_xfer_to_litter(p)) * wtcol(p) * croot_prof(p,j) - - do i = i_litr_min, i_litr_max - gap_mortality_n_to_litr_n(c,j,i) = & - gap_mortality_n_to_litr_n(c,j,i) + & - ! leaf gap mortality nitrogen fluxes - m_leafn_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & - ! fine root litter nitrogen fluxes - m_frootn_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) - end do - - ! wood gap mortality nitrogen fluxes - gap_mortality_n_to_cwdn(c,j) = gap_mortality_n_to_cwdn(c,j) + & - (m_livestemn_to_litter(p) + m_deadstemn_to_litter(p)) * wtcol(p) * stem_prof(p,j) - gap_mortality_n_to_cwdn(c,j) = gap_mortality_n_to_cwdn(c,j) + & - (m_livecrootn_to_litter(p) + m_deadcrootn_to_litter(p)) * wtcol(p) * croot_prof(p,j) - - ! Metabolic litter is treated differently than other types - ! of litter, so it gets this additional line after the - ! most recent loop over all litter types - gap_mortality_n_to_litr_n(c,j,i_met_lit) = & - gap_mortality_n_to_litr_n(c,j,i_met_lit) + & - ! retranslocated N pool gap mortality fluxes - m_retransn_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - ! storage gap mortality nitrogen fluxes - m_leafn_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - m_frootn_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - (m_livestemn_storage_to_litter(p) + m_deadstemn_storage_to_litter(p)) * wtcol(p) * stem_prof(p,j) + & - (m_livecrootn_storage_to_litter(p) + m_deadcrootn_storage_to_litter(p)) * wtcol(p) * croot_prof(p,j) + & - ! transfer gap mortality nitrogen fluxes - m_leafn_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - m_frootn_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - (m_livestemn_xfer_to_litter(p) + m_deadstemn_xfer_to_litter(p)) * wtcol(p) * stem_prof(p,j) + & - (m_livecrootn_xfer_to_litter(p) + m_deadcrootn_xfer_to_litter(p)) * wtcol(p) * croot_prof(p,j) - - end if - end if + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) + + do i = i_litr_min, i_litr_max + gap_mortality_c_to_litr_c(c,j,i) = & + gap_mortality_c_to_litr_c(c,j,i) + & + ! leaf gap mortality carbon fluxes + m_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & + ! fine root gap mortality carbon fluxes + m_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + end do + ! wood gap mortality carbon fluxes + gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & + (m_livestemc_to_litter(p) + m_deadstemc_to_litter(p)) * wtcol(p) * stem_prof(p,j) + gap_mortality_c_to_cwdc(c,j) = gap_mortality_c_to_cwdc(c,j) + & + (m_livecrootc_to_litter(p) + m_deadcrootc_to_litter(p)) * wtcol(p) * croot_prof(p,j) + + ! storage gap mortality carbon fluxes + ! Metabolic litter is treated differently than other types + ! of litter, so it gets this additional line after the + ! most recent loop over all litter types + gap_mortality_c_to_litr_c(c,j,i_met_lit) = & + gap_mortality_c_to_litr_c(c,j,i_met_lit) + & + (m_leafc_storage_to_litter(p) + m_gresp_storage_to_litter(p)) * wtcol(p) * leaf_prof(p,j) + & + m_frootc_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + (m_livestemc_storage_to_litter(p) + m_deadstemc_storage_to_litter(p)) * wtcol(p) * stem_prof(p,j) + & + (m_livecrootc_storage_to_litter(p) + m_deadcrootc_storage_to_litter(p)) * wtcol(p) * croot_prof(p,j) + & + + ! transfer gap mortality carbon fluxes + (m_leafc_xfer_to_litter(p) + m_gresp_xfer_to_litter(p)) * wtcol(p) * leaf_prof(p,j) + & + m_frootc_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + (m_livestemc_xfer_to_litter(p) + m_deadstemc_xfer_to_litter(p)) * wtcol(p) * stem_prof(p,j) + & + (m_livecrootc_xfer_to_litter(p) + m_deadcrootc_xfer_to_litter(p)) * wtcol(p) * croot_prof(p,j) + + do i = i_litr_min, i_litr_max + gap_mortality_n_to_litr_n(c,j,i) = & + gap_mortality_n_to_litr_n(c,j,i) + & + ! leaf gap mortality nitrogen fluxes + m_leafn_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & + ! fine root litter nitrogen fluxes + m_frootn_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) end do + + ! wood gap mortality nitrogen fluxes + gap_mortality_n_to_cwdn(c,j) = gap_mortality_n_to_cwdn(c,j) + & + (m_livestemn_to_litter(p) + m_deadstemn_to_litter(p)) * wtcol(p) * stem_prof(p,j) + gap_mortality_n_to_cwdn(c,j) = gap_mortality_n_to_cwdn(c,j) + & + (m_livecrootn_to_litter(p) + m_deadcrootn_to_litter(p)) * wtcol(p) * croot_prof(p,j) + + ! Metabolic litter is treated differently than other types + ! of litter, so it gets this additional line after the + ! most recent loop over all litter types + gap_mortality_n_to_litr_n(c,j,i_met_lit) = & + gap_mortality_n_to_litr_n(c,j,i_met_lit) + & + ! retranslocated N pool gap mortality fluxes + m_retransn_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + ! storage gap mortality nitrogen fluxes + m_leafn_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + m_frootn_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + (m_livestemn_storage_to_litter(p) + m_deadstemn_storage_to_litter(p)) * wtcol(p) * stem_prof(p,j) + & + (m_livecrootn_storage_to_litter(p) + m_deadcrootn_storage_to_litter(p)) * wtcol(p) * croot_prof(p,j) + & + ! transfer gap mortality nitrogen fluxes + m_leafn_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + m_frootn_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + (m_livestemn_xfer_to_litter(p) + m_deadstemn_xfer_to_litter(p)) * wtcol(p) * stem_prof(p,j) + & + (m_livecrootn_xfer_to_litter(p) + m_deadcrootn_xfer_to_litter(p)) * wtcol(p) * croot_prof(p,j) + end do end do diff --git a/src/biogeochem/CNNDynamicsMod.F90 b/src/biogeochem/CNNDynamicsMod.F90 index a658a63768..10c0f5ea38 100644 --- a/src/biogeochem/CNNDynamicsMod.F90 +++ b/src/biogeochem/CNNDynamicsMod.F90 @@ -25,6 +25,7 @@ module CNNDynamicsMod use ColumnType , only : col use PatchType , only : patch use perf_mod , only : t_startf, t_stopf + use CLMFatesInterfaceMod , only : hlm_fates_interface_type ! implicit none private @@ -192,7 +193,8 @@ end subroutine CNFreeLivingFixation !----------------------------------------------------------------------- subroutine CNNFixation(num_soilc, filter_soilc, & - cnveg_carbonflux_inst, soilbiogeochem_nitrogenflux_inst) + cnveg_carbonflux_inst, soilbiogeochem_nitrogenflux_inst, & + clm_fates, clump_index) ! ! !DESCRIPTION: ! On the radiation time step, update the nitrogen fixation rate @@ -209,12 +211,15 @@ subroutine CNNFixation(num_soilc, filter_soilc, & integer , intent(in) :: num_soilc ! number of soil columns in filter integer , intent(in) :: filter_soilc(:) ! filter for soil columns type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst - type(soilbiogeochem_nitrogenflux_type) , intent(inout) :: soilbiogeochem_nitrogenflux_inst + type(soilbiogeochem_nitrogenflux_type) , intent(inout) :: soilbiogeochem_nitrogenflux_inst + type(hlm_fates_interface_type) , intent(inout) :: clm_fates + integer , intent(in) :: clump_index ! ! !LOCAL VARIABLES: - integer :: c,fc ! indices + integer :: c,fc,s ! indices real(r8) :: t ! temporary real(r8) :: dayspyr ! days per year + real(r8) :: npp ! lag or smoothed net primary productivity (gC/m2/s) !----------------------------------------------------------------------- associate( & @@ -225,16 +230,26 @@ subroutine CNNFixation(num_soilc, filter_soilc, & ) dayspyr = get_curr_days_per_year() - if ( nfix_timeconst > 0._r8 .and. nfix_timeconst < 500._r8 ) then ! use exponential relaxation with time constant nfix_timeconst for NPP - NFIX relation ! Loop through columns do fc = 1,num_soilc c = filter_soilc(fc) - if (col_lag_npp(c) /= spval) then + if(col%is_fates(c))then + s = clm_fates%f2hmap(clump_index)%hsites(c) + ! %ema_npp is Smoothed [gc/m2/yr] + !npp = clm_fates%fates(clump_index)%bc_out(s)%ema_npp/(dayspyr*secspday) + ! FATES N cycling is not yet active, so runs are supplemented anyway + ! this will be added when FATES N cycling is completed. + npp = 0._r8 + else + npp = col_lag_npp(c) + end if + + if (npp /= spval) then ! need to put npp in units of gC/m^2/year here first - t = (1.8_r8 * (1._r8 - exp(-0.003_r8 * col_lag_npp(c)*(secspday * dayspyr))))/(secspday * dayspyr) + t = (1.8_r8 * (1._r8 - exp(-0.003_r8 * npp *(secspday * dayspyr))))/(secspday * dayspyr) nfix_to_sminn(c) = max(0._r8,t) else nfix_to_sminn(c) = 0._r8 @@ -245,7 +260,16 @@ subroutine CNNFixation(num_soilc, filter_soilc, & do fc = 1,num_soilc c = filter_soilc(fc) - t = (1.8_r8 * (1._r8 - exp(-0.003_r8 * cannsum_npp(c))))/(secspday * dayspyr) + if(col%is_fates(c))then + s = clm_fates%f2hmap(clump_index)%hsites(c) + !npp = clm_fates%fates(clump_index)%bc_out(s)%ema_npp + ! See above regarding FATES and N fixation + npp = 0._r8 + else + npp = cannsum_npp(c) + end if + + t = (1.8_r8 * (1._r8 - exp(-0.003_r8 * npp)))/(secspday * dayspyr) nfix_to_sminn(c) = max(0._r8,t) end do endif diff --git a/src/biogeochem/CNNStateUpdate1Mod.F90 b/src/biogeochem/CNNStateUpdate1Mod.F90 index c99729b2ee..833a65cbc3 100644 --- a/src/biogeochem/CNNStateUpdate1Mod.F90 +++ b/src/biogeochem/CNNStateUpdate1Mod.F90 @@ -23,7 +23,10 @@ module CNNStateUpdate1Mod use SoilBiogeochemNitrogenFluxType , only : soilbiogeochem_nitrogenflux_type use SoilBiogeochemNitrogenStateType , only : soilbiogeochem_nitrogenstate_type use CropReprPoolsMod , only : nrepr, repr_grain_min, repr_grain_max, repr_structure_min, repr_structure_max - use PatchType , only : patch + use PatchType , only : patch + use CLMFatesInterfaceMod , only : hlm_fates_interface_type + use ColumnType , only : col + ! implicit none private @@ -96,7 +99,9 @@ end subroutine NStateUpdateDynPatch !----------------------------------------------------------------------- subroutine NStateUpdate1(num_soilc, filter_soilc, num_soilp, filter_soilp, & - cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst) + cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & + clm_fates, clump_index) + use CNSharedParamsMod , only : use_fun ! ! !DESCRIPTION: @@ -111,6 +116,9 @@ subroutine NStateUpdate1(num_soilc, filter_soilc, num_soilp, filter_soilp, & type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(cnveg_nitrogenstate_type) , intent(inout) :: cnveg_nitrogenstate_inst type(soilbiogeochem_nitrogenflux_type) , intent(inout) :: soilbiogeochem_nitrogenflux_inst + type(hlm_fates_interface_type) , intent(inout) :: clm_fates + integer , intent(in) :: clump_index + ! ! !LOCAL VARIABLES: integer :: c,p,j,l,g,k,i ! indices @@ -134,9 +142,23 @@ subroutine NStateUpdate1(num_soilc, filter_soilc, num_soilp, filter_soilp, & ! soilbiogeochemistry fluxes TODO - this should be moved elsewhere ! plant to litter fluxes - phenology and dynamic landcover fluxes - do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + + fc_loop: do fc = 1,num_soilc + c = filter_soilc(fc) + + fates_if: if( col%is_fates(c) ) then + + ! If this is a fates column, then we ask fates for the + ! litter fluxes, the following routine simply copies + ! prepared litter c flux boundary conditions into + ! cf_soil%decomp_cpools_sourcesink_col + + call clm_fates%UpdateNLitterfluxes(nf_soil,clump_index,c) + + else + + do j = 1, nlevdecomp + ! ! State update without the matrix solution ! @@ -151,19 +173,20 @@ subroutine NStateUpdate1(num_soilc, filter_soilc, num_soilp, filter_soilp, & ! time step, but to be safe, I'm explicitly setting it to zero here. nf_soil%decomp_npools_sourcesink_col(c,j,i_cwd) = 0._r8 - ! - ! For the matrix solution the actual state update comes after the matrix - ! multiply in SoilMatrix, but the matrix needs to be setup with - ! the equivalent of above. Those changes can be here or in the - ! native subroutines dealing with that field - ! + ! + ! For the matrix solution the actual state update comes after the matrix + ! multiply in SoilMatrix, but the matrix needs to be setup with + ! the equivalent of above. Those changes can be here or in the + ! native subroutines dealing with that field + ! else ! Do the above to the matrix solution do i = i_litr_min, i_litr_max end do end if end do - end do + end if fates_if + end do fc_loop do fp = 1,num_soilp p = filter_soilp(fp) @@ -257,8 +280,10 @@ subroutine NStateUpdate1(num_soilc, filter_soilc, num_soilp, filter_soilp, & ns_veg%frootn_patch(p) = ns_veg%frootn_patch(p) - nf_veg%frootn_to_retransn_patch(p)*dt ns_veg%retransn_patch(p) = ns_veg%retransn_patch(p) + nf_veg%frootn_to_retransn_patch(p)*dt ns_veg%livestemn_patch(p) = ns_veg%livestemn_patch(p) - nf_veg%livestemn_to_litter_patch(p)*dt - ns_veg%livestemn_patch(p) = ns_veg%livestemn_patch(p) - nf_veg%livestemn_to_biofueln_patch(p)*dt - ns_veg%leafn_patch(p) = ns_veg%leafn_patch(p) - nf_veg%leafn_to_biofueln_patch(p)*dt + ns_veg%livestemn_patch(p) = ns_veg%livestemn_patch(p) - & + (nf_veg%livestemn_to_biofueln_patch(p) + nf_veg%livestemn_to_removedresiduen_patch(p))*dt + ns_veg%leafn_patch(p) = ns_veg%leafn_patch(p) - & + (nf_veg%leafn_to_biofueln_patch(p) + nf_veg%leafn_to_removedresiduen_patch(p))*dt ns_veg%livestemn_patch(p) = ns_veg%livestemn_patch(p) - nf_veg%livestemn_to_retransn_patch(p)*dt ns_veg%retransn_patch(p) = ns_veg%retransn_patch(p) + nf_veg%livestemn_to_retransn_patch(p)*dt ! diff --git a/src/biogeochem/CNNStateUpdate2Mod.F90 b/src/biogeochem/CNNStateUpdate2Mod.F90 index 6ff5fb238b..678bdf05d3 100644 --- a/src/biogeochem/CNNStateUpdate2Mod.F90 +++ b/src/biogeochem/CNNStateUpdate2Mod.F90 @@ -26,6 +26,7 @@ module CNNStateUpdate2Mod ! !PUBLIC MEMBER FUNCTIONS: public:: NStateUpdate2 public:: NStateUpdate2h + public:: NStateUpdate2g !----------------------------------------------------------------------- contains @@ -310,4 +311,111 @@ subroutine NStateUpdate2h(num_soilc, filter_soilc, num_soilp, filter_soilp, & end subroutine NStateUpdate2h + !----------------------------------------------------------------------- + subroutine NStateUpdate2g(num_soilc, filter_soilc, num_soilp, filter_soilp, & + cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, soilbiogeochem_nitrogenstate_inst) + ! + ! !DESCRIPTION: + ! Update all the prognostic nitrogen state + ! variables affected by gross unrepresented landcover change mortality fluxes + ! NOTE - associate statements have been removed where there are + ! no science equations. This increases readability and maintainability + ! + ! !ARGUMENTS: + integer , intent(in) :: num_soilc ! number of soil columns in filter + integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! filter for soil patches + type(cnveg_nitrogenflux_type) , intent(in) :: cnveg_nitrogenflux_inst + type(cnveg_nitrogenstate_type) , intent(inout) :: cnveg_nitrogenstate_inst + type(soilbiogeochem_nitrogenstate_type) , intent(inout) :: soilbiogeochem_nitrogenstate_inst + ! + ! !LOCAL VARIABLES: + integer :: c,p,j,l,i ! indices + integer :: fp,fc ! lake filter indices + real(r8):: dt ! radiation time step (seconds) + !----------------------------------------------------------------------- + + associate( & + nf_veg => cnveg_nitrogenflux_inst , & + ns_veg => cnveg_nitrogenstate_inst , & + ns_soil => soilbiogeochem_nitrogenstate_inst & + ) + + ! set time steps + dt = get_step_size_real() + + ! column-level nitrogen fluxes from gross unrepresented landcover change mortality + + do j = 1,nlevdecomp + do fc = 1,num_soilc + c = filter_soilc(fc) + do i = i_litr_min, i_litr_max + ns_soil%decomp_npools_vr_col(c,j,i) = & + ns_soil%decomp_npools_vr_col(c,j,i) + nf_veg%gru_n_to_litr_n_col(c,j,i) * dt + end do + ! Currently i_cwd .ne. i_litr_max + 1 if .not. fates and + ! i_cwd = 0 if fates, so not including in the i-loop + ns_soil%decomp_npools_vr_col(c,j,i_cwd) = & + ns_soil%decomp_npools_vr_col(c,j,i_cwd) + nf_veg%gru_n_to_cwdn_col(c,j) * dt + end do + end do + + ! patch-level nitrogen fluxes from gross unrepresented landcover change mortality + + do fp = 1,num_soilp + p = filter_soilp(fp) + + ! displayed pools + ns_veg%leafn_patch(p) = ns_veg%leafn_patch(p) & + - nf_veg%gru_leafn_to_litter_patch(p) * dt + ns_veg%frootn_patch(p) = ns_veg%frootn_patch(p) & + - nf_veg%gru_frootn_to_litter_patch(p) * dt + ns_veg%livestemn_patch(p) = ns_veg%livestemn_patch(p) & + - nf_veg%gru_livestemn_to_atm_patch(p) * dt + ns_veg%deadstemn_patch(p) = ns_veg%deadstemn_patch(p) & + - nf_veg%gru_deadstemn_to_atm_patch(p) * dt + ns_veg%deadstemn_patch(p) = ns_veg%deadstemn_patch(p) & + - nf_veg%gru_wood_productn_gain_patch(p) * dt + ns_veg%livecrootn_patch(p) = ns_veg%livecrootn_patch(p) & + - nf_veg%gru_livecrootn_to_litter_patch(p) * dt + ns_veg%deadcrootn_patch(p) = ns_veg%deadcrootn_patch(p) & + - nf_veg%gru_deadcrootn_to_litter_patch(p) * dt + ns_veg%retransn_patch(p) = ns_veg%retransn_patch(p) & + - nf_veg%gru_retransn_to_litter_patch(p) * dt + + ! storage pools + ns_veg%leafn_storage_patch(p) = ns_veg%leafn_storage_patch(p) & + - nf_veg%gru_leafn_storage_to_atm_patch(p) * dt + ns_veg%frootn_storage_patch(p) = ns_veg%frootn_storage_patch(p) & + - nf_veg%gru_frootn_storage_to_atm_patch(p) * dt + ns_veg%livestemn_storage_patch(p) = ns_veg%livestemn_storage_patch(p) & + - nf_veg%gru_livestemn_storage_to_atm_patch(p) * dt + ns_veg%deadstemn_storage_patch(p) = ns_veg%deadstemn_storage_patch(p) & + - nf_veg%gru_deadstemn_storage_to_atm_patch(p) * dt + ns_veg%livecrootn_storage_patch(p) = ns_veg%livecrootn_storage_patch(p) & + - nf_veg%gru_livecrootn_storage_to_atm_patch(p) * dt + ns_veg%deadcrootn_storage_patch(p) = ns_veg%deadcrootn_storage_patch(p) & + - nf_veg%gru_deadcrootn_storage_to_atm_patch(p) * dt + + ! transfer pools + ns_veg%leafn_xfer_patch(p) = ns_veg%leafn_xfer_patch(p) & + - nf_veg%gru_leafn_xfer_to_atm_patch(p) *dt + ns_veg%frootn_xfer_patch(p) = ns_veg%frootn_xfer_patch(p) & + - nf_veg%gru_frootn_xfer_to_atm_patch(p) *dt + ns_veg%livestemn_xfer_patch(p) = ns_veg%livestemn_xfer_patch(p) & + - nf_veg%gru_livestemn_xfer_to_atm_patch(p) *dt + ns_veg%deadstemn_xfer_patch(p) = ns_veg%deadstemn_xfer_patch(p) & + - nf_veg%gru_deadstemn_xfer_to_atm_patch(p) *dt + ns_veg%livecrootn_xfer_patch(p) = ns_veg%livecrootn_xfer_patch(p) & + - nf_veg%gru_livecrootn_xfer_to_atm_patch(p) *dt + ns_veg%deadcrootn_xfer_patch(p) = ns_veg%deadcrootn_xfer_patch(p) & + - nf_veg%gru_deadcrootn_xfer_to_atm_patch(p) *dt + + end do + + end associate + + end subroutine NStateUpdate2g + end module CNNStateUpdate2Mod diff --git a/src/biogeochem/CNNStateUpdate3Mod.F90 b/src/biogeochem/CNNStateUpdate3Mod.F90 index b5e3f32fec..26902cef22 100644 --- a/src/biogeochem/CNNStateUpdate3Mod.F90 +++ b/src/biogeochem/CNNStateUpdate3Mod.F90 @@ -25,11 +25,63 @@ module CNNStateUpdate3Mod private ! ! !PUBLIC MEMBER FUNCTIONS: - public:: NStateUpdate3 + public :: NStateUpdate3 + public :: NStateUpdateLeaching !----------------------------------------------------------------------- contains + subroutine NStateUpdateLeaching(num_soilc, filter_soilc, & + soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst) + ! + ! !DESCRIPTION: + ! On the radiation time step, update all the prognostic nitrogen state + ! variables affected by the Sminn leaching flux. + ! RGK: This code was separated from gap mortality fluxes to make this + ! compatible with FATES. + ! + ! !ARGUMENTS: + integer , intent(in) :: num_soilc ! number of soil columns in filter + integer , intent(in) :: filter_soilc(:) ! filter for soil columns + type(soilbiogeochem_nitrogenflux_type) , intent(inout) :: soilbiogeochem_nitrogenflux_inst + type(soilbiogeochem_nitrogenstate_type) , intent(inout) :: soilbiogeochem_nitrogenstate_inst + + ! !LOCAL VARIABLES: + integer :: c,p,j,l,k ! indices + integer :: fp,fc ! lake filter indices + real(r8):: dt ! radiation time step (seconds) + !----------------------------------------------------------------------- + + associate( & + nf_soil => soilbiogeochem_nitrogenflux_inst , & ! Input + ns_soil => soilbiogeochem_nitrogenstate_inst & ! Output + ) + + ! set time steps + dt = get_step_size_real() + + do j = 1, nlevdecomp + ! column loop + do fc = 1,num_soilc + c = filter_soilc(fc) + + if (.not. use_nitrif_denitrif) then + ! mineral N loss due to leaching + ns_soil%sminn_vr_col(c,j) = ns_soil%sminn_vr_col(c,j) - nf_soil%sminn_leached_vr_col(c,j) * dt + else + ! mineral N loss due to leaching and runoff + ns_soil%smin_no3_vr_col(c,j) = max( ns_soil%smin_no3_vr_col(c,j) - & + ( nf_soil%smin_no3_leached_vr_col(c,j) + nf_soil%smin_no3_runoff_vr_col(c,j) ) * dt, 0._r8) + + ns_soil%sminn_vr_col(c,j) = ns_soil%smin_no3_vr_col(c,j) + ns_soil%smin_nh4_vr_col(c,j) + end if + end do + end do + + end associate + return + end subroutine NStateUpdateLeaching + !----------------------------------------------------------------------- subroutine NStateUpdate3(num_soilc, filter_soilc, num_soilp, filter_soilp, & cnveg_nitrogenflux_inst, cnveg_nitrogenstate_inst, & @@ -37,7 +89,7 @@ subroutine NStateUpdate3(num_soilc, filter_soilc, num_soilp, filter_soilp, & ! ! !DESCRIPTION: ! On the radiation time step, update all the prognostic nitrogen state - ! variables affected by gap-phase mortality fluxes. Also the Sminn leaching flux. + ! variables affected by gap-phase mortality fluxes. ! NOTE - associate statements have been removed where there are ! no science equations. This increases readability and maintainability. ! @@ -72,20 +124,8 @@ subroutine NStateUpdate3(num_soilc, filter_soilc, num_soilp, filter_soilp, & do fc = 1,num_soilc c = filter_soilc(fc) - if (.not. use_nitrif_denitrif) then - ! mineral N loss due to leaching - ns_soil%sminn_vr_col(c,j) = ns_soil%sminn_vr_col(c,j) - nf_soil%sminn_leached_vr_col(c,j) * dt - else - ! mineral N loss due to leaching and runoff - ns_soil%smin_no3_vr_col(c,j) = max( ns_soil%smin_no3_vr_col(c,j) - & - ( nf_soil%smin_no3_leached_vr_col(c,j) + nf_soil%smin_no3_runoff_vr_col(c,j) ) * dt, 0._r8) - - ns_soil%sminn_vr_col(c,j) = ns_soil%smin_no3_vr_col(c,j) + ns_soil%smin_nh4_vr_col(c,j) - end if - ! column level nitrogen fluxes from fire ! patch-level wood to column-level CWD (uncombusted wood) - ! ! State update without the matrix solution ! diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index ec04fcbf54..05041527a7 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -52,12 +52,15 @@ module CNPhenologyMod public :: CNPhenologyInit ! Initialization public :: CNPhenology ! Update public :: CropPhase ! Get the current phase of each crop patch + public :: DaysPastPlanting ! Get how many days it's been since crop was planted ! !PUBLIC for unit testing public :: CNPhenologySetNML ! Set the namelist setttings explicitly for unit tests public :: CNPhenologySetParams ! Set the parameters explicitly for unit tests public :: SeasonalDecidOnset ! Logical function to determine is seasonal decidious onset should be triggered public :: SeasonalCriticalDaylength ! Critical day length needed for Seasonal decidious offset + public :: get_swindow + public :: was_sown_in_this_window ! !PRIVITE MEMBER FIUNCTIONS: private :: CNPhenologyClimate ! Get climatological everages to figure out triggers for Phenology @@ -135,6 +138,10 @@ module CNPhenologyMod real(r8), private :: initial_seed_at_planting = 3._r8 ! Initial seed at planting + real(r8) :: min_gddmaturity = 1._r8 ! Weird things can happen if gddmaturity is tiny + logical, public :: generate_crop_gdds = .false. ! If true, harvest the day before next sowing + logical, public :: use_mxmat = .true. ! If true, ignore crop maximum growing season length + ! Constants for seasonal decidious leaf onset and offset logical, private :: onset_thresh_depends_on_veg = .false. ! If onset threshold depends on vegetation type integer, public, parameter :: critical_daylight_constant = 1 @@ -145,6 +152,14 @@ module CNPhenologyMod ! For determining leaf offset latitude that's considered high latitude (see Eitel 2019) real(r8), parameter :: critical_offset_high_lat = 65._r8 ! Start of what's considered high latitude (degrees) + real(r8), parameter :: HARVEST_REASON_MATURE = 1._r8 + real(r8), parameter :: HARVEST_REASON_MAXSEASLENGTH = 2._r8 + real(r8), parameter :: HARVEST_REASON_SOWNBADDEC31 = 3._r8 + real(r8), parameter :: HARVEST_REASON_SOWTODAY = 4._r8 + real(r8), parameter :: HARVEST_REASON_SOWTOMORROW = 5._r8 + real(r8), parameter :: HARVEST_REASON_IDOPTOMORROW = 6._r8 + real(r8), parameter :: HARVEST_REASON_VERNFREEZEKILL = 7._r8 + character(len=*), parameter, private :: sourcefile = & __FILE__ !----------------------------------------------------------------------- @@ -176,7 +191,8 @@ subroutine CNPhenologyReadNML( NLFilename ) character(len=*), parameter :: nmlname = 'cnphenology' !----------------------------------------------------------------------- namelist /cnphenology/ initial_seed_at_planting, onset_thresh_depends_on_veg, & - min_critical_dayl_method + min_critical_dayl_method, generate_crop_gdds, & + use_mxmat ! Initialize options to default values, in case they are not specified in ! the namelist @@ -200,6 +216,8 @@ subroutine CNPhenologyReadNML( NLFilename ) call shr_mpi_bcast (initial_seed_at_planting, mpicom) call shr_mpi_bcast (onset_thresh_depends_on_veg, mpicom) call shr_mpi_bcast (min_critical_dayl_method, mpicom) + call shr_mpi_bcast (generate_crop_gdds, mpicom) + call shr_mpi_bcast (use_mxmat, mpicom) if ( min_critical_dayl_method == "DependsOnLat" )then critical_daylight_method = critical_daylight_depends_on_lat @@ -352,7 +370,7 @@ subroutine CNPhenology (bounds, num_soilc, filter_soilc, num_soilp, & if ( phase == 1 ) then - call CNPhenologyClimate(num_soilp, filter_soilp, num_pcropp, filter_pcropp, & + call CNPhenologyClimate(num_soilp, filter_soilp, & temperature_inst, cnveg_state_inst, crop_inst) call CNEvergreenPhenology(num_soilp, filter_soilp, & @@ -393,7 +411,8 @@ subroutine CNPhenology (bounds, num_soilc, filter_soilc, num_soilp, & cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) call CNOffsetLitterfall(num_soilp, filter_soilp, & - cnveg_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) + cnveg_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & + crop_inst) call CNBackgroundLitterfall(num_soilp, filter_soilp, & cnveg_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) @@ -406,7 +425,7 @@ subroutine CNPhenology (bounds, num_soilc, filter_soilc, num_soilp, & ! gather all patch-level litterfall fluxes to the column for litter C and N inputs - call CNLitterToColumn(bounds, num_soilc, filter_soilc, & + call CNLitterToColumn(bounds, num_soilp, filter_soilp, & cnveg_state_inst, cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & leaf_prof_patch(bounds%begp:bounds%endp,1:nlevdecomp_full), & froot_prof_patch(bounds%begp:bounds%endp,1:nlevdecomp_full)) @@ -425,7 +444,7 @@ subroutine CNPhenologyInit(bounds) ! ! !USES: use clm_time_manager, only: get_step_size_real - use clm_varctl , only: use_crop + use clm_varctl , only: use_crop, use_cropcal_rx_swindows use clm_varcon , only: secspday ! ! !ARGUMENTS: @@ -513,7 +532,7 @@ subroutine CNPhenologyInit(bounds) end subroutine CNPhenologyInit !----------------------------------------------------------------------- - subroutine CNPhenologyClimate (num_soilp, filter_soilp, num_pcropp, filter_pcropp, & + subroutine CNPhenologyClimate (num_soilp, filter_soilp, & temperature_inst, cnveg_state_inst, crop_inst) ! ! !DESCRIPTION: @@ -526,34 +545,20 @@ subroutine CNPhenologyClimate (num_soilp, filter_soilp, num_pcropp, filter_pcrop ! !ARGUMENTS: integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches - integer , intent(in) :: num_pcropp ! number of prognostic crops in filter - integer , intent(in) :: filter_pcropp(:)! filter for prognostic crop patches type(temperature_type) , intent(inout) :: temperature_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(crop_type) , intent(inout) :: crop_inst ! ! !LOCAL VARIABLES: integer :: p ! indices - integer :: fp ! lake filter patch index + integer :: fp ! filter patch index real(r8) :: dayspyr ! days per year (days) - integer :: kyr ! current year - integer :: kmo ! month of year (1, ..., 12) - integer :: kda ! day of month (1, ..., 31) - integer :: mcsec ! seconds of day (0, ..., seconds/day) - real(r8), parameter :: yravg = 20.0_r8 ! length of years to average for gdd - real(r8), parameter :: yravgm1 = yravg-1.0_r8 ! minus 1 of above !----------------------------------------------------------------------- associate( & nyrs_crop_active => crop_inst%nyrs_crop_active_patch, & ! InOut: [integer (:) ] number of years this crop patch has been active t_ref2m => temperature_inst%t_ref2m_patch , & ! Input: [real(r8) (:) ] 2m air temperature (K) - gdd0 => temperature_inst%gdd0_patch , & ! Output: [real(r8) (:) ] growing deg. days base 0 deg C (ddays) - gdd8 => temperature_inst%gdd8_patch , & ! Output: [real(r8) (:) ] " " " " 8 " " " - gdd10 => temperature_inst%gdd10_patch , & ! Output: [real(r8) (:) ] " " " " 10 " " " - gdd020 => temperature_inst%gdd020_patch , & ! Output: [real(r8) (:) ] 20-yr mean of gdd0 (ddays) - gdd820 => temperature_inst%gdd820_patch , & ! Output: [real(r8) (:) ] 20-yr mean of gdd8 (ddays) - gdd1020 => temperature_inst%gdd1020_patch , & ! Output: [real(r8) (:) ] 20-yr mean of gdd10 (ddays) tempavg_t2m => cnveg_state_inst%tempavg_t2m_patch & ! Output: [real(r8) (:) ] temp. avg 2m air temperature (K) ) @@ -567,41 +572,6 @@ subroutine CNPhenologyClimate (num_soilp, filter_soilp, num_pcropp, filter_pcrop tempavg_t2m(p) = tempavg_t2m(p) + t_ref2m(p) * (fracday/dayspyr) end do - ! - ! The following crop related steps are done here rather than CropPhenology - ! so that they will be completed each time-step rather than with doalb. - ! - ! NOTE(wjs, 2022-02-03) The above comment about doalb no longer applies, because - ! there is no longer a doalb conditional around the CropPhenology call. Therefore, - ! we could move these calculations into CropPhenology if it made sense to do so. - ! - ! The following lines come from ibis's climate.f + stats.f - ! gdd SUMMATIONS ARE RELATIVE TO THE PLANTING DATE (see subr. updateAccFlds) - - if (num_pcropp > 0) then - ! get time-related info - call get_curr_date(kyr, kmo, kda, mcsec) - end if - - do fp = 1,num_pcropp - p = filter_pcropp(fp) - if (kmo == 1 .and. kda == 1 .and. nyrs_crop_active(p) == 0) then ! YR 1: - gdd020(p) = 0._r8 ! set gdd..20 variables to 0 - gdd820(p) = 0._r8 ! and crops will not be planted - gdd1020(p) = 0._r8 - end if - if (kmo == 1 .and. kda == 1 .and. mcsec == 0) then ! <-- END of EVERY YR: - if (nyrs_crop_active(p) == 1) then ! <-- END of YR 1 - gdd020(p) = gdd0(p) ! <-- END of YR 1 - gdd820(p) = gdd8(p) ! <-- END of YR 1 - gdd1020(p) = gdd10(p) ! <-- END of YR 1 - end if ! <-- END of YR 1 - gdd020(p) = (yravgm1* gdd020(p) + gdd0(p)) / yravg ! gdd..20 must be long term avgs - gdd820(p) = (yravgm1* gdd820(p) + gdd8(p)) / yravg ! so ignore results for yrs 1 & 2 - gdd1020(p) = (yravgm1* gdd1020(p) + gdd10(p)) / yravg - end if - end do - end associate end subroutine CNPhenologyClimate @@ -631,7 +601,7 @@ subroutine CNEvergreenPhenology (num_soilp, filter_soilp , & ! !LOCAL VARIABLES: real(r8):: avg_dayspyr ! Average days per year integer :: p ! indices - integer :: fp ! lake filter patch index + integer :: fp ! filter patch index real(r8):: tranr real(r8):: t1 ! temporary variable @@ -816,7 +786,7 @@ subroutine CNSeasonDecidPhenology (num_soilp, filter_soilp , & ! ! !LOCAL VARIABLES: integer :: g,c,p !indices - integer :: fp !lake filter patch index + integer :: fp !filter patch index real(r8):: ws_flag !winter-summer solstice flag (0 or 1) real(r8):: crit_onset_gdd !critical onset growing degree-day sum real(r8):: crit_daylat !latitudinal light gradient in arctic-boreal @@ -1272,7 +1242,7 @@ subroutine CNStressDecidPhenology (num_soilp, filter_soilp , & ! !LOCAL VARIABLES: real(r8),parameter :: secspqtrday = secspday / 4 ! seconds per quarter day integer :: g,c,p ! indices - integer :: fp ! lake filter patch index + integer :: fp ! filter patch index real(r8):: avg_dayspyr ! average days per year real(r8):: crit_onset_gdd ! degree days for onset trigger real(r8):: soilt ! temperature of top soil layer @@ -1696,6 +1666,118 @@ subroutine CNStressDecidPhenology (num_soilp, filter_soilp , & end subroutine CNStressDecidPhenology + + !----------------------------------------------------------------------- + subroutine get_swindow(jday, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + ! !DESCRIPTION: + ! Determine when the "next" sowing window is. This is either the sowing window we are + ! currently in or, if not in a sowing window, the next one that will occur. + + ! !USES: + use clm_time_manager , only : get_curr_days_per_year, is_doy_in_interval, get_doy_tomorrow + ! !ARGUMENTS: + integer, intent(in) :: jday ! Day of year + integer, dimension(:), intent(in) :: rx_starts, rx_ends ! All prescribed sowing window start and end dates for this patch + integer, intent(in) :: param_start, param_end ! Sowing window start and end dates from parameter file + integer, intent(out) :: w ! Index of "next" sowing window + integer, intent(out) :: start_w, end_w ! Start and end dates of "next" sowing window + ! + ! !LOCAL VARIABLES + integer :: jday_tomorrow + integer :: mxsowings_in ! Due to unit testing, we can't assume the length of the rx sowing window arrays is mxsowings as set in clm_varpar + + ! Initialize + w = -1 + start_w = -1 + end_w = -1 + + ! Get info + jday_tomorrow = get_doy_tomorrow(jday) + mxsowings_in = size(rx_starts) + + ! If no sowing windows are prescribed, use the values from the parameter file. + if (maxval(rx_starts) < 1) then + w = 1 + start_w = param_start + end_w = param_end + return + + ! Otherwise, if today is after the latest sowing window end date, use the first sowing window. This works only if sowing windows that span the new year are located at index w = 1. + else if (jday > maxval(rx_ends)) then + w = 1 + start_w = rx_starts(w) + end_w = rx_ends(w) + return + end if + + ! Otherwise, use the first prescribed sowing window we find whose end is >= today. This works only if sowing windows that span the new year are located at index w = 1. + do w = 1, mxsowings_in + ! If nothing prescribed at this w, stop looking and exit loop. Will trigger "No sowing window found" error, which we do not move here because it's possible that no start or end date is < 1. + if (min(rx_starts(w), rx_ends(w)) < 1) then + exit + end if + + if (jday <= rx_ends(w)) then + start_w = rx_starts(w) + end_w = rx_ends(w) + exit + end if + end do + + ! Ensure that a window was found. + ! SSR 2023-10-17: This shouldn't currently be reachable, but its being here casts the widest possible net in case code changes in future. + if (start_w < 1 .or. end_w < 1) then + call endrun(msg="get_swindow(): No sowing window found") + end if + + end subroutine get_swindow + + + !----------------------------------------------------------------------- + function was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, sown_in_this_window) + ! !DESCRIPTION: + ! Determine whether the crop was sown in the current sowing window. Although sown_in_this_window is set to false in last timestep of sowing window at the end of CropPhenology(), these extra checks may be necessary if sowing windows change. + ! + ! !USES: + use clm_time_manager , only : is_doy_in_interval + ! !ARGUMENTS: + integer, intent(in) :: sowing_window_startdate, sowing_window_enddate, jday, idop + logical, intent(in) :: sown_in_this_window + ! !LOCAL VARIABLES + logical :: is_in_sowing_window, idop_in_sowing_window + ! !RESULT + logical :: was_sown_in_this_window + + was_sown_in_this_window = sown_in_this_window + + ! If not in a sowing window, sown_in_this_window must be false. + is_in_sowing_window = is_doy_in_interval(sowing_window_startdate, sowing_window_enddate, jday) + if (.not. is_in_sowing_window) then + was_sown_in_this_window = .false. + return + end if + + ! If we're in a sowing window but the day of planting isn't in the active sowing window, we must be in a different sowing window. + idop_in_sowing_window = is_doy_in_interval(sowing_window_startdate, sowing_window_enddate, idop) + if (is_in_sowing_window .and. .not. idop_in_sowing_window) then + was_sown_in_this_window = .false. + return + end if + + ! Sometimes we're in an active sowing window, and the patch was sown between the start and end dates of the window, but not *the currently active* window. Note that windows with start==end are not checked here; we always trust the input value of sown_in_this_window in such cases. + if (sowing_window_startdate < sowing_window_enddate .and. idop > jday) then + was_sown_in_this_window = .false. + else if (sowing_window_startdate > sowing_window_enddate) then + if (jday <= sowing_window_enddate .and. idop <= sowing_window_enddate .and. idop > jday) then + was_sown_in_this_window = .false. + else if (jday >= sowing_window_startdate .and. (idop > jday .or. idop <= sowing_window_enddate)) then + was_sown_in_this_window = .false. + end if + end if + + end function was_sown_in_this_window + + !----------------------------------------------------------------------- subroutine CropPhenology(num_pcropp, filter_pcropp , & waterdiagnosticbulk_inst, temperature_inst, crop_inst, canopystate_inst, cnveg_state_inst , & @@ -1709,6 +1791,9 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & ! !USES: use clm_time_manager , only : get_prev_calday, get_curr_days_per_year, is_beg_curr_year use clm_time_manager , only : get_average_days_per_year + use clm_time_manager , only : get_prev_date + use clm_time_manager , only : is_doy_in_interval, is_end_curr_day + use clm_time_manager , only : get_doy_tomorrow use pftconMod , only : ntmp_corn, nswheat, nwwheat, ntmp_soybean use pftconMod , only : nirrig_tmp_corn, nirrig_swheat, nirrig_wwheat, nirrig_tmp_soybean use pftconMod , only : ntrp_corn, nsugarcane, ntrp_soybean, ncotton, nrice @@ -1720,6 +1805,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & use clm_varctl , only : use_fertilizer use clm_varctl , only : use_c13, use_c14 use clm_varcon , only : c13ratio, c14ratio + use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams ! ! !ARGUMENTS: integer , intent(in) :: num_pcropp ! number of prog crop patches in filter @@ -1743,26 +1829,47 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & integer g ! gridcell indices integer h ! hemisphere indices integer s ! growing season indices + integer k ! grain pool indices + integer w ! sowing window index integer idpp ! number of days past planting + integer mxmat ! maximum growing season length + integer kyr ! current year + integer kmo ! month of year (1, ..., 12) + integer kda ! day of month (1, ..., 31) + integer mcsec ! seconds of day (0, ..., seconds/day) + integer sowing_window_startdate ! date (day of year) of first day of sowing window + integer sowing_window_enddate ! date (day of year) of last day of sowing window + real(r8) harvest_reason real(r8) dayspyr ! days per year in this year real(r8) avg_dayspyr ! average number of days per year real(r8) crmcorn ! comparitive relative maturity for corn real(r8) ndays_on ! number of days to fertilize + logical has_rx_sowing_date ! does the crop have a single sowing date instead of a window? + logical is_in_sowing_window ! is the crop in its sowing window? + logical is_end_sowing_window ! is it the last day of the crop's sowing window? + logical sowing_gdd_requirement_met ! has the gridcell historically been warm enough to support the crop? logical do_plant_normal ! are the normal planting rules defined and satisfied? logical do_plant_lastchance ! if not the above, what about relaxed rules for the last day of the planting window? + logical do_plant_prescribed ! is today the prescribed sowing date? + logical do_plant_prescribed_tomorrow ! is tomorrow the prescribed sowing date? + logical do_plant ! are we planting in this time step for any reason? + logical did_plant ! did we plant the crop in this time step? + logical allow_unprescribed_planting ! should crop be allowed to be planted according to sowing window rules? + logical do_harvest ! Are harvest conditions satisfied? + logical fake_harvest ! Dealing with incorrect Dec. 31 planting + logical did_plant_prescribed_today ! Was the crop sown today? + logical vernalization_forces_harvest ! Was the crop killed by freezing during vernalization? !------------------------------------------------------------------------ associate( & ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type - leaf_long => pftcon%leaf_long , & ! Input: leaf longevity (yrs) + leaf_long => pftcon%leaf_long , & ! Input: leaf longevity (yrs) leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) manunitro => pftcon%manunitro , & ! Input: max manure to be applied in total (kgN/m2) - mxmat => pftcon%mxmat , & ! Input: minplanttemp => pftcon%minplanttemp , & ! Input: planttemp => pftcon%planttemp , & ! Input: gddmin => pftcon%gddmin , & ! Input: - hybgdd => pftcon%hybgdd , & ! Input: lfemerg => pftcon%lfemerg , & ! Input: grnfill => pftcon%grnfill , & ! Input: @@ -1772,20 +1879,20 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & a10tmin => temperature_inst%t_a10min_patch , & ! Input: [real(r8) (:) ] 10-day running mean of min 2-m temperature gdd020 => temperature_inst%gdd020_patch , & ! Input: [real(r8) (:) ] 20 yr mean of gdd0 gdd820 => temperature_inst%gdd820_patch , & ! Input: [real(r8) (:) ] 20 yr mean of gdd8 - gdd1020 => temperature_inst%gdd1020_patch , & ! Input: [real(r8) (:) ] 20 yr mean of gdd10 fertnitro => crop_inst%fertnitro_patch , & ! Input: [real(r8) (:) ] fertilizer nitrogen hui => crop_inst%hui_patch , & ! Input: [real(r8) (:) ] crop patch heat unit index (growing degree-days); set to 0 at sowing and accumulated until harvest - leafout => crop_inst%gddtsoi_patch , & ! Input: [real(r8) (:) ] gdd from top soil layer temperature + leafout => crop_inst%gddtsoi_patch , & ! Input: [real(r8) (:) ] gdd from top soil layer temperature harvdate => crop_inst%harvdate_patch , & ! Output: [integer (:) ] harvest date - croplive => crop_inst%croplive_patch , & ! Output: [logical (:) ] Flag, true if planted, not harvested + croplive => crop_inst%croplive_patch , & ! Output: [logical (:) ] Flag, true if planted, not harvested vf => crop_inst%vf_patch , & ! Output: [real(r8) (:) ] vernalization factor sowing_count => crop_inst%sowing_count , & ! Inout: [integer (:) ] number of sowing events this year for this patch harvest_count => crop_inst%harvest_count , & ! Inout: [integer (:) ] number of harvest events this year for this patch peaklai => cnveg_state_inst%peaklai_patch , & ! Output: [integer (:) ] 1: max allowed lai; 0: not at max tlai => canopystate_inst%tlai_patch , & ! Input: [real(r8) (:) ] one-sided leaf area index, no burying by snow - idop => cnveg_state_inst%idop_patch , & ! Output: [integer (:) ] date of planting + idop => cnveg_state_inst%idop_patch , & ! Output: [integer (:) ] date of planting (day of year) + iyop => cnveg_state_inst%iyop_patch , & ! Output: [integer (:) ] year of planting (day of year) gddmaturity => cnveg_state_inst%gddmaturity_patch , & ! Output: [real(r8) (:) ] gdd needed to harvest huileaf => cnveg_state_inst%huileaf_patch , & ! Output: [real(r8) (:) ] heat unit index needed from planting to leaf emergence huigrain => cnveg_state_inst%huigrain_patch , & ! Output: [real(r8) (:) ] same to reach vegetative maturity @@ -1814,6 +1921,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & dayspyr = get_curr_days_per_year() avg_dayspyr = get_average_days_per_year() jday = get_prev_calday() + call get_prev_date(kyr, kmo, kda, mcsec) if (use_fertilizer) then ndays_on = 20._r8 ! number of days to fertilize @@ -1833,6 +1941,9 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & bgtr(p) = 0._r8 lgsf(p) = 0._r8 + ! Should never be saved as zero, but including this so it's initialized just in case + harvest_reason = 0._r8 + ! --------------------------------- ! from AgroIBIS subroutine planting ! --------------------------------- @@ -1843,28 +1954,116 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & ! Second condition ensures everything is correctly set when resuming from a run with old code ! OR starting a run mid-year without any restart file OR handling a new crop column that just ! came into existence (and not at the year boundary for some reason). - if ( is_beg_curr_year() .or. crop_inst%sdates_thisyr(p,1) == spval ) then + if ( is_beg_curr_year() .or. crop_inst%sdates_thisyr_patch(p,1) == spval ) then sowing_count(p) = 0 harvest_count(p) = 0 do s = 1, mxsowings - crop_inst%sdates_thisyr(p,s) = -1._r8 + crop_inst%sdates_thisyr_patch(p,s) = -1._r8 + crop_inst%swindow_starts_thisyr_patch(p,s) = -1._r8 + crop_inst%swindow_ends_thisyr_patch (p,s) = -1._r8 + crop_inst%sowing_reason_thisyr_patch(p,s) = -1._r8 end do do s = 1, mxharvests - crop_inst%hdates_thisyr(p,s) = -1._r8 + crop_inst%sdates_perharv_patch(p,s) = -1._r8 + crop_inst%syears_perharv_patch(p,s) = -1._r8 + crop_inst%hdates_thisyr_patch(p,s) = -1._r8 + cnveg_state_inst%gddmaturity_thisyr(p,s) = -1._r8 + crop_inst%gddaccum_thisyr_patch(p,s) = -1._r8 + crop_inst%hui_thisyr_patch(p,s) = -1._r8 + crop_inst%sowing_reason_perharv_patch = -1._r8 + crop_inst%harvest_reason_thisyr_patch(p,s) = -1._r8 + do k = repr_grain_min, repr_grain_max + cnveg_carbonflux_inst%repr_grainc_to_food_perharv_patch(p,s,k) = 0._r8 + cnveg_carbonflux_inst%repr_grainc_to_seed_perharv_patch(p,s,k) = 0._r8 + cnveg_nitrogenflux_inst%repr_grainn_to_food_perharv_patch(p,s,k) = 0._r8 + cnveg_nitrogenflux_inst%repr_grainn_to_seed_perharv_patch(p,s,k) = 0._r8 + end do + end do + do k = repr_grain_min, repr_grain_max + cnveg_carbonflux_inst%repr_grainc_to_food_thisyr_patch(p,k) = 0._r8 + cnveg_carbonflux_inst%repr_grainc_to_seed_thisyr_patch(p,k) = 0._r8 + cnveg_nitrogenflux_inst%repr_grainn_to_food_thisyr_patch(p,k) = 0._r8 + cnveg_nitrogenflux_inst%repr_grainn_to_seed_thisyr_patch(p,k) = 0._r8 end do end if + ! Get dates of current or next sowing window. + call get_swindow(jday, crop_inst%rx_swindow_starts_thisyr_patch(p,:), crop_inst%rx_swindow_ends_thisyr_patch(p,:), minplantjday(ivt(p),h), maxplantjday(ivt(p),h), w, sowing_window_startdate, sowing_window_enddate) + + ! Are we currently in a sowing window? + ! This is outside the croplive check so that the "harvest if planting conditions were met today" conditional works. + is_in_sowing_window = is_doy_in_interval(sowing_window_startdate, sowing_window_enddate, jday) + crop_inst%sown_in_this_window(p) = was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop(p), crop_inst%sown_in_this_window(p)) + is_end_sowing_window = jday == sowing_window_enddate + + ! We only want to plant on a specific day if the prescribed sowing window starts AND ends on the same day. Also make sure we haven't planted yet today. + has_rx_sowing_date = sowing_window_startdate == sowing_window_enddate + do_plant_prescribed = has_rx_sowing_date .and. & + sowing_window_startdate == jday .and. & + .not. crop_inst%sown_in_this_window(p) + do_plant_prescribed_tomorrow = & + has_rx_sowing_date .and. & + sowing_window_startdate == get_doy_tomorrow(jday) + ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2022-02-18) ! When resuming from a run with old code, may need to manually set these. ! Will be needed until we can rely on all restart files have been generated ! with CropPhenology() getting the day of the year from the START of the timestep ! (i.e., jday = get_prev_calday()) instead of the END of the timestep (i.e., ! jday = get_calday()). See CTSM issue #1623. + ! Once removed, can also remove the "Instead, always harvest the day before idop" bit. if (croplive(p) .and. idop(p) <= jday .and. sowing_count(p) == 0) then sowing_count(p) = 1 - crop_inst%sdates_thisyr(p,1) = real(idop(p), r8) + crop_inst%sdates_thisyr_patch(p,1) = real(idop(p), r8) end if + ! Save these diagnostic variables only on the last day of the window to ensure that windows spanning the new year aren't double-counted. Doing this on the last day ensures that outputs are ordered as inputs should be. + if (jday == sowing_window_enddate) then + crop_inst%swindow_starts_thisyr_patch(p,w) = sowing_window_startdate + crop_inst%swindow_ends_thisyr_patch (p,w) = sowing_window_enddate + end if + ! + ! Only allow sowing according to normal "window" rules if not using prescribed + ! sowing dates. + allow_unprescribed_planting = .not. has_rx_sowing_date + if (sowing_count(p) == mxsowings) then + do_plant_normal = .false. + do_plant_lastchance = .false. + else if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then + ! winter temperate cereal : use gdd0 as a limit to plant winter cereal + sowing_gdd_requirement_met = gdd020(p) /= spval .and. gdd020(p) >= gddmin(ivt(p)) + ! Are all the normal requirements for planting met? + do_plant_normal = allow_unprescribed_planting .and. & + a5tmin(p) /= spval .and. & + a5tmin(p) <= minplanttemp(ivt(p)) .and. & + is_in_sowing_window .and. & + sowing_gdd_requirement_met + ! If not, but it's the last day of the planting window, what about relaxed rules? + do_plant_lastchance = allow_unprescribed_planting .and. & + (.not. do_plant_normal) .and. & + is_end_sowing_window .and. & + sowing_gdd_requirement_met + else ! not winter cereal... slevis: added distinction between NH and SH + ! slevis: The idea is that jday will equal idop sooner or later in the year + ! while the gdd part is either true or false for the year. + ! Are all the normal requirements for planting met? + do_plant_normal = allow_unprescribed_planting .and. & + t10(p) /= spval .and. a10tmin(p) /= spval .and. & + t10(p) > planttemp(ivt(p)) .and. & + a10tmin(p) > minplanttemp(ivt(p)) .and. & + is_in_sowing_window .and. & + gdd820(p) /= spval .and. & + gdd820(p) >= gddmin(ivt(p)) + ! If not, but it's the last day of the planting window, what about relaxed rules? + do_plant_lastchance = allow_unprescribed_planting .and. & + (.not. do_plant_normal) .and. & + is_end_sowing_window .and. & + gdd820(p) > 0._r8 .and. & + gdd820(p) /= spval + end if + do_plant = do_plant_prescribed .or. do_plant_normal .or. do_plant_lastchance + do_plant = do_plant .and. .not. crop_inst%sown_in_this_window(p) + did_plant = .false. ! Once outputs can handle >1 planting per year, remove 2nd condition. if ( (.not. croplive(p)) .and. sowing_count(p) == 0 ) then @@ -1884,96 +2083,30 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & ! According to Chris Kucharik, the dataset of ! xinpdate was generated from a previous model run at 0.5 deg resolution - ! winter temperate cereal : use gdd0 as a limit to plant winter cereal - - if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then - - ! Are all the normal requirements for planting met? - do_plant_normal = a5tmin(p) /= spval .and. & - a5tmin(p) <= minplanttemp(ivt(p)) .and. & - jday >= minplantjday(ivt(p),h) .and. & - jday <= maxplantjday(ivt(p),h) .and. & - (gdd020(p) /= spval .and. & - gdd020(p) >= gddmin(ivt(p))) - ! If not, but it's the last day of the planting window, what about relaxed rules? - do_plant_lastchance = (.not. do_plant_normal) .and. & - jday == maxplantjday(ivt(p),h) .and. & - gdd020(p) /= spval .and. & - gdd020(p) >= gddmin(ivt(p)) - - if (do_plant_normal .or. do_plant_lastchance) then + if (do_plant) then + if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then cumvd(p) = 0._r8 hdidx(p) = 0._r8 vf(p) = 0._r8 - - call PlantCrop(p, leafcn(ivt(p)), jday, crop_inst, cnveg_state_inst, & - cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & - cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & - c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst) - - gddmaturity(p) = hybgdd(ivt(p)) - - else - gddmaturity(p) = 0._r8 end if - else ! not winter cereal... slevis: added distinction between NH and SH - ! slevis: The idea is that jday will equal idop sooner or later in the year - ! while the gdd part is either true or false for the year. - - ! Are all the normal requirements for planting met? - do_plant_normal = t10(p) /= spval .and. a10tmin(p) /= spval .and. & - t10(p) > planttemp(ivt(p)) .and. & - a10tmin(p) > minplanttemp(ivt(p)) .and. & - jday >= minplantjday(ivt(p),h) .and. & - jday <= maxplantjday(ivt(p),h) .and. & - gdd820(p) /= spval .and. & - gdd820(p) >= gddmin(ivt(p)) - ! If not, but it's the last day of the planting window, what about relaxed rules? - do_plant_lastchance = (.not. do_plant_normal) .and. & - jday == maxplantjday(ivt(p),h) .and. & - gdd820(p) > 0._r8 .and. & - gdd820(p) /= spval - - if (do_plant_normal .or. do_plant_lastchance) then - - call PlantCrop(p, leafcn(ivt(p)), jday, crop_inst, cnveg_state_inst, & - cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & - cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & - c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst) - - ! go a specified amount of time before/after - ! climatological date - if (ivt(p) == ntmp_soybean .or. ivt(p) == nirrig_tmp_soybean .or. & - ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean) then - gddmaturity(p) = min(gdd1020(p), hybgdd(ivt(p))) - end if - - if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & - ivt(p) == ntrp_corn .or. ivt(p) == nirrig_trp_corn .or. & - ivt(p) == nsugarcane .or. ivt(p) == nirrig_sugarcane .or. & - ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & - ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then - gddmaturity(p) = max(950._r8, min(gdd820(p)*0.85_r8, hybgdd(ivt(p)))) - if (do_plant_normal) then - gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) - end if - end if - if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & - ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & - ivt(p) == nrice .or. ivt(p) == nirrig_rice) then - gddmaturity(p) = min(gdd020(p), hybgdd(ivt(p))) - end if + call PlantCrop(p, leafcn(ivt(p)), jday, kyr, do_plant_normal, & + do_plant_lastchance, do_plant_prescribed, & + temperature_inst, crop_inst, cnveg_state_inst, & + cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & + cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & + c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst) + did_plant = .true. - else - gddmaturity(p) = 0._r8 - end if - end if ! crop patch distinction + else + gddmaturity(p) = 0._r8 + end if ! crop phenology (gdd thresholds) controlled by gdd needed for ! maturity (physiological) which is based on the average gdd - ! accumulation and hybrids in United States from April 1 - Sept 30 + ! accumulation and hybrids in United States from April 1 - Sept 30, + ! unless using cultivar GDD target inputs ! calculate threshold from phase 1 to phase 2: ! threshold for attaining leaf emergence (based on fraction of @@ -2065,20 +2198,16 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & ! vernalization factor is not 1; ! vf affects the calculation of gddtsoi & hui + vernalization_forces_harvest = .false. if (t_ref2m_min(p) < 1.e30_r8 .and. vf(p) /= 1._r8 .and. & (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat)) then call vernalization(p, & canopystate_inst, temperature_inst, waterdiagnosticbulk_inst, cnveg_state_inst, & - crop_inst) + crop_inst, vernalization_forces_harvest) end if ! days past planting may determine harvest - - if (jday >= idop(p)) then - idpp = jday - idop(p) - else - idpp = int(dayspyr) + jday - idop(p) - end if + idpp = DaysPastPlanting(idop(p), jday) ! onset_counter initialized to zero when .not. croplive ! offset_counter relevant only at time step of harvest @@ -2092,13 +2221,81 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & hui(p) = max(hui(p),huigrain(p)) endif + do_harvest = .false. + fake_harvest = .false. + did_plant_prescribed_today = .false. + if (use_cropcal_rx_swindows .and. sowing_count(p) > 0) then + did_plant_prescribed_today = crop_inst%sdates_thisyr_patch(p,sowing_count(p)) == real(jday, r8) + end if + + ! Optionally ignore maximum growing season length + mxmat = pftcon%mxmat(ivt(p)) + if (.not. use_mxmat) then + mxmat = 999 + end if + + if (jday == 1 .and. croplive(p) .and. idop(p) == 1 .and. sowing_count(p) == 0) then + ! BACKWARDS_COMPATIBILITY(ssr, 2022-02-03): To get rid of crops incorrectly planted in last time step of Dec. 31. That was fixed in commit dadbc62 ("Call CropPhenology regardless of doalb"), but this handles restart files with the old behavior. fake_harvest ensures that outputs aren't polluted. + do_harvest = .true. + fake_harvest = .true. + harvest_reason = HARVEST_REASON_SOWNBADDEC31 + else if (use_cropcal_streams .and. do_plant .and. .not. did_plant) then + ! Today was supposed to be the planting day, but the previous crop still hasn't been harvested. + do_harvest = .true. + harvest_reason = HARVEST_REASON_SOWTODAY + + ! If generate_crop_gdds and this patch has prescribed sowing inputs + else if (generate_crop_gdds .and. crop_inst%rx_swindow_starts_thisyr_patch(p,1) .gt. 0) then + ! Harvest the day before the next prescribed sowing. + do_harvest = do_plant_prescribed_tomorrow + + ! ... unless that will lead to growing season length 365 (or 366, + ! if last year was a leap year). This would result in idop==jday, + ! which would invoke the "manually setting sowing_count and + ! sdates_thisyr" code. This would lead to crops never getting + ! harvested. Instead, always harvest the day before idop. + if ((.not. do_harvest) .and. & + (idop(p) > 1 .and. jday == idop(p) - 1) .or. & + (idop(p) == 1 .and. jday == dayspyr)) then + do_harvest = .true. + harvest_reason = HARVEST_REASON_IDOPTOMORROW + else if (do_harvest) then + harvest_reason = HARVEST_REASON_SOWTOMORROW + end if + + else if (did_plant_prescribed_today) then + ! Do not harvest on the day this growing season began; + ! would create challenges for postprocessing. + do_harvest = .false. + else if (vernalization_forces_harvest) then + do_harvest = .true. + harvest_reason = HARVEST_REASON_VERNFREEZEKILL + else + ! Original harvest rule + do_harvest = hui(p) >= gddmaturity(p) .or. idpp >= mxmat + + ! Always harvest the day before the next prescribed sowing date, if still alive. + ! WARNING: This implementation assumes that prescribed sowing dates don't change over time! + ! In order to avoid this, you'd have to read this year's AND next year's prescribed + ! sowing dates. + do_harvest = do_harvest .or. do_plant_prescribed_tomorrow + + if (hui(p) >= gddmaturity(p)) then + harvest_reason = HARVEST_REASON_MATURE + else if (idpp >= mxmat) then + harvest_reason = HARVEST_REASON_MAXSEASLENGTH + else if (do_plant_prescribed_tomorrow) then + harvest_reason = HARVEST_REASON_SOWTOMORROW + end if + endif + ! The following conditionals are similar to those in CropPhase. However, they ! differ slightly because here we are potentially setting a new crop phase, ! whereas CropPhase is just designed to get the current, already-determined ! phase. However, despite these differences: if you make changes to the ! following conditionals, you should also check to see if you should make ! similar changes in CropPhase. - if (leafout(p) >= huileaf(p) .and. hui(p) < huigrain(p) .and. idpp < mxmat(ivt(p))) then + if ((.not. do_harvest) .and. leafout(p) >= huileaf(p) .and. hui(p) < huigrain(p) .and. idpp < mxmat) then cphase(p) = cphase_leafemerge if (abs(onset_counter(p)) > 1.e-6_r8) then onset_flag(p) = 1._r8 @@ -2124,10 +2321,23 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & ! the onset_counter would change from dt and you'd need to make ! changes to the offset subroutine below - else if (hui(p) >= gddmaturity(p) .or. idpp >= mxmat(ivt(p))) then - if (harvdate(p) >= NOT_Harvested) harvdate(p) = jday - harvest_count(p) = harvest_count(p) + 1 - crop_inst%hdates_thisyr(p, harvest_count(p)) = real(jday, r8) + else if (do_harvest) then + ! Don't update these if you're just harvesting because of incorrect Dec. + ! 31 planting + if (.not. fake_harvest) then + if (harvdate(p) >= NOT_Harvested) harvdate(p) = jday + harvest_count(p) = harvest_count(p) + 1 + crop_inst%sdates_perharv_patch(p, harvest_count(p)) = real(idop(p), r8) + crop_inst%syears_perharv_patch(p, harvest_count(p)) = real(iyop(p), r8) + crop_inst%hdates_thisyr_patch(p, harvest_count(p)) = real(jday, r8) + cnveg_state_inst%gddmaturity_thisyr(p,harvest_count(p)) = gddmaturity(p) + crop_inst%gddaccum_thisyr_patch(p, harvest_count(p)) = crop_inst%gddaccum_patch(p) + crop_inst%hui_thisyr_patch(p, harvest_count(p)) = hui(p) + crop_inst%sowing_reason_perharv_patch(p, harvest_count(p)) = real(crop_inst%sowing_reason_patch(p), r8) + crop_inst%sowing_reason_patch(p) = -1 ! "Reason for most recent sowing of this patch." So in the line above we save, and here we reset. + crop_inst%harvest_reason_thisyr_patch(p, harvest_count(p)) = harvest_reason + endif + croplive(p) = .false. ! no re-entry in greater if-block cphase(p) = cphase_harvest if (tlai(p) > 0._r8) then ! plant had emerged before harvest @@ -2142,6 +2352,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & crop_seedc_to_leaf(p) = crop_seedc_to_leaf(p) - leafc_xfer(p)/dt crop_seedn_to_leaf(p) = crop_seedn_to_leaf(p) - leafn_xfer(p)/dt leafc_xfer(p) = 0._r8 + leafn_xfer(p) = leafc_xfer(p) / leafcn(ivt(p)) if (use_c13) then c13_cnveg_carbonstate_inst%leafc_xfer_patch(p) = 0._r8 @@ -2191,6 +2402,11 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & endif end if ! croplive + ! At the end of the sowing window, AFTER we've done everything crop-related, set this to false + if (is_end_sowing_window .and. is_end_curr_day()) then + crop_inst%sown_in_this_window(p) = .false. + end if + end do ! prognostic crops loop end associate @@ -2325,8 +2541,9 @@ subroutine CropPhenologyInit(bounds) end subroutine CropPhenologyInit !----------------------------------------------------------------------- - subroutine PlantCrop(p, leafcn_in, jday, & - crop_inst, cnveg_state_inst, & + subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & + do_plant_lastchance, do_plant_prescribed, & + temperature_inst, crop_inst, cnveg_state_inst, & cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst) @@ -2339,12 +2556,25 @@ subroutine PlantCrop(p, leafcn_in, jday, & ! !USES: use clm_varctl , only : use_c13, use_c14 + use clm_varctl , only : use_cropcal_rx_cultivar_gdds, use_cropcal_streams use clm_varcon , only : c13ratio, c14ratio + use clm_varpar , only : mxsowings + use pftconMod , only : ntmp_corn, nswheat, nwwheat, ntmp_soybean + use pftconMod , only : nirrig_tmp_corn, nirrig_swheat, nirrig_wwheat, nirrig_tmp_soybean + use pftconMod , only : ntrp_corn, nsugarcane, ntrp_soybean, ncotton, nrice + use pftconMod , only : nirrig_trp_corn, nirrig_sugarcane, nirrig_trp_soybean + use pftconMod , only : nirrig_cotton, nirrig_rice + use pftconMod , only : nmiscanthus, nirrig_miscanthus, nswitchgrass, nirrig_switchgrass ! ! !ARGUMENTS: integer , intent(in) :: p ! PATCH index running over real(r8) , intent(in) :: leafcn_in ! leaf C:N (gC/gN) of this patch's vegetation type (pftcon%leafcn(ivt(p))) integer , intent(in) :: jday ! julian day of the year + integer , intent(in) :: kyr ! current year + logical , intent(in) :: do_plant_normal ! Are all the normal requirements for planting met? + logical , intent(in) :: do_plant_lastchance ! Are the last-chance requirements for planting met? + logical , intent(in) :: do_plant_prescribed ! are we planting because it was prescribed? + type(temperature_type) , intent(in) :: temperature_inst type(crop_type) , intent(inout) :: crop_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst @@ -2353,26 +2583,63 @@ subroutine PlantCrop(p, leafcn_in, jday, & type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst type(cnveg_carbonstate_type) , intent(inout) :: c13_cnveg_carbonstate_inst type(cnveg_carbonstate_type) , intent(inout) :: c14_cnveg_carbonstate_inst + ! + ! LOCAL VARAIBLES: + integer s ! growing season index + integer k ! grain pool index + real(r8) gdd_target ! cultivar GDD target this growing season + real(r8) this_sowing_reason ! number representing sowing reason(s) + logical did_rx_gdds ! did this patch use a prescribed harvest requirement? !------------------------------------------------------------------------ associate( & + ivt => patch%itype , & ! Input: [integer (:) ] patch vegetation type croplive => crop_inst%croplive_patch , & ! Output: [logical (:) ] Flag, true if planted, not harvested harvdate => crop_inst%harvdate_patch , & ! Output: [integer (:) ] harvest date sowing_count => crop_inst%sowing_count , & ! Inout: [integer (:) ] number of sowing events this year for this patch - idop => cnveg_state_inst%idop_patch , & ! Output: [integer (:) ] date of planting + sowing_reason => crop_inst%sowing_reason_thisyr_patch , & ! Output: [real(r8) (:) ] reason for each sowing this year for this patch + gddmaturity => cnveg_state_inst%gddmaturity_patch , & ! Output: [real(r8) (:) ] gdd needed to harvest + idop => cnveg_state_inst%idop_patch , & ! Output: [integer (:) ] date of planting + iyop => cnveg_state_inst%iyop_patch , & ! Output: [integer (:) ] year of planting leafc_xfer => cnveg_carbonstate_inst%leafc_xfer_patch , & ! Output: [real(r8) (:) ] (gC/m2) leaf C transfer leafn_xfer => cnveg_nitrogenstate_inst%leafn_xfer_patch , & ! Output: [real(r8) (:) ] (gN/m2) leaf N transfer crop_seedc_to_leaf => cnveg_carbonflux_inst%crop_seedc_to_leaf_patch , & ! Output: [real(r8) (:) ] (gC/m2/s) seed source to leaf - crop_seedn_to_leaf => cnveg_nitrogenflux_inst%crop_seedn_to_leaf_patch & ! Output: [real(r8) (:) ] (gN/m2/s) seed source to leaf + crop_seedn_to_leaf => cnveg_nitrogenflux_inst%crop_seedn_to_leaf_patch, & ! Output: [real(r8) (:) ] (gN/m2/s) seed source to leaf + hybgdd => pftcon%hybgdd , & ! Input: [real(r8) (:) ] + gddmin => pftcon%gddmin , & ! Input: + gdd020 => temperature_inst%gdd020_patch , & ! Input: [real(r8) (:) ] 20 yr mean of gdd0 + gdd820 => temperature_inst%gdd820_patch , & ! Input: [real(r8) (:) ] 20 yr mean of gdd8 + gdd1020 => temperature_inst%gdd1020_patch , & ! Input: [real(r8) (:) ] 20 yr mean of gdd10 + aleafi => cnveg_state_inst%aleafi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 + astemi => cnveg_state_inst%astemi_patch , & ! Output: [real(r8) (:) ] saved allocation coefficient from phase 2 + aleaf => cnveg_state_inst%aleaf_patch , & ! Output: [real(r8) (:) ] leaf allocation coefficient + astem => cnveg_state_inst%astem_patch , & ! Output: [real(r8) (:) ] stem allocation coefficient + aroot => cnveg_state_inst%aroot_patch , & ! Output: [real(r8) (:) ] root allocation coefficient + arepr => cnveg_state_inst%arepr_patch & ! Output: [real(r8) (:,:) ] reproductive allocation coefficient(s) ) ! impose limit on growing season length needed ! for crop maturity - for cold weather constraints croplive(p) = .true. + crop_inst%sown_in_this_window(p) = .true. idop(p) = jday + iyop(p) = kyr harvdate(p) = NOT_Harvested sowing_count(p) = sowing_count(p) + 1 - crop_inst%sdates_thisyr(p,sowing_count(p)) = jday + + crop_inst%sdates_thisyr_patch(p,sowing_count(p)) = real(jday, r8) + + this_sowing_reason = 0._r8 + if (do_plant_prescribed) then + this_sowing_reason = 10._r8 + end if + if (do_plant_normal) then + this_sowing_reason = this_sowing_reason + 1._r8 + else if (do_plant_lastchance) then + this_sowing_reason = this_sowing_reason + 2._r8 + end if + sowing_reason(p,sowing_count(p)) = this_sowing_reason + crop_inst%sowing_reason_patch(p) = this_sowing_reason leafc_xfer(p) = initial_seed_at_planting leafn_xfer(p) = leafc_xfer(p) / leafcn_in ! with onset @@ -2398,13 +2665,96 @@ subroutine PlantCrop(p, leafcn_in, jday, & endif endif + ! set GDD target + did_rx_gdds = .false. + if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then + gddmaturity(p) = crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) + did_rx_gdds = .true. + else if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then + gddmaturity(p) = hybgdd(ivt(p)) + else + if (ivt(p) == ntmp_soybean .or. ivt(p) == nirrig_tmp_soybean .or. & + ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean) then + gddmaturity(p) = min(gdd1020(p), hybgdd(ivt(p))) + end if + if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & + ivt(p) == ntrp_corn .or. ivt(p) == nirrig_trp_corn .or. & + ivt(p) == nsugarcane .or. ivt(p) == nirrig_sugarcane .or. & + ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & + ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then + gddmaturity(p) = max(950._r8, min(gdd820(p)*0.85_r8, hybgdd(ivt(p)))) + if (do_plant_normal) then + gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) + end if + end if + if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & + ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & + ivt(p) == nrice .or. ivt(p) == nirrig_rice) then + gddmaturity(p) = min(gdd020(p), hybgdd(ivt(p))) + end if + + endif + + if (use_cropcal_streams .and. gddmaturity(p) < min_gddmaturity) then + if (did_rx_gdds) then + write(iulog,*) 'Some patch with ivt ',ivt(p),' has rx gddmaturity',gddmaturity(p),'; using min_gddmaturity instead (',min_gddmaturity,')' + endif + gddmaturity(p) = min_gddmaturity + endif + + ! Initialize allocation coefficients. + ! Because crops have no live carbon pools when planted but not emerged, this shouldn't + ! matter unless they skip the vegetative phase (which only happens in very weird run + ! setups). + aleaf(p) = 1._r8 + aleafi(p) = 1._r8 + astem(p) = 0._r8 + astemi(p) = 0._r8 + aroot(p) = 0._r8 + do k = 1, nrepr + arepr(p,k) = 0._r8 + end do + end associate end subroutine PlantCrop + !----------------------------------------------------------------------- + function DaysPastPlanting(idop, jday_in) + ! !USES: + use clm_time_manager, only : get_prev_calday, get_curr_days_per_year + ! + ! !ARGUMENTS: + integer, intent(in) :: idop ! patch day of planting + integer, optional, intent(in) :: jday_in ! julian day of the year + ! + ! !LOCAL VARIABLES + integer :: DaysPastPlanting + integer :: jday + + ! Must use separate jday_in and jday because we can't redefine an intent(in) + ! variable, even if it wasn't provided in the function call. + if (present(jday_in)) then + jday = jday_in + else + ! Use prev instead of curr to avoid jday=1 in last timestep of year + jday = get_prev_calday() + end if + + if (jday >= idop) then + DaysPastPlanting = jday - idop + else + ! As long as crops have at most a 365-day growing season, using get_curr_days_per_year() + ! should give the same result of this function as using get_prev_days_per_year(). + DaysPastPlanting = jday - idop + get_curr_days_per_year() + end if + + end function DaysPastPlanting + !----------------------------------------------------------------------- subroutine vernalization(p, & - canopystate_inst, temperature_inst, waterdiagnosticbulk_inst, cnveg_state_inst, crop_inst) + canopystate_inst, temperature_inst, waterdiagnosticbulk_inst, cnveg_state_inst, crop_inst, & + force_harvest) ! ! !DESCRIPTION: ! @@ -2423,6 +2773,7 @@ subroutine vernalization(p, & type(waterdiagnosticbulk_type) , intent(in) :: waterdiagnosticbulk_inst type(cnveg_state_type) , intent(inout) :: cnveg_state_inst type(crop_type) , intent(inout) :: crop_inst + logical , intent(inout) :: force_harvest ! "harvest" forced if freeze-killed ! ! LOCAL VARAIBLES: real(r8) tcrown ! ? @@ -2442,8 +2793,6 @@ subroutine vernalization(p, & hdidx => cnveg_state_inst%hdidx_patch , & ! Output: [real(r8) (:) ] cold hardening index? cumvd => cnveg_state_inst%cumvd_patch , & ! Output: [real(r8) (:) ] cumulative vernalization d?ependence? - gddmaturity => cnveg_state_inst%gddmaturity_patch , & ! Output: [real(r8) (:) ] gdd needed to harvest - huigrain => cnveg_state_inst%huigrain_patch , & ! Output: [real(r8) (:) ] heat unit index needed to reach vegetative maturity vf => crop_inst%vf_patch & ! Output: [real(r8) (:) ] vernalization factor for cereal ) @@ -2533,9 +2882,14 @@ subroutine vernalization(p, & if (tkil >= tcrown) then if ((0.95_r8 - 0.02_r8 * (tcrown - tkil)**2) >= 0.02_r8) then write (iulog,*) 'crop damaged by cold temperatures at p,c =', p,c - else if (tlai(p) > 0._r8) then ! slevis: kill if past phase1 - gddmaturity(p) = 0._r8 ! by forcing through - huigrain(p) = 0._r8 ! harvest + else if (tlai(p) > 0._r8) then + ! slevis: kill if past phase1 by forcing through harvest + ! srabin: do this with force_harvest instead of setting + ! gddmaturity = huigrain = 0, since gddmaturity==0 can + ! lead to 0/0 when the crop isn't actually harvested based + ! on "maturity." This can occur when generate_crop_gdds + ! is true. + force_harvest = .true. write (iulog,*) '95% of crop killed by cold temperatures at p,c =', p,c end if end if @@ -2565,7 +2919,7 @@ subroutine CNOnsetGrowth (num_soilp, filter_soilp, & ! ! !LOCAL VARIABLES: integer :: p ! indices - integer :: fp ! lake filter patch index + integer :: fp ! filter patch index real(r8):: t1 ! temporary variable !----------------------------------------------------------------------- @@ -2675,7 +3029,8 @@ end subroutine CNOnsetGrowth !----------------------------------------------------------------------- subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & - cnveg_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) + cnveg_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & + crop_inst) ! ! !DESCRIPTION: ! Determines the flux of C and N from displayed pools to litter @@ -2686,7 +3041,7 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & use pftconMod , only : nmiscanthus, nirrig_miscanthus, nswitchgrass, nirrig_switchgrass use CNSharedParamsMod, only : use_fun - use clm_varctl , only : CNratio_floating + use clm_varctl , only : CNratio_floating, crop_residue_removal_frac ! ! !ARGUMENTS: integer , intent(in) :: num_soilp ! number of soil patches in filter @@ -2696,10 +3051,11 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & type(cnveg_nitrogenstate_type), intent(in) :: cnveg_nitrogenstate_inst type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst + type(crop_type) , intent(in) :: crop_inst ! ! !LOCAL VARIABLES: - integer :: p, c, k ! indices - integer :: fp ! lake filter patch index + integer :: p, c, k, h ! indices + integer :: fp ! filter patch index real(r8):: t1 ! temporary variable real(r8):: denom ! temporary variable for divisor real(r8) :: ntovr_leaf @@ -2709,6 +3065,11 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & real(r8) :: cropseedn_deficit_remaining ! remaining amount of crop seed N deficit that still needs to be restored (gN/m2) (positive, in contrast to the negative cropseedn_deficit) real(r8) :: cropseedc_deficit_to_restore ! amount of crop seed C deficit that will be restored from this grain pool (gC/m2) real(r8) :: cropseedn_deficit_to_restore ! amount of crop seed N deficit that will be restored from this grain pool (gN/m2) + real(r8) :: repr_grainc_to_food_thispool ! amount added to / subtracted from repr_grainc_to_food for the pool in question (gC/m2/s) + real(r8) :: repr_grainn_to_food_thispool ! amount added to / subtracted from repr_grainn_to_food for the pool in question (gN/m2/s) + real(r8) :: leafc_remaining, livestemc_remaining + real(r8) :: leafn_remaining, livestemn_remaining + real(r8) :: removedresidue_fraction !----------------------------------------------------------------------- associate( & @@ -2746,21 +3107,33 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & frootc_to_litter => cnveg_carbonflux_inst%frootc_to_litter_patch , & ! Output: [real(r8) (:) ] fine root C litterfall (gC/m2/s) livestemc_to_litter => cnveg_carbonflux_inst%livestemc_to_litter_patch , & ! Output: [real(r8) (:) ] live stem C litterfall (gC/m2/s) repr_grainc_to_food => cnveg_carbonflux_inst%repr_grainc_to_food_patch , & ! Output: [real(r8) (:,:) ] grain C to food (gC/m2/s) + repr_grainc_to_food_perharv => cnveg_carbonflux_inst%repr_grainc_to_food_perharv_patch, & ! Output: [real(r8) (:,:,:) ] grain C to food per harvest (gC/m2) + repr_grainc_to_food_thisyr => cnveg_carbonflux_inst%repr_grainc_to_food_thisyr_patch, & ! Output: [real(r8) (:,:) ] grain C to food harvested this calendar year (gC/m2) repr_grainc_to_seed => cnveg_carbonflux_inst%repr_grainc_to_seed_patch , & ! Output: [real(r8) (:,:) ] grain C to seed (gC/m2/s) + repr_grainc_to_seed_perharv => cnveg_carbonflux_inst%repr_grainc_to_seed_perharv_patch, & ! Output: [real(r8) (:,:,:) ] grain C to seed per harvest (gC/m2) + repr_grainc_to_seed_thisyr => cnveg_carbonflux_inst%repr_grainc_to_seed_thisyr_patch, & ! Output: [real(r8) (:,:) ] grain C to seed harvested this calendar year (gC/m2) repr_structurec_to_cropprod => cnveg_carbonflux_inst%repr_structurec_to_cropprod_patch, & ! Output: [real(r8) (:,:) ] reproductive structure C to crop product pool (gC/m2/s) repr_structurec_to_litter => cnveg_carbonflux_inst%repr_structurec_to_litter_patch, & ! Output: [real(r8) (:,:) ] reproductive structure C to litter (gC/m2/s) leafc_to_biofuelc => cnveg_carbonflux_inst%leafc_to_biofuelc_patch , & ! Output: [real(r8) (:) ] leaf C to biofuel C (gC/m2/s) livestemc_to_biofuelc => cnveg_carbonflux_inst%livestemc_to_biofuelc_patch , & ! Output: [real(r8) (:) ] livestem C to biofuel C (gC/m2/s) + leafc_to_removedresiduec => cnveg_carbonflux_inst%leafc_to_removedresiduec_patch , & ! Output: [real(r8) (:) ] leaf C to removed residue C (gC/m2/s) + livestemc_to_removedresiduec => cnveg_carbonflux_inst%livestemc_to_removedresiduec_patch , & ! Output: [real(r8) (:) ] livestem C to removed residue C (gC/m2/s) leafn => cnveg_nitrogenstate_inst%leafn_patch , & ! Input: [real(r8) (:) ] (gN/m2) leaf N frootn => cnveg_nitrogenstate_inst%frootn_patch , & ! Input: [real(r8) (:) ] (gN/m2) fine root N livestemn_to_litter => cnveg_nitrogenflux_inst%livestemn_to_litter_patch , & ! Output: [real(r8) (:) ] livestem N to litter (gN/m2/s) repr_grainn_to_food => cnveg_nitrogenflux_inst%repr_grainn_to_food_patch , & ! Output: [real(r8) (:,:) ] grain N to food (gN/m2/s) + repr_grainn_to_food_perharv => cnveg_nitrogenflux_inst%repr_grainn_to_food_perharv_patch, & ! Output: [real(r8) (:,:,:) ] grain N to food per harvest (gN/m2) + repr_grainn_to_food_thisyr => cnveg_nitrogenflux_inst%repr_grainn_to_food_thisyr_patch, & ! Output: [real(r8) (:,:) ] grain N to food harvested this calendar year (gN/m2) repr_grainn_to_seed => cnveg_nitrogenflux_inst%repr_grainn_to_seed_patch , & ! Output: [real(r8) (:,:) ] grain N to seed (gN/m2/s) + repr_grainn_to_seed_perharv => cnveg_nitrogenflux_inst%repr_grainn_to_seed_perharv_patch, & ! Output: [real(r8) (:,:,:) ] grain N to seed per harvest (gN/m2) + repr_grainn_to_seed_thisyr => cnveg_nitrogenflux_inst%repr_grainn_to_seed_thisyr_patch, & ! Output: [real(r8) (:,:) ] grain N to seed harvested this calendar year (gN/m2) repr_structuren_to_cropprod => cnveg_nitrogenflux_inst%repr_structuren_to_cropprod_patch, & ! Output: [real(r8) (:,:) ] reproductive structure N to crop product pool (gN/m2/s) repr_structuren_to_litter => cnveg_nitrogenflux_inst%repr_structuren_to_litter_patch, & ! Output: [real(r8) (:,:) ] reproductive structure N to litter (gN/m2/s) leafn_to_biofueln => cnveg_nitrogenflux_inst%leafn_to_biofueln_patch , & ! Output: [real(r8) (:) ] leaf N to biofuel N (gN/m2/s) livestemn_to_biofueln => cnveg_nitrogenflux_inst%livestemn_to_biofueln_patch, & ! Output: [real(r8) (:) ] livestem N to biofuel N (gN/m2/s) + leafn_to_removedresiduen => cnveg_nitrogenflux_inst%leafn_to_removedresiduen_patch , & ! Output: [real(r8) (:) ] leaf N to removed residue N (gN/m2/s) + livestemn_to_removedresiduen => cnveg_nitrogenflux_inst%livestemn_to_removedresiduen_patch, & ! Output: [real(r8) (:) ] livestem N to removed residue N (gN/m2/s) leafn_to_litter => cnveg_nitrogenflux_inst%leafn_to_litter_patch , & ! Output: [real(r8) (:) ] leaf N litterfall (gN/m2/s) leafn_to_retransn => cnveg_nitrogenflux_inst%leafn_to_retransn_patch , & ! Input: [real(r8) (:) ] leaf N to retranslocated N pool (gN/m2/s) free_retransn_to_npool=> cnveg_nitrogenflux_inst%free_retransn_to_npool_patch , & ! Input: [real(r8) (:) ] free leaf N to retranslocated N pool (gN/m2/s) @@ -2773,7 +3146,7 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & ! The litterfall transfer rate starts at 0.0 and increases linearly ! over time, with displayed growth going to 0.0 on the last day of litterfall - + do fp = 1,num_soilp p = filter_soilp(fp) @@ -2784,9 +3157,6 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & t1 = 1.0_r8 / dt frootc_to_litter(p) = t1 * frootc(p) + cpool_to_frootc(p) - ! biofuel_harvfrac is only non-zero for prognostic crops. - leafc_to_litter(p) = t1 * leafc(p)*(1._r8-biofuel_harvfrac(ivt(p))) + cpool_to_leafc(p) - ! leafc_litter and frootc_to_litter for matrix if (use_matrixcn) then else @@ -2797,6 +3167,10 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & ! this assumes that offset_counter == dt for crops ! if this were ever changed, we'd need to add code to the "else" if (ivt(p) >= npcropmin) then + + ! How many harvests have occurred? + h = crop_inst%harvest_count(p) + ! Replenish the seed deficits from grain, if there is enough available ! grain. (If there is not enough available grain, the seed deficits will ! accumulate until there is eventually enough grain to replenish them.) @@ -2814,16 +3188,39 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & cropseedc_deficit_to_restore = min(cropseedc_deficit_remaining, reproductivec(p,k)) cropseedc_deficit_remaining = cropseedc_deficit_remaining - cropseedc_deficit_to_restore repr_grainc_to_seed(p,k) = t1 * cropseedc_deficit_to_restore - + if (cropseedc_deficit_to_restore > 0._r8) then + repr_grainc_to_seed_perharv(p,h,k) = cropseedc_deficit_to_restore + repr_grainc_to_seed_thisyr(p,k) = repr_grainc_to_seed_thisyr(p,k) & + + repr_grainc_to_seed_perharv(p,h,k) + end if cropseedn_deficit_to_restore = min(cropseedn_deficit_remaining, reproductiven(p,k)) cropseedn_deficit_remaining = cropseedn_deficit_remaining - cropseedn_deficit_to_restore repr_grainn_to_seed(p,k) = t1 * cropseedn_deficit_to_restore + if (cropseedn_deficit_to_restore > 0._r8) then + repr_grainn_to_seed_perharv(p,h,k) = cropseedn_deficit_to_restore + repr_grainn_to_seed_thisyr(p,k) = repr_grainn_to_seed_thisyr(p,k) & + + repr_grainn_to_seed_perharv(p,h,k) + end if ! Send the remaining grain to the food product pool + repr_grainc_to_food_thispool = cpool_to_reproductivec(p,k) - repr_grainc_to_seed(p,k) repr_grainc_to_food(p,k) = t1 * reproductivec(p,k) & - + cpool_to_reproductivec(p,k) - repr_grainc_to_seed(p,k) + + repr_grainc_to_food_thispool + if (reproductivec(p,k) + repr_grainc_to_food_thispool * dt > 0._r8) then + repr_grainc_to_food_perharv(p,h,k) = reproductivec(p,k) & + + repr_grainc_to_food_thispool * dt + repr_grainc_to_food_thisyr(p,k) = repr_grainc_to_food_thisyr(p,k) & + + repr_grainc_to_food_perharv(p,h,k) + end if + repr_grainn_to_food_thispool = npool_to_reproductiven(p,k) - repr_grainn_to_seed(p,k) repr_grainn_to_food(p,k) = t1 * reproductiven(p,k) & + npool_to_reproductiven(p,k) - repr_grainn_to_seed(p,k) + if (reproductiven(p,k) + repr_grainn_to_food_thispool * dt > 0._r8) then + repr_grainn_to_food_perharv(p,h,k) = reproductiven(p,k) & + + repr_grainn_to_food_thispool * dt + repr_grainn_to_food_thisyr(p,k) = repr_grainn_to_food_thisyr(p,k) & + + repr_grainn_to_food_perharv(p,h,k) + end if end do do k = repr_structure_min, repr_structure_max @@ -2840,13 +3237,34 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & ! Cut a certain fraction (i.e., biofuel_harvfrac(ivt(p))) (e.g., biofuel_harvfrac(ivt(p)=70% for bioenergy crops) of leaf C ! and move this fration of leaf C to biofuel C, rather than move it to litter leafc_to_biofuelc(p) = t1 * leafc(p) * biofuel_harvfrac(ivt(p)) + leafc_remaining = leafc(p)*(1._r8-biofuel_harvfrac(ivt(p))) leafn_to_biofueln(p) = t1 * leafn(p) * biofuel_harvfrac(ivt(p)) + leafn_remaining = leafn(p)*(1._r8-biofuel_harvfrac(ivt(p))) ! Cut a certain fraction (i.e., biofuel_harvfrac(ivt(p))) (e.g., biofuel_harvfrac(ivt(p)=70% for bioenergy crops) of livestem C ! and move this fration of leaf C to biofuel C, rather than move it to litter - livestemc_to_litter(p) = t1 * livestemc(p)*(1._r8-biofuel_harvfrac(ivt(p))) + cpool_to_livestemc(p) livestemc_to_biofuelc(p) = t1 * livestemc(p) * biofuel_harvfrac(ivt(p)) livestemn_to_biofueln(p) = t1 * livestemn(p) * biofuel_harvfrac(ivt(p)) + livestemc_remaining = livestemc(p)*(1._r8-biofuel_harvfrac(ivt(p))) + livestemn_remaining = livestemn(p)*(1._r8-biofuel_harvfrac(ivt(p))) + + ! Remove residues + leafc_to_removedresiduec(p) = t1 * leafc_remaining * crop_residue_removal_frac + leafn_to_removedresiduen(p) = t1 * leafn_remaining * crop_residue_removal_frac + livestemc_to_removedresiduec(p) = t1 * livestemc_remaining * crop_residue_removal_frac + livestemn_to_removedresiduen(p) = t1 * livestemn_remaining * crop_residue_removal_frac + leafc_remaining = leafc_remaining * (1._r8 - crop_residue_removal_frac) + leafn_remaining = leafn_remaining * (1._r8 - crop_residue_removal_frac) + livestemc_remaining = livestemc_remaining * (1._r8 - crop_residue_removal_frac) + livestemn_remaining = livestemn_remaining * (1._r8 - crop_residue_removal_frac) + + leafc_to_litter(p) = t1 * leafc_remaining + cpool_to_leafc(p) + livestemc_to_litter(p) = t1 * livestemc_remaining + cpool_to_livestemc(p) + livestemn_to_litter(p) = t1 * livestemn_remaining + ! Sam Rabin 2023-09-11: + ! leafn_to_litter is calculated below based on leafc_to_litter (updated above) + ! as well as leaf C:N ratio (unaffected by biofuel harvest). It thus does not + ! need to be updated here. ! Matrix for grain, livestem to litter and biofuel if(use_matrixcn)then @@ -2984,18 +3402,6 @@ subroutine CNOffsetLitterfall (num_soilp, filter_soilp, & endif end if - if (ivt(p) >= npcropmin) then - ! NOTE(slevis, 2014-12) results in -ve livestemn and -ve totpftn - !X! livestemn_to_litter(p) = livestemc_to_litter(p) / livewdcn(ivt(p)) - ! NOTE(slevis, 2014-12) Beth Drewniak suggested this instead - livestemn_to_litter(p) = livestemn(p) / dt * (1._r8 - biofuel_harvfrac(ivt(p))) - - ! Matrix update for livestemn to litter - if(use_matrixcn)then - else - end if - end if - ! save the current litterfall fluxes prev_leafc_to_litter(p) = leafc_to_litter(p) prev_frootc_to_litter(p) = frootc_to_litter(p) @@ -3032,7 +3438,7 @@ subroutine CNBackgroundLitterfall (num_soilp, filter_soilp, & ! ! !LOCAL VARIABLES: integer :: p ! indices - integer :: fp ! lake filter patch index + integer :: fp ! filter patch index real(r8) :: fr_leafn_to_litter ! fraction of the nitrogen turnover that goes to litter; remaining fraction is retranslocated real(r8) :: ntovr_leaf real(r8) :: denom @@ -3189,7 +3595,7 @@ subroutine CNLivewoodTurnover (num_soilp, filter_soilp, & ! ! !LOCAL VARIABLES: integer :: p ! indices - integer :: fp ! lake filter patch index + integer :: fp ! filter patch index real(r8):: ctovr ! temporary variable for carbon turnover real(r8):: ntovr ! temporary variable for nitrogen turnover !----------------------------------------------------------------------- @@ -3338,10 +3744,14 @@ subroutine CNCropHarvestToProductPools(bounds, num_soilp, filter_soilp, num_soil p = filter_soilp(fp) cnveg_carbonflux_inst%crop_harvestc_to_cropprodc_patch(p) = & cnveg_carbonflux_inst%leafc_to_biofuelc_patch(p) + & - cnveg_carbonflux_inst%livestemc_to_biofuelc_patch(p) + cnveg_carbonflux_inst%livestemc_to_biofuelc_patch(p) + & + cnveg_carbonflux_inst%leafc_to_removedresiduec_patch(p) + & + cnveg_carbonflux_inst%livestemc_to_removedresiduec_patch(p) cnveg_nitrogenflux_inst%crop_harvestn_to_cropprodn_patch(p) = & cnveg_nitrogenflux_inst%leafn_to_biofueln_patch(p) + & - cnveg_nitrogenflux_inst%livestemn_to_biofueln_patch(p) + cnveg_nitrogenflux_inst%livestemn_to_biofueln_patch(p) + & + cnveg_nitrogenflux_inst%leafn_to_removedresiduen_patch(p) + & + cnveg_nitrogenflux_inst%livestemn_to_removedresiduen_patch(p) end do if (use_grainproduct) then @@ -3383,7 +3793,7 @@ subroutine CNCropHarvestToProductPools(bounds, num_soilp, filter_soilp, num_soil end subroutine CNCropHarvestToProductPools !----------------------------------------------------------------------- - subroutine CNLitterToColumn (bounds, num_soilc, filter_soilc, & + subroutine CNLitterToColumn (bounds, num_bgc_vegp, filter_bgc_vegp, & cnveg_state_inst,cnveg_carbonflux_inst, cnveg_nitrogenflux_inst, & leaf_prof_patch, froot_prof_patch) ! @@ -3392,14 +3802,14 @@ subroutine CNLitterToColumn (bounds, num_soilc, filter_soilc, & ! to the column level and assign them to the three litter pools ! ! !USES: - use clm_varpar , only : max_patch_per_col, nlevdecomp + use clm_varpar , only : nlevdecomp use pftconMod , only : npcropmin use clm_varctl , only : use_grainproduct ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_vegp ! number of bgc veg patches + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc veg patches type(cnveg_state_type) , intent(in) :: cnveg_state_inst type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst @@ -3407,7 +3817,7 @@ subroutine CNLitterToColumn (bounds, num_soilc, filter_soilc, & real(r8) , intent(in) :: froot_prof_patch(bounds%begp:,1:) ! ! !LOCAL VARIABLES: - integer :: fc,c,pi,p,k,j,i ! indices + integer :: fp,c,p,k,j,i ! indices !----------------------------------------------------------------------- SHR_ASSERT_ALL_FL((ubound(leaf_prof_patch) == (/bounds%endp,nlevdecomp_full/)), sourcefile, __LINE__) @@ -3437,95 +3847,87 @@ subroutine CNLitterToColumn (bounds, num_soilc, filter_soilc, & frootn_to_litter => cnveg_nitrogenflux_inst%frootn_to_litter_patch , & ! Input: [real(r8) (:) ] fine root N litterfall (gN/m2/s) phenology_n_to_litr_n => cnveg_nitrogenflux_inst%phenology_n_to_litr_n_col & ! Output: [real(r8) (:,:,:) ] N fluxes associated with phenology (litterfall and crop) to litter pools (gN/m3/s) ) - - do j = 1, nlevdecomp - do pi = 1,max_patch_per_col - do fc = 1,num_soilc - c = filter_soilc(fc) + + do_nlev: do j = 1, nlevdecomp - if ( pi <= col%npatches(c) ) then - p = col%patchi(c) + pi - 1 - if (patch%active(p)) then + do_vegp: do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) + c = patch%column(p) - do i = i_litr_min, i_litr_max - ! leaf litter carbon fluxes - phenology_c_to_litr_c(c,j,i) = & - phenology_c_to_litr_c(c,j,i) + & - leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - - ! leaf litter nitrogen fluxes - phenology_n_to_litr_n(c,j,i) = & - phenology_n_to_litr_n(c,j,i) + & - leafn_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - - ! fine root litter carbon fluxes + do_ilit: do i = i_litr_min, i_litr_max + ! leaf litter carbon fluxes + phenology_c_to_litr_c(c,j,i) = & + phenology_c_to_litr_c(c,j,i) + & + leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + + ! leaf litter nitrogen fluxes + phenology_n_to_litr_n(c,j,i) = & + phenology_n_to_litr_n(c,j,i) + & + leafn_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + + ! fine root litter carbon fluxes + phenology_c_to_litr_c(c,j,i) = & + phenology_c_to_litr_c(c,j,i) + & + frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + + ! fine root litter nitrogen fluxes + phenology_n_to_litr_n(c,j,i) = & + phenology_n_to_litr_n(c,j,i) + & + frootn_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + end do do_ilit + + ! agroibis puts crop stem litter together with leaf litter + ! so I've used the leaf lf_f* parameters instead of making + ! new ones for now (slevis) + ! also for simplicity I've put "food" into the litter pools + + if (ivt(p) >= npcropmin) then ! add livestemc to litter + do i = i_litr_min, i_litr_max + ! stem litter carbon fluxes + phenology_c_to_litr_c(c,j,i) = & + phenology_c_to_litr_c(c,j,i) + & + livestemc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + + ! stem litter nitrogen fluxes + phenology_n_to_litr_n(c,j,i) = & + phenology_n_to_litr_n(c,j,i) + & + livestemn_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + end do + + if (.not. use_grainproduct) then + do i = i_litr_min, i_litr_max + do k = repr_grain_min, repr_grain_max + ! grain litter carbon fluxes phenology_c_to_litr_c(c,j,i) = & - phenology_c_to_litr_c(c,j,i) + & - frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + phenology_c_to_litr_c(c,j,i) + & + repr_grainc_to_food(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - ! fine root litter nitrogen fluxes + ! grain litter nitrogen fluxes phenology_n_to_litr_n(c,j,i) = & - phenology_n_to_litr_n(c,j,i) + & - frootn_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + phenology_n_to_litr_n(c,j,i) + & + repr_grainn_to_food(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) end do - - ! agroibis puts crop stem litter together with leaf litter - ! so I've used the leaf lf_f* parameters instead of making - ! new ones for now (slevis) - ! also for simplicity I've put "food" into the litter pools - - if (ivt(p) >= npcropmin) then ! add livestemc to litter - do i = i_litr_min, i_litr_max - ! stem litter carbon fluxes - phenology_c_to_litr_c(c,j,i) = & - phenology_c_to_litr_c(c,j,i) + & - livestemc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - - ! stem litter nitrogen fluxes - phenology_n_to_litr_n(c,j,i) = & - phenology_n_to_litr_n(c,j,i) + & - livestemn_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - end do - - if (.not. use_grainproduct) then - do i = i_litr_min, i_litr_max - do k = repr_grain_min, repr_grain_max - ! grain litter carbon fluxes - phenology_c_to_litr_c(c,j,i) = & - phenology_c_to_litr_c(c,j,i) + & - repr_grainc_to_food(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - - ! grain litter nitrogen fluxes - phenology_n_to_litr_n(c,j,i) = & - phenology_n_to_litr_n(c,j,i) + & - repr_grainn_to_food(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - end do - end do - end if - - do i = i_litr_min, i_litr_max - do k = repr_structure_min, repr_structure_max - ! reproductive structure litter carbon fluxes - phenology_c_to_litr_c(c,j,i) = & - phenology_c_to_litr_c(c,j,i) + & - repr_structurec_to_litter(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - - ! reproductive structure litter nitrogen fluxes - phenology_n_to_litr_n(c,j,i) = & - phenology_n_to_litr_n(c,j,i) + & - repr_structuren_to_litter(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - end do - end do - end if - end if + end do end if - end do - - end do - end do + do i = i_litr_min, i_litr_max + do k = repr_structure_min, repr_structure_max + ! reproductive structure litter carbon fluxes + phenology_c_to_litr_c(c,j,i) = & + phenology_c_to_litr_c(c,j,i) + & + repr_structurec_to_litter(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + + ! reproductive structure litter nitrogen fluxes + phenology_n_to_litr_n(c,j,i) = & + phenology_n_to_litr_n(c,j,i) + & + repr_structuren_to_litter(p,k) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + end do + end do + end if + end do do_vegp + end do do_nlev - end associate + end associate end subroutine CNLitterToColumn diff --git a/src/biogeochem/CNPrecisionControlMod.F90 b/src/biogeochem/CNPrecisionControlMod.F90 index 8b98f6c3fb..787a5b54d7 100644 --- a/src/biogeochem/CNPrecisionControlMod.F90 +++ b/src/biogeochem/CNPrecisionControlMod.F90 @@ -96,7 +96,7 @@ subroutine CNPrecisionControlReadNML( NLFilename ) end subroutine CNPrecisionControlReadNML !----------------------------------------------------------------------- - subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & + subroutine CNPrecisionControl(bounds, num_bgc_vegp, filter_bgc_vegp, & cnveg_carbonstate_inst, c13_cnveg_carbonstate_inst, c14_cnveg_carbonstate_inst, & cnveg_nitrogenstate_inst) ! @@ -111,8 +111,8 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds ! bounds - integer , intent(in) :: num_soilp ! number of soil patchs in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_vegp ! number of bgc veg patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc veg patches type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst type(cnveg_carbonstate_type) , intent(inout) :: c13_cnveg_carbonstate_inst type(cnveg_carbonstate_type) , intent(inout) :: c14_cnveg_carbonstate_inst @@ -190,8 +190,8 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & ) ! patch loop - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) ! initialize the patch-level C and N truncation terms pc(p) = 0._r8 @@ -205,7 +205,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & ! the C component, but truncate C, C13, and N components ! leaf C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%leafc_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%leafc_patch(bounds%begp:bounds%endp), & ns%leafn_patch(bounds%begp:bounds%endp), & pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep) @@ -223,7 +223,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & ! leaf storage C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%leafc_storage_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%leafc_storage_patch(bounds%begp:bounds%endp), & ns%leafn_storage_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep) if (use_c13) then @@ -238,7 +238,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! leaf transfer C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%leafc_xfer_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%leafc_xfer_patch(bounds%begp:bounds%endp), & ns%leafn_xfer_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep) if (use_c13) then @@ -256,7 +256,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & ! EBK KO DML: For some reason frootc/frootn can go negative and allowing ! it to be negative is important for C4 crops (otherwise they die) Jun/3/2016 if ( prec_control_for_froot ) then - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%frootc_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%frootc_patch(bounds%begp:bounds%endp), & ns%frootn_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep, allowneg=.true.) if (use_c13) then @@ -272,7 +272,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! froot storage C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%frootc_storage_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%frootc_storage_patch(bounds%begp:bounds%endp), & ns%frootn_storage_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep) if (use_c13) then @@ -287,7 +287,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! froot transfer C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%frootc_xfer_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%frootc_xfer_patch(bounds%begp:bounds%endp), & ns%frootn_xfer_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep) if (use_c13) then @@ -304,7 +304,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & if ( use_crop )then do k = 1, nrepr ! grain C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%reproductivec_patch(bounds%begp:bounds%endp,k), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%reproductivec_patch(bounds%begp:bounds%endp,k), & ns%reproductiven_patch(bounds%begp:bounds%endp,k), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep, croponly=.true. ) if (use_c13) then @@ -319,7 +319,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! grain storage C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, & cs%reproductivec_storage_patch(bounds%begp:bounds%endp,k), & ns%reproductiven_storage_patch(bounds%begp:bounds%endp,k), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep, croponly=.true. ) @@ -336,7 +336,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! grain transfer C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, & cs%reproductivec_xfer_patch(bounds%begp:bounds%endp,k), & ns%reproductiven_xfer_patch(bounds%begp:bounds%endp,k), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep, croponly=.true.) @@ -352,7 +352,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if end do ! grain transfer C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%cropseedc_deficit_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%cropseedc_deficit_patch(bounds%begp:bounds%endp), & ns%cropseedn_deficit_patch(bounds%begp:bounds%endp), pc(bounds%begp:), & pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep, & @@ -371,7 +371,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! livestem C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%livestemc_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%livestemc_patch(bounds%begp:bounds%endp), & ns%livestemn_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep) if (use_c13) then @@ -386,7 +386,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! livestem storage C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%livestemc_storage_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%livestemc_storage_patch(bounds%begp:bounds%endp), & ns%livestemn_storage_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep) @@ -401,7 +401,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & __LINE__) end if ! livestem transfer C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%livestemc_xfer_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%livestemc_xfer_patch(bounds%begp:bounds%endp), & ns%livestemn_xfer_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep) if (use_c13) then @@ -416,7 +416,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! deadstem C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%deadstemc_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%deadstemc_patch(bounds%begp:bounds%endp), & ns%deadstemn_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep) if (use_c13) then @@ -430,7 +430,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & __LINE__) end if ! deadstem storage C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%deadstemc_storage_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%deadstemc_storage_patch(bounds%begp:bounds%endp), & ns%deadstemn_storage_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep) if (use_c13) then @@ -445,7 +445,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! deadstem transfer C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%deadstemc_xfer_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%deadstemc_xfer_patch(bounds%begp:bounds%endp), & ns%deadstemn_xfer_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep) if (use_c13) then @@ -460,7 +460,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! livecroot C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%livecrootc_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%livecrootc_patch(bounds%begp:bounds%endp), & ns%livecrootn_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep) if (use_c13) then @@ -475,7 +475,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! livecroot storage C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%livecrootc_storage_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%livecrootc_storage_patch(bounds%begp:bounds%endp), & ns%livecrootn_storage_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep) if (use_c13) then @@ -490,7 +490,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! livecroot transfer C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%livecrootc_xfer_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%livecrootc_xfer_patch(bounds%begp:bounds%endp), & ns%livecrootn_xfer_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep) @@ -506,7 +506,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! deadcroot C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%deadcrootc_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%deadcrootc_patch(bounds%begp:bounds%endp), & ns%deadcrootn_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), __LINE__, & num_truncatep, filter_truncatep) if (use_c13) then @@ -521,7 +521,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! deadcroot storage C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%deadcrootc_storage_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%deadcrootc_storage_patch(bounds%begp:bounds%endp), & ns%deadcrootn_storage_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep) if (use_c13) then @@ -536,7 +536,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! deadcroot transfer C and N - call TruncateCandNStates( bounds, filter_soilp, num_soilp, cs%deadcrootc_xfer_patch(bounds%begp:bounds%endp), & + call TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%deadcrootc_xfer_patch(bounds%begp:bounds%endp), & ns%deadcrootn_xfer_patch(bounds%begp:bounds%endp), pc(bounds%begp:), pn(bounds%begp:), & __LINE__, num_truncatep, filter_truncatep) if (use_c13) then @@ -551,7 +551,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! gresp_storage (C only) - call TruncateCStates( bounds, filter_soilp, num_soilp, cs%gresp_storage_patch(bounds%begp:bounds%endp), & + call TruncateCStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%gresp_storage_patch(bounds%begp:bounds%endp), & pc(bounds%begp:), __LINE__, num_truncatep, filter_truncatep) if (use_c13) then call TruncateAdditional( bounds, num_truncatep, filter_truncatep, & @@ -565,7 +565,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! gresp_xfer(c only) - call TruncateCStates( bounds, filter_soilp, num_soilp, cs%gresp_xfer_patch(bounds%begp:bounds%endp), & + call TruncateCStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%gresp_xfer_patch(bounds%begp:bounds%endp), & pc(bounds%begp:), __LINE__, num_truncatep, filter_truncatep) if (use_c13) then call TruncateAdditional( bounds, num_truncatep, filter_truncatep, & @@ -579,7 +579,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! cpool (C only) - call TruncateCStates( bounds, filter_soilp, num_soilp, cs%cpool_patch(bounds%begp:bounds%endp), & + call TruncateCStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%cpool_patch(bounds%begp:bounds%endp), & pc(bounds%begp:), __LINE__, num_truncatep, filter_truncatep) if (use_c13) then call TruncateAdditional( bounds, num_truncatep, filter_truncatep, & @@ -595,7 +595,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & if ( use_crop )then ! xsmrpool (C only) ! xsmr is a pool to balance the budget and as such can be freely negative - call TruncateCStates( bounds, filter_soilp, num_soilp, cs%xsmrpool_patch(bounds%begp:bounds%endp), & + call TruncateCStates( bounds, filter_bgc_vegp, num_bgc_vegp, cs%xsmrpool_patch(bounds%begp:bounds%endp), & pc(bounds%begp:), __LINE__, num_truncatep, filter_truncatep, & allowneg=.true., croponly=.true. ) if (use_c13) then @@ -612,16 +612,16 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end if ! retransn (N only) - call TruncateNStates( bounds, filter_soilp, num_soilp, ns%retransn_patch(bounds%begp:bounds%endp), pn(bounds%begp:), & + call TruncateNStates( bounds, filter_bgc_vegp, num_bgc_vegp, ns%retransn_patch(bounds%begp:bounds%endp), pn(bounds%begp:), & __LINE__ ) ! npool (N only) - call TruncateNStates( bounds, filter_soilp, num_soilp, ns%npool_patch(bounds%begp:bounds%endp), pn(bounds%begp:), & + call TruncateNStates( bounds, filter_bgc_vegp, num_bgc_vegp, ns%npool_patch(bounds%begp:bounds%endp), pn(bounds%begp:), & __LINE__ ) ! patch loop - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) cs%ctrunc_patch(p) = cs%ctrunc_patch(p) + pc(p) @@ -639,7 +639,7 @@ subroutine CNPrecisionControl(bounds, num_soilp, filter_soilp, & end subroutine CNPrecisionControl - subroutine TruncateCandNStates( bounds, filter_soilp, num_soilp, carbon_patch, nitrogen_patch, pc, pn, lineno, & + subroutine TruncateCandNStates( bounds, filter_bgc_vegp, num_bgc_vegp, carbon_patch, nitrogen_patch, pc, pn, lineno, & num_truncatep, filter_truncatep, croponly, allowneg ) ! ! !DESCRIPTION: @@ -657,8 +657,8 @@ subroutine TruncateCandNStates( bounds, filter_soilp, num_soilp, carbon_patch, n ! !ARGUMENTS: implicit none type(bounds_type) , intent(in) :: bounds ! bounds - integer , intent(in) :: num_soilp ! number of soil patchs in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_vegp ! number of bgc veg patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc veg patches real(r8), intent(inout) :: carbon_patch(bounds%begp:) real(r8), intent(inout) :: nitrogen_patch(bounds%begp:) real(r8), intent(inout) :: pc(bounds%begp:) @@ -688,8 +688,8 @@ subroutine TruncateCandNStates( bounds, filter_soilp, num_soilp, carbon_patch, n end if num_truncatep = 0 - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) if ( .not. lcroponly .or. (patch%itype(p) >= nc3crop) ) then if ( .not. lallowneg .and. ((carbon_patch(p) < cnegcrit) .or. (nitrogen_patch(p) < nnegcrit)) ) then @@ -733,7 +733,7 @@ subroutine TruncateCandNStates( bounds, filter_soilp, num_soilp, carbon_patch, n end do end subroutine TruncateCandNStates - subroutine TruncateCStates( bounds, filter_soilp, num_soilp, carbon_patch, pc, lineno, & + subroutine TruncateCStates( bounds, filter_bgc_vegp, num_bgc_vegp, carbon_patch, pc, lineno, & num_truncatep, filter_truncatep, croponly, allowneg ) ! ! !DESCRIPTION: @@ -751,8 +751,8 @@ subroutine TruncateCStates( bounds, filter_soilp, num_soilp, carbon_patch, pc, l ! !ARGUMENTS: implicit none type(bounds_type), intent(in) :: bounds ! bounds - integer , intent(in) :: num_soilp ! number of soil patchs in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_vegp ! number of bgc veg patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc veg patches real(r8) , intent(inout) :: carbon_patch(bounds%begp:) real(r8) , intent(inout) :: pc(bounds%begp:) integer , intent(in) :: lineno @@ -780,8 +780,8 @@ subroutine TruncateCStates( bounds, filter_soilp, num_soilp, carbon_patch, pc, l end if num_truncatep = 0 - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) if ( .not. lcroponly .or. (patch%itype(p) >= nc3crop) ) then if ( .not. lallowneg .and. (carbon_patch(p) < cnegcrit) ) then @@ -801,7 +801,7 @@ subroutine TruncateCStates( bounds, filter_soilp, num_soilp, carbon_patch, pc, l end do end subroutine TruncateCStates - subroutine TruncateNStates( bounds, filter_soilp, num_soilp, nitrogen_patch, pn, lineno ) + subroutine TruncateNStates( bounds, filter_bgc_vegp, num_bgc_vegp, nitrogen_patch, pn, lineno ) ! ! !DESCRIPTION: ! Truncate Nitrogen states. If a nitrogen state is too small truncate it to @@ -816,8 +816,8 @@ subroutine TruncateNStates( bounds, filter_soilp, num_soilp, nitrogen_patch, pn, ! !ARGUMENTS: implicit none type(bounds_type) , intent(in) :: bounds ! bounds - integer , intent(in) :: num_soilp ! number of soil patchs in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_vegp ! number of bgc veg patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc veg patches real(r8), intent(inout) :: nitrogen_patch(bounds%begp:) real(r8), intent(inout) :: pn(bounds%begp:) integer, intent(in) :: lineno @@ -826,8 +826,8 @@ subroutine TruncateNStates( bounds, filter_soilp, num_soilp, nitrogen_patch, pn, SHR_ASSERT_ALL_FL((ubound(nitrogen_patch) == (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(pn) == (/bounds%endp/)), sourcefile, __LINE__) - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) if ( nitrogen_patch(p) < nnegcrit ) then ! write(iulog,*) 'WARNING: Nitrogen patch negative = ', nitrogen_patch ! call endrun(subgrid_index=p, subgrid_level=subgrid_level_patch, & diff --git a/src/biogeochem/CNProductsMod.F90 b/src/biogeochem/CNProductsMod.F90 index 9744b04aed..a5f69696b0 100644 --- a/src/biogeochem/CNProductsMod.F90 +++ b/src/biogeochem/CNProductsMod.F90 @@ -13,13 +13,16 @@ module CNProductsMod use clm_time_manager , only : get_step_size_real use SpeciesBaseType , only : species_base_type use PatchType , only : patch + use clm_varctl , only : use_fates_bgc ! implicit none private ! ! !PUBLIC TYPES: type, public :: cn_products_type - private + + private ! Default these procedures to private, unless specified otherwise + ! ------------------------------------------------------------------------ ! Public instance variables ! ------------------------------------------------------------------------ @@ -43,10 +46,15 @@ module CNProductsMod real(r8), pointer :: dwt_prod100_gain_grc(:) ! (g[C or N]/m2/s) dynamic landcover addition to 100-year wood product pool real(r8), pointer :: dwt_woodprod_gain_grc(:) ! (g[C or N]/m2/s) dynamic landcover addition to wood product pools real(r8), pointer :: dwt_cropprod1_gain_grc(:) ! (g[C or N]/m2/s) dynamic landcover addition to 1-year crop product pool + real(r8), pointer :: gru_prod10_gain_patch(:) ! (g[C or N]/m2/s) gross unrepresented landcover addition to 10-year wood product pool + real(r8), pointer :: gru_prod10_gain_grc(:) ! (g[C or N]/m2/s) gross unrepresented landcover addition to 10-year wood product pool + real(r8), pointer :: gru_prod100_gain_patch(:) ! (g[C or N]/m2/s) gross unrepresented landcover addition to 100-year wood product pool + real(r8), pointer :: gru_prod100_gain_grc(:) ! (g[C or N]/m2/s) gross unrepresented landcover addition to 100-year wood product pool + real(r8), pointer :: gru_woodprod_gain_grc(:) ! (g[C or N]/m2/s) gross unrepresented landcover addition to wood product pools real(r8), pointer :: hrv_deadstem_to_prod10_patch(:) ! (g[C or N]/m2/s) dead stem harvest to 10-year wood product pool - real(r8), pointer :: hrv_deadstem_to_prod10_grc(:) ! (g[C or N]/m2/s) dead stem harvest to 10-year wood product pool + real(r8), pointer,public :: hrv_deadstem_to_prod10_grc(:) ! (g[C or N]/m2/s) dead stem harvest to 10-year wood product pool real(r8), pointer :: hrv_deadstem_to_prod100_patch(:) ! (g[C or N]/m2/s) dead stem harvest to 100-year wood product pool - real(r8), pointer :: hrv_deadstem_to_prod100_grc(:) ! (g[C or N]/m2/s) dead stem harvest to 100-year wood product pool + real(r8), pointer,public :: hrv_deadstem_to_prod100_grc(:) ! (g[C or N]/m2/s) dead stem harvest to 100-year wood product pool real(r8), pointer :: crop_harvest_to_cropprod1_patch(:) ! (g[C or N]/m2/s) crop harvest to 1-year crop product pool real(r8), pointer :: crop_harvest_to_cropprod1_grc(:) ! (g[C or N]/m2/s) crop harvest to 1-year crop product pool @@ -64,12 +72,14 @@ module CNProductsMod procedure, private :: InitHistory procedure, private :: InitCold procedure, public :: Restart - + procedure, public :: SetValues + ! Science routines procedure, public :: UpdateProducts procedure, private :: PartitionWoodFluxes procedure, private :: PartitionCropFluxes - procedure, private :: ComputeSummaryVars + procedure, public :: ComputeProductSummaryVars + procedure, public :: ComputeSummaryVars end type cn_products_type @@ -133,9 +143,14 @@ subroutine InitAllocate(this, bounds) allocate(this%dwt_cropprod1_gain_grc(begg:endg)) ; this%dwt_cropprod1_gain_grc(:) = nan + allocate(this%gru_prod10_gain_patch(begp:endp)) ; this%gru_prod10_gain_patch(:) = nan + allocate(this%gru_prod10_gain_grc(begg:endg)) ; this%gru_prod10_gain_grc(:) = nan + allocate(this%gru_prod100_gain_patch(begp:endp)) ; this%gru_prod100_gain_patch(:) = nan + allocate(this%gru_prod100_gain_grc(begg:endg)) ; this%gru_prod100_gain_grc(:) = nan + allocate(this%gru_woodprod_gain_grc(begg:endg)) ; this%gru_woodprod_gain_grc(:) = nan + allocate(this%hrv_deadstem_to_prod10_patch(begp:endp)) ; this%hrv_deadstem_to_prod10_patch(:) = nan allocate(this%hrv_deadstem_to_prod10_grc(begg:endg)) ; this%hrv_deadstem_to_prod10_grc(:) = nan - allocate(this%hrv_deadstem_to_prod100_patch(begp:endp)) ; this%hrv_deadstem_to_prod100_patch(:) = nan allocate(this%hrv_deadstem_to_prod100_grc(begg:endg)) ; this%hrv_deadstem_to_prod100_grc(:) = nan @@ -150,6 +165,29 @@ subroutine InitAllocate(this, bounds) end subroutine InitAllocate + subroutine SetValues(this, bounds, setval) + + ! !ARGUMENTS: + class(cn_products_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds + real(r8), intent(in) :: setval + + ! This zero's arrays that are incremented on each model time-step + ! the hrv_deadstem arrays use a p2g routine for the use_cn portion + ! but we added this zero'ing here because FATES needs it zero'd + + this%dwt_prod10_gain_grc(bounds%begg:bounds%endg) = setval + this%dwt_prod100_gain_grc(bounds%begg:bounds%endg) = setval + this%dwt_cropprod1_gain_grc(bounds%begg:bounds%endg) = setval + + this%crop_harvest_to_cropprod1_grc(bounds%begg:bounds%endg) = setval + this%hrv_deadstem_to_prod10_grc(bounds%begg:bounds%endg) = setval + this%hrv_deadstem_to_prod100_grc(bounds%begg:bounds%endg) = setval + + return + end subroutine SetValues + + !----------------------------------------------------------------------- subroutine InitHistory(this, bounds) ! !USES: @@ -240,6 +278,22 @@ subroutine InitHistory(this, bounds) long_name = 'landcover change-driven addition to 1-year crop product pool', & ptr_gcell = this%dwt_cropprod1_gain_grc, default=active_if_non_isotope) + this%gru_prod10_gain_grc(begg:endg) = spval + call hist_addfld1d( & + fname = this%species%hist_fname('GRU_PROD10', suffix='_GAIN'), & + units = 'g' // this%species%get_species() // '/m^2/s', & + avgflag = 'A', & + long_name = 'gross unrepresented landcover change addition to 10-yr wood product pool', & + ptr_gcell = this%gru_prod10_gain_grc, default='inactive') + + this%gru_prod100_gain_grc(begg:endg) = spval + call hist_addfld1d( & + fname = this%species%hist_fname('GRU_PROD100', suffix='_GAIN'), & + units = 'g' // this%species%get_species() // '/m^2/s', & + avgflag = 'A', & + long_name = 'gross unrepresented landcover change addition to 100-yr wood product pool', & + ptr_gcell = this%gru_prod100_gain_grc, default='inactive') + this%cropprod1_loss_grc(begg:endg) = spval call hist_addfld1d( & fname = this%species%hist_fname('CROPPROD1', suffix='_LOSS'), & @@ -293,11 +347,24 @@ subroutine InitCold(this, bounds) this%tot_woodprod_grc(g) = 0._r8 end do + ! We don't call the woodproduct fluxes routine if + ! no veg patches are active. This is what happens + ! when fates is on. Woodproduct fluxes use a p2g + ! upscaling for the gru_ pools. Must zero it here then. + if(use_fates_bgc)then + do g = bounds%begg, bounds%endg + this%gru_prod10_gain_grc(g) = 0._r8 + this%gru_prod100_gain_grc(g) = 0._r8 + end do + end if + ! Need to set these patch-level fluxes to 0 everywhere for the sake of special ! landunits (because they don't get set over special landunits in the run loop) do p = bounds%begp, bounds%endp this%hrv_deadstem_to_prod10_patch(p) = 0._r8 this%hrv_deadstem_to_prod100_patch(p) = 0._r8 + this%gru_prod10_gain_patch(p) = 0._r8 + this%gru_prod100_gain_patch(p) = 0._r8 this%crop_harvest_to_cropprod1_patch(p) = 0._r8 end do @@ -332,6 +399,7 @@ subroutine Restart(this, bounds, ncid, flag, & ! !LOCAL VARIABLES: logical :: template_provided logical :: readvar + integer :: g character(len=*), parameter :: subname = 'Restart' !----------------------------------------------------------------------- @@ -430,7 +498,20 @@ subroutine Restart(this, bounds, ncid, flag, & end if if (flag == 'read') then + + ! We don't call the woodproduct fluxes routine if + ! no veg patches are active. This is what happens + ! when fates is on. Woodproduct fluxes use a p2g + ! upscaling for the gru_ pools. Must zero it here then. + if(use_fates_bgc)then + do g = bounds%begg, bounds%endg + this%gru_prod10_gain_grc(g) = 0._r8 + this%gru_prod100_gain_grc(g) = 0._r8 + end do + end if + call this%ComputeSummaryVars(bounds) + end if end subroutine Restart @@ -439,6 +520,7 @@ end subroutine Restart subroutine UpdateProducts(this, bounds, & num_soilp, filter_soilp, & dwt_wood_product_gain_patch, & + gru_wood_product_gain_patch, & wood_harvest_patch, & dwt_crop_product_gain_patch, & crop_harvest_to_cropprod_patch) @@ -446,6 +528,7 @@ subroutine UpdateProducts(this, bounds, & ! !DESCRIPTION: ! Update all loss fluxes from wood and crop product pools, and update product pool ! state variables for both loss and gain terms + ! This is only for non-fates patches and columns ! ! !ARGUMENTS: class(cn_products_type) , intent(inout) :: this @@ -455,10 +538,13 @@ subroutine UpdateProducts(this, bounds, & ! dynamic landcover addition to wood product pools (g/m2/s) [patch]; although this is ! a patch-level flux, it is expressed per unit GRIDCELL area - real(r8), intent(in) :: dwt_wood_product_gain_patch( bounds%begp: ) + real(r8), intent(in) :: dwt_wood_product_gain_patch(bounds%begp:) + + ! gross unrepresented landcover addition to wood product pools (g/m2/s) [patch] + real(r8), intent(in) :: gru_wood_product_gain_patch( bounds%begp: ) ! wood harvest addition to wood product pools (g/m2/s) [patch] - real(r8), intent(in) :: wood_harvest_patch( bounds%begp: ) + real(r8), intent(in) :: wood_harvest_patch(bounds%begp:) ! dynamic landcover addition to crop product pools (g/m2/s) [patch]; although this is ! a patch-level flux, it is expressed per unit GRIDCELL area @@ -466,75 +552,34 @@ subroutine UpdateProducts(this, bounds, & ! crop harvest to crop product pool (g/m2/s) [patch] real(r8), intent(in) :: crop_harvest_to_cropprod_patch( bounds%begp: ) - ! - ! !LOCAL VARIABLES: - integer :: g ! indices - real(r8) :: dt ! time step (seconds) - real(r8) :: kprod1 ! decay constant for 1-year product pool - real(r8) :: kprod10 ! decay constant for 10-year product pool - real(r8) :: kprod100 ! decay constant for 100-year product pool - !----------------------------------------------------------------------- + SHR_ASSERT_ALL_FL((ubound(dwt_wood_product_gain_patch) == (/bounds%endp/)), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(gru_wood_product_gain_patch) == (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(wood_harvest_patch) == (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(dwt_crop_product_gain_patch) == (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(crop_harvest_to_cropprod_patch) == (/bounds%endp/)), sourcefile, __LINE__) - + call this%PartitionWoodFluxes(bounds, & num_soilp, filter_soilp, & dwt_wood_product_gain_patch(bounds%begp:bounds%endp), & + gru_wood_product_gain_patch(bounds%begp:bounds%endp), & wood_harvest_patch(bounds%begp:bounds%endp)) - + call this%PartitionCropFluxes(bounds, & num_soilp, filter_soilp, & dwt_crop_product_gain_patch(bounds%begp:bounds%endp), & crop_harvest_to_cropprod_patch(bounds%begp:bounds%endp)) - ! calculate losses from product pools - ! the following (1/s) rate constants result in ~90% loss of initial state over 1, 10 and 100 years, - ! respectively, using a discrete-time fractional decay algorithm. - kprod1 = 7.2e-8_r8 - kprod10 = 7.2e-9_r8 - kprod100 = 7.2e-10_r8 - - do g = bounds%begg, bounds%endg - ! calculate fluxes out of product pools (1/sec) - this%cropprod1_loss_grc(g) = this%cropprod1_grc(g) * kprod1 - this%prod10_loss_grc(g) = this%prod10_grc(g) * kprod10 - this%prod100_loss_grc(g) = this%prod100_grc(g) * kprod100 - end do - - ! set time steps - dt = get_step_size_real() - - ! update product state variables - do g = bounds%begg, bounds%endg - - ! fluxes into wood & crop product pools, from landcover change - this%cropprod1_grc(g) = this%cropprod1_grc(g) + this%dwt_cropprod1_gain_grc(g)*dt - this%prod10_grc(g) = this%prod10_grc(g) + this%dwt_prod10_gain_grc(g)*dt - this%prod100_grc(g) = this%prod100_grc(g) + this%dwt_prod100_gain_grc(g)*dt - - ! fluxes into wood & crop product pools, from harvest - this%cropprod1_grc(g) = this%cropprod1_grc(g) + this%crop_harvest_to_cropprod1_grc(g)*dt - this%prod10_grc(g) = this%prod10_grc(g) + this%hrv_deadstem_to_prod10_grc(g)*dt - this%prod100_grc(g) = this%prod100_grc(g) + this%hrv_deadstem_to_prod100_grc(g)*dt - - ! fluxes out of wood & crop product pools, from decomposition - this%cropprod1_grc(g) = this%cropprod1_grc(g) - this%cropprod1_loss_grc(g)*dt - this%prod10_grc(g) = this%prod10_grc(g) - this%prod10_loss_grc(g)*dt - this%prod100_grc(g) = this%prod100_grc(g) - this%prod100_loss_grc(g)*dt - - end do - - call this%ComputeSummaryVars(bounds) - + return end subroutine UpdateProducts !----------------------------------------------------------------------- + subroutine PartitionWoodFluxes(this, bounds, & num_soilp, filter_soilp, & dwt_wood_product_gain_patch, & + gru_wood_product_gain_patch, & wood_harvest_patch) ! ! !DESCRIPTION: @@ -554,6 +599,9 @@ subroutine PartitionWoodFluxes(this, bounds, & ! a patch-level flux, it is expressed per unit GRIDCELL area real(r8), intent(in) :: dwt_wood_product_gain_patch( bounds%begp: ) + ! gross unrepresented landcover addition to wood product pools (g/m2/s) [patch] + real(r8), intent(in) :: gru_wood_product_gain_patch( bounds%begp: ) + ! wood harvest addition to wood product pools (g/m2/s) [patch] real(r8), intent(in) :: wood_harvest_patch( bounds%begp: ) @@ -571,6 +619,44 @@ subroutine PartitionWoodFluxes(this, bounds, & character(len=*), parameter :: subname = 'PartitionWoodFluxes' !----------------------------------------------------------------------- + ! Partition patch-level gross unrepresented fluxes to 10 and 100-year product pools + do fp = 1, num_soilp + p = filter_soilp(fp) + + pprod10 = pftcon%pprod10(patch%itype(p)) + pprod100 = pftcon%pprod100(patch%itype(p)) + pprod_tot = pprod10 + pprod100 + if (pprod_tot > 0) then + pprod10_frac = pprod10 / pprod_tot + pprod100_frac = pprod100 / pprod_tot + else + ! Avoid divide by 0 + pprod10_frac = 0._r8 + pprod100_frac = 0._r8 + end if + + this%gru_prod10_gain_patch(p) = & + gru_wood_product_gain_patch(p) * pprod10_frac + this%gru_prod100_gain_patch(p) = & + gru_wood_product_gain_patch(p) * pprod100_frac + + end do + + ! Average gross unrepresented fluxes from patch to gridcell + call p2g(bounds, & + this%gru_prod10_gain_patch(bounds%begp:bounds%endp), & + this%gru_prod10_gain_grc(bounds%begg:bounds%endg), & + p2c_scale_type = 'unity', & + c2l_scale_type = 'unity', & + l2g_scale_type = 'unity') + + call p2g(bounds, & + this%gru_prod100_gain_patch(bounds%begp:bounds%endp), & + this%gru_prod100_gain_grc(bounds%begg:bounds%endg), & + p2c_scale_type = 'unity', & + c2l_scale_type = 'unity', & + l2g_scale_type = 'unity') + ! Partition patch-level harvest fluxes to 10 and 100-year product pools do fp = 1, num_soilp p = filter_soilp(fp) @@ -579,7 +665,7 @@ subroutine PartitionWoodFluxes(this, bounds, & this%hrv_deadstem_to_prod100_patch(p) = & wood_harvest_patch(p) * (1.0_r8 - pftcon%pprodharv10(patch%itype(p))) end do - + ! Average harvest fluxes from patch to gridcell call p2g(bounds, & this%hrv_deadstem_to_prod10_patch(bounds%begp:bounds%endp), & @@ -587,24 +673,18 @@ subroutine PartitionWoodFluxes(this, bounds, & p2c_scale_type = 'unity', & c2l_scale_type = 'unity', & l2g_scale_type = 'unity') - + call p2g(bounds, & this%hrv_deadstem_to_prod100_patch(bounds%begp:bounds%endp), & this%hrv_deadstem_to_prod100_grc(bounds%begg:bounds%endg), & p2c_scale_type = 'unity', & c2l_scale_type = 'unity', & l2g_scale_type = 'unity') - - ! Zero the dwt gains - do g = bounds%begg, bounds%endg - this%dwt_prod10_gain_grc(g) = 0._r8 - this%dwt_prod100_gain_grc(g) = 0._r8 - end do - + ! Partition dynamic land cover fluxes to 10 and 100-year product pools. do p = bounds%begp, bounds%endp g = patch%gridcell(p) - + ! Note that pprod10 + pprod100 do NOT sum to 1: some fraction of the dwt changes ! was lost to other fluxes. dwt_wood_product_gain_patch gives the amount that goes ! to all product pools, so we need to determine the fraction of that flux that @@ -615,21 +695,22 @@ subroutine PartitionWoodFluxes(this, bounds, & if (pprod_tot > 0) then pprod10_frac = pprod10 / pprod_tot pprod100_frac = pprod100 / pprod_tot - else - ! Avoid divide by 0 - pprod10_frac = 0._r8 - pprod100_frac = 0._r8 + ! Note that the patch-level fluxes are expressed per unit gridcell area. So, to go + ! from patch-level fluxes to gridcell-level fluxes, we simply add up the various + ! patch contributions, without having to multiply by any area weightings. + this%dwt_prod10_gain_grc(g) = this%dwt_prod10_gain_grc(g) + & + dwt_wood_product_gain_patch(p) * pprod10_frac + this%dwt_prod100_gain_grc(g) = this%dwt_prod100_gain_grc(g) + & + dwt_wood_product_gain_patch(p) * pprod100_frac + + else if (dwt_wood_product_gain_patch(p) > 0) then + call endrun(& + msg='ERROR: dwt_wood_product_gain_patch(p) > 0' // & + errMsg(sourcefile, __LINE__)) end if - - ! Note that the patch-level fluxes are expressed per unit gridcell area. So, to go - ! from patch-level fluxes to gridcell-level fluxes, we simply add up the various - ! patch contributions, without having to multiply by any area weightings. - this%dwt_prod10_gain_grc(g) = this%dwt_prod10_gain_grc(g) + & - dwt_wood_product_gain_patch(p) * pprod10_frac - this%dwt_prod100_gain_grc(g) = this%dwt_prod100_gain_grc(g) + & - dwt_wood_product_gain_patch(p) * pprod100_frac + end do - + end subroutine PartitionWoodFluxes !----------------------------------------------------------------------- @@ -688,10 +769,6 @@ subroutine PartitionCropFluxes(this, bounds, & ! Determine gains from dynamic landcover - do g = bounds%begg, bounds%endg - this%dwt_cropprod1_gain_grc(g) = 0._r8 - end do - do p = bounds%begp, bounds%endp g = patch%gridcell(p) @@ -704,6 +781,62 @@ subroutine PartitionCropFluxes(this, bounds, & end subroutine PartitionCropFluxes + !----------------------------------------------------------------------- + subroutine ComputeProductSummaryVars(this, bounds) + + class(cn_products_type) , intent(inout) :: this + type(bounds_type) , intent(in) :: bounds + + integer :: g ! indices + real(r8) :: dt ! time step (seconds) + real(r8) :: kprod1 ! decay constant for 1-year product pool + real(r8) :: kprod10 ! decay constant for 10-year product pool + real(r8) :: kprod100 ! decay constant for 100-year product pool + + ! calculate losses from product pools + ! the following (1/s) rate constants result in ~90% loss of initial state over 1, 10 and 100 years, + ! respectively, using a discrete-time fractional decay algorithm. + kprod1 = 7.2e-8_r8 + kprod10 = 7.2e-9_r8 + kprod100 = 7.2e-10_r8 + + do g = bounds%begg, bounds%endg + ! calculate fluxes out of product pools (1/sec) + this%cropprod1_loss_grc(g) = this%cropprod1_grc(g) * kprod1 + this%prod10_loss_grc(g) = this%prod10_grc(g) * kprod10 + this%prod100_loss_grc(g) = this%prod100_grc(g) * kprod100 + end do + + ! set time steps + dt = get_step_size_real() + + ! update product state variables + do g = bounds%begg, bounds%endg + + ! fluxes into wood & crop product pools, from landcover change + this%cropprod1_grc(g) = this%cropprod1_grc(g) + this%dwt_cropprod1_gain_grc(g)*dt + + this%prod10_grc(g) = this%prod10_grc(g) + this%dwt_prod10_gain_grc(g)*dt + this%prod100_grc(g) = this%prod100_grc(g) + this%dwt_prod100_gain_grc(g)*dt + + ! fluxes into wood & grain product pools, from gross unrepresented landcover change + this%prod10_grc(g) = this%prod10_grc(g) + this%gru_prod10_gain_grc(g)*dt + this%prod100_grc(g) = this%prod100_grc(g) + this%gru_prod100_gain_grc(g)*dt + + ! fluxes into wood & crop product pools, from harvest + this%cropprod1_grc(g) = this%cropprod1_grc(g) + this%crop_harvest_to_cropprod1_grc(g)*dt + this%prod10_grc(g) = this%prod10_grc(g) + this%hrv_deadstem_to_prod10_grc(g)*dt + this%prod100_grc(g) = this%prod100_grc(g) + this%hrv_deadstem_to_prod100_grc(g)*dt + + ! fluxes out of wood & crop product pools, from decomposition + this%cropprod1_grc(g) = this%cropprod1_grc(g) - this%cropprod1_loss_grc(g)*dt + this%prod10_grc(g) = this%prod10_grc(g) - this%prod10_loss_grc(g)*dt + this%prod100_grc(g) = this%prod100_grc(g) - this%prod100_loss_grc(g)*dt + end do + + return + end subroutine ComputeProductSummaryVars + !----------------------------------------------------------------------- subroutine ComputeSummaryVars(this, bounds) @@ -719,17 +852,16 @@ subroutine ComputeSummaryVars(this, bounds) ! ! !LOCAL VARIABLES: integer :: g ! indices - - character(len=*), parameter :: subname = 'ComputeSummaryVars' !----------------------------------------------------------------------- - + character(len=*), parameter :: subname = 'ComputeSummaryVars' + do g = bounds%begg, bounds%endg ! total wood products this%tot_woodprod_grc(g) = & this%prod10_grc(g) + & this%prod100_grc(g) - + ! total loss from wood products this%tot_woodprod_loss_grc(g) = & this%prod10_loss_grc(g) + & @@ -744,6 +876,10 @@ subroutine ComputeSummaryVars(this, bounds) this%dwt_woodprod_gain_grc(g) = & this%dwt_prod100_gain_grc(g) + & this%dwt_prod10_gain_grc(g) + + this%gru_woodprod_gain_grc(g) = & + this%gru_prod100_gain_grc(g) + & + this%gru_prod10_gain_grc(g) end do end subroutine ComputeSummaryVars diff --git a/src/biogeochem/CNVegCarbonFluxType.F90 b/src/biogeochem/CNVegCarbonFluxType.F90 index e531dac39a..8210bafc97 100644 --- a/src/biogeochem/CNVegCarbonFluxType.F90 +++ b/src/biogeochem/CNVegCarbonFluxType.F90 @@ -13,6 +13,7 @@ module CNVegCarbonFluxType use clm_varpar , only : ndecomp_cascade_transitions, ndecomp_pools use clm_varpar , only : nvegcpool use clm_varpar , only : nlevdecomp_full, nlevdecomp, i_litr_min, i_litr_max + use clm_varpar , only : mxharvests use clm_varcon , only : spval, dzsoi_decomp use clm_varctl , only : use_cndv, use_c13, use_c14, use_nitrif_denitrif, use_crop use CNSharedParamsMod , only : use_matrixcn @@ -26,7 +27,7 @@ module CNVegCarbonFluxType use ColumnType , only : col use PatchType , only : patch use AnnualFluxDribbler , only : annual_flux_dribbler_type, annual_flux_dribbler_gridcell - use dynSubgridControlMod , only : get_for_testing_allow_non_annual_changes + use dynSubgridControlMod , only : get_for_testing_allow_non_annual_changes, get_do_grossunrep use abortutils , only : endrun use SparseMatrixMultiplyMod , only : sparse_matrix_type, diag_matrix_type, vector_type ! @@ -139,12 +140,18 @@ module CNVegCarbonFluxType real(r8), pointer :: frootc_to_litter_patch (:) ! fine root C litterfall (gC/m2/s) real(r8), pointer :: livestemc_to_litter_patch (:) ! live stem C litterfall (gC/m2/s) real(r8), pointer :: repr_grainc_to_food_patch (:,:) ! grain C to food for prognostic crop(gC/m2/s) [patch, repr_grain_min:repr_grain_max] + real(r8), pointer :: repr_grainc_to_food_perharv_patch (:,:,:) ! grain C to food for prognostic crop accumulated by harvest (gC/m2) [patch, harvest, repr_grain_min:repr_grain_max]. Not per-second because this variable represents an accumulation over each growing season, to be instantaneously at the end of each calendar year, to provide output that's easier to work with. + real(r8), pointer :: repr_grainc_to_food_thisyr_patch (:,:) ! grain C to food for prognostic crop accumulated this calendar year (gC/m2) [patch, repr_grain_min:repr_grain_max]. Not per-second because this variable represents an accumulation over an entire calendar year, to be saved instantaneously at the end of each calendar year, to provide output that's easier to work with. real(r8), pointer :: repr_structurec_to_cropprod_patch (:,:) ! reproductive structure C to crop product pool for prognostic crop (gC/m2/s) [patch, repr_structure_min:repr_structure_max] real(r8), pointer :: repr_structurec_to_litter_patch (:,:) ! reproductive structure C to litter for prognostic crop (gC/m2/s) [patch, repr_structure_min:repr_structure_max] real(r8), pointer :: leafc_to_biofuelc_patch (:) ! leaf C to biofuel C (gC/m2/s) real(r8), pointer :: livestemc_to_biofuelc_patch (:) ! livestem C to biofuel C (gC/m2/s) + real(r8), pointer :: leafc_to_removedresiduec_patch (:) ! leaf C to removed residues C (gC/m2/s) + real(r8), pointer :: livestemc_to_removedresiduec_patch (:) ! livestem C to removed residues C (gC/m2/s) real(r8), pointer :: repr_grainc_to_seed_patch (:,:) ! grain C to seed for prognostic crop(gC/m2/s) [patch, repr_grain_min:repr_grain_max] + real(r8), pointer :: repr_grainc_to_seed_perharv_patch (:,:,:) ! grain C to seed for prognostic crop accumulated by harvest (gC/m2) [patch, harvest, repr_grain_min:repr_grain_max]. Not per-second because this variable represents an accumulation over each growing season, to be instantaneously at the end of each calendar year, to provide output that's easier to work with. + real(r8), pointer :: repr_grainc_to_seed_thisyr_patch (:,:) ! grain C to seed for prognostic crop accumulated this calendar year (gC/m2) [patch, repr_grain_min:repr_grain_max]. Not per-second because this variable represents an accumulation over an entire calendar year, to be saved instantaneously at the end of each calendar year, to provide output that's easier to work with. ! maintenance respiration fluxes real(r8), pointer :: cpool_to_resp_patch (:) ! CNflex excess C maintenance respiration (gC/m2/s) @@ -273,6 +280,39 @@ module CNVegCarbonFluxType real(r8), pointer :: dwt_livecrootc_to_cwdc_col (:,:) ! (gC/m3/s) live coarse root to CWD due to landcover change real(r8), pointer :: dwt_deadcrootc_to_cwdc_col (:,:) ! (gC/m3/s) dead coarse root to CWD due to landcover change + ! gross unrepresented landcover fluxes + real(r8), pointer :: gru_leafc_to_litter_patch (:) ! leaf C gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_leafc_storage_to_atm_patch (:) ! leaf C storage gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_leafc_xfer_to_atm_patch (:) ! leaf C transfer gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_frootc_to_litter_patch (:) ! fine root C gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_frootc_storage_to_atm_patch (:) ! fine root C storage gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_frootc_xfer_to_atm_patch (:) ! fine root C transfer gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_livestemc_to_atm_patch (:) ! live stem C gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_livestemc_storage_to_atm_patch (:) ! live stem C storage gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_livestemc_xfer_to_atm_patch (:) ! live stem C transfer gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_deadstemc_to_atm_patch (:) ! dead stem C gross unrepresented landcover change mortality to the atmosphere (gC/m2/s) + real(r8), pointer :: gru_deadstemc_storage_to_atm_patch (:) ! dead stem C storage gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_deadstemc_xfer_to_atm_patch (:) ! dead stem C transfer gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_livecrootc_to_litter_patch (:) ! live coarse root C gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_livecrootc_storage_to_atm_patch (:) ! live coarse root C storage gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_livecrootc_xfer_to_atm_patch (:) ! live coarse root C transfer gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_deadcrootc_to_litter_patch (:) ! dead coarse root C gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_deadcrootc_storage_to_atm_patch (:) ! dead coarse root C storage gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_deadcrootc_xfer_to_atm_patch (:) ! dead coarse root C transfer gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_gresp_storage_to_atm_patch (:) ! growth respiration storage gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_gresp_xfer_to_atm_patch (:) ! growth respiration transfer gross unrepresented landcover change mortality (gC/m2/s) + real(r8), pointer :: gru_xsmrpool_to_atm_patch (:) ! excess MR pool gross unrepresented landcover change mortality (gC/m2/s) + + real(r8), pointer :: gru_conv_cflux_patch (:) ! (gC/m2/s) conversion C flux (immediate loss to atm) + real(r8), pointer :: gru_conv_cflux_col (:) ! (gC/m2/s) gru_conv_cflux_patch summed to the column-level + real(r8), pointer :: gru_conv_cflux_grc (:) ! (gC/m2/s) gru_conv_cflux_patch summed to the gridcell-level + real(r8), pointer :: gru_conv_cflux_dribbled_grc (:) ! (gC/m2/s) gru_conv_cflux_grc dribbled evenly throughout the year + real(r8), pointer :: gru_wood_productc_gain_patch (:) ! (gC/m2/s) addition to wood product pools from gross unrepresented landcover change + real(r8), pointer :: gru_wood_productc_gain_col (:) ! (gC/m2/s) gru_wood_productc_gain_patch summed to the column-level + real(r8), pointer :: gru_slash_cflux_patch (:) ! (gC/m2/s) conversion slash flux due to gross unrepresented landcover change + real(r8), pointer :: gru_c_to_litr_c_col (:,:,:) ! C fluxes due to gross unrepresented landcover change to litter pools (gC/m3/s) + real(r8), pointer :: gru_c_to_cwdc_col (:,:) ! (gC/m3/s) C to CWD due to gross unrepresented landcover change + ! crop fluxes real(r8), pointer :: crop_seedc_to_leaf_patch (:) ! (gC/m2/s) seed source to leaf, for crops @@ -342,7 +382,7 @@ module CNVegCarbonFluxType real(r8), pointer :: nee_grc (:) ! (gC/m2/s) net ecosystem exchange of carbon, includes fire and hrv_xsmrpool, excludes landuse and harvest flux, positive for source ! Dynamic landcover fluxnes - real(r8), pointer :: landuseflux_grc(:) ! (gC/m2/s) dwt_conv_cflux+product_closs + real(r8), pointer :: landuseflux_grc(:) ! (gC/m2/s) dwt_conv_cflux+gru_conv_cflux+product_closs real(r8), pointer :: npp_Nactive_patch (:) ! C used by mycorrhizal uptake (gC/m2/s) real(r8), pointer :: npp_burnedoff_patch (:) ! C that cannot be used for N uptake (gC/m2/s) real(r8), pointer :: npp_Nnonmyc_patch (:) ! C used by non-myc uptake (gC/m2/s) @@ -371,6 +411,7 @@ module CNVegCarbonFluxType ! Objects that help convert once-per-year dynamic land cover changes into fluxes ! that are dribbled throughout the year type(annual_flux_dribbler_type) :: dwt_conv_cflux_dribbler + type(annual_flux_dribbler_type) :: gru_conv_cflux_dribbler type(annual_flux_dribbler_type) :: hrv_xsmrpool_to_atm_dribbler logical, private :: dribble_crophrv_xsmrpool_2atm contains @@ -385,6 +426,7 @@ module CNVegCarbonFluxType procedure , private :: RestartAllIsotopes ! Handle restart fields present for both bulk C and isotopes procedure , public :: SetValues procedure , public :: ZeroDWT + procedure , public :: ZeroGRU procedure , public :: Summary => Summary_carbonflux end type cnveg_carbonflux_type @@ -396,21 +438,23 @@ module CNVegCarbonFluxType contains !------------------------------------------------------------------------ - subroutine Init(this, bounds, carbon_type, dribble_crophrv_xsmrpool_2atm) + subroutine Init(this, bounds, carbon_type, dribble_crophrv_xsmrpool_2atm,alloc_full_veg) class(cnveg_carbonflux_type) :: this type(bounds_type), intent(in) :: bounds character(len=3) , intent(in) :: carbon_type ! one of ['c12', c13','c14'] logical , intent(in) :: dribble_crophrv_xsmrpool_2atm + logical , intent(in) :: alloc_full_veg this%dribble_crophrv_xsmrpool_2atm = dribble_crophrv_xsmrpool_2atm - call this%InitAllocate ( bounds, carbon_type) - if(use_matrixcn)then - call this%InitTransfer () + call this%InitAllocate ( bounds, carbon_type,alloc_full_veg) + if(alloc_full_veg)then + if(use_matrixcn)then + call this%InitTransfer () + end if + call this%InitHistory ( bounds, carbon_type ) + call this%InitCold (bounds ) end if - call this%InitHistory ( bounds, carbon_type ) - call this%InitCold (bounds ) - end subroutine Init subroutine InitTransfer (this) @@ -423,12 +467,13 @@ subroutine InitTransfer (this) end subroutine InitTransfer !------------------------------------------------------------------------ - subroutine InitAllocate(this, bounds, carbon_type) + subroutine InitAllocate(this, bounds, carbon_type, alloc_full_veg) ! ! !ARGUMENTS: class (cnveg_carbonflux_type) :: this type(bounds_type), intent(in) :: bounds character(len=*) , intent(in) :: carbon_type ! one of ['c12', c13','c14'] + logical , intent(in) :: alloc_full_veg ! ! !LOCAL VARIABLES: integer :: begp,endp @@ -438,9 +483,15 @@ subroutine InitAllocate(this, bounds, carbon_type) character(len=:), allocatable :: carbon_type_suffix !------------------------------------------------------------------------ - begp = bounds%begp; endp = bounds%endp - begc = bounds%begc; endc = bounds%endc - begg = bounds%begg; endg = bounds%endg + if(alloc_full_veg)then + begp = bounds%begp; endp = bounds%endp + begc = bounds%begc; endc = bounds%endc + begg = bounds%begg; endg = bounds%endg + else + begp = 0; endp = 0 + begc = 0; endc = 0 + begg = 0; endg = 0 + end if allocate(this%m_leafc_to_litter_patch (begp:endp)) ; this%m_leafc_to_litter_patch (:) = nan allocate(this%m_frootc_to_litter_patch (begp:endp)) ; this%m_frootc_to_litter_patch (:) = nan @@ -616,13 +667,19 @@ subroutine InitAllocate(this, bounds, carbon_type) allocate(this%cpool_to_reproductivec_storage_patch(begp:endp, nrepr)); this%cpool_to_reproductivec_storage_patch (:,:) = nan allocate(this%livestemc_to_litter_patch (begp:endp)) ; this%livestemc_to_litter_patch (:) = nan allocate(this%repr_grainc_to_food_patch(begp:endp, repr_grain_min:repr_grain_max)) ; this%repr_grainc_to_food_patch (:,:) = nan + allocate(this%repr_grainc_to_food_perharv_patch(begp:endp, 1:mxharvests, repr_grain_min:repr_grain_max)) ; this%repr_grainc_to_food_perharv_patch (:,:,:) = nan + allocate(this%repr_grainc_to_food_thisyr_patch(begp:endp, repr_grain_min:repr_grain_max)) ; this%repr_grainc_to_food_thisyr_patch (:,:) = nan allocate(this%repr_structurec_to_cropprod_patch(begp:endp, repr_structure_min:repr_structure_max)) this%repr_structurec_to_cropprod_patch(:,:) = nan allocate(this%repr_structurec_to_litter_patch(begp:endp, repr_structure_min:repr_structure_max)) this%repr_structurec_to_litter_patch(:,:) = nan allocate(this%leafc_to_biofuelc_patch (begp:endp)) ; this%leafc_to_biofuelc_patch (:) = nan allocate(this%livestemc_to_biofuelc_patch (begp:endp)) ; this%livestemc_to_biofuelc_patch (:) = nan + allocate(this%leafc_to_removedresiduec_patch (begp:endp)) ; this%leafc_to_removedresiduec_patch (:) = nan + allocate(this%livestemc_to_removedresiduec_patch (begp:endp)) ; this%livestemc_to_removedresiduec_patch (:) = nan allocate(this%repr_grainc_to_seed_patch(begp:endp, repr_grain_min:repr_grain_max)) ; this%repr_grainc_to_seed_patch (:,:) = nan + allocate(this%repr_grainc_to_seed_perharv_patch(begp:endp, 1:mxharvests, repr_grain_min:repr_grain_max)) ; this%repr_grainc_to_seed_perharv_patch (:,:,:) = nan + allocate(this%repr_grainc_to_seed_thisyr_patch(begp:endp, repr_grain_min:repr_grain_max)) ; this%repr_grainc_to_seed_thisyr_patch (:,:) = nan allocate(this%reproductivec_xfer_to_reproductivec_patch(begp:endp, nrepr)) this%reproductivec_xfer_to_reproductivec_patch(:,:) = nan allocate(this%cpool_reproductive_gr_patch (begp:endp, nrepr)) ; this%cpool_reproductive_gr_patch (:,:) = nan @@ -668,6 +725,38 @@ subroutine InitAllocate(this, bounds, carbon_type) allocate(this%dwt_wood_productc_gain_patch (begp:endp)) ; this%dwt_wood_productc_gain_patch(:) =nan allocate(this%dwt_crop_productc_gain_patch (begp:endp)) ; this%dwt_crop_productc_gain_patch(:) =nan + allocate(this%gru_leafc_to_litter_patch (begp:endp)) ; this%gru_leafc_to_litter_patch (:) = nan + allocate(this%gru_leafc_storage_to_atm_patch (begp:endp)) ; this%gru_leafc_storage_to_atm_patch (:) = nan + allocate(this%gru_leafc_xfer_to_atm_patch (begp:endp)) ; this%gru_leafc_xfer_to_atm_patch (:) = nan + allocate(this%gru_frootc_to_litter_patch (begp:endp)) ; this%gru_frootc_to_litter_patch (:) = nan + allocate(this%gru_frootc_storage_to_atm_patch (begp:endp)) ; this%gru_frootc_storage_to_atm_patch (:) = nan + allocate(this%gru_frootc_xfer_to_atm_patch (begp:endp)) ; this%gru_frootc_xfer_to_atm_patch (:) = nan + allocate(this%gru_livestemc_to_atm_patch (begp:endp)) ; this%gru_livestemc_to_atm_patch (:) = nan + allocate(this%gru_livestemc_storage_to_atm_patch (begp:endp)) ; this%gru_livestemc_storage_to_atm_patch (:) = nan + allocate(this%gru_livestemc_xfer_to_atm_patch (begp:endp)) ; this%gru_livestemc_xfer_to_atm_patch (:) = nan + allocate(this%gru_deadstemc_to_atm_patch (begp:endp)) ; this%gru_deadstemc_to_atm_patch (:) = nan + allocate(this%gru_deadstemc_storage_to_atm_patch (begp:endp)) ; this%gru_deadstemc_storage_to_atm_patch (:) = nan + allocate(this%gru_deadstemc_xfer_to_atm_patch (begp:endp)) ; this%gru_deadstemc_xfer_to_atm_patch (:) = nan + allocate(this%gru_livecrootc_to_litter_patch (begp:endp)) ; this%gru_livecrootc_to_litter_patch (:) = nan + allocate(this%gru_livecrootc_storage_to_atm_patch (begp:endp)) ; this%gru_livecrootc_storage_to_atm_patch (:) = nan + allocate(this%gru_livecrootc_xfer_to_atm_patch (begp:endp)) ; this%gru_livecrootc_xfer_to_atm_patch (:) = nan + allocate(this%gru_deadcrootc_to_litter_patch (begp:endp)) ; this%gru_deadcrootc_to_litter_patch (:) = nan + allocate(this%gru_deadcrootc_storage_to_atm_patch (begp:endp)) ; this%gru_deadcrootc_storage_to_atm_patch (:) = nan + allocate(this%gru_deadcrootc_xfer_to_atm_patch (begp:endp)) ; this%gru_deadcrootc_xfer_to_atm_patch (:) = nan + allocate(this%gru_gresp_storage_to_atm_patch (begp:endp)) ; this%gru_gresp_storage_to_atm_patch (:) = nan + allocate(this%gru_gresp_xfer_to_atm_patch (begp:endp)) ; this%gru_gresp_xfer_to_atm_patch (:) = nan + allocate(this%gru_xsmrpool_to_atm_patch (begp:endp)) ; this%gru_xsmrpool_to_atm_patch (:) = nan + + allocate(this%gru_conv_cflux_patch (begp:endp)) ; this%gru_conv_cflux_patch (:) =nan + allocate(this%gru_conv_cflux_col (begc:endc)) ; this%gru_conv_cflux_col (:) =nan + allocate(this%gru_conv_cflux_grc (begg:endg)) ; this%gru_conv_cflux_grc (:) =nan + allocate(this%gru_conv_cflux_dribbled_grc (begg:endg)) ; this%gru_conv_cflux_dribbled_grc (:) =nan + allocate(this%gru_wood_productc_gain_patch (begp:endp)) ; this%gru_wood_productc_gain_patch (:) =nan + allocate(this%gru_wood_productc_gain_col (begc:endc)) ; this%gru_wood_productc_gain_col (:) =nan + allocate(this%gru_slash_cflux_patch (begp:endp)) ; this%gru_slash_cflux_patch (:) =nan + allocate(this%gru_c_to_litr_c_col (begc:endc,1:nlevdecomp_full,1:ndecomp_pools)); this%gru_c_to_litr_c_col (:,:,:)=nan + allocate(this%gru_c_to_cwdc_col (begc:endc,1:nlevdecomp_full)); this%gru_c_to_cwdc_col (:,:)=nan + allocate(this%crop_seedc_to_leaf_patch (begp:endp)) ; this%crop_seedc_to_leaf_patch (:) =nan allocate(this%cwdc_loss_col (begc:endc)) ; this%cwdc_loss_col (:) =nan @@ -787,6 +876,11 @@ subroutine InitAllocate(this, bounds, carbon_type) name = 'dwt_conv_flux_' // carbon_type_suffix, & units = 'gC/m^2', & allows_non_annual_delta = allows_non_annual_delta) + this%gru_conv_cflux_dribbler = annual_flux_dribbler_gridcell( & + bounds = bounds, & + name = 'gru_conv_flux_' // carbon_type_suffix, & + units = 'gC/m^2', & + allows_non_annual_delta = allows_non_annual_delta) this%hrv_xsmrpool_to_atm_dribbler = annual_flux_dribbler_gridcell( & bounds = bounds, & name = 'hrv_xsmrpool_to_atm_' // carbon_type_suffix, & @@ -853,6 +947,33 @@ subroutine InitHistory(this, bounds, carbon_type) ptr_patch=data1dptr) end do + this%repr_grainc_to_food_perharv_patch(begp:endp,:,:) = spval + do k = repr_grain_min, repr_grain_max + data2dptr => this%repr_grainc_to_food_perharv_patch(:,:,k) + call hist_addfld2d ( & + ! e.g., GRAINC_TO_FOOD_PERHARV + fname=get_repr_hist_fname(k)//'C_TO_FOOD_PERHARV', & + units='gC/m^2', & + type2d='mxharvests', & + avgflag='I', & + long_name=get_repr_longname(k)//' C to food per harvest; should only be output annually', & + ptr_patch=data2dptr, & + default='inactive') + end do + + this%repr_grainc_to_food_thisyr_patch(begp:endp,:) = spval + do k = repr_grain_min, repr_grain_max + data1dptr => this%repr_grainc_to_food_thisyr_patch(:,k) + call hist_addfld1d ( & + ! e.g., GRAINC_TO_FOOD_ANN + fname=get_repr_hist_fname(k)//'C_TO_FOOD_ANN', & + units='gC/m^2', & + avgflag='I', & + long_name=get_repr_longname(k)//' C to food harvested per calendar year; should only be output annually', & + ptr_patch=data1dptr, & + default='inactive') + end do + this%leafc_to_biofuelc_patch(begp:endp) = spval call hist_addfld1d (fname='LEAFC_TO_BIOFUELC', units='gC/m^2/s', & avgflag='A', long_name='leaf C to biofuel C', & @@ -863,7 +984,21 @@ subroutine InitHistory(this, bounds, carbon_type) avgflag='A', long_name='livestem C to biofuel C', & ptr_patch=this%livestemc_to_biofuelc_patch) + this%leafc_to_removedresiduec_patch(begp:endp) = spval + call hist_addfld1d (fname='LEAFC_TO_REMOVEDRESIDUEC', units='gC/m^2/s', & + avgflag='A', long_name='leaf C to removed residue C', & + ptr_patch=this%leafc_to_removedresiduec_patch, & + default='inactive') + + this%livestemc_to_removedresiduec_patch(begp:endp) = spval + call hist_addfld1d (fname='LIVESTEMC_TO_REMOVEDRESIDUEC', units='gC/m^2/s', & + avgflag='A', long_name='livestem C to removed residue C', & + ptr_patch=this%livestemc_to_removedresiduec_patch, & + default='inactive') + this%repr_grainc_to_seed_patch(begp:endp,:) = spval + this%repr_grainc_to_seed_perharv_patch(begp:endp,:,:) = spval + this%repr_grainc_to_seed_thisyr_patch(begp:endp,:) = spval do k = repr_grain_min, repr_grain_max data1dptr => this%repr_grainc_to_seed_patch(:,k) call hist_addfld1d ( & @@ -873,6 +1008,25 @@ subroutine InitHistory(this, bounds, carbon_type) avgflag='A', & long_name=get_repr_longname(k)//' C to seed', & ptr_patch=data1dptr) + data2dptr => this%repr_grainc_to_seed_perharv_patch(:,:,k) + call hist_addfld2d ( & + ! e.g., GRAINC_TO_SEED_PERHARV + fname=get_repr_hist_fname(k)//'C_TO_SEED_PERHARV', & + units='gC/m^2', & + type2d='mxharvests', & + avgflag='I', & + long_name=get_repr_longname(k)//' C to seed per harvest; should only be output annually', & + ptr_patch=data2dptr, & + default='inactive') + data1dptr => this%repr_grainc_to_seed_thisyr_patch(:,k) + call hist_addfld1d ( & + ! e.g., GRAINC_TO_SEED_ANN + fname=get_repr_hist_fname(k)//'C_TO_SEED_ANN', & + units='gC/m^2', & + avgflag='I', & + long_name=get_repr_longname(k)//' C to seed harvested per calendar year; should only be output annually', & + ptr_patch=data1dptr, & + default='inactive') end do end if @@ -2854,7 +3008,7 @@ subroutine InitHistory(this, bounds, carbon_type) do k = 1, ndecomp_pools if ( decomp_cascade_con%is_litter(k) .or. decomp_cascade_con%is_cwd(k) ) then data1dptr => this%m_decomp_cpools_to_fire_col(:,k) - fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'C_TO_FIRE' + fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_C_TO_FIRE' longname = trim(decomp_cascade_con%decomp_pool_name_long(k))//' C fire loss' call hist_addfld1d (fname=fieldname, units='gC/m^2/s', & avgflag='A', long_name=longname, & @@ -2862,7 +3016,7 @@ subroutine InitHistory(this, bounds, carbon_type) if ( nlevdecomp_full > 1 ) then data2dptr => this%m_decomp_cpools_to_fire_vr_col(:,:,k) - fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'C_TO_FIRE'//trim(vr_suffix) + fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_C_TO_FIRE'//trim(vr_suffix) longname = trim(decomp_cascade_con%decomp_pool_name_long(k))//' C fire loss' call hist_addfld_decomp (fname=fieldname, units='gC/m^3/s', type2d='levdcmp', & avgflag='A', long_name=longname, & @@ -2957,6 +3111,29 @@ subroutine InitHistory(this, bounds, carbon_type) avgflag='A', long_name='dead coarse root to CWD due to landcover change', & ptr_col=this%dwt_deadcrootc_to_cwdc_col, default='inactive') + if ( get_do_grossunrep() )then + this%gru_conv_cflux_patch(begp:endp) = spval + call hist_addfld1d (fname='GRU_CONV_CFLUX', units='gC/m^2/s', & + avgflag='A', long_name='gross unrepresented conversion C flux (immediate loss to atm) (0 at all times except first timestep of year)', & + ptr_patch=this%gru_conv_cflux_patch) + + this%gru_conv_cflux_dribbled_grc(begg:endg) = spval + call hist_addfld1d (fname='GRU_CONV_CFLUX_DRIBBLED', units='gC/m^2/s', & + avgflag='A', & + long_name='gross unrepresented conversion C flux (immediate loss to atm), dribbled throughout the year', & + ptr_gcell=this%gru_conv_cflux_dribbled_grc) + + this%gru_wood_productc_gain_patch(begp:endp) = spval + call hist_addfld1d (fname='GRU_WOODPRODC_GAIN', units='gC/m^2/s', & + avgflag='A', long_name='gross unrepresented landcover change driven addition to wood product carbon pools (0 at all times except first timestep of year)', & + ptr_patch=this%gru_wood_productc_gain_patch) + + this%gru_slash_cflux_patch(begp:endp) = spval + call hist_addfld1d (fname='GRU_SLASH_CFLUX', units='gC/m^2/s', & + avgflag='A', long_name='slash gross unrepresented landcover change carbon (to litter)', & + ptr_patch=this%gru_slash_cflux_patch) + end if + this%crop_seedc_to_leaf_patch(begp:endp) = spval call hist_addfld1d (fname='CROP_SEEDC_TO_LEAF', units='gC/m^2/s', & avgflag='A', long_name='crop seed source to leaf', & @@ -3040,7 +3217,7 @@ subroutine InitHistory(this, bounds, carbon_type) do k = 1, ndecomp_pools if ( decomp_cascade_con%is_litter(k) .or. decomp_cascade_con%is_cwd(k) ) then data1dptr => this%m_decomp_cpools_to_fire_col(:,k) - fieldname = 'C13_M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'C_TO_FIRE' + fieldname = 'C13_M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_C_TO_FIRE' longname = 'C13 '//trim(decomp_cascade_con%decomp_pool_name_long(k))//' C fire loss' call hist_addfld1d (fname=fieldname, units='gC13/m^2', & avgflag='A', long_name=longname, & @@ -3048,7 +3225,7 @@ subroutine InitHistory(this, bounds, carbon_type) if ( nlevdecomp_full > 1 ) then data2dptr => this%m_decomp_cpools_to_fire_vr_col(:,:,k) - fieldname = 'C13_M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'C_TO_FIRE'//trim(vr_suffix) + fieldname = 'C13_M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_C_TO_FIRE'//trim(vr_suffix) longname = 'C13 '//trim(decomp_cascade_con%decomp_pool_name_long(k))//' C fire loss' call hist_addfld_decomp (fname=fieldname, units='gC13/m^3', type2d='levdcmp', & avgflag='A', long_name=longname, & @@ -3200,7 +3377,7 @@ subroutine InitHistory(this, bounds, carbon_type) do k = 1, ndecomp_pools if ( decomp_cascade_con%is_litter(k) .or. decomp_cascade_con%is_cwd(k) ) then data1dptr => this%m_decomp_cpools_to_fire_col(:,k) - fieldname = 'C14_M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'C_TO_FIRE' + fieldname = 'C14_M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_C_TO_FIRE' longname = 'C14 '//trim(decomp_cascade_con%decomp_pool_name_long(k))//' C fire loss' call hist_addfld1d (fname=fieldname, units='gC14/m^2', & avgflag='A', long_name=longname, & @@ -3208,7 +3385,7 @@ subroutine InitHistory(this, bounds, carbon_type) if ( nlevdecomp_full > 1 ) then data2dptr => this%m_decomp_cpools_to_fire_vr_col(:,:,k) - fieldname = 'C14_M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'C_TO_FIRE'//trim(vr_suffix) + fieldname = 'C14_M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_C_TO_FIRE'//trim(vr_suffix) longname = 'C14 '//trim(decomp_cascade_con%decomp_pool_name_long(k))//' C fire loss' call hist_addfld_decomp (fname=fieldname, units='gC14/m^3', type2d='levdcmp', & avgflag='A', long_name=longname, & @@ -3424,15 +3601,18 @@ subroutine InitCold(this, bounds) do c = bounds%begc, bounds%endc l = col%landunit(c) - ! also initialize dynamic landcover fluxes so that they have + ! also initialize dynamic landcover fluxes + ! and gross unrepresented landcover fluxes so that they have ! real values on first timestep, prior to calling pftdyn_cnbal if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then do j = 1, nlevdecomp_full do i = i_litr_min, i_litr_max this%dwt_frootc_to_litr_c_col(c,j,i) = 0._r8 + this%gru_c_to_litr_c_col(c,j,i) = 0._r8 end do this%dwt_livecrootc_to_cwdc_col(c,j) = 0._r8 this%dwt_deadcrootc_to_cwdc_col(c,j) = 0._r8 + this%gru_c_to_cwdc_col(c,j) = 0._r8 end do end if end do @@ -3527,6 +3707,7 @@ subroutine RestartBulkOnly ( this, bounds, ncid, flag ) logical :: readvar ! determine if variable is on initial file character(len=256) :: varname real(r8), pointer :: data1dptr(:) ! temp. pointer for slicing larger arrays + real(r8), pointer :: data2dptr(:,:) ! temp. pointer for slicing larger arrays !------------------------------------------------------------------------ if (use_crop) then @@ -3559,6 +3740,62 @@ subroutine RestartBulkOnly ( this, bounds, ncid, flag ) units='gC/m2/s', & interpinic_flag='interp', readvar=readvar, data=data1dptr) end do + + ! Read or write variable(s) with mxharvests dimension + ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2022-06-10) See note in CallRestartvarDimOK() + if (CallRestartvarDimOK(ncid, flag, 'mxharvests')) then + do k = repr_grain_min, repr_grain_max + ! e.g., grainc_to_food_perharv + data2dptr => this%repr_grainc_to_food_perharv_patch(:,:,k) + varname = get_repr_rest_fname(k)//'c_to_food_perharv' + call restartvar(ncid=ncid, flag=flag, varname=varname, & + xtype=ncd_double, & + dim1name='pft', & + dim2name='mxharvests', & + switchdim=.true., & + long_name=get_repr_longname(k)//' C to food per harvest; should only be output annually', & + units='gC/m2', & + readvar=readvar, & + scale_by_thickness=.false., & + interpinic_flag='interp', data=data2dptr) + + ! e.g., grainc_to_seed_perharv + data2dptr => this%repr_grainc_to_seed_perharv_patch(:,:,k) + varname = get_repr_rest_fname(k)//'c_to_seed_perharv' + call restartvar(ncid=ncid, flag=flag, varname=varname, & + xtype=ncd_double, & + dim1name='pft', & + dim2name='mxharvests', & + switchdim=.true., & + long_name=get_repr_longname(k)//' C to seed per harvest; should only be output annually', & + units='gC/m2', & + readvar=readvar, & + scale_by_thickness=.false., & + interpinic_flag='interp', data=data2dptr) + end do + end if + + do k = repr_grain_min, repr_grain_max + ! e.g., grainc_to_food_thisyr + data1dptr => this%repr_grainc_to_food_thisyr_patch(:,k) + varname = get_repr_rest_fname(k)//'c_to_food_thisyr' + call restartvar(ncid=ncid, flag=flag, varname=varname, & + xtype=ncd_double, & + dim1name='pft', & + long_name=get_repr_longname(k)//' C to food per calendar year; should only be output annually', & + units='gC/m2', & + interpinic_flag='interp', readvar=readvar, data=data1dptr) + + ! e.g., grainc_to_seed_thisyr + data1dptr => this%repr_grainc_to_seed_thisyr_patch(:,k) + varname = get_repr_rest_fname(k)//'c_to_seed_thisyr' + call restartvar(ncid=ncid, flag=flag, varname=varname, & + xtype=ncd_double, & + dim1name='pft', & + long_name=get_repr_longname(k)//' C to seed per calendar year; should only be output annually', & + units='gC/m2', & + interpinic_flag='interp', readvar=readvar, data=data1dptr) + end do do k = 1, nrepr data1dptr => this%cpool_to_reproductivec_patch(:,k) @@ -3728,6 +3965,7 @@ subroutine RestartAllIsotopes ( this, bounds, ncid, flag ) !----------------------------------------------------------------------- call this%dwt_conv_cflux_dribbler%Restart(bounds, ncid, flag) + call this%gru_conv_cflux_dribbler%Restart(bounds, ncid, flag) call this%hrv_xsmrpool_to_atm_dribbler%Restart(bounds, ncid, flag) end subroutine RestartAllIsotopes @@ -3799,6 +4037,32 @@ subroutine SetValues ( this, nvegcpool, & this%hrv_gresp_xfer_to_litter_patch(i) = value_patch this%hrv_xsmrpool_to_atm_patch(i) = value_patch + this%gru_leafc_to_litter_patch(i) = value_patch + this%gru_leafc_storage_to_atm_patch(i) = value_patch + this%gru_leafc_xfer_to_atm_patch(i) = value_patch + this%gru_frootc_to_litter_patch(i) = value_patch + this%gru_frootc_storage_to_atm_patch(i) = value_patch + this%gru_frootc_xfer_to_atm_patch(i) = value_patch + this%gru_livestemc_to_atm_patch(i) = value_patch + this%gru_livestemc_storage_to_atm_patch(i) = value_patch + this%gru_livestemc_xfer_to_atm_patch(i) = value_patch + this%gru_deadstemc_to_atm_patch(i) = value_patch + this%gru_deadstemc_storage_to_atm_patch(i) = value_patch + this%gru_deadstemc_xfer_to_atm_patch(i) = value_patch + this%gru_livecrootc_to_litter_patch(i) = value_patch + this%gru_livecrootc_storage_to_atm_patch(i) = value_patch + this%gru_livecrootc_xfer_to_atm_patch(i) = value_patch + this%gru_deadcrootc_to_litter_patch(i) = value_patch + this%gru_deadcrootc_storage_to_atm_patch(i) = value_patch + this%gru_deadcrootc_xfer_to_atm_patch(i) = value_patch + this%gru_gresp_storage_to_atm_patch(i) = value_patch + this%gru_gresp_xfer_to_atm_patch(i) = value_patch + this%gru_xsmrpool_to_atm_patch(i) = value_patch + + this%gru_conv_cflux_patch(i) = value_patch + this%gru_wood_productc_gain_patch(i) = value_patch + this%gru_slash_cflux_patch(i) = value_patch + this%m_leafc_to_fire_patch(i) = value_patch this%m_leafc_storage_to_fire_patch(i) = value_patch this%m_leafc_xfer_to_fire_patch(i) = value_patch @@ -3953,6 +4217,8 @@ subroutine SetValues ( this, nvegcpool, & this%livestemc_to_litter_patch(i) = value_patch this%leafc_to_biofuelc_patch(i) = value_patch this%livestemc_to_biofuelc_patch(i) = value_patch + this%leafc_to_removedresiduec_patch(i) = value_patch + this%livestemc_to_removedresiduec_patch(i) = value_patch end do do k = 1, nrepr @@ -3994,10 +4260,13 @@ subroutine SetValues ( this, nvegcpool, & this%gap_mortality_c_to_litr_c_col(i,j,k) = value_column this%harvest_c_to_litr_c_col(i,j,k) = value_column this%m_c_to_litr_fire_col(i,j,k) = value_column + this%gru_c_to_litr_c_col(i,j,k) = value_column end do this%gap_mortality_c_to_cwdc_col(i,j) = value_column this%fire_mortality_c_to_cwdc_col(i,j) = value_column this%harvest_c_to_cwdc_col(i,j) = value_column + this%gru_c_to_cwdc_col(i,j) = value_column + end do end do @@ -4081,6 +4350,9 @@ subroutine SetValues ( this, nvegcpool, & this%fire_closs_col(i) = value_column this%wood_harvestc_col(i) = value_column this%hrv_xsmrpool_to_atm_col(i) = value_column + this%gru_conv_cflux_col(i) = value_column + this%gru_wood_productc_gain_col(i) = value_column + this%nep_col(i) = value_column if ( use_crop )then this%xsmrpool_to_atm_col(i) = value_column @@ -4125,6 +4397,28 @@ subroutine ZeroDwt( this, bounds ) end subroutine ZeroDwt + !----------------------------------------------------------------------- + subroutine ZeroGru( this, bounds ) + ! + ! !DESCRIPTION + ! Initialize flux variables needed for dynamic land use. + ! + ! !ARGUMENTS: + class(cnveg_carbonflux_type) :: this + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + integer :: c, g, j ! indices + !----------------------------------------------------------------------- + + ! set conversion and product pool fluxes to 0 at the beginning of every timestep + + do g = bounds%begg, bounds%endg + this%gru_conv_cflux_grc(g) = 0._r8 + end do + + end subroutine ZeroGru + !----------------------------------------------------------------------- subroutine Summary_carbonflux(this, & bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, isotope, & @@ -4168,6 +4462,7 @@ subroutine Summary_carbonflux(this, & real(r8) :: hrv_xsmrpool_to_atm_delta_grc(bounds%begg:bounds%endg) ! hrv_xsmrpool_to_atm_col averaged to gridcell, expressed as a delta (not a flux) (gC/m2) real(r8) :: hrv_xsmrpool_to_atm_dribbled_grc(bounds%begg:bounds%endg) ! hrv_xsmrpool_to_atm, dribbled over the year (gC/m2/s) real(r8) :: dwt_conv_cflux_delta_grc(bounds%begg:bounds%endg) ! dwt_conv_cflux_grc expressed as a total delta (not a flux) (gC/m2) + real(r8) :: gru_conv_cflux_delta_grc(bounds%begg:bounds%endg) ! gru_conv_cflux_grc expressed as a total delta (not a flux) (gC/m2) !----------------------------------------------------------------------- SHR_ASSERT_ALL_FL((ubound(product_closs_grc) == (/bounds%endg/)), sourcefile, __LINE__) @@ -4294,6 +4589,7 @@ subroutine Summary_carbonflux(this, & ! root respiration (RR) this%rr_patch(p) = & this%froot_mr_patch(p) + & + this%livecroot_mr_patch(p) + & this%cpool_froot_gr_patch(p) + & this%cpool_livecroot_gr_patch(p) + & this%cpool_deadcroot_gr_patch(p) + & @@ -4411,7 +4707,12 @@ subroutine Summary_carbonflux(this, & this%hrv_deadcrootc_storage_to_litter_patch(p) + & this%hrv_deadcrootc_xfer_to_litter_patch(p) + & this%hrv_gresp_storage_to_litter_patch(p) + & - this%hrv_gresp_xfer_to_litter_patch(p) + this%hrv_gresp_xfer_to_litter_patch(p) + & + + this%gru_leafc_to_litter_patch(p) + & + this%gru_frootc_to_litter_patch(p) + & + this%gru_livecrootc_to_litter_patch(p) + & + this%gru_deadcrootc_to_litter_patch(p) if ( use_crop .and. patch%itype(p) >= npcropmin )then this%litfall_patch(p) = & @@ -4492,6 +4793,7 @@ subroutine Summary_carbonflux(this, & this%m_leafc_to_fire_patch(p) + & this%m_leafc_to_litter_fire_patch(p) + & this%hrv_leafc_to_litter_patch(p) + & + this%gru_leafc_to_litter_patch(p) + & this%leafc_to_litter_patch(p) ! (WOODC_ALLOC) - wood C allocation @@ -4526,7 +4828,20 @@ subroutine Summary_carbonflux(this, & this%hrv_livecrootc_xfer_to_litter_patch(p) + & this%hrv_deadcrootc_to_litter_patch(p) + & this%hrv_deadcrootc_storage_to_litter_patch(p) + & - this%hrv_deadcrootc_xfer_to_litter_patch(p) + this%hrv_deadcrootc_xfer_to_litter_patch(p) + & + this%gru_livestemc_to_atm_patch(p) + & + this%gru_livestemc_storage_to_atm_patch(p) + & + this%gru_livestemc_xfer_to_atm_patch(p) + & + this%gru_deadstemc_to_atm_patch(p) + & + this%gru_wood_productc_gain_patch(p) + & + this%gru_deadstemc_storage_to_atm_patch(p) + & + this%gru_deadstemc_xfer_to_atm_patch(p) + & + this%gru_livecrootc_to_litter_patch(p) + & + this%gru_livecrootc_storage_to_atm_patch(p) + & + this%gru_livecrootc_xfer_to_atm_patch(p) + & + this%gru_deadcrootc_to_litter_patch(p) + & + this%gru_deadcrootc_storage_to_atm_patch(p) + & + this%gru_deadcrootc_xfer_to_atm_patch(p) ! (Slash Harvest Flux) - Additional Wood Harvest Veg C Losses this%slash_harvestc_patch(p) = & @@ -4551,6 +4866,32 @@ subroutine Summary_carbonflux(this, & this%hrv_gresp_storage_to_litter_patch(p) + & this%hrv_gresp_xfer_to_litter_patch(p) + ! (Gross Unrepresented Landcover Change Conversion Flux) - Direct Veg C Loss to Atmosphere + this%gru_conv_cflux_patch(p) = & + this%gru_livestemc_to_atm_patch(p) + & + this%gru_deadstemc_to_atm_patch(p) + & + this%gru_xsmrpool_to_atm_patch(p) + & + this%gru_leafc_storage_to_atm_patch(p) + & + this%gru_frootc_storage_to_atm_patch(p) + & + this%gru_livestemc_storage_to_atm_patch(p) + & + this%gru_deadstemc_storage_to_atm_patch(p) + & + this%gru_livecrootc_storage_to_atm_patch(p) + & + this%gru_deadcrootc_storage_to_atm_patch(p) + & + this%gru_gresp_storage_to_atm_patch(p) + & + this%gru_leafc_xfer_to_atm_patch(p) + & + this%gru_frootc_xfer_to_atm_patch(p) + & + this%gru_livestemc_xfer_to_atm_patch(p) + & + this%gru_deadstemc_xfer_to_atm_patch(p) + & + this%gru_livecrootc_xfer_to_atm_patch(p) + & + this%gru_deadcrootc_xfer_to_atm_patch(p) + & + this%gru_gresp_xfer_to_atm_patch(p) + + ! (Gross Unrepresented Landcover Change Slash Flux) - Direct Veg C Loss to Atmosphere + this%gru_slash_cflux_patch(p) = & + this%gru_leafc_to_litter_patch(p) + & + this%gru_frootc_to_litter_patch(p) + & + this%gru_livecrootc_to_litter_patch(p) + & + this%gru_deadcrootc_to_litter_patch(p) end do ! end of patches loop !------------------------------------------------ @@ -4575,6 +4916,10 @@ subroutine Summary_carbonflux(this, & l2g_scale_type = 'unity') end if + call p2c(bounds, num_soilc, filter_soilc, & + this%gru_conv_cflux_patch(bounds%begp:bounds%endp), & + this%gru_conv_cflux_col(bounds%begc:bounds%endc)) + call p2c(bounds, num_soilc, filter_soilc, & this%fire_closs_patch(bounds%begp:bounds%endp), & this%fire_closs_p2c_col(bounds%begc:bounds%endc)) @@ -4700,6 +5045,18 @@ subroutine Summary_carbonflux(this, & call this%dwt_conv_cflux_dribbler%get_curr_flux(bounds, & this%dwt_conv_cflux_dribbled_grc(bounds%begg:bounds%endg)) + call c2g( bounds = bounds, & + carr = this%gru_conv_cflux_col(bounds%begc:bounds%endc), & + garr = this%gru_conv_cflux_grc(bounds%begg:bounds%endg), & + c2l_scale_type = 'unity', & + l2g_scale_type = 'unity') + gru_conv_cflux_delta_grc(bounds%begg:bounds%endg) = & + this%gru_conv_cflux_grc(bounds%begg:bounds%endg) * dtime + call this%gru_conv_cflux_dribbler%set_curr_delta(bounds, & + gru_conv_cflux_delta_grc(bounds%begg:bounds%endg)) + call this%gru_conv_cflux_dribbler%get_curr_flux(bounds, & + this%gru_conv_cflux_dribbled_grc(bounds%begg:bounds%endg)) + do g = bounds%begg, bounds%endg ! net ecosystem exchange of carbon, includes fire flux and hrv_xsmrpool flux, ! positive for source (NEE) @@ -4710,6 +5067,7 @@ subroutine Summary_carbonflux(this, & this%landuseflux_grc(g) = & this%dwt_conv_cflux_dribbled_grc(g) + & + this%gru_conv_cflux_dribbled_grc(g) + & product_closs_grc(g) ! net biome production of carbon, positive for sink diff --git a/src/biogeochem/CNVegCarbonStateType.F90 b/src/biogeochem/CNVegCarbonStateType.F90 index 610689fdb6..c7e21da1d6 100644 --- a/src/biogeochem/CNVegCarbonStateType.F90 +++ b/src/biogeochem/CNVegCarbonStateType.F90 @@ -68,7 +68,6 @@ module CNVegCarbonStateType real(r8), pointer :: ctrunc_patch (:) ! (gC/m2) patch-level sink for C truncation real(r8), pointer :: woodc_patch (:) ! (gC/m2) wood C real(r8), pointer :: leafcmax_patch (:) ! (gC/m2) ann max leaf C - real(r8), pointer :: totc_patch (:) ! (gC/m2) total patch-level carbon, including cpool real(r8), pointer :: rootc_col (:) ! (gC/m2) root carbon at column level (fire) real(r8), pointer :: leafc_col (:) ! (gC/m2) column-level leafc (fire) real(r8), pointer :: deadstemc_col (:) ! (gC/m2) column-level deadstemc (fire) @@ -82,21 +81,21 @@ module CNVegCarbonStateType ! summary (diagnostic) state variables, not involved in mass balance real(r8), pointer :: dispvegc_patch (:) ! (gC/m2) displayed veg carbon, excluding storage and cpool real(r8), pointer :: storvegc_patch (:) ! (gC/m2) stored vegetation carbon, excluding cpool + + logical, private :: dribble_crophrv_xsmrpool_2atm ! Flag to indicate if should harvest xsmrpool to the atmosphere + ! it originates and is defined in CNVegetationFacade.F90 + + ! Total C pools + real(r8), pointer :: totc_patch (:) ! (gC/m2) total patch-level carbon, including cpool real(r8), pointer :: totvegc_patch (:) ! (gC/m2) total vegetation carbon, excluding cpool - real(r8), pointer :: totvegc_col (:) ! (gC/m2) total vegetation carbon, excluding cpool averaged to column (p2c) - - ! Total C pools + real(r8), pointer :: totvegc_col (:) ! (gC/m2) total vegetation carbon, excluding cpool averaged to column (p2c) real(r8), pointer :: totc_p2c_col (:) ! (gC/m2) totc_patch averaged to col - real(r8), pointer :: totc_col (:) ! (gC/m2) total column carbon, incl veg and cpool - real(r8), pointer :: totecosysc_col (:) ! (gC/m2) total ecosystem carbon, incl veg but excl cpool - real(r8), pointer :: totc_grc (:) ! (gC/m2) total gridcell carbon - - logical, private :: dribble_crophrv_xsmrpool_2atm + contains procedure , public :: Init procedure , public :: SetValues - procedure , public :: ZeroDWT + procedure , public :: ZeroDwt procedure , public :: Restart procedure , public :: Summary => Summary_carbonstate procedure , public :: DynamicPatchAdjustments ! adjust state variables when patch areas change @@ -127,7 +126,7 @@ module CNVegCarbonStateType !------------------------------------------------------------------------ subroutine Init(this, bounds, carbon_type, ratio, NLFilename, & - dribble_crophrv_xsmrpool_2atm, c12_cnveg_carbonstate_inst) + dribble_crophrv_xsmrpool_2atm, alloc_full_veg, c12_cnveg_carbonstate_inst) class(cnveg_carbonstate_type) :: this type(bounds_type) , intent(in) :: bounds @@ -135,6 +134,7 @@ subroutine Init(this, bounds, carbon_type, ratio, NLFilename, & character(len=*) , intent(in) :: carbon_type ! Carbon isotope type C12, C13 or C1 character(len=*) , intent(in) :: NLFilename ! Namelist filename logical , intent(in) :: dribble_crophrv_xsmrpool_2atm + logical , intent(in) :: alloc_full_veg ! total number of bgc patches (non-fates) type(cnveg_carbonstate_type) , intent(in), optional :: c12_cnveg_carbonstate_inst ! cnveg_carbonstate for C12 (if C13 or C14) !----------------------------------------------------------------------- @@ -142,15 +142,17 @@ subroutine Init(this, bounds, carbon_type, ratio, NLFilename, & this%dribble_crophrv_xsmrpool_2atm = dribble_crophrv_xsmrpool_2atm - call this%InitAllocate ( bounds) - call this%InitReadNML ( NLFilename ) - call this%InitHistory ( bounds, carbon_type) - if (present(c12_cnveg_carbonstate_inst)) then - call this%InitCold ( bounds, ratio, carbon_type, c12_cnveg_carbonstate_inst ) - else - call this%InitCold ( bounds, ratio, carbon_type ) + call this%InitAllocate ( bounds, alloc_full_veg) + if(alloc_full_veg)then + call this%InitReadNML ( NLFilename ) + call this%InitHistory ( bounds, carbon_type) + if (present(c12_cnveg_carbonstate_inst)) then + call this%InitCold ( bounds, ratio, carbon_type, c12_cnveg_carbonstate_inst ) + else + call this%InitCold ( bounds, ratio, carbon_type ) + end if end if - + end subroutine Init !------------------------------------------------------------------------ @@ -214,21 +216,28 @@ subroutine InitReadNML(this, NLFilename) end subroutine InitReadNML !------------------------------------------------------------------------ - subroutine InitAllocate(this, bounds) + subroutine InitAllocate(this, bounds, alloc_full_veg) ! ! !ARGUMENTS: class (cnveg_carbonstate_type) :: this - type(bounds_type), intent(in) :: bounds + type(bounds_type), intent(in) :: bounds + logical,intent(in) :: alloc_full_veg ! Total number of bgc patches on the proc (non_fates) ! ! !LOCAL VARIABLES: integer :: begp,endp integer :: begc,endc integer :: begg,endg !------------------------------------------------------------------------ - - begp = bounds%begp; endp = bounds%endp - begc = bounds%begc; endc = bounds%endc - begg = bounds%begg; endg = bounds%endg + + if(alloc_full_veg)then + begp = bounds%begp; endp = bounds%endp + begc = bounds%begc; endc = bounds%endc + begg = bounds%begg; endg = bounds%endg + else + begp = 0;endp=0 + begc = 0;endc=0 + begg = 0;endg=0 + end if allocate(this%leafc_patch (begp:endp)) ; this%leafc_patch (:) = nan allocate(this%leafc_storage_patch (begp:endp)) ; this%leafc_storage_patch (:) = nan @@ -277,9 +286,6 @@ subroutine InitAllocate(this, bounds) allocate(this%totvegc_col (begc:endc)) ; this%totvegc_col (:) = nan allocate(this%totc_p2c_col (begc:endc)) ; this%totc_p2c_col (:) = nan - allocate(this%totc_col (begc:endc)) ; this%totc_col (:) = nan - allocate(this%totecosysc_col (begc:endc)) ; this%totecosysc_col (:) = nan - allocate(this%totc_grc (begg:endg)) ; this%totc_grc (:) = nan ! Matrix solution variables if(use_matrixcn)then @@ -509,16 +515,6 @@ subroutine InitHistory(this, bounds, carbon_type) avgflag='A', long_name='fuel load', & ptr_col=this%fuelc_col) - this%totc_col(begc:endc) = spval - call hist_addfld1d (fname='TOTCOLC', units='gC/m^2', & - avgflag='A', long_name='total column carbon, incl veg and cpool but excl product pools', & - ptr_col=this%totc_col) - - this%totecosysc_col(begc:endc) = spval - call hist_addfld1d (fname='TOTECOSYSC', units='gC/m^2', & - avgflag='A', long_name='total ecosystem carbon, incl veg but excl cpool and product pools', & - ptr_col=this%totecosysc_col) - ! Matrix solution history variables if ( use_matrixcn )then end if @@ -675,16 +671,6 @@ subroutine InitHistory(this, bounds, carbon_type) avgflag='A', long_name='C13 pool for seeding new PFTs via dynamic landcover', & ptr_gcell=this%seedc_grc, default='inactive') - this%totc_col(begc:endc) = spval - call hist_addfld1d (fname='C13_TOTCOLC', units='gC13/m^2', & - avgflag='A', long_name='C13 total column carbon, incl veg and cpool but excl product pools', & - ptr_col=this%totc_col, default='inactive') - - this%totecosysc_col(begc:endc) = spval - call hist_addfld1d (fname='C13_TOTECOSYSC', units='gC13/m^2', & - avgflag='A', long_name='C13 total ecosystem carbon, incl veg but excl cpool and product pools', & - ptr_col=this%totecosysc_col) - if (use_crop) then this%reproductivec_patch(begp:endp,:) = spval do k = 1, nrepr @@ -866,16 +852,6 @@ subroutine InitHistory(this, bounds, carbon_type) avgflag='A', long_name='C14 pool for seeding new PFTs via dynamic landcover', & ptr_gcell=this%seedc_grc, default='inactive') - this%totc_col(begc:endc) = spval - call hist_addfld1d (fname='C14_TOTCOLC', units='gC14/m^2', & - avgflag='A', long_name='C14 total column carbon, incl veg and cpool but excl product pools', & - ptr_col=this%totc_col, default='inactive') - - this%totecosysc_col(begc:endc) = spval - call hist_addfld1d (fname='C14_TOTECOSYSC', units='gC14/m^2', & - avgflag='A', long_name='C14 total ecosystem carbon, incl veg but excl cpool and product pools', & - ptr_col=this%totecosysc_col) - if (use_crop) then this%reproductivec_patch(begp:endp,:) = spval do k = 1, nrepr @@ -1099,16 +1075,14 @@ subroutine InitCold(this, bounds, ratio, carbon_type, c12_cnveg_carbonstate_inst ! this%totgrainc_col(c) = 0._r8 ! total carbon pools - this%totecosysc_col(c) = 0._r8 this%totc_p2c_col(c) = 0._r8 - this%totc_col(c) = 0._r8 + end if end do do g = bounds%begg, bounds%endg this%seedc_grc(g) = 0._r8 - this%totc_grc(g) = 0._r8 end do ! initialize fields for special filters @@ -1165,8 +1139,12 @@ subroutine Restart ( this, bounds, ncid, flag, carbon_type, reseed_dead_plants, real(r8), pointer :: data1dptr(:) ! temp. pointer for slicing larger arrays real(r8), parameter:: totvegcthresh = 1.0_r8 ! Total vegetation carbon threshold to reseed dead vegetation + logical :: missing_ciso ! whether C isotope fields are missing from the input file, despite the run containing C isotopes + !------------------------------------------------------------------------ + missing_ciso = .false. + if (carbon_type == 'c13' .or. carbon_type == 'c14') then if (.not. present(c12_cnveg_carbonstate_inst)) then call endrun(msg=' ERROR: for C14 must pass in c12_cnveg_carbonstate_inst as argument' //& @@ -1358,86 +1336,14 @@ subroutine Restart ( this, bounds, ncid, flag, carbon_type, reseed_dead_plants, end if end if end if - !-------------------------------- - ! C12 carbon state variables - !-------------------------------- - - if (carbon_type == 'c12') then - call restartvar(ncid=ncid, flag=flag, varname='totvegc', xtype=ncd_double, & - dim1name='pft', long_name='', units='', & - interpinic_flag='interp', readvar=readvar, data=this%totvegc_patch) - ! totvegc_col needed for resetting soil carbon stocks during AD spinup exit - call restartvar(ncid=ncid, flag=flag, varname='totvegc_col', xtype=ncd_double, & - dim1name='column', long_name='', units='', & - interpinic_flag='interp', readvar=readvar, data=this%totvegc_col) - end if - - !-------------------------------- - ! C13 carbon state variables - !-------------------------------- - - if ( carbon_type == 'c13') then - call restartvar(ncid=ncid, flag=flag, varname='totvegc_13', xtype=ncd_double, & - dim1name='pft', long_name='', units='', & - interpinic_flag='interp', readvar=readvar, data=this%totvegc_patch) - if (flag=='read' .and. .not. readvar) then - if ( masterproc ) write(iulog,*) 'initializing cnveg_carbonstate_inst%totvegc with atmospheric c13 value' - do i = bounds%begp,bounds%endp - if (pftcon%c3psn(patch%itype(i)) == 1._r8) then - this%totvegc_patch(i) = c12_cnveg_carbonstate_inst%totvegc_patch(i) * c3_r2 - else - this%totvegc_patch(i) = c12_cnveg_carbonstate_inst%totvegc_patch(i) * c4_r2 - endif - end do - end if - - call restartvar(ncid=ncid, flag=flag, varname='totvegc_col_13', xtype=ncd_double, & - dim1name='column', long_name='', units='', & - interpinic_flag='interp', readvar=readvar, data=this%totvegc_col) - if (flag=='read' .and. .not. readvar) then - if ( masterproc ) write(iulog,*) 'initializing cnveg_carbonstate_inst%totvegc with atmospheric c13 value' - do i = bounds%begc,bounds%endc - if (pftcon%c3psn(patch%itype(i)) == 1._r8) then - this%totvegc_col(i) = c12_cnveg_carbonstate_inst%totvegc_col(i) * c3_r2 - else - this%totvegc_col(i) = c12_cnveg_carbonstate_inst%totvegc_col(i) * c4_r2 - endif - end do - end if - - end if - - !-------------------------------- - ! C14 patch carbon state variables - !-------------------------------- - - if ( carbon_type == 'c14') then - call restartvar(ncid=ncid, flag=flag, varname='totvegc_14', xtype=ncd_double, & - dim1name='pft', long_name='', units='', & - interpinic_flag='interp', readvar=readvar, data=this%totvegc_patch) - if (flag=='read' .and. .not. readvar) then - if ( masterproc ) write(iulog,*) 'initializing this%totvegc_patch with atmospheric c14 value' - do i = bounds%begp,bounds%endp - if (this%totvegc_patch(i) /= spval .and. & - .not. isnan(this%totvegc_patch(i)) ) then - this%totvegc_patch(i) = c12_cnveg_carbonstate_inst%totvegc_patch(i) * c14ratio - endif - end do - endif - call restartvar(ncid=ncid, flag=flag, varname='totvegc_col_14', xtype=ncd_double, & - dim1name='column', long_name='', units='', & - interpinic_flag='interp', readvar=readvar, data=this%totvegc_col) - if (flag=='read' .and. .not. readvar) then - if ( masterproc ) write(iulog,*) 'initializing cnveg_carbonstate_inst%totvegc with atmospheric c14 value' - do i = bounds%begc,bounds%endc - if (this%totvegc_col(i) /= spval .and. & - .not. isnan(this%totvegc_col(i)) ) then - this%totvegc_col(i) = c12_cnveg_carbonstate_inst%totvegc_col(i) * c14ratio - endif - end do - end if - end if + call restartvar(ncid=ncid, flag=flag, varname='totvegc', xtype=ncd_double, & + dim1name='pft', long_name='', units='', & + interpinic_flag='interp', readvar=readvar, data=this%totvegc_patch) + ! totvegc_col needed for resetting soil carbon stocks during AD spinup exit + call restartvar(ncid=ncid, flag=flag, varname='totvegc_col', xtype=ncd_double, & + dim1name='column', long_name='', units='', & + interpinic_flag='interp', readvar=readvar, data=this%totvegc_col) if ( flag == 'read' .and. (enter_spinup .or. (reseed_dead_plants .and. .not. is_restart())) .and. .not. use_cndv) then @@ -1614,6 +1520,77 @@ subroutine Restart ( this, bounds, ncid, flag, carbon_type, reseed_dead_plants, !-------------------------------- if ( carbon_type == 'c13') then + + call restartvar(ncid=ncid, flag=flag, varname='totvegc_13', xtype=ncd_double, & + dim1name='pft', long_name='', units='', & + interpinic_flag='interp', readvar=readvar, data=this%totvegc_patch) + if (flag=='read' .and. .not. readvar) then + ! BUG(kwo, 2023-10-19, ESCOMP/ctsm#2119) There is a bug that causes incorrect values for + ! C isotopes if running from a case without C isotopes (an initial file) to a case with C + ! isotopes (https://github.com/ESCOMP/ctsm/issues/2119). Here we check if the user + ! is doing this and abort if they are. This particular check is covering the case + ! when use_init_interp=.false. There is a similar check (but for the purpose of working around + ! a different bug) in initInterp.F90. This check below should be removed if bug #2119 is ever + ! fully resolved (i.e., we decide we need to support going from a case without C isotopes (an + ! initial file) to a case with C isotopes), and replaced by the logic shown below for .e.g, + ! totvegc_col_13, where totvegc_col_13 is initialized with atmospheric c13 values. + ! We arbitrarily check totvegc_13 (we could pick any c13 restart field). + if (masterproc) then + write(iulog,*) 'Cannot initialize from a run without c13 to a run with c13,' + write(iulog,*) 'due to .' + write(iulog,*) 'Either use an input initial conditions file with c13 information,' + write(iulog,*) 'or re-spinup from cold start.' + end if + missing_ciso = .true. + end if + + end if + + if ( carbon_type == 'c14') then + call restartvar(ncid=ncid, flag=flag, varname='totvegc_14', xtype=ncd_double, & + dim1name='pft', long_name='', units='', & + interpinic_flag='interp', readvar=readvar, data=this%totvegc_patch) + if (flag=='read' .and. .not. readvar) then + ! BUG(kwo, 2023-10-19, ESCOMP/ctsm#2119) There is a bug that causes incorrect values for + ! C isotopes if running from a case without C isotopes (an initial file) to a case with C + ! isotopes (https://github.com/ESCOMP/ctsm/issues/2119). Here we check if the user + ! is doing this and abort if they are. This particular check is covering the case + ! when use_init_interp=.false. There is a similar check (but for the purpose of working around + ! a different bug) in initInterp.F90. This check below should be removed if bug #2119 is ever + ! fully resolved (i.e., we decide we need to support going from a case without C isotopes (an + ! initial file) to a case with C isotopes), and replaced by the logic shown below for .e.g, + ! totvegc_col_14, where totvegc_col_l4 is initialized with atmospheric c14 values. + ! We arbitrarily check totvegc_14 (we could pick any c14 restart field). + if (masterproc) then + write(iulog,*) 'Cannot interpolate from a run without c14 to a run with c14,' + write(iulog,*) 'due to .' + write(iulog,*) 'Either use an input initial conditions file with c14 information,' + write(iulog,*) 'or re-spinup from cold start.' + end if + missing_ciso = .true. + endif + end if + + if (missing_ciso) then + call endrun(msg='Cannot initialize from a run without c13/c14 to a run with c13/c14', & + additional_msg=errMsg(sourcefile, __LINE__)) + end if + + if ( carbon_type == 'c13') then + + call restartvar(ncid=ncid, flag=flag, varname='totvegc_col_13', xtype=ncd_double, & + dim1name='column', long_name='', units='', & + interpinic_flag='interp', readvar=readvar, data=this%totvegc_col) + if (flag=='read' .and. .not. readvar) then + if ( masterproc ) write(iulog,*) 'initializing cnveg_carbonstate_inst%totvegc with atmospheric c13 value' + do i = bounds%begc,bounds%endc + if (this%totvegc_col(i) /= spval .and. & + .not. isnan(this%totvegc_col(i)) ) then + this%totvegc_col(i) = c12_cnveg_carbonstate_inst%totvegc_col(i) * c13ratio + endif + end do + end if + call restartvar(ncid=ncid, flag=flag, varname='leafc_13', xtype=ncd_double, & dim1name='pft', long_name='', units='', & interpinic_flag='interp', readvar=readvar, data=this%leafc_patch) @@ -1638,7 +1615,6 @@ subroutine Restart ( this, bounds, ncid, flag, carbon_type, reseed_dead_plants, this%leafc_storage_patch(i) = c12_cnveg_carbonstate_inst%leafc_storage_patch(i) * c3_r2 else this%leafc_storage_patch(i) = c12_cnveg_carbonstate_inst%leafc_storage_patch(i) * c4_r2 - this%leafc_storage_patch(i) = c12_cnveg_carbonstate_inst%leafc_storage_patch(i) * c4_r2 endif end do end if @@ -1969,6 +1945,20 @@ subroutine Restart ( this, bounds, ncid, flag, carbon_type, reseed_dead_plants, !-------------------------------- if ( carbon_type == 'c14') then + + call restartvar(ncid=ncid, flag=flag, varname='totvegc_col_14', xtype=ncd_double, & + dim1name='column', long_name='', units='', & + interpinic_flag='interp', readvar=readvar, data=this%totvegc_col) + if (flag=='read' .and. .not. readvar) then + if ( masterproc ) write(iulog,*) 'initializing cnveg_carbonstate_inst%totvegc with atmospheric c14 value' + do i = bounds%begc,bounds%endc + if (this%totvegc_col(i) /= spval .and. & + .not. isnan(this%totvegc_col(i)) ) then + this%totvegc_col(i) = c12_cnveg_carbonstate_inst%totvegc_col(i) * c14ratio + endif + end do + end if + call restartvar(ncid=ncid, flag=flag, varname='leafc_14', xtype=ncd_double, & dim1name='pft', long_name='', units='', & interpinic_flag='interp', readvar=readvar, data=this%leafc_patch) @@ -2596,8 +2586,6 @@ subroutine SetValues ( this, & this%fuelc_crop_col(i) = value_column this%totvegc_col(i) = value_column this%totc_p2c_col(i) = value_column - this%totc_col(i) = value_column - this%totecosysc_col(i) = value_column end do end subroutine SetValues @@ -2625,11 +2613,8 @@ subroutine ZeroDwt( this, bounds ) end subroutine ZeroDwt !----------------------------------------------------------------------- - subroutine Summary_carbonstate(this, bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_cwdc_col, soilbiogeochem_totlitc_col, & - soilbiogeochem_totmicc_col, soilbiogeochem_totsomc_col, & - soilbiogeochem_ctrunc_col) + subroutine Summary_carbonstate(this, bounds, num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp) + ! ! !USES: use subgridAveMod, only : p2c @@ -2642,33 +2627,20 @@ subroutine Summary_carbonstate(this, bounds, num_allc, filter_allc, & ! !ARGUMENTS: class(cnveg_carbonstate_type) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_allc ! number of columns in allc filter - integer , intent(in) :: filter_allc(:) ! filter for all active columns - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches - real(r8) , intent(in) :: soilbiogeochem_cwdc_col(bounds%begc:) - real(r8) , intent(in) :: soilbiogeochem_totmicc_col(bounds%begc:) - real(r8) , intent(in) :: soilbiogeochem_totlitc_col(bounds%begc:) - real(r8) , intent(in) :: soilbiogeochem_totsomc_col(bounds%begc:) - real(r8) , intent(in) :: soilbiogeochem_ctrunc_col(bounds%begc:) + integer , intent(in) :: num_bgc_soilc ! number of bgc soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for bgc soil columns + integer , intent(in) :: num_bgc_vegp ! number of soil patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for soil patches + ! ! !LOCAL VARIABLES: integer :: c,p,j,k,l ! indices integer :: fp,fc ! lake filter indices !----------------------------------------------------------------------- - SHR_ASSERT_ALL_FL((ubound(soilbiogeochem_cwdc_col) == (/bounds%endc/)), sourcefile, __LINE__) - SHR_ASSERT_ALL_FL((ubound(soilbiogeochem_totmicc_col) == (/bounds%endc/)), sourcefile, __LINE__) - SHR_ASSERT_ALL_FL((ubound(soilbiogeochem_totlitc_col) == (/bounds%endc/)), sourcefile, __LINE__) - SHR_ASSERT_ALL_FL((ubound(soilbiogeochem_totsomc_col) == (/bounds%endc/)), sourcefile, __LINE__) - SHR_ASSERT_ALL_FL((ubound(soilbiogeochem_ctrunc_col) == (/bounds%endc/)), sourcefile, __LINE__) - ! calculate patch -level summary of carbon state - - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) ! displayed vegetation carbon, excluding storage and cpool (DISPVEGC) this%dispvegc_patch(p) = & @@ -2738,36 +2710,16 @@ subroutine Summary_carbonstate(this, bounds, num_allc, filter_allc, & ! -------------------------------------------- ! column level summary ! -------------------------------------------- - - call p2c(bounds, num_soilc, filter_soilc, & - this%totvegc_patch(bounds%begp:bounds%endp), & - this%totvegc_col(bounds%begc:bounds%endc)) - - call p2c(bounds, num_soilc, filter_soilc, & - this%totc_patch(bounds%begp:bounds%endp), & - this%totc_p2c_col(bounds%begc:bounds%endc)) - - do fc = 1,num_allc - c = filter_allc(fc) - - ! total ecosystem carbon, including veg but excluding cpool (TOTECOSYSC) - this%totecosysc_col(c) = & - soilbiogeochem_cwdc_col(c) + & - soilbiogeochem_totmicc_col(c) + & - soilbiogeochem_totlitc_col(c) + & - soilbiogeochem_totsomc_col(c) + & - this%totvegc_col(c) - - ! total column carbon, including veg and cpool (TOTCOLC) - this%totc_col(c) = this%totc_p2c_col(c) + & - soilbiogeochem_cwdc_col(c) + & - soilbiogeochem_totmicc_col(c) + & - soilbiogeochem_totlitc_col(c) + & - soilbiogeochem_totsomc_col(c) + & - soilbiogeochem_ctrunc_col(c) - - end do - + if(num_bgc_vegp>0)then + call p2c(bounds, num_bgc_soilc, filter_bgc_soilc, & + this%totvegc_patch(bounds%begp:bounds%endp), & + this%totvegc_col(bounds%begc:bounds%endc)) + + call p2c(bounds, num_bgc_soilc, filter_bgc_soilc, & + this%totc_patch(bounds%begp:bounds%endp), & + this%totc_p2c_col(bounds%begc:bounds%endc)) + end if + end subroutine Summary_carbonstate !----------------------------------------------------------------------- diff --git a/src/biogeochem/CNVegNitrogenFluxType.F90 b/src/biogeochem/CNVegNitrogenFluxType.F90 index 5dedff262a..3806de9838 100644 --- a/src/biogeochem/CNVegNitrogenFluxType.F90 +++ b/src/biogeochem/CNVegNitrogenFluxType.F90 @@ -6,14 +6,16 @@ module CNVegNitrogenFluxType use clm_varpar , only : ndecomp_cascade_transitions, ndecomp_pools use clm_varpar , only : nlevdecomp_full, nlevdecomp, i_litr_min, i_litr_max use clm_varpar , only : nvegnpool + use clm_varpar , only : mxharvests use clm_varcon , only : spval, ispval, dzsoi_decomp use clm_varctl , only : use_nitrif_denitrif, use_crop use CNSharedParamsMod , only : use_fun, use_matrixcn use decompMod , only : bounds_type use abortutils , only : endrun use SoilBiogeochemDecompCascadeConType , only : decomp_cascade_con + use dynSubgridControlMod , only : get_do_grossunrep use CropReprPoolsMod , only : nrepr, repr_grain_min, repr_grain_max, repr_structure_min, repr_structure_max - use CropReprPoolsMod , only : get_repr_rest_fname, get_repr_longname + use CropReprPoolsMod , only : get_repr_hist_fname, get_repr_rest_fname, get_repr_longname use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch @@ -131,11 +133,17 @@ module CNVegNitrogenFluxType ! litterfall fluxes real(r8), pointer :: livestemn_to_litter_patch (:) ! patch livestem N to litter (gN/m2/s) real(r8), pointer :: repr_grainn_to_food_patch (:,:) ! patch grain N to food for prognostic crop (gN/m2/s) [patch, repr_grain_min:repr_grain_max] + real(r8), pointer :: repr_grainn_to_food_perharv_patch (:,:,:) ! grain N to food for prognostic crop accumulated by harvest (gN/m2) [patch, harvest, repr_grain_min:repr_grain_max]. Not per-second because this variable represents an accumulation over each growing season, to be instantaneously at the end of each calendar year, to provide output that's easier to work with. + real(r8), pointer :: repr_grainn_to_food_thisyr_patch (:,:) ! grain N to food for prognostic crop accumulated this calendar year (gN/m2) [patch, repr_grain_min:repr_grain_max]. Not per-second because this variable represents an accumulation over an entire calendar year, to be saved instantaneously at the end of each calendar year, to provide output that's easier to work with. real(r8), pointer :: repr_structuren_to_cropprod_patch (:,:) ! patch reproductive structure N to crop product pool for prognostic crop (gN/m2/s) [patch, repr_structure_min:repr_structure_max] real(r8), pointer :: repr_structuren_to_litter_patch (:,:) ! patch reproductive structure N to litter for prognostic crop (gN/m2/s) [patch, repr_structure_min:repr_structure_max] real(r8), pointer :: leafn_to_biofueln_patch (:) ! patch leaf N to biofuel N (gN/m2/s) real(r8), pointer :: livestemn_to_biofueln_patch (:) ! patch livestem N to biofuel N (gN/m2/s) + real(r8), pointer :: leafn_to_removedresiduen_patch (:) ! patch leaf N to removed residue N (gN/m2/s) + real(r8), pointer :: livestemn_to_removedresiduen_patch (:) ! patch livestem N to removed residue N (gN/m2/s) real(r8), pointer :: repr_grainn_to_seed_patch (:,:) ! patch grain N to seed for prognostic crop (gN/m2/s) [patch, repr_grain_min:repr_grain_max] + real(r8), pointer :: repr_grainn_to_seed_perharv_patch (:,:,:) ! grain N to seed for prognostic crop accumulated by harvest (gN/m2) [patch, harvest, repr_grain_min:repr_grain_max]. Not per-second because this variable represents an accumulation over each growing season, to be instantaneously at the end of each calendar year, to provide output that's easier to work with. + real(r8), pointer :: repr_grainn_to_seed_thisyr_patch (:,:) ! grain N to seed for prognostic crop accumulated this calendar year (gN/m2) [patch, repr_grain_min:repr_grain_max]. Not per-second because this variable represents an accumulation over an entire calendar year, to be saved instantaneously at the end of each calendar year, to provide output that's easier to work with. real(r8), pointer :: leafn_to_litter_patch (:) ! patch leaf N litterfall (gN/m2/s) real(r8), pointer :: leafn_to_retransn_patch (:) ! patch leaf N to retranslocated N pool (gN/m2/s) real(r8), pointer :: frootn_to_retransn_patch (:) ! patch fine root N to retranslocated N pool (gN/m2/s) @@ -202,6 +210,36 @@ module CNVegNitrogenFluxType real(r8), pointer :: dwt_livecrootn_to_cwdn_col (:,:) ! col (gN/m3/s) live coarse root to CWD due to landcover change real(r8), pointer :: dwt_deadcrootn_to_cwdn_col (:,:) ! col (gN/m3/s) dead coarse root to CWD due to landcover change + ! gross unrepresented landcover fluxes + real(r8), pointer :: gru_leafn_to_litter_patch (:) ! patch leaf N gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_leafn_storage_to_atm_patch (:) ! patch leaf N storage gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_leafn_xfer_to_atm_patch (:) ! patch leaf N transfer gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_frootn_to_litter_patch (:) ! patch fine root N gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_frootn_storage_to_atm_patch (:) ! patch fine root N storage gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_frootn_xfer_to_atm_patch (:) ! patch fine root N transfer gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_livestemn_to_atm_patch (:) ! patch live stem N gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_livestemn_storage_to_atm_patch (:) ! patch live stem N storage gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_livestemn_xfer_to_atm_patch (:) ! patch live stem N transfer gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_deadstemn_to_atm_patch (:) ! patch dead stem N gross unrepresented landcover change mortality to the atmosphere (gC/m2/s) + real(r8), pointer :: gru_deadstemn_storage_to_atm_patch (:) ! patch dead stem N storage gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_deadstemn_xfer_to_atm_patch (:) ! patch dead stem N transfer gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_livecrootn_to_litter_patch (:) ! patch live coarse root N gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_livecrootn_storage_to_atm_patch (:) ! patch live coarse root N storage gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_livecrootn_xfer_to_atm_patch (:) ! patch live coarse root N transfer gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_deadcrootn_to_litter_patch (:) ! patch dead coarse root N gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_deadcrootn_storage_to_atm_patch (:) ! patch dead coarse root N storage gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_deadcrootn_xfer_to_atm_patch (:) ! patch dead coarse root N transfer gross unrepresented landcover change mortality (gN/m2/s) + real(r8), pointer :: gru_retransn_to_litter_patch (:) ! patch retranslocated N pool gross unrepresented landcover change mortality (gN/m2/s) + + real(r8), pointer :: gru_conv_nflux_patch (:) ! (gN/m2/s) conversion N flux (immediate loss to atm) + real(r8), pointer :: gru_conv_nflux_col (:) ! (gN/m2/s) conversion N flux (immediate loss to atm) + real(r8), pointer :: gru_conv_nflux_grc (:) ! (gN/m2/s) gru_conv_nflux_patch summed to the gridcell-level + real(r8), pointer :: gru_wood_productn_gain_patch (:) ! patch (gN/m2/s) addition to wood product pools from gross unrepresented landcover change + real(r8), pointer :: gru_wood_productn_gain_col (:) ! column (gN/m2/s) addition to wood product pools from gross unrepresented landcover change + real(r8), pointer :: gru_wood_productn_gain_grc (:) ! gridcell (gN/m2/s) addition to wood product pools from gross unrepresented landcover change + real(r8), pointer :: gru_n_to_litr_n_col (:,:,:) ! col (gN/m3/s) N to litter due to gross unrepresented landcover change + real(r8), pointer :: gru_n_to_cwdn_col (:,:) ! col (gN/m3/s) N to CWD due to gross unrepresented landcover change + ! crop fluxes real(r8), pointer :: crop_seedn_to_leaf_patch (:) ! patch (gN/m2/s) seed source to leaf, for crops @@ -248,6 +286,7 @@ module CNVegNitrogenFluxType procedure , public :: Restart procedure , public :: SetValues procedure , public :: ZeroDWT + procedure , public :: ZeroGRU procedure , public :: Summary => Summary_nitrogenflux procedure , private :: InitAllocate procedure , private :: InitTransfer @@ -260,18 +299,21 @@ module CNVegNitrogenFluxType contains !------------------------------------------------------------------------ - subroutine Init(this, bounds) + subroutine Init(this, bounds, alloc_full_veg) class(cnveg_nitrogenflux_type) :: this - type(bounds_type), intent(in) :: bounds + type(bounds_type), intent(in) :: bounds + logical,intent(in) :: alloc_full_veg - call this%InitAllocate (bounds) - if(use_matrixcn)then - call this%InitTransfer () + call this%InitAllocate (bounds,alloc_full_veg) + if(alloc_full_veg)then + if(use_matrixcn)then + call this%InitTransfer () + end if + call this%InitHistory (bounds) + call this%InitCold (bounds) end if - call this%InitHistory (bounds) - call this%InitCold (bounds) - + end subroutine Init subroutine InitTransfer (this) @@ -291,14 +333,15 @@ subroutine InitTransfer (this) end subroutine InitTransfer !------------------------------------------------------------------------ - subroutine InitAllocate(this, bounds) + subroutine InitAllocate(this, bounds, alloc_full_veg) ! ! !DESCRIPTION: ! Initialize patch nitrogen flux ! ! !ARGUMENTS: class (cnveg_nitrogenflux_type) :: this - type(bounds_type) , intent(in) :: bounds + type(bounds_type) , intent(in) :: bounds + logical,intent(in) :: alloc_full_veg ! ! !LOCAL VARIABLES: integer :: begp,endp @@ -306,10 +349,16 @@ subroutine InitAllocate(this, bounds) integer :: begg,endg !------------------------------------------------------------------------ - begp = bounds%begp; endp = bounds%endp - begc = bounds%begc; endc = bounds%endc - begg = bounds%begg; endg = bounds%endg - + if(alloc_full_veg)then + begp = bounds%begp; endp = bounds%endp + begc = bounds%begc; endc = bounds%endc + begg = bounds%begg; endg = bounds%endg + else + begp = 0; endp = 0 + begc = 0; endc = 0 + begg = 0; endg = 0 + end if + allocate(this%m_leafn_to_litter_patch (begp:endp)) ; this%m_leafn_to_litter_patch (:) = nan allocate(this%m_frootn_to_litter_patch (begp:endp)) ; this%m_frootn_to_litter_patch (:) = nan allocate(this%m_leafn_storage_to_litter_patch (begp:endp)) ; this%m_leafn_storage_to_litter_patch (:) = nan @@ -433,13 +482,19 @@ subroutine InitAllocate(this, bounds) allocate(this%npool_to_reproductiven_storage_patch(begp:endp, nrepr)); this%npool_to_reproductiven_storage_patch (:,:) = nan allocate(this%livestemn_to_litter_patch (begp:endp)) ; this%livestemn_to_litter_patch (:) = nan allocate(this%repr_grainn_to_food_patch(begp:endp, repr_grain_min:repr_grain_max)) ; this%repr_grainn_to_food_patch (:,:) = nan + allocate(this%repr_grainn_to_food_perharv_patch(begp:endp, 1:mxharvests, repr_grain_min:repr_grain_max)) ; this%repr_grainn_to_food_perharv_patch (:,:,:) = nan + allocate(this%repr_grainn_to_food_thisyr_patch(begp:endp, repr_grain_min:repr_grain_max)) ; this%repr_grainn_to_food_thisyr_patch (:,:) = nan allocate(this%repr_structuren_to_cropprod_patch(begp:endp, repr_structure_min:repr_structure_max)) this%repr_structuren_to_cropprod_patch(:,:) = nan allocate(this%repr_structuren_to_litter_patch(begp:endp, repr_structure_min:repr_structure_max)) this%repr_structuren_to_litter_patch(:,:) = nan allocate(this%leafn_to_biofueln_patch (begp:endp)) ; this%leafn_to_biofueln_patch (:) = nan allocate(this%livestemn_to_biofueln_patch (begp:endp)) ; this%livestemn_to_biofueln_patch (:) = nan + allocate(this%leafn_to_removedresiduen_patch (begp:endp)) ; this%leafn_to_removedresiduen_patch (:) = nan + allocate(this%livestemn_to_removedresiduen_patch (begp:endp)) ; this%livestemn_to_removedresiduen_patch (:) = nan allocate(this%repr_grainn_to_seed_patch(begp:endp, repr_grain_min:repr_grain_max)) ; this%repr_grainn_to_seed_patch (:,:) = nan + allocate(this%repr_grainn_to_seed_perharv_patch(begp:endp, 1:mxharvests, repr_grain_min:repr_grain_max)) ; this%repr_grainn_to_seed_perharv_patch (:,:,:) = nan + allocate(this%repr_grainn_to_seed_thisyr_patch(begp:endp, repr_grain_min:repr_grain_max)) ; this%repr_grainn_to_seed_thisyr_patch (:,:) = nan allocate(this%reproductiven_xfer_to_reproductiven_patch(begp:endp, nrepr)) this%reproductiven_xfer_to_reproductiven_patch(:,:) = nan allocate(this%reproductiven_storage_to_xfer_patch(begp:endp, nrepr)) ; this%reproductiven_storage_to_xfer_patch (:,:) = nan @@ -469,6 +524,35 @@ subroutine InitAllocate(this, bounds) allocate(this%dwt_livecrootn_to_cwdn_col (begc:endc,1:nlevdecomp_full)) ; this%dwt_livecrootn_to_cwdn_col (:,:) = nan allocate(this%dwt_deadcrootn_to_cwdn_col (begc:endc,1:nlevdecomp_full)) ; this%dwt_deadcrootn_to_cwdn_col (:,:) = nan + allocate(this%gru_leafn_to_litter_patch (begp:endp)) ; this%gru_leafn_to_litter_patch (:) = nan + allocate(this%gru_leafn_storage_to_atm_patch (begp:endp)) ; this%gru_leafn_storage_to_atm_patch (:) = nan + allocate(this%gru_leafn_xfer_to_atm_patch (begp:endp)) ; this%gru_leafn_xfer_to_atm_patch (:) = nan + allocate(this%gru_frootn_to_litter_patch (begp:endp)) ; this%gru_frootn_to_litter_patch (:) = nan + allocate(this%gru_frootn_storage_to_atm_patch (begp:endp)) ; this%gru_frootn_storage_to_atm_patch (:) = nan + allocate(this%gru_frootn_xfer_to_atm_patch (begp:endp)) ; this%gru_frootn_xfer_to_atm_patch (:) = nan + allocate(this%gru_livestemn_to_atm_patch (begp:endp)) ; this%gru_livestemn_to_atm_patch (:) = nan + allocate(this%gru_livestemn_storage_to_atm_patch (begp:endp)) ; this%gru_livestemn_storage_to_atm_patch (:) = nan + allocate(this%gru_livestemn_xfer_to_atm_patch (begp:endp)) ; this%gru_livestemn_xfer_to_atm_patch (:) = nan + allocate(this%gru_deadstemn_to_atm_patch (begp:endp)) ; this%gru_deadstemn_to_atm_patch (:) = nan + allocate(this%gru_deadstemn_storage_to_atm_patch (begp:endp)) ; this%gru_deadstemn_storage_to_atm_patch (:) = nan + allocate(this%gru_deadstemn_xfer_to_atm_patch (begp:endp)) ; this%gru_deadstemn_xfer_to_atm_patch (:) = nan + allocate(this%gru_livecrootn_storage_to_atm_patch (begp:endp)) ; this%gru_livecrootn_storage_to_atm_patch (:) = nan + allocate(this%gru_livecrootn_xfer_to_atm_patch (begp:endp)) ; this%gru_livecrootn_xfer_to_atm_patch (:) = nan + allocate(this%gru_livecrootn_to_litter_patch (begp:endp)) ; this%gru_livecrootn_to_litter_patch (:) = nan + allocate(this%gru_deadcrootn_storage_to_atm_patch (begp:endp)) ; this%gru_deadcrootn_storage_to_atm_patch (:) = nan + allocate(this%gru_deadcrootn_xfer_to_atm_patch (begp:endp)) ; this%gru_deadcrootn_xfer_to_atm_patch (:) = nan + allocate(this%gru_deadcrootn_to_litter_patch (begp:endp)) ; this%gru_deadcrootn_to_litter_patch (:) = nan + allocate(this%gru_retransn_to_litter_patch (begp:endp)) ; this%gru_retransn_to_litter_patch (:) = nan + + allocate(this%gru_conv_nflux_patch (begp:endp)) ; this%gru_conv_nflux_patch (:) =nan + allocate(this%gru_conv_nflux_col (begc:endc)) ; this%gru_conv_nflux_col (:) =nan + allocate(this%gru_conv_nflux_grc (begg:endg)) ; this%gru_conv_nflux_grc (:) =nan + allocate(this%gru_wood_productn_gain_patch (begp:endp)) ; this%gru_wood_productn_gain_patch (:) =nan + allocate(this%gru_wood_productn_gain_col (begc:endc)) ; this%gru_wood_productn_gain_col (:) =nan + allocate(this%gru_wood_productn_gain_grc (begg:endg)) ; this%gru_wood_productn_gain_grc (:) =nan + allocate(this%gru_n_to_litr_n_col (begc:endc,1:nlevdecomp_full,1:i_litr_max)); this%gru_n_to_litr_n_col(:,:,:)=nan + allocate(this%gru_n_to_cwdn_col (begc:endc,1:nlevdecomp_full)); this%gru_n_to_cwdn_col (:,:)=nan + allocate(this%crop_seedn_to_leaf_patch (begp:endp)) ; this%crop_seedn_to_leaf_patch (:) = nan allocate(this%m_decomp_npools_to_fire_vr_col (begc:endc,1:nlevdecomp_full,1:ndecomp_pools)) @@ -963,6 +1047,71 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='NFERTILIZATION', units='gN/m^2/s', & avgflag='A', long_name='fertilizer added', & ptr_patch=this%fert_patch) + + this%repr_grainn_to_food_patch(begp:endp,:) = spval + this%repr_grainn_to_food_perharv_patch(begp:endp,:,:) = spval + this%repr_grainn_to_food_thisyr_patch(begp:endp,:) = spval + this%repr_grainn_to_seed_patch(begp:endp,:) = spval + this%repr_grainn_to_seed_perharv_patch(begp:endp,:,:) = spval + this%repr_grainn_to_seed_thisyr_patch(begp:endp,:) = spval + do k = repr_grain_min, repr_grain_max + data1dptr => this%repr_grainn_to_food_patch(:,k) + call hist_addfld1d ( & + ! e.g., GRAINN_TO_FOOD + fname=get_repr_hist_fname(k)//'N_TO_FOOD', & + units='gN/m^2/s', & + avgflag='A', & + long_name=get_repr_longname(k)//' N to food (not scientifically supported)', & + ptr_patch=data1dptr, & + default='inactive') + data1dptr => this%repr_grainn_to_seed_patch(:,k) + call hist_addfld1d ( & + ! e.g., GRAINN_TO_SEED + fname=get_repr_hist_fname(k)//'N_TO_SEED', & + units='gN/m^2/s', & + avgflag='A', & + long_name=get_repr_longname(k)//' N to seed (not scientifically supported)', & + ptr_patch=data1dptr, & + default='inactive') + data2dptr => this%repr_grainn_to_food_perharv_patch(:,:,k) + call hist_addfld2d ( & + ! e.g., GRAINN_TO_FOOD_PERHARV + fname=get_repr_hist_fname(k)//'N_TO_FOOD_PERHARV', & + units='gN/m^2', & + type2d='mxharvests', & + avgflag='I', & + long_name=get_repr_longname(k)//' N to food per harvest; should only be output annually (not scientifically supported)', & + ptr_patch=data2dptr, & + default='inactive') + data1dptr => this%repr_grainn_to_food_thisyr_patch(:,k) + call hist_addfld1d ( & + ! e.g., GRAINN_TO_FOOD_ANN + fname=get_repr_hist_fname(k)//'N_TO_FOOD_ANN', & + units='gN/m^2', & + avgflag='I', & + long_name=get_repr_longname(k)//' N to food harvested per calendar year; should only be output annually (not scientifically supported)', & + ptr_patch=data1dptr, & + default='inactive') + data2dptr => this%repr_grainn_to_seed_perharv_patch(:,:,k) + call hist_addfld2d ( & + ! e.g., GRAINN_TO_SEED_PERHARV + fname=get_repr_hist_fname(k)//'N_TO_SEED_PERHARV', & + units='gN/m^2', & + type2d='mxharvests', & + avgflag='I', & + long_name=get_repr_longname(k)//' N to seed per harvest; should only be output annually (not scientifically supported)', & + ptr_patch=data2dptr, & + default='inactive') + data1dptr => this%repr_grainn_to_seed_thisyr_patch(:,k) + call hist_addfld1d ( & + ! e.g., GRAINN_TO_SEED_ANN + fname=get_repr_hist_fname(k)//'N_TO_SEED_ANN', & + units='gN/m^2', & + avgflag='I', & + long_name=get_repr_longname(k)//' N to seed harvested per calendar year; should only be output annually (not scientifically supported)', & + ptr_patch=data1dptr, & + default='inactive') + end do end if if (use_crop .and. .not. use_fun) then @@ -987,7 +1136,7 @@ subroutine InitHistory(this, bounds) if ( decomp_cascade_con%is_litter(k) .or. decomp_cascade_con%is_cwd(k) ) then this%m_decomp_npools_to_fire_col(begc:endc,k) = spval data1dptr => this%m_decomp_npools_to_fire_col(:,k) - fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'N_TO_FIRE' + fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_N_TO_FIRE' longname = trim(decomp_cascade_con%decomp_pool_name_long(k))//' N fire loss' call hist_addfld1d (fname=fieldname, units='gN/m^2', & avgflag='A', long_name=longname, & @@ -996,7 +1145,7 @@ subroutine InitHistory(this, bounds) if ( nlevdecomp_full > 1 ) then this%m_decomp_npools_to_fire_vr_col(begc:endc,:,k) = spval data2dptr => this%m_decomp_npools_to_fire_vr_col(:,:,k) - fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'N_TO_FIRE'//trim(vr_suffix) + fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_N_TO_FIRE'//trim(vr_suffix) longname = trim(decomp_cascade_con%decomp_pool_name_long(k))//' N fire loss' call hist_addfld_decomp (fname=fieldname, units='gN/m^3', type2d='levdcmp', & avgflag='A', long_name=longname, & @@ -1068,6 +1217,18 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='dead coarse root to CWD due to landcover change', & ptr_col=this%dwt_deadcrootn_to_cwdn_col, default='inactive') + if ( get_do_grossunrep() )then + this%gru_conv_nflux_patch(begp:endp) = spval + call hist_addfld1d (fname='GRU_CONV_NFLUX', units='gN/m^2/s', & + avgflag='A', long_name='gross unrepresented conversion N flux (immediate loss to atm) (0 at all times except first timestep of year)', & + ptr_patch=this%gru_conv_nflux_patch) + + this%gru_wood_productn_gain_patch(begp:endp) = spval + call hist_addfld1d (fname='GRU_WOODPRODN_GAIN', units='gN/m^2/s', & + avgflag='A', long_name='gross unrepresented landcover change driven addition to wood product nitrogen pools (0 at all times except first timestep of year)', & + ptr_patch=this%gru_wood_productn_gain_patch) + end if + this%crop_seedn_to_leaf_patch(begp:endp) = spval call hist_addfld1d (fname='CROP_SEEDN_TO_LEAF', units='gN/m^2/s', & avgflag='A', long_name='crop seed source to leaf', & @@ -1350,6 +1511,7 @@ subroutine Restart (this, bounds, ncid, flag ) logical :: readvar ! determine if variable is on initial file character(len=256) :: varname real(r8), pointer :: data1dptr(:) ! temp. pointer for slicing larger arrays + real(r8), pointer :: data2dptr(:,:) ! temp. pointer for slicing larger arrays !------------------------------------------------------------------------ if (use_crop) then @@ -1376,6 +1538,60 @@ subroutine Restart (this, bounds, ncid, flag ) units='gN/m2/s', & interpinic_flag='interp', readvar=readvar, data=data1dptr) end do + + ! Read or write variable(s) with mxharvests dimension + ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2022-06-10) See note in CallRestartvarDimOK() + if (CallRestartvarDimOK(ncid, flag, 'mxharvests')) then + do k = repr_grain_min, repr_grain_max + data2dptr => this%repr_grainn_to_food_perharv_patch(:,:,k) + ! e.g., grainn_to_food_perharv + varname = get_repr_rest_fname(k)//'n_to_food_perharv' + call restartvar(ncid=ncid, flag=flag, varname=varname, & + xtype=ncd_double, & + dim1name='pft', & + dim2name='mxharvests', & + switchdim=.true., & + long_name=get_repr_longname(k)//' N to food per harvest; should only be output annually', & + units='gN/m2', & + readvar=readvar, & + scale_by_thickness=.false., & + interpinic_flag='interp', data=data2dptr) + data2dptr => this%repr_grainn_to_seed_perharv_patch(:,:,k) + ! e.g., grainn_to_seed_perharv + varname = get_repr_rest_fname(k)//'n_to_seed_perharv' + call restartvar(ncid=ncid, flag=flag, varname=varname, & + xtype=ncd_double, & + dim1name='pft', & + dim2name='mxharvests', & + switchdim=.true., & + long_name=get_repr_longname(k)//' N to seed per harvest; should only be output annually', & + units='gN/m2', & + readvar=readvar, & + scale_by_thickness=.false., & + interpinic_flag='interp', data=data2dptr) + end do + end if + + do k = repr_grain_min, repr_grain_max + data1dptr => this%repr_grainn_to_food_thisyr_patch(:,k) + ! e.g., grainn_to_food_thisyr + varname = get_repr_rest_fname(k)//'n_to_food_thisyr' + call restartvar(ncid=ncid, flag=flag, varname=varname, & + xtype=ncd_double, & + dim1name='pft', & + long_name=get_repr_longname(k)//' N to food per calendar year; should only be output annually', & + units='gN/m2', & + interpinic_flag='interp', readvar=readvar, data=data1dptr) + data1dptr => this%repr_grainn_to_seed_thisyr_patch(:,k) + ! e.g., grainn_to_seed_thisyr + varname = get_repr_rest_fname(k)//'n_to_seed_thisyr' + call restartvar(ncid=ncid, flag=flag, varname=varname, & + xtype=ncd_double, & + dim1name='pft', & + long_name=get_repr_longname(k)//' N to seed per calendar year; should only be output annually', & + units='gN/m2', & + interpinic_flag='interp', readvar=readvar, data=data1dptr) + end do end if if (use_crop) then @@ -1639,6 +1855,26 @@ subroutine SetValues ( this, nvegnpool, & this%hrv_deadcrootn_to_litter_patch(i) = value_patch this%hrv_retransn_to_litter_patch(i) = value_patch + this%gru_leafn_to_litter_patch(i) = value_patch + this%gru_leafn_storage_to_atm_patch(i) = value_patch + this%gru_leafn_xfer_to_atm_patch(i) = value_patch + this%gru_frootn_to_litter_patch(i) = value_patch + this%gru_frootn_storage_to_atm_patch(i) = value_patch + this%gru_frootn_xfer_to_atm_patch(i) = value_patch + this%gru_livestemn_to_atm_patch(i) = value_patch + this%gru_livestemn_storage_to_atm_patch(i) = value_patch + this%gru_livestemn_xfer_to_atm_patch(i) = value_patch + this%gru_deadstemn_to_atm_patch(i) = value_patch + this%gru_deadstemn_storage_to_atm_patch(i) = value_patch + this%gru_deadstemn_xfer_to_atm_patch(i) = value_patch + this%gru_livecrootn_to_litter_patch(i) = value_patch + this%gru_livecrootn_storage_to_atm_patch(i) = value_patch + this%gru_livecrootn_xfer_to_atm_patch(i) = value_patch + this%gru_deadcrootn_storage_to_atm_patch(i) = value_patch + this%gru_deadcrootn_xfer_to_atm_patch(i) = value_patch + this%gru_deadcrootn_to_litter_patch(i) = value_patch + this%gru_retransn_to_litter_patch(i) = value_patch + this%m_leafn_to_fire_patch(i) = value_patch this%m_leafn_storage_to_fire_patch(i) = value_patch this%m_leafn_xfer_to_fire_patch(i) = value_patch @@ -1659,6 +1895,8 @@ subroutine SetValues ( this, nvegnpool, & this%m_deadcrootn_xfer_to_fire_patch(i) = value_patch this%m_retransn_to_fire_patch(i) = value_patch + this%gru_conv_nflux_patch(i) = value_patch + this%gru_wood_productn_gain_patch(i) = value_patch this%m_leafn_to_litter_fire_patch(i) = value_patch this%m_leafn_storage_to_litter_fire_patch(i) = value_patch @@ -1730,6 +1968,8 @@ subroutine SetValues ( this, nvegnpool, & this%livestemn_to_litter_patch(i) = value_patch this%leafn_to_biofueln_patch(i) = value_patch this%livestemn_to_biofueln_patch(i) = value_patch + this%leafn_to_removedresiduen_patch(i) = value_patch + this%livestemn_to_removedresiduen_patch(i) = value_patch this%soyfixn_patch(i) = value_patch this%frootn_to_retransn_patch(i) = value_patch end do @@ -1770,11 +2010,16 @@ subroutine SetValues ( this, nvegnpool, & this%gap_mortality_n_to_litr_n_col(i,j,k) = value_column this%harvest_n_to_litr_n_col(i,j,k) = value_column this%m_n_to_litr_fire_col(i,j,k) = value_column + ! gross unrepresented landcover change + this%gru_n_to_litr_n_col(i,j,k) = value_column end do this%gap_mortality_n_to_cwdn_col(i,j) = value_column this%fire_mortality_n_to_cwdn_col(i,j) = value_column this%harvest_n_to_cwdn_col(i,j) = value_column + + ! gross unrepresented landcover change + this%gru_n_to_cwdn_col(i,j) = value_column end do end do @@ -1787,6 +2032,8 @@ subroutine SetValues ( this, nvegnpool, & ! Zero p2c column fluxes this%fire_nloss_col(i) = value_column this%wood_harvestn_col(i) = value_column + this%gru_conv_nflux_col(i) = value_column + this%gru_wood_productn_gain_col(i) = value_column end do do k = 1, ndecomp_pools @@ -1844,13 +2091,34 @@ subroutine ZeroDwt( this, bounds ) end subroutine ZeroDwt + !----------------------------------------------------------------------- + subroutine ZeroGru( this, bounds ) + ! + ! !DESCRIPTION + ! Initialize flux variables needed for dynamic land use. + ! + ! !ARGUMENTS: + class(cnveg_nitrogenflux_type) :: this + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + integer :: g ! indices + !----------------------------------------------------------------------- + + do g = bounds%begg, bounds%endg + this%gru_conv_nflux_grc(g) = 0._r8 + this%gru_wood_productn_gain_grc(g) = 0._r8 + end do + + end subroutine ZeroGru + !----------------------------------------------------------------------- subroutine Summary_nitrogenflux(this, bounds, num_soilc, filter_soilc, num_soilp, filter_soilp) ! ! !USES: use clm_varpar , only: nlevdecomp,ndecomp_cascade_transitions,ndecomp_pools use clm_varctl , only: use_nitrif_denitrif - use subgridAveMod , only: p2c + use subgridAveMod , only: p2c, c2g ! ! !ARGUMENTS: class (cnveg_nitrogenflux_type) :: this @@ -1897,12 +2165,45 @@ subroutine Summary_nitrogenflux(this, bounds, num_soilc, filter_soilc, num_soilp this%m_deadcrootn_xfer_to_fire_patch(p) + & this%m_retransn_to_fire_patch(p) + ! (Gross Unrepresented Landcover Change Conversion Flux) - Direct Veg N Loss to Atmosphere + this%gru_conv_nflux_patch(p) = & + this%gru_livestemn_to_atm_patch(p) + & + this%gru_deadstemn_to_atm_patch(p) + & + this%gru_leafn_storage_to_atm_patch(p) + & + this%gru_frootn_storage_to_atm_patch(p) + & + this%gru_livestemn_storage_to_atm_patch(p) + & + this%gru_deadstemn_storage_to_atm_patch(p) + & + this%gru_livecrootn_storage_to_atm_patch(p) + & + this%gru_deadcrootn_storage_to_atm_patch(p) + & + this%gru_leafn_xfer_to_atm_patch(p) + & + this%gru_frootn_xfer_to_atm_patch(p) + & + this%gru_livestemn_xfer_to_atm_patch(p) + & + this%gru_deadstemn_xfer_to_atm_patch(p) + & + this%gru_livecrootn_xfer_to_atm_patch(p) + & + this%gru_deadcrootn_xfer_to_atm_patch(p) + end do call p2c(bounds, num_soilc, filter_soilc, & this%fire_nloss_patch(bounds%begp:bounds%endp), & this%fire_nloss_p2c_col(bounds%begc:bounds%endc)) + call p2c(bounds, num_soilc, filter_soilc, & + this%gru_conv_nflux_patch(bounds%begp:bounds%endp), & + this%gru_conv_nflux_col(bounds%begc:bounds%endc)) + + call c2g( bounds = bounds, & + carr = this%gru_conv_nflux_col(bounds%begc:bounds%endc), & + garr = this%gru_conv_nflux_grc(bounds%begg:bounds%endg), & + c2l_scale_type = 'unity', & + l2g_scale_type = 'unity') + + call c2g( bounds = bounds, & + carr = this%gru_wood_productn_gain_col(bounds%begc:bounds%endc), & + garr = this%gru_wood_productn_gain_grc(bounds%begg:bounds%endg), & + c2l_scale_type = 'unity', & + l2g_scale_type = 'unity') + ! vertically integrate column-level fire N losses do k = 1, ndecomp_pools do j = 1, nlevdecomp diff --git a/src/biogeochem/CNVegNitrogenStateType.F90 b/src/biogeochem/CNVegNitrogenStateType.F90 index f09311e518..42bc47e102 100644 --- a/src/biogeochem/CNVegNitrogenStateType.F90 +++ b/src/biogeochem/CNVegNitrogenStateType.F90 @@ -69,9 +69,7 @@ module CNVegNitrogenStateType real(r8), pointer :: totvegn_col (:) ! (gN/m2) total vegetation nitrogen (p2c) real(r8), pointer :: totn_patch (:) ! (gN/m2) total patch-level nitrogen real(r8), pointer :: totn_p2c_col (:) ! (gN/m2) totn_patch averaged to col - real(r8), pointer :: totn_col (:) ! (gN/m2) total column nitrogen, incl veg - real(r8), pointer :: totecosysn_col (:) ! (gN/m2) total ecosystem nitrogen, incl veg - real(r8), pointer :: totn_grc (:) ! (gN/m2) total gridcell nitrogen + ! acc spinup for matrix solution @@ -98,40 +96,49 @@ module CNVegNitrogenStateType !------------------------------------------------------------------------ subroutine Init(this, bounds, & - leafc_patch, leafc_storage_patch, frootc_patch, frootc_storage_patch, deadstemc_patch) + leafc_patch, leafc_storage_patch, frootc_patch, frootc_storage_patch, & + deadstemc_patch, alloc_full_veg) class(cnveg_nitrogenstate_type) :: this type(bounds_type) , intent(in) :: bounds - real(r8) , intent(in) :: leafc_patch (bounds%begp:) - real(r8) , intent(in) :: leafc_storage_patch (bounds%begp:) - real(r8) , intent(in) :: frootc_patch (bounds%begp:) - real(r8) , intent(in) :: frootc_storage_patch (bounds%begp:) - real(r8) , intent(in) :: deadstemc_patch (bounds%begp:) - - call this%InitAllocate (bounds ) - call this%InitHistory (bounds) - call this%InitCold ( bounds, & - leafc_patch, leafc_storage_patch, frootc_patch, frootc_storage_patch, deadstemc_patch) - + real(r8) , intent(in) :: leafc_patch (:) !(begp:) + real(r8) , intent(in) :: leafc_storage_patch (:) !(begp:) + real(r8) , intent(in) :: frootc_patch (:) !(begp:) + real(r8) , intent(in) :: frootc_storage_patch(:) !(begp:) + real(r8) , intent(in) :: deadstemc_patch (:) !(begp:) + logical , intent(in) :: alloc_full_veg + + call this%InitAllocate (bounds, alloc_full_veg) + if(alloc_full_veg) then + call this%InitHistory (bounds) + call this%InitCold ( bounds, & + leafc_patch, leafc_storage_patch, frootc_patch, frootc_storage_patch, deadstemc_patch) + end if end subroutine Init !------------------------------------------------------------------------ - subroutine InitAllocate(this, bounds) + subroutine InitAllocate(this, bounds, alloc_full_veg) ! ! !ARGUMENTS: class (cnveg_nitrogenstate_type) :: this - type(bounds_type) , intent(in) :: bounds + type(bounds_type) , intent(in) :: bounds + logical,intent(in) :: alloc_full_veg ! ! !LOCAL VARIABLES: integer :: begp,endp integer :: begc,endc integer :: begg,endg !------------------------------------------------------------------------ - - begp = bounds%begp; endp = bounds%endp - begc = bounds%begc; endc = bounds%endc - begg = bounds%begg; endg = bounds%endg - + if(alloc_full_veg) then + begp = bounds%begp; endp = bounds%endp + begc = bounds%begc; endc = bounds%endc + begg = bounds%begg; endg = bounds%endg + else + begp = 0; endp = 0 + begc = 0; endc = 0 + begg = 0; endg = 0 + end if + allocate(this%reproductiven_patch (begp:endp, nrepr)) ; this%reproductiven_patch (:,:) = nan allocate(this%reproductiven_storage_patch (begp:endp, nrepr)) ; this%reproductiven_storage_patch (:,:) = nan allocate(this%reproductiven_xfer_patch (begp:endp, nrepr)) ; this%reproductiven_xfer_patch (:,:) = nan @@ -167,9 +174,7 @@ subroutine InitAllocate(this, bounds) allocate(this%seedn_grc (begg:endg)) ; this%seedn_grc (:) = nan allocate(this%totvegn_col (begc:endc)) ; this%totvegn_col (:) = nan allocate(this%totn_p2c_col (begc:endc)) ; this%totn_p2c_col (:) = nan - allocate(this%totn_col (begc:endc)) ; this%totn_col (:) = nan - allocate(this%totecosysn_col (begc:endc)) ; this%totecosysn_col (:) = nan - allocate(this%totn_grc (begg:endg)) ; this%totn_grc (:) = nan + ! Matrix solution allocations if ( use_matrixcn )then @@ -376,15 +381,7 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='pool for seeding new PFTs via dynamic landcover', & ptr_gcell=this%seedn_grc) - this%totecosysn_col(begc:endc) = spval - call hist_addfld1d (fname='TOTECOSYSN', units='gN/m^2', & - avgflag='A', long_name='total ecosystem N, excluding product pools', & - ptr_col=this%totecosysn_col) - this%totn_col(begc:endc) = spval - call hist_addfld1d (fname='TOTCOLN', units='gN/m^2', & - avgflag='A', long_name='total column-level N, excluding product pools', & - ptr_col=this%totn_col) end subroutine InitHistory @@ -558,16 +555,13 @@ subroutine InitCold(this, bounds, & l = col%landunit(c) if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then ! total nitrogen pools - this%totecosysn_col(c) = 0._r8 this%totn_p2c_col(c) = 0._r8 - this%totn_col(c) = 0._r8 end if end do do g = bounds%begg, bounds%endg this%seedn_grc(g) = 0._r8 - this%totn_grc(g) = 0._r8 end do ! now loop through special filters and explicitly set the variables that @@ -1024,11 +1018,8 @@ subroutine SetValues ( this, & do fi = 1,num_column i = filter_column(fi) - - this%totecosysn_col(i) = value_column this%totvegn_col(i) = value_column this%totn_p2c_col(i) = value_column - this%totn_col(i) = value_column end do end subroutine SetValues @@ -1057,24 +1048,20 @@ subroutine ZeroDwt( this, bounds ) end subroutine ZeroDwt !----------------------------------------------------------------------- - subroutine Summary_nitrogenstate(this, bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp,& - soilbiogeochem_nitrogenstate_inst) + subroutine Summary_nitrogenstate(this, bounds, num_soilc, filter_soilc, num_soilp, filter_soilp) ! ! !USES: use subgridAveMod, only : p2c - use SoilBiogeochemNitrogenStateType, only : soilbiogeochem_nitrogenstate_type + ! ! !ARGUMENTS: class(cnveg_nitrogenstate_type) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_allc ! number of columns in allc filter - integer , intent(in) :: filter_allc(:) ! filter for all active columns integer , intent(in) :: num_soilc ! number of soil columns in filter integer , intent(in) :: filter_soilc(:) ! filter for soil columns integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches - type(soilbiogeochem_nitrogenstate_type) , intent(in) :: soilbiogeochem_nitrogenstate_inst + ! ! !LOCAL VARIABLES: integer :: c,p,j,k,l ! indices @@ -1148,38 +1135,15 @@ subroutine Summary_nitrogenstate(this, bounds, num_allc, filter_allc, & ! -------------------------------------------- ! column level summary ! -------------------------------------------- - - call p2c(bounds, num_soilc, filter_soilc, & - this%totvegn_patch(bounds%begp:bounds%endp), & - this%totvegn_col(bounds%begc:bounds%endc)) - - call p2c(bounds, num_soilc, filter_soilc, & - this%totn_patch(bounds%begp:bounds%endp), & - this%totn_p2c_col(bounds%begc:bounds%endc)) - - do fc = 1,num_allc - c = filter_allc(fc) - - ! total ecosystem nitrogen, including veg (TOTECOSYSN) - this%totecosysn_col(c) = & - soilbiogeochem_nitrogenstate_inst%cwdn_col(c) + & - soilbiogeochem_nitrogenstate_inst%totlitn_col(c) + & - soilbiogeochem_nitrogenstate_inst%totmicn_col(c) + & - soilbiogeochem_nitrogenstate_inst%totsomn_col(c) + & - soilbiogeochem_nitrogenstate_inst%sminn_col(c) + & - this%totvegn_col(c) - - ! total column nitrogen, including patch (TOTCOLN) - - this%totn_col(c) = this%totn_p2c_col(c) + & - soilbiogeochem_nitrogenstate_inst%cwdn_col(c) + & - soilbiogeochem_nitrogenstate_inst%totlitn_col(c) + & - soilbiogeochem_nitrogenstate_inst%totmicn_col(c) + & - soilbiogeochem_nitrogenstate_inst%totsomn_col(c) + & - soilbiogeochem_nitrogenstate_inst%sminn_col(c) + & - soilbiogeochem_nitrogenstate_inst%ntrunc_col(c) - - end do + if(num_soilp>0)then + call p2c(bounds, num_soilc, filter_soilc, & + this%totvegn_patch(bounds%begp:bounds%endp), & + this%totvegn_col(bounds%begc:bounds%endc)) + + call p2c(bounds, num_soilc, filter_soilc, & + this%totn_patch(bounds%begp:bounds%endp), & + this%totn_p2c_col(bounds%begc:bounds%endc)) + end if end subroutine Summary_nitrogenstate diff --git a/src/biogeochem/CNVegStateType.F90 b/src/biogeochem/CNVegStateType.F90 index e30bb9c7e7..c286c0344f 100644 --- a/src/biogeochem/CNVegStateType.F90 +++ b/src/biogeochem/CNVegStateType.F90 @@ -35,6 +35,7 @@ module CNVegStateType real(r8) , pointer :: hdidx_patch (:) ! patch cold hardening index? real(r8) , pointer :: cumvd_patch (:) ! patch cumulative vernalization d?ependence? real(r8) , pointer :: gddmaturity_patch (:) ! patch growing degree days (gdd) needed to harvest (ddays) + real(r8) , pointer :: gddmaturity_thisyr (:,:) ! all at-harvest values of the above for this patch this year (ddays) [patch, mxharvests] real(r8) , pointer :: huileaf_patch (:) ! patch heat unit index needed from planting to leaf emergence real(r8) , pointer :: huigrain_patch (:) ! patch heat unit index needed to reach vegetative maturity real(r8) , pointer :: aleafi_patch (:) ! patch saved leaf allocation coefficient from phase 2 @@ -55,7 +56,8 @@ module CNVegStateType real(r8) , pointer :: htmx_patch (:) ! patch max hgt attained by a crop during yr (m) integer , pointer :: peaklai_patch (:) ! patch 1: max allowed lai; 0: not at max - integer , pointer :: idop_patch (:) ! patch date of planting + integer , pointer :: idop_patch (:) ! patch date of planting (day of year) + integer , pointer :: iyop_patch (:) ! patch year of planting real(r8) , pointer :: lgdp_col (:) ! col gdp limitation factor for fire occurrence (0-1) real(r8) , pointer :: lgdp1_col (:) ! col gdp limitation factor for fire spreading (0-1) @@ -128,31 +130,36 @@ module CNVegStateType contains !------------------------------------------------------------------------ - subroutine Init(this, bounds) + subroutine Init(this, bounds, alloc_full_veg) class(cnveg_state_type) :: this type(bounds_type), intent(in) :: bounds + logical,intent(in) :: alloc_full_veg ! Total number of bgc patches on proc (non-fates) - call this%InitAllocate ( bounds ) + call this%InitAllocate ( bounds, alloc_full_veg) if (use_cn) then call this%InitHistory ( bounds ) end if - call this%InitCold ( bounds ) - + if(alloc_full_veg) then !This is true if not use_fates_bgc + call this%InitCold ( bounds ) + end if + end subroutine Init !------------------------------------------------------------------------ - subroutine InitAllocate(this, bounds) + subroutine InitAllocate(this, bounds, alloc_full_veg) ! ! !DESCRIPTION: ! Initialize module data structure ! ! !USES: use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) + use clm_varpar, only : mxsowings, mxharvests ! ! !ARGUMENTS: class(cnveg_state_type) :: this type(bounds_type), intent(in) :: bounds + logical, intent(in) :: alloc_full_veg ! Total number of bgc patches on proc (non-fates) ! ! !LOCAL VARIABLES: integer :: begp, endp @@ -160,9 +167,15 @@ subroutine InitAllocate(this, bounds) logical :: allows_non_annual_delta !------------------------------------------------------------------------ - begp = bounds%begp; endp= bounds%endp - begc = bounds%begc; endc= bounds%endc - + if(alloc_full_veg)then + begp = bounds%begp; endp= bounds%endp + begc = bounds%begc; endc= bounds%endc + else + begp = 0;endp = 0 + begc = 0;endc = 0 + end if + + ! Note that we set allows_non_annual_delta to false because we expect land cover ! change to be applied entirely at the start of the year. Currently the fire code ! appears to assume that the land cover change rate is constant throughout the year, @@ -207,6 +220,7 @@ subroutine InitAllocate(this, bounds) allocate(this%hdidx_patch (begp:endp)) ; this%hdidx_patch (:) = nan allocate(this%cumvd_patch (begp:endp)) ; this%cumvd_patch (:) = nan allocate(this%gddmaturity_patch (begp:endp)) ; this%gddmaturity_patch (:) = spval + allocate(this%gddmaturity_thisyr (begp:endp,1:mxharvests)) ; this%gddmaturity_thisyr (:,:) = spval allocate(this%huileaf_patch (begp:endp)) ; this%huileaf_patch (:) = nan allocate(this%huigrain_patch (begp:endp)) ; this%huigrain_patch (:) = 0.0_r8 allocate(this%aleafi_patch (begp:endp)) ; this%aleafi_patch (:) = nan @@ -228,6 +242,7 @@ subroutine InitAllocate(this, bounds) allocate(this%peaklai_patch (begp:endp)) ; this%peaklai_patch (:) = 0 allocate(this%idop_patch (begp:endp)) ; this%idop_patch (:) = huge(1) + allocate(this%iyop_patch (begp:endp)) ; this%iyop_patch (:) = ispval allocate(this%lgdp_col (begc:endc)) ; allocate(this%lgdp1_col (begc:endc)) ; @@ -309,10 +324,18 @@ subroutine InitHistory(this, bounds) begc = bounds%begc; endc= bounds%endc if ( use_crop) then + ! Daily this%gddmaturity_patch(begp:endp) = spval call hist_addfld1d (fname='GDDHARV', units='ddays', & avgflag='A', long_name='Growing degree days (gdd) needed to harvest', & ptr_patch=this%gddmaturity_patch, default='inactive') + + ! Per harvest + this%gddmaturity_thisyr(begp:endp,:) = spval + call hist_addfld2d (fname='GDDHARV_PERHARV', units='ddays', type2d='mxharvests', & + avgflag='I', long_name='Growing degree days (gdd) needed to harvest; should only be output annually', & + ptr_patch=this%gddmaturity_thisyr, default='inactive') + end if this%lfc2_col(begc:endc) = spval @@ -482,7 +505,7 @@ subroutine InitHistory(this, bounds) end subroutine InitHistory !----------------------------------------------------------------------- - subroutine initCold(this, bounds) + subroutine InitCold(this, bounds) ! ! !USES: ! @@ -602,7 +625,7 @@ subroutine initCold(this, bounds) this%lfc2_col(c) = 0._r8 end do - end subroutine initCold + end subroutine InitCold !------------------------------------------------------------------------ subroutine Restart(this, bounds, ncid, flag, cnveg_carbonstate, & @@ -842,6 +865,16 @@ subroutine Restart(this, bounds, ncid, flag, cnveg_carbonstate, & call restartvar(ncid=ncid, flag=flag, varname='grain_flag', xtype=ncd_double, & dim1name='pft', long_name='', units='', & interpinic_flag='interp', readvar=readvar, data=this%grain_flag_patch) + + ! Read or write variable(s) with mxharvests dimension + ! BACKWARDS_COMPATIBILITY(ssr, 2022-03-31) See note in CallRestartvarDimOK() + if (CallRestartvarDimOK(ncid, flag, 'mxharvests')) then + call restartvar(ncid=ncid, flag=flag, varname='gddmaturity_thisyr', xtype=ncd_double, & + dim1name='pft', dim2name='mxharvests', switchdim=.true., & + long_name='crop harvest dates for this patch this year', units='day of year', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%gddmaturity_thisyr) + end if end if if ( flag == 'read' .and. num_reseed_patch > 0 )then if ( masterproc ) write(iulog, *) 'Reseed dead plants for CNVegState' diff --git a/src/biogeochem/CNVegStructUpdateMod.F90 b/src/biogeochem/CNVegStructUpdateMod.F90 index 01209c678f..2e8ed8539b 100644 --- a/src/biogeochem/CNVegStructUpdateMod.F90 +++ b/src/biogeochem/CNVegStructUpdateMod.F90 @@ -143,7 +143,7 @@ subroutine CNVegStructUpdate(bounds,num_soilp, filter_soilp, & dt = real( get_rad_step_size(), r8 ) ! patch loop - do fp = 1,num_soilp + do_patch:do fp = 1,num_soilp p = filter_soilp(fp) c = patch%column(p) g = patch%gridcell(p) @@ -317,7 +317,7 @@ subroutine CNVegStructUpdate(bounds,num_soilp, filter_soilp, & frac_veg_nosno_alb(p) = 0 end if - end do + end do do_patch end associate diff --git a/src/biogeochem/CNVegetationFacade.F90 b/src/biogeochem/CNVegetationFacade.F90 index f7457a505f..61e2e9cf91 100644 --- a/src/biogeochem/CNVegetationFacade.F90 +++ b/src/biogeochem/CNVegetationFacade.F90 @@ -44,7 +44,7 @@ module CNVegetationFacade use shr_log_mod , only : errMsg => shr_log_errMsg use perf_mod , only : t_startf, t_stopf use decompMod , only : bounds_type - use clm_varctl , only : iulog, use_cn, use_cndv, use_c13, use_c14 + use clm_varctl , only : iulog, use_cn, use_cndv, use_c13, use_c14, use_fates_bgc use abortutils , only : endrun use spmdMod , only : masterproc use clm_time_manager , only : get_curr_date, get_ref_date @@ -206,6 +206,8 @@ subroutine Init(this, bounds, NLFilename, nskip_steps, params_ncid) use CNFireFactoryMod , only : create_cnfire_method use clm_varcon , only : c13ratio, c14ratio use ncdio_pio , only : file_desc_t + use filterMod , only : filter + use decompMod , only : get_proc_clumps ! ! !ARGUMENTS: class(cn_vegetation_type), intent(inout) :: this @@ -215,16 +217,30 @@ subroutine Init(this, bounds, NLFilename, nskip_steps, params_ncid) type(file_desc_t), intent(inout) :: params_ncid ! NetCDF handle to parameter file ! ! !LOCAL VARIABLES: - integer :: begp, endp + integer :: begp, endp, ci + integer :: nclumps ! number of clumps on the proc + logical :: alloc_full_veg ! Signal to allocate vegetation data fully or trivialy + character(len=*), parameter :: subname = 'Init' !----------------------------------------------------------------------- - begp = bounds%begp - endp = bounds%endp - ! Note - always initialize the memory for cnveg_state_inst (used in biogeophys/) - call this%cnveg_state_inst%Init(bounds) + ! - Even if FATES is the only vegetation option, we still allocate + ! - a single value for both column and patch, using index 0 only + ! - that is why we pass the number of bgc veg patches here + + if(use_fates_bgc)then + alloc_full_veg=.false. + begp = 0 + endp = 0 + else + alloc_full_veg=.true. + begp = bounds%begp + endp = bounds%endp + end if + + call this%cnveg_state_inst%Init(bounds,alloc_full_veg) skip_steps = nskip_steps @@ -232,34 +248,43 @@ subroutine Init(this, bounds, NLFilename, nskip_steps, params_ncid) ! Read in the general CN namelist call this%CNReadNML( NLFilename ) ! MUST be called first as passes down control information to others + end if + if(use_cn.or.use_fates_bgc)then call this%cnveg_carbonstate_inst%Init(bounds, carbon_type='c12', ratio=1._r8, & - NLFilename=NLFilename, dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm ) + NLFilename=NLFilename, dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm, & + alloc_full_veg=alloc_full_veg) + if (use_c13) then call this%c13_cnveg_carbonstate_inst%Init(bounds, carbon_type='c13', ratio=c13ratio, & NLFilename=NLFilename, dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm, & - c12_cnveg_carbonstate_inst=this%cnveg_carbonstate_inst) + alloc_full_veg=alloc_full_veg, c12_cnveg_carbonstate_inst=this%cnveg_carbonstate_inst) end if if (use_c14) then call this%c14_cnveg_carbonstate_inst%Init(bounds, carbon_type='c14', ratio=c14ratio, & NLFilename=NLFilename, dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm, & - c12_cnveg_carbonstate_inst=this%cnveg_carbonstate_inst) + alloc_full_veg=alloc_full_veg,c12_cnveg_carbonstate_inst=this%cnveg_carbonstate_inst) end if - call this%cnveg_carbonflux_inst%Init(bounds, carbon_type='c12', dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm ) + + call this%cnveg_carbonflux_inst%Init(bounds, carbon_type='c12', & + dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm, alloc_full_veg=alloc_full_veg ) if (use_c13) then - call this%c13_cnveg_carbonflux_inst%Init(bounds, carbon_type='c13', dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm) + call this%c13_cnveg_carbonflux_inst%Init(bounds, carbon_type='c13', & + dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm,alloc_full_veg=alloc_full_veg) end if if (use_c14) then - call this%c14_cnveg_carbonflux_inst%Init(bounds, carbon_type='c14', dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm) + call this%c14_cnveg_carbonflux_inst%Init(bounds, carbon_type='c14', & + dribble_crophrv_xsmrpool_2atm=this%dribble_crophrv_xsmrpool_2atm,alloc_full_veg=alloc_full_veg) end if call this%cnveg_nitrogenstate_inst%Init(bounds, & this%cnveg_carbonstate_inst%leafc_patch(begp:endp), & this%cnveg_carbonstate_inst%leafc_storage_patch(begp:endp), & this%cnveg_carbonstate_inst%frootc_patch(begp:endp), & this%cnveg_carbonstate_inst%frootc_storage_patch(begp:endp), & - this%cnveg_carbonstate_inst%deadstemc_patch(begp:endp) ) - call this%cnveg_nitrogenflux_inst%Init(bounds) - + this%cnveg_carbonstate_inst%deadstemc_patch(begp:endp), & + alloc_full_veg=alloc_full_veg) + call this%cnveg_nitrogenflux_inst%Init(bounds,alloc_full_veg=alloc_full_veg) + call this%c_products_inst%Init(bounds, species_non_isotope_type('C')) if (use_c13) then call this%c13_products_inst%Init(bounds, species_isotope_type('C', '13')) @@ -268,15 +293,17 @@ subroutine Init(this, bounds, NLFilename, nskip_steps, params_ncid) call this%c14_products_inst%Init(bounds, species_isotope_type('C', '14')) end if call this%n_products_inst%Init(bounds, species_non_isotope_type('N')) - + call this%cn_balance_inst%Init(bounds) - + end if + + if(use_cn)then ! Initialize the memory for the dgvs_inst data structure regardless of whether ! use_cndv is true so that it can be used in associate statements (nag compiler ! complains otherwise) call this%dgvs_inst%Init(bounds) end if - + call create_cnfire_method(NLFilename, this%cnfire_method) call this%cnfire_method%CNFireReadParams( params_ncid ) @@ -502,6 +529,10 @@ subroutine Restart(this, bounds, ncid, flag) cnveg_nitrogenstate=this%cnveg_nitrogenstate_inst, & filter_reseed_patch=reseed_patch, num_reseed_patch=num_reseed_patch) + end if + + if (use_cn .or. use_fates_bgc) then + call this%c_products_inst%restart(bounds, ncid, flag) if (use_c13) then call this%c13_products_inst%restart(bounds, ncid, flag, & @@ -514,9 +545,8 @@ subroutine Restart(this, bounds, ncid, flag) template_multiplier = c14ratio) end if call this%n_products_inst%restart(bounds, ncid, flag) - end if - + if (use_cndv) then call this%dgvs_inst%Restart(bounds, ncid, flag=flag) end if @@ -554,7 +584,7 @@ end subroutine Init2 !----------------------------------------------------------------------- - subroutine InitEachTimeStep(this, bounds, num_soilc, filter_soilc) + subroutine InitEachTimeStep(this, bounds, num_bgc_soilc, filter_bgc_soilc) ! ! !DESCRIPTION: ! Do initializations that need to be done at the start of every time step @@ -568,8 +598,8 @@ subroutine InitEachTimeStep(this, bounds, num_soilc, filter_soilc) ! !ARGUMENTS: class(cn_vegetation_type) , intent(inout) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns ! ! !LOCAL VARIABLES: @@ -587,6 +617,15 @@ subroutine InitEachTimeStep(this, bounds, num_soilc, filter_soilc) call this%cnveg_carbonstate_inst%ZeroDWT(bounds) call this%cnveg_nitrogenstate_inst%ZeroDWT(bounds) + call this%cnveg_carbonflux_inst%ZeroGRU(bounds) + if (use_c13) then + call this%c13_cnveg_carbonflux_inst%ZeroGRU(bounds) + end if + if (use_c14) then + call this%c14_cnveg_carbonflux_inst%ZeroGRU(bounds) + end if + call this%cnveg_nitrogenflux_inst%ZeroGRU(bounds) + end subroutine InitEachTimeStep !----------------------------------------------------------------------- @@ -754,7 +793,7 @@ end subroutine DynamicAreaConservation !----------------------------------------------------------------------- subroutine InitColumnBalance(this, bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & soilbiogeochem_carbonstate_inst, & c13_soilbiogeochem_carbonstate_inst, & c14_soilbiogeochem_carbonstate_inst, & @@ -773,10 +812,10 @@ subroutine InitColumnBalance(this, bounds, num_allc, filter_allc, & type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_allc ! number of columns in allc filter integer , intent(in) :: filter_allc(:) ! filter for all active columns - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of bgc soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for bgc soil columns + integer , intent(in) :: num_bgc_vegp ! number of bgc vegetation patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc vegetation patches type(soilbiogeochem_carbonstate_type) , intent(inout) :: soilbiogeochem_carbonstate_inst type(soilbiogeochem_carbonstate_type) , intent(inout) :: c13_soilbiogeochem_carbonstate_inst type(soilbiogeochem_carbonstate_type) , intent(inout) :: c14_soilbiogeochem_carbonstate_inst @@ -789,8 +828,8 @@ subroutine InitColumnBalance(this, bounds, num_allc, filter_allc, & call CNDriverSummarizeStates(bounds, & num_allc, filter_allc, & - num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & this%cnveg_carbonstate_inst, & this%c13_cnveg_carbonstate_inst, & this%c14_cnveg_carbonstate_inst, & @@ -801,15 +840,15 @@ subroutine InitColumnBalance(this, bounds, num_allc, filter_allc, & soilbiogeochem_nitrogenstate_inst) call this%cn_balance_inst%BeginCNColumnBalance( & - bounds, num_soilc, filter_soilc, & - this%cnveg_carbonstate_inst, this%cnveg_nitrogenstate_inst) + bounds, num_bgc_soilc, filter_bgc_soilc, & + soilbiogeochem_carbonstate_inst,soilbiogeochem_nitrogenstate_inst) end subroutine InitColumnBalance !----------------------------------------------------------------------- subroutine InitGridcellBalance(this, bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, & soilbiogeochem_carbonstate_inst, & c13_soilbiogeochem_carbonstate_inst, & c14_soilbiogeochem_carbonstate_inst, & @@ -829,10 +868,10 @@ subroutine InitGridcellBalance(this, bounds, num_allc, filter_allc, & type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_allc ! number of columns in allc filter integer , intent(in) :: filter_allc(:) ! filter for all active columns - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of bgc soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for bgc soil columns + integer , intent(in) :: num_bgc_vegp ! number of bgc vegetation patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc vegetation patches type(soilbiogeochem_carbonstate_type) , intent(inout) :: soilbiogeochem_carbonstate_inst type(soilbiogeochem_carbonstate_type) , intent(inout) :: c13_soilbiogeochem_carbonstate_inst type(soilbiogeochem_carbonstate_type) , intent(inout) :: c14_soilbiogeochem_carbonstate_inst @@ -843,10 +882,11 @@ subroutine InitGridcellBalance(this, bounds, num_allc, filter_allc, & character(len=*), parameter :: subname = 'InitGridcellBalance' !----------------------------------------------------------------------- + call CNDriverSummarizeStates(bounds, & num_allc, filter_allc, & - num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & this%cnveg_carbonstate_inst, & this%c13_cnveg_carbonstate_inst, & this%c14_cnveg_carbonstate_inst, & @@ -858,20 +898,22 @@ subroutine InitGridcellBalance(this, bounds, num_allc, filter_allc, & ! total gridcell carbon (TOTGRIDCELLC) call c2g( bounds = bounds, & - carr = this%cnveg_carbonstate_inst%totc_col(bounds%begc:bounds%endc), & - garr = this%cnveg_carbonstate_inst%totc_grc(bounds%begg:bounds%endg), & + carr = soilbiogeochem_carbonstate_inst%totc_col(bounds%begc:bounds%endc), & + garr = soilbiogeochem_carbonstate_inst%totc_grc(bounds%begg:bounds%endg), & c2l_scale_type = 'unity', & l2g_scale_type = 'unity') + ! total gridcell nitrogen (TOTGRIDCELLN) call c2g( bounds = bounds, & - carr = this%cnveg_nitrogenstate_inst%totn_col(bounds%begc:bounds%endc), & - garr = this%cnveg_nitrogenstate_inst%totn_grc(bounds%begg:bounds%endg), & + carr = soilbiogeochem_nitrogenstate_inst%totn_col(bounds%begc:bounds%endc), & + garr = soilbiogeochem_nitrogenstate_inst%totn_grc(bounds%begg:bounds%endg), & c2l_scale_type = 'unity', & l2g_scale_type = 'unity') call this%cn_balance_inst%BeginCNGridcellBalance( bounds, & this%cnveg_carbonflux_inst, & - this%cnveg_carbonstate_inst, this%cnveg_nitrogenstate_inst, & + soilbiogeochem_carbonstate_inst, & + soilbiogeochem_nitrogenstate_inst, & this%c_products_inst, this%n_products_inst) end subroutine InitGridcellBalance @@ -879,8 +921,8 @@ end subroutine InitGridcellBalance !----------------------------------------------------------------------- subroutine EcosystemDynamicsPreDrainage(this, bounds, & - num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & num_actfirec, filter_actfirec, & num_actfirep, filter_actfirep, & num_pcropp, filter_pcropp, & @@ -900,9 +942,10 @@ subroutine EcosystemDynamicsPreDrainage(this, bounds, & nutrient_competition_method, fireemis_inst) ! ! !DESCRIPTION: - ! Do the main science for CN vegetation that needs to be done before hydrology-drainage + ! Do the main science for biogeochemistry that needs to be done before hydrology-drainage ! - ! Should only be called if use_cn is true + ! Can be called for either use_cn or use_fates_bgc. + ! Will skip most vegetation patch calls for the latter ! ! !USES: @@ -910,10 +953,10 @@ subroutine EcosystemDynamicsPreDrainage(this, bounds, & ! !ARGUMENTS: class(cn_vegetation_type) , intent(inout) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of bgc soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for bgc soil columns + integer , intent(in) :: num_bgc_vegp ! number of bgc veg patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc veg patches integer , intent(out) :: num_actfirec ! number of soil columns on fire in filter integer , intent(out) :: filter_actfirec(:)! filter for soil columns on fire integer , intent(out) :: num_actfirep ! number of soil patches on fire in filter @@ -962,8 +1005,8 @@ subroutine EcosystemDynamicsPreDrainage(this, bounds, & call crop_inst%CropIncrementYear(num_pcropp, filter_pcropp) call CNDriverNoLeaching(bounds, & - num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & num_pcropp, filter_pcropp, & num_soilnopcropp, filter_soilnopcropp, & num_actfirec, filter_actfirec, & @@ -990,19 +1033,19 @@ subroutine EcosystemDynamicsPreDrainage(this, bounds, & nutrient_competition_method, this%cnfire_method, this%dribble_crophrv_xsmrpool_2atm) ! fire carbon emissions - call CNFireEmisUpdate(bounds, num_soilp, filter_soilp, & + call CNFireEmisUpdate(bounds, num_bgc_vegp, filter_bgc_vegp, & this%cnveg_carbonflux_inst, this%cnveg_carbonstate_inst, fireemis_inst ) call CNAnnualUpdate(bounds, & - num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & this%cnveg_state_inst, this%cnveg_carbonflux_inst) end subroutine EcosystemDynamicsPreDrainage !----------------------------------------------------------------------- subroutine EcosystemDynamicsPostDrainage(this, bounds, num_allc, filter_allc, & - num_soilc, filter_soilc, num_soilp, filter_soilp, num_actfirec, filter_actfirec, num_actfirep, filter_actfirep,& + num_bgc_soilc, filter_bgc_soilc, num_bgc_vegp, filter_bgc_vegp, num_actfirec, filter_actfirec, num_actfirep, filter_actfirep,& doalb, crop_inst, soilstate_inst, soilbiogeochem_state_inst, & waterstatebulk_inst, waterdiagnosticbulk_inst, waterfluxbulk_inst, frictionvel_inst, canopystate_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & @@ -1013,7 +1056,7 @@ subroutine EcosystemDynamicsPostDrainage(this, bounds, num_allc, filter_allc, & ! !DESCRIPTION: ! Do the main science for CN vegetation that needs to be done after hydrology-drainage ! - ! Should only be called if use_cn is true + ! Should only be called if use_cn is true or use_fates_bgc is true ! ! !USES: ! @@ -1022,10 +1065,10 @@ subroutine EcosystemDynamicsPostDrainage(this, bounds, num_allc, filter_allc, & type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_allc ! number of columns in allc filter integer , intent(in) :: filter_allc(:) ! filter for all active columns - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of bgc soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for bgc soil columns + integer , intent(in) :: num_bgc_vegp ! number of bgc veg patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for bgc veg patches integer , intent(in) :: num_actfirec ! number of soil columns on fire in filter integer , intent(in) :: filter_actfirec(:) ! filter for soil columns on fire integer , intent(in) :: num_actfirep ! number of soil patches on fire in filter @@ -1057,8 +1100,8 @@ subroutine EcosystemDynamicsPostDrainage(this, bounds, num_allc, filter_allc, & ! and total soil water outflow. call CNDriverLeaching(bounds, & - num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & num_actfirec, filter_actfirec, & num_actfirep, filter_actfirep, & waterstatebulk_inst, waterfluxbulk_inst, soilstate_inst, this%cnveg_state_inst, & @@ -1073,24 +1116,26 @@ subroutine EcosystemDynamicsPostDrainage(this, bounds, num_allc, filter_allc, & ! Set controls on very low values in critical state variables - call t_startf('CNPrecisionControl') - call CNPrecisionControl(bounds, num_soilp, filter_soilp, & - this%cnveg_carbonstate_inst, this%c13_cnveg_carbonstate_inst, & - this%c14_cnveg_carbonstate_inst, this%cnveg_nitrogenstate_inst) - call t_stopf('CNPrecisionControl') - + if(num_bgc_vegp>0)then + call t_startf('CNPrecisionControl') + call CNPrecisionControl(bounds, num_bgc_vegp, filter_bgc_vegp, & + this%cnveg_carbonstate_inst, this%c13_cnveg_carbonstate_inst, & + this%c14_cnveg_carbonstate_inst, this%cnveg_nitrogenstate_inst) + call t_stopf('CNPrecisionControl') + end if + call t_startf('SoilBiogeochemPrecisionControl') - call SoilBiogeochemPrecisionControl(num_soilc, filter_soilc, & + call SoilBiogeochemPrecisionControl(num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst, & c14_soilbiogeochem_carbonstate_inst,soilbiogeochem_nitrogenstate_inst) call t_stopf('SoilBiogeochemPrecisionControl') ! Call to all CN summary routines - call CNDriverSummarizeStates(bounds, & + call CNDriverSummarizeStates(bounds, & num_allc, filter_allc, & - num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & this%cnveg_carbonstate_inst, & this%c13_cnveg_carbonstate_inst, & this%c14_cnveg_carbonstate_inst, & @@ -1100,9 +1145,9 @@ subroutine EcosystemDynamicsPostDrainage(this, bounds, num_allc, filter_allc, & c14_soilbiogeochem_carbonstate_inst, & soilbiogeochem_nitrogenstate_inst) - call CNDriverSummarizeFluxes(bounds, & - num_soilc, filter_soilc, & - num_soilp, filter_soilp, & + call CNDriverSummarizeFluxes(bounds, & + num_bgc_soilc, filter_bgc_soilc, & + num_bgc_vegp, filter_bgc_vegp, & this%cnveg_carbonflux_inst, & this%c13_cnveg_carbonflux_inst, & this%c14_cnveg_carbonflux_inst, & @@ -1119,24 +1164,27 @@ subroutine EcosystemDynamicsPostDrainage(this, bounds, num_allc, filter_allc, & ! On the radiation time step, use C state variables to calculate ! vegetation structure (LAI, SAI, height) - - if (doalb) then - call CNVegStructUpdate(bounds,num_soilp, filter_soilp, & - waterdiagnosticbulk_inst, frictionvel_inst, this%dgvs_inst, this%cnveg_state_inst, & - crop_inst, this%cnveg_carbonstate_inst, canopystate_inst) + if(num_bgc_vegp>0)then + if (doalb) then + call CNVegStructUpdate(bounds,num_bgc_vegp, filter_bgc_vegp, & + waterdiagnosticbulk_inst, frictionvel_inst, this%dgvs_inst, this%cnveg_state_inst, & + crop_inst, this%cnveg_carbonstate_inst, canopystate_inst) + end if end if - + end subroutine EcosystemDynamicsPostDrainage !----------------------------------------------------------------------- - subroutine BalanceCheck(this, bounds, num_soilc, filter_soilc, & + subroutine BalanceCheck(this, bounds, num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenflux_inst, & - atm2lnd_inst) + soilbiogeochem_carbonstate_inst, soilbiogeochem_nitrogenstate_inst, & + atm2lnd_inst, clm_fates) + ! ! !DESCRIPTION: ! Check the carbon and nitrogen balance ! - ! Should only be called if use_cn is true + ! Should only be called if use_cn is true or use_fates_bgc is true ! ! !USES: use clm_time_manager , only : get_nstep_since_startup_or_lastDA_restart_or_pause @@ -1144,11 +1192,14 @@ subroutine BalanceCheck(this, bounds, num_soilc, filter_soilc, & ! !ARGUMENTS: class(cn_vegetation_type) , intent(inout) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns type(soilbiogeochem_carbonflux_type) , intent(inout) :: soilbiogeochem_carbonflux_inst type(soilbiogeochem_nitrogenflux_type) , intent(inout) :: soilbiogeochem_nitrogenflux_inst + type(soilbiogeochem_carbonstate_type) , intent(inout) :: soilbiogeochem_carbonstate_inst + type(soilbiogeochem_nitrogenstate_type) , intent(inout) :: soilbiogeochem_nitrogenstate_inst type(atm2lnd_type) , intent(in) :: atm2lnd_inst + type(hlm_fates_interface_type) , intent(inout) :: clm_fates ! ! !LOCAL VARIABLES: integer :: DA_nstep ! time step number @@ -1166,19 +1217,23 @@ subroutine BalanceCheck(this, bounds, num_soilc, filter_soilc, & else call this%cn_balance_inst%CBalanceCheck( & - bounds, num_soilc, filter_soilc, & + bounds, num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_carbonflux_inst, & + soilbiogeochem_carbonstate_inst, & this%cnveg_carbonflux_inst, & this%cnveg_carbonstate_inst, & - this%c_products_inst) + this%c_products_inst, & + clm_fates) call this%cn_balance_inst%NBalanceCheck( & - bounds, num_soilc, filter_soilc, & + bounds, num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_nitrogenflux_inst, & + soilbiogeochem_nitrogenstate_inst, & this%cnveg_nitrogenflux_inst, & this%cnveg_nitrogenstate_inst, & this%n_products_inst, & - atm2lnd_inst) + atm2lnd_inst, & + clm_fates) end if diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index 6ceeccf7e3..a96e9e939f 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -23,6 +23,7 @@ module CropType private ! ! !PUBLIC DATA TYPES: + public :: latbaset ! ! Possible values of cphase @@ -42,12 +43,26 @@ module CropType real(r8), pointer :: gddtsoi_patch (:) ! patch growing degree-days from planting (top two soil layers) (ddays) real(r8), pointer :: vf_patch (:) ! patch vernalization factor for cereal real(r8), pointer :: cphase_patch (:) ! phenology phase (see cphase_* constants above for possible values) + integer , pointer :: sowing_reason_patch (:) ! reason for most recent sowing of this patch real(r8), pointer :: latbaset_patch (:) ! Latitude vary baset for hui (degree C) character(len=20) :: baset_mapping real(r8) :: baset_latvary_intercept real(r8) :: baset_latvary_slope - real(r8), pointer :: sdates_thisyr (:,:) ! all actual sowing dates for this patch this year - real(r8), pointer :: hdates_thisyr (:,:) ! all actual harvest dates for this patch this year + logical , pointer :: sown_in_this_window (:) ! patch flag. True if the crop has already been sown during the current sowing window. False otherwise or if not in a sowing window. + integer , pointer :: rx_swindow_starts_thisyr_patch(:,:) ! all prescribed sowing window start dates for this patch this year (day of year) [patch, mxsowings] + integer , pointer :: rx_swindow_ends_thisyr_patch (:,:) ! all prescribed sowing window end dates for this patch this year (day of year) [patch, mxsowings] + real(r8), pointer :: rx_cultivar_gdds_thisyr_patch (:,:) ! all cultivar GDD targets for this patch this year (ddays) [patch, mxsowings] + real(r8), pointer :: sdates_thisyr_patch (:,:) ! all actual sowing dates for this patch this year (day of year) [patch, mxsowings] + real(r8), pointer :: swindow_starts_thisyr_patch(:,:) ! all sowing window start dates for this patch this year (day of year) [patch, mxsowings] + real(r8), pointer :: swindow_ends_thisyr_patch (:,:) ! all sowing window end dates for this patch this year (day of year) [patch, mxsowings] + real(r8), pointer :: sdates_perharv_patch (:,:) ! all actual sowing dates for crops *harvested* this year (day of year) [patch, mxharvests] + real(r8), pointer :: syears_perharv_patch (:,:) ! all actual sowing years for crops *harvested* this year (day of year) [patch, mxharvests] + real(r8), pointer :: hdates_thisyr_patch (:,:) ! all actual harvest dates for this patch this year (day of year) [patch, mxharvests] + real(r8), pointer :: gddaccum_thisyr_patch (:,:) ! accumulated GDD at harvest for this patch this year (ddays) [patch, mxharvests] + real(r8), pointer :: hui_thisyr_patch (:,:) ! accumulated heat unit index at harvest for this patch this year (ddays) [patch, mxharvests] + real(r8), pointer :: sowing_reason_thisyr_patch (:,:) ! reason for each sowing for this patch this year [patch, mxsowings] + real(r8), pointer :: sowing_reason_perharv_patch (:,:) ! reason for each sowing of crops *harvested* this year [patch, mxharvests] + real(r8), pointer :: harvest_reason_thisyr_patch (:,:) ! reason for each harvest for this patch this year [patch, mxharvests] integer , pointer :: sowing_count (:) ! number of sowing events this year for this patch integer , pointer :: harvest_count (:) ! number of sowing events this year for this patch ! gddaccum tracks the actual growing degree-days accumulated over the growing season. @@ -62,7 +77,7 @@ module CropType procedure, public :: InitAccBuffer procedure, public :: InitAccVars procedure, public :: Restart - procedure, public :: ReadNML ! Read in the crop namelist + procedure, public :: ReadNML ! Read in the crop_inparm namelist ! NOTE(wjs, 2014-09-29) need to rename this from UpdateAccVars to CropUpdateAccVars ! to prevent cryptic error messages with pgi (v. 13.9 on yellowstone) @@ -132,12 +147,12 @@ subroutine ReadNML(this, NLFilename ) integer :: unitn ! unit for namelist file character(len=*), parameter :: subname = 'Crop::ReadNML' - character(len=*), parameter :: nmlname = 'crop' + character(len=*), parameter :: nmlname = 'crop_inparm' !----------------------------------------------------------------------- character(len=20) :: baset_mapping real(r8) :: baset_latvary_intercept real(r8) :: baset_latvary_slope - namelist /crop/ baset_mapping, baset_latvary_intercept, baset_latvary_slope + namelist /crop_inparm/ baset_mapping, baset_latvary_intercept, baset_latvary_slope ! Initialize options to default values, in case they are not specified in ! the namelist @@ -151,7 +166,7 @@ subroutine ReadNML(this, NLFilename ) call opnfil (NLFilename, unitn, 'F') call shr_nl_find_group_name(unitn, nmlname, status=ierr) if (ierr == 0) then - read(unitn, nml=crop, iostat=ierr) + read(unitn, nml=crop_inparm, iostat=ierr) if (ierr /= 0) then call endrun(msg="ERROR reading "//nmlname//"namelist"//errmsg(sourcefile, __LINE__)) end if @@ -179,7 +194,7 @@ subroutine ReadNML(this, NLFilename ) if (masterproc) then write(iulog,*) ' ' write(iulog,*) nmlname//' settings:' - write(iulog,nml=crop) + write(iulog,nml=crop_inparm) write(iulog,*) ' ' end if @@ -214,9 +229,23 @@ subroutine InitAllocate(this, bounds) allocate(this%gddtsoi_patch (begp:endp)) ; this%gddtsoi_patch (:) = spval allocate(this%vf_patch (begp:endp)) ; this%vf_patch (:) = 0.0_r8 allocate(this%cphase_patch (begp:endp)) ; this%cphase_patch (:) = cphase_not_planted + allocate(this%sowing_reason_patch (begp:endp)) ; this%sowing_reason_patch (:) = -1 allocate(this%latbaset_patch (begp:endp)) ; this%latbaset_patch (:) = spval - allocate(this%sdates_thisyr(begp:endp,1:mxsowings)) ; this%sdates_thisyr(:,:) = spval - allocate(this%hdates_thisyr(begp:endp,1:mxharvests)) ; this%hdates_thisyr(:,:) = spval + allocate(this%sown_in_this_window(begp:endp)) ; this%sown_in_this_window(:) = .false. + allocate(this%rx_swindow_starts_thisyr_patch(begp:endp,1:mxsowings)); this%rx_swindow_starts_thisyr_patch(:,:) = -1 + allocate(this%rx_swindow_ends_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_swindow_ends_thisyr_patch (:,:) = -1 + allocate(this%rx_cultivar_gdds_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_cultivar_gdds_thisyr_patch(:,:) = spval + allocate(this%sdates_thisyr_patch(begp:endp,1:mxsowings)) ; this%sdates_thisyr_patch(:,:) = spval + allocate(this%swindow_starts_thisyr_patch(begp:endp,1:mxsowings)) ; this%swindow_starts_thisyr_patch(:,:) = spval + allocate(this%swindow_ends_thisyr_patch (begp:endp,1:mxsowings)) ; this%swindow_ends_thisyr_patch (:,:) = spval + allocate(this%sdates_perharv_patch(begp:endp,1:mxharvests)) ; this%sdates_perharv_patch(:,:) = spval + allocate(this%syears_perharv_patch(begp:endp,1:mxharvests)) ; this%syears_perharv_patch(:,:) = spval + allocate(this%hdates_thisyr_patch(begp:endp,1:mxharvests)) ; this%hdates_thisyr_patch(:,:) = spval + allocate(this%gddaccum_thisyr_patch(begp:endp,1:mxharvests)) ; this%gddaccum_thisyr_patch(:,:) = spval + allocate(this%hui_thisyr_patch(begp:endp,1:mxharvests)) ; this%hui_thisyr_patch(:,:) = spval + allocate(this%sowing_reason_thisyr_patch(begp:endp,1:mxsowings)) ; this%sowing_reason_thisyr_patch(:,:) = spval + allocate(this%sowing_reason_perharv_patch(begp:endp,1:mxharvests)) ; this%sowing_reason_perharv_patch(:,:) = spval + allocate(this%harvest_reason_thisyr_patch(begp:endp,1:mxharvests)) ; this%harvest_reason_thisyr_patch(:,:) = spval allocate(this%sowing_count(begp:endp)) ; this%sowing_count(:) = 0 allocate(this%harvest_count(begp:endp)) ; this%harvest_count(:) = 0 @@ -272,15 +301,60 @@ subroutine InitHistory(this, bounds) ptr_patch=this%latbaset_patch, default='inactive') end if - this%sdates_thisyr(begp:endp,:) = spval + this%sdates_thisyr_patch(begp:endp,:) = spval call hist_addfld2d (fname='SDATES', units='day of year', type2d='mxsowings', & avgflag='I', long_name='actual crop sowing dates; should only be output annually', & - ptr_patch=this%sdates_thisyr, default='inactive') + ptr_patch=this%sdates_thisyr_patch, default='inactive') - this%hdates_thisyr(begp:endp,:) = spval + this%swindow_starts_thisyr_patch(begp:endp,:) = spval + call hist_addfld2d (fname='SWINDOW_STARTS', units='day of year', type2d='mxsowings', & + avgflag='I', long_name='crop sowing window start dates; should only be output annually', & + ptr_patch=this%swindow_starts_thisyr_patch, default='inactive') + + this%swindow_ends_thisyr_patch(begp:endp,:) = spval + call hist_addfld2d (fname='SWINDOW_ENDS', units='day of year', type2d='mxsowings', & + avgflag='I', long_name='crop sowing window end dates; should only be output annually', & + ptr_patch=this%swindow_ends_thisyr_patch, default='inactive') + + this%sdates_perharv_patch(begp:endp,:) = spval + call hist_addfld2d (fname='SDATES_PERHARV', units='day of year', type2d='mxharvests', & + avgflag='I', long_name='actual sowing dates for crops harvested this year; should only be output annually', & + ptr_patch=this%sdates_perharv_patch, default='inactive') + + this%syears_perharv_patch(begp:endp,:) = spval + call hist_addfld2d (fname='SYEARS_PERHARV', units='year', type2d='mxharvests', & + avgflag='I', long_name='actual sowing years for crops harvested this year; should only be output annually', & + ptr_patch=this%syears_perharv_patch, default='inactive') + + this%hdates_thisyr_patch(begp:endp,:) = spval call hist_addfld2d (fname='HDATES', units='day of year', type2d='mxharvests', & avgflag='I', long_name='actual crop harvest dates; should only be output annually', & - ptr_patch=this%hdates_thisyr, default='inactive') + ptr_patch=this%hdates_thisyr_patch, default='inactive') + + this%gddaccum_thisyr_patch(begp:endp,:) = spval + call hist_addfld2d (fname='GDDACCUM_PERHARV', units='ddays', type2d='mxharvests', & + avgflag='I', long_name='At-harvest accumulated growing degree days past planting date for crop; should only be output annually', & + ptr_patch=this%gddaccum_thisyr_patch, default='inactive') + + this%hui_thisyr_patch(begp:endp,:) = spval + call hist_addfld2d (fname='HUI_PERHARV', units='ddays', type2d='mxharvests', & + avgflag='I', long_name='At-harvest accumulated heat unit index for crop; should only be output annually', & + ptr_patch=this%hui_thisyr_patch, default='inactive') + + this%sowing_reason_thisyr_patch(begp:endp,:) = spval + call hist_addfld2d (fname='SOWING_REASON', units='unitless', type2d='mxsowings', & + avgflag='I', long_name='Reason for each crop sowing; should only be output annually', & + ptr_patch=this%sowing_reason_thisyr_patch, default='inactive') + + this%sowing_reason_perharv_patch(begp:endp,:) = spval + call hist_addfld2d (fname='SOWING_REASON_PERHARV', units='unitless', type2d='mxharvests', & + avgflag='I', long_name='Reason for sowing of each crop harvested this year; should only be output annually', & + ptr_patch=this%sowing_reason_perharv_patch, default='inactive') + + this%harvest_reason_thisyr_patch(begp:endp,:) = spval + call hist_addfld2d (fname='HARVEST_REASON_PERHARV', units='1 = mature; 2 = max season length; 3 = incorrect Dec. 31 sowing; 4 = sowing today; 5 = sowing tomorrow; 6 = tomorrow == idop; 7 = killed by cold temperature during vernalization', type2d='mxharvests', & + avgflag='I', long_name='Reason for each crop harvest; should only be output annually', & + ptr_patch=this%harvest_reason_thisyr_patch, default='inactive') end subroutine InitHistory @@ -298,43 +372,32 @@ subroutine InitCold(this, bounds) type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: - integer :: c, l, g, p, m, ivt ! indices + integer :: l, g, p, ivt ! indices + logical :: latvary_baset character(len=*), parameter :: subname = 'InitCold' !----------------------------------------------------------------------- -!DLL - added wheat & sugarcane restrictions to base T vary by lat + latvary_baset = trim(this%baset_mapping) == baset_map_latvary + if (.not. latvary_baset) then + this%latbaset_patch(bounds%begp:bounds%endp) = nan + end if + do p= bounds%begp,bounds%endp - g = patch%gridcell(p) - ivt = patch%itype(p) + l = patch%landunit(p) this%nyrs_crop_active_patch(p) = 0 - if ( grc%latdeg(g) >= 0.0_r8 .and. grc%latdeg(g) <= 30.0_r8) then - this%latbaset_patch(p)=pftcon%baset(ivt)+12._r8-0.4_r8*grc%latdeg(g) - else if (grc%latdeg(g) < 0.0_r8 .and. grc%latdeg(g) >= -30.0_r8) then - this%latbaset_patch(p)=pftcon%baset(ivt)+12._r8+0.4_r8*grc%latdeg(g) - else - this%latbaset_patch(p)=pftcon%baset(ivt) - end if - if ( trim(this%baset_mapping) == baset_map_constant ) then - this%latbaset_patch(p) = nan - end if - end do -!DLL -- end of mods - - if (use_crop) then - do p= bounds%begp,bounds%endp + if (lun%itype(l) == istcrop) then g = patch%gridcell(p) - l = patch%landunit(p) - c = patch%column(p) + ivt = patch%itype(p) + this%fertnitro_patch(p) = fert_cft(g,ivt) - if (lun%itype(l) == istcrop) then - m = patch%itype(p) - this%fertnitro_patch(p) = fert_cft(g,m) + if (latvary_baset) then + this%latbaset_patch(p) = latbaset(pftcon%baset(ivt), grc%latdeg(g), this%baset_latvary_intercept, this%baset_latvary_slope) end if - end do - end if + end if + end do end subroutine InitCold @@ -438,40 +501,7 @@ subroutine InitAccVars(this, bounds) end subroutine InitAccVars !----------------------------------------------------------------------- - logical function CallRestartvarDimOK (ncid, flag, dimname) - ! - ! !DESCRIPTION: - ! Answer whether to call restartvar(), if necessary checking whether - ! a dimension exists in the restart file - ! - ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2022-02-02) - ! Used in Restart(). Even though restartvar() can safely be called for a - ! non-existent variable, it gives an error for a non-existent dimension, so - ! check whether the dimension exists before trying to read. The need for this - ! function arose because we recently added the mxsowings and mxharvests - ! dimensions to the restart file. - ! - ! !USES: - use ncdio_pio - ! - ! !ARGUMENTS: - type(file_desc_t), intent(inout) :: ncid - character(len=*) , intent(in) :: flag - character(len=*) , intent(in) :: dimname - ! - ! !LOCAL VARIABLES: - !----------------------------------------------------------------------- - - if (flag == 'read') then - call check_dim(ncid, dimname, dimexist=CallRestartvarDimOK) - else - CallRestartvarDimOK = .true. - end if - - end function CallRestartvarDimOK - - !----------------------------------------------------------------------- - subroutine Restart(this, bounds, ncid, flag) + subroutine Restart(this, bounds, ncid, cnveg_state_inst, flag) ! ! !USES: use restUtilMod @@ -479,11 +509,15 @@ subroutine Restart(this, bounds, ncid, flag) use PatchType, only : patch use pftconMod, only : npcropmin, npcropmax use clm_varpar, only : mxsowings, mxharvests + ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2023-01-09) + use CNVegstateType, only : cnveg_state_type + use clm_time_manager , only : get_curr_calday, get_curr_date ! ! !ARGUMENTS: class(crop_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds type(file_desc_t), intent(inout) :: ncid + type(cnveg_state_type) , intent(inout) :: cnveg_state_inst ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2023-01-09) character(len=*) , intent(in) :: flag ! ! !LOCAL VARIABLES: @@ -492,6 +526,14 @@ subroutine Restart(this, bounds, ncid, flag) integer :: p logical :: readvar ! determine if variable is on initial file integer :: seasons_found, seasons_loopvar ! getting number of sowings/harvests in patch + ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2023-01-09) + integer jday ! julian day of the year + integer kyr ! current year + integer kmo ! month of year (1, ..., 12) + integer kda ! day of month (1, ..., 31) + integer mcsec ! seconds of day (0, ..., seconds/day) + ! BACKWARDS_COMPATIBILITY(ssr, 2023-01-13) + logical read_hdates_thisyr_patch character(len=*), parameter :: subname = 'Restart' !----------------------------------------------------------------------- @@ -547,6 +589,31 @@ subroutine Restart(this, bounds, ncid, flag) end if deallocate(temp1d) + allocate(temp1d(bounds%begp:bounds%endp)) + if (flag == 'write') then + do p= bounds%begp,bounds%endp + if (this%sown_in_this_window(p)) then + temp1d(p) = 1 + else + temp1d(p) = 0 + end if + end do + end if + call restartvar(ncid=ncid, flag=flag, varname='sown_in_this_window', xtype=ncd_log, & + dim1name='pft', & + long_name='Flag that patch was sown already during the current sowing window', & + interpinic_flag='interp', readvar=readvar, data=temp1d) + if (flag == 'read') then + do p= bounds%begp,bounds%endp + if (temp1d(p) == 1) then + this%sown_in_this_window(p) = .true. + else + this%sown_in_this_window(p) = .false. + end if + end do + end if + deallocate(temp1d) + call restartvar(ncid=ncid, flag=flag, varname='harvdate', xtype=ncd_int, & dim1name='pft', long_name='harvest date', units='jday', nvalid_range=(/1,366/), & interpinic_flag='interp', readvar=readvar, data=this%harvdate_patch) @@ -565,20 +632,35 @@ subroutine Restart(this, bounds, ncid, flag) ! the crop phases end if + call restartvar(ncid=ncid, flag=flag, varname='sowing_reason_patch',xtype=ncd_int, & + dim1name='pft', long_name='sowing reason for this patch', & + units='none', & + interpinic_flag='interp', readvar=readvar, data=this%sowing_reason_patch) + ! Read or write variable(s) with mxsowings dimension ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2022-02-02) See note in CallRestartvarDimOK() if (CallRestartvarDimOK(ncid, flag, 'mxsowings')) then - call restartvar(ncid=ncid, flag=flag, varname='sdates_thisyr', xtype=ncd_double, & + call restartvar(ncid=ncid, flag=flag, varname='sdates_thisyr_patch', xtype=ncd_double, & dim1name='pft', dim2name='mxsowings', switchdim=.true., & long_name='crop sowing dates for this patch this year', units='day of year', & scale_by_thickness=.false., & - interpinic_flag='interp', readvar=readvar, data=this%sdates_thisyr) + interpinic_flag='interp', readvar=readvar, data=this%sdates_thisyr_patch) + call restartvar(ncid=ncid, flag=flag, varname='swindow_starts_thisyr_patch', xtype=ncd_double, & + dim1name='pft', dim2name='mxsowings', switchdim=.true., & + long_name='sowing window start dates for this patch this year', units='day of year', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%swindow_starts_thisyr_patch) + call restartvar(ncid=ncid, flag=flag, varname='swindow_ends_thisyr_patch', xtype=ncd_double, & + dim1name='pft', dim2name='mxsowings', switchdim=.true., & + long_name='sowing window end dates for this patch this year', units='day of year', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%swindow_ends_thisyr_patch) ! Fill variable(s) derived from read-in variable(s) if (flag == 'read' .and. readvar) then do p = bounds%begp,bounds%endp seasons_found = 0 do seasons_loopvar = 1,mxsowings - if (this%sdates_thisyr(p,seasons_loopvar) >= 1 .and. this%sdates_thisyr(p,seasons_loopvar) <= 366) then + if (this%sdates_thisyr_patch(p,seasons_loopvar) >= 1 .and. this%sdates_thisyr_patch(p,seasons_loopvar) <= 366) then seasons_found = seasons_loopvar else exit @@ -592,26 +674,89 @@ subroutine Restart(this, bounds, ncid, flag) ! Read or write variable(s) with mxharvests dimension ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2022-02-02) See note in CallRestartvarDimOK() if (CallRestartvarDimOK(ncid, flag, 'mxharvests')) then - call restartvar(ncid=ncid, flag=flag, varname='hdates_thisyr', xtype=ncd_double, & + call restartvar(ncid=ncid, flag=flag, varname='sdates_perharv_patch', xtype=ncd_double, & + dim1name='pft', dim2name='mxharvests', switchdim=.true., & + long_name='sowing dates for crops harvested in this patch this year', units='day of year', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%sdates_perharv_patch) + call restartvar(ncid=ncid, flag=flag, varname='syears_perharv_patch', xtype=ncd_double, & + dim1name='pft', dim2name='mxharvests', switchdim=.true., & + long_name='sowing years for crops harvested in this patch this year', units='year', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%syears_perharv_patch) + call restartvar(ncid=ncid, flag=flag, varname='hdates_thisyr_patch', xtype=ncd_double, & dim1name='pft', dim2name='mxharvests', switchdim=.true., & long_name='crop harvest dates for this patch this year', units='day of year', & scale_by_thickness=.false., & - interpinic_flag='interp', readvar=readvar, data=this%hdates_thisyr) + interpinic_flag='interp', readvar=read_hdates_thisyr_patch, data=this%hdates_thisyr_patch) + call restartvar(ncid=ncid, flag=flag, varname='gddaccum_thisyr_patch', xtype=ncd_double, & + dim1name='pft', dim2name='mxharvests', switchdim=.true., & + long_name='accumulated GDD at harvest for this patch this year', units='ddays', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%gddaccum_thisyr_patch) + call restartvar(ncid=ncid, flag=flag, varname='hui_thisyr_patch', xtype=ncd_double, & + dim1name='pft', dim2name='mxharvests', switchdim=.true., & + long_name='accumulated heat unit index at harvest for this patch this year', units='ddays', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%hui_thisyr_patch) + call restartvar(ncid=ncid, flag=flag, varname='sowing_reason_thisyr_patch', xtype=ncd_double, & + dim1name='pft', dim2name='mxsowings', switchdim=.true., & + long_name='reason for each sowing for this patch this year', units='unitless', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%sowing_reason_thisyr_patch) + call restartvar(ncid=ncid, flag=flag, varname='sowing_reason_perharv_patch', xtype=ncd_double, & + dim1name='pft', dim2name='mxharvests', switchdim=.true., & + long_name='reason for sowing of each crop harvested this year', units='unitless', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%sowing_reason_perharv_patch) + call restartvar(ncid=ncid, flag=flag, varname='harvest_reason_thisyr_patch', xtype=ncd_double, & + dim1name='pft', dim2name='mxharvests', switchdim=.true., & + long_name='reason for each harvest for this patch this year', units='unitless', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%harvest_reason_thisyr_patch) + ! Fill variable(s) derived from read-in variable(s) - if (flag == 'read' .and. readvar) then + if (flag == 'read') then + jday = get_curr_calday() + call get_curr_date(kyr, kmo, kda, mcsec) do p = bounds%begp,bounds%endp - seasons_found = 0 - do seasons_loopvar = 1,mxharvests - if (this%hdates_thisyr(p,seasons_loopvar) >= 1 .and. this%hdates_thisyr(p,seasons_loopvar) <= 366) then - seasons_found = seasons_loopvar - else - exit - end if - end do ! loop through possible harvests - this%harvest_count(p) = seasons_found + + ! Harvest count + if (read_hdates_thisyr_patch) then + seasons_found = 0 + do seasons_loopvar = 1,mxharvests + if (this%hdates_thisyr_patch(p,seasons_loopvar) >= 1 .and. this%hdates_thisyr_patch(p,seasons_loopvar) <= 366) then + seasons_found = seasons_loopvar + else + exit + end if + end do ! loop through possible harvests + this%harvest_count(p) = seasons_found + end if + + ! Year of planting + ! Calculating this here instead of saving in restart file to allow for + ! sensible iyop values in startup/hybrid runs. + ! * Assumes no growing season is longer than 364 days (or 365 days if + ! spanning a leap day). + if (cnveg_state_inst%idop_patch(p) <= jday) then + cnveg_state_inst%iyop_patch(p) = kyr + else + cnveg_state_inst%iyop_patch(p) = kyr - 1 + end if end do ! loop through patches end if end if + + ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2023-01-09) + if (flag == 'read') then + do p = bounds%begp,bounds%endp + ! Will be needed until we can rely on all restart files including sowing_reason_patch. + if (this%croplive_patch(p) .and. this%sowing_reason_patch(p) < 0) then + this%sowing_reason_patch(p) = 0 + end if + end do ! loop through patches + end if end if end subroutine Restart @@ -827,4 +972,24 @@ subroutine checkDates( ) end subroutine checkDates + real(r8) function latbaset(baset, latdeg, baset_latvary_intercept, baset_latvary_slope) + ! !ARGUMENTS: + real(r8), intent(in) :: baset + real(r8), intent(in) :: latdeg + real(r8), intent(in) :: baset_latvary_intercept + real(r8), intent(in) :: baset_latvary_slope + + ! Was originally + ! maxlat = baset_latvary_intercept / baset_latvary_slope + ! if (abs(latdeg) > maxlat) then + ! latbaset = baset + ! else + ! latbaset = baset + baset_latvary_intercept - baset_latvary_slope*abs(latdeg) + ! end if + ! But the one-liner below should improve efficiency, at least marginally. + + latbaset = baset + baset_latvary_intercept - min(baset_latvary_intercept, baset_latvary_slope * abs(latdeg)) + + end function latbaset + end module CropType diff --git a/src/biogeochem/DryDepVelocity.F90 b/src/biogeochem/DryDepVelocity.F90 index f50e218e1b..6ca6868ed5 100644 --- a/src/biogeochem/DryDepVelocity.F90 +++ b/src/biogeochem/DryDepVelocity.F90 @@ -102,13 +102,15 @@ Module DryDepVelocity !------------------------------------------------------------------------ subroutine Init(this, bounds) - use clm_varctl , only : use_fates, use_fates_sp + use clm_varctl , only : use_fates, use_fates_nocomp class(drydepvel_type) :: this type(bounds_type), intent(in) :: bounds - if ( (.not. use_fates_sp) .and. use_fates .and. (n_drydep > 0) ) then - call endrun( msg='ERROR: Dry-deposition currently does NOT work with FATES outside of FATES-SP mode (see github issue #1044)'//& + if (use_fates .and. (n_drydep > 0)) then + if (.not. use_fates_nocomp) then + call endrun( msg='ERROR: Dry-deposition currently only works with when FATES is in SP and/or NOCOMP mode '//& errMsg(sourcefile, __LINE__)) + end if end if call this%InitAllocate(bounds) call this%InitHistory(bounds) @@ -285,7 +287,6 @@ subroutine depvel_compute( bounds, & if ( n_drydep == 0 ) return associate( & - forc_solai => atm2lnd_inst%forc_solai_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (visible only) forc_solad => atm2lnd_inst%forc_solad_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (visible only) forc_t => atm2lnd_inst%forc_t_downscaled_col , & ! Input: [real(r8) (:) ] downscaled atmospheric temperature (Kelvin) forc_q => wateratm2lndbulk_inst%forc_q_downscaled_col , & ! Input: [real(r8) (:) ] downscaled atmospheric specific humidity (kg/kg) diff --git a/src/biogeochem/EDBGCDynMod.F90 b/src/biogeochem/EDBGCDynMod.F90 deleted file mode 100644 index eb13932d13..0000000000 --- a/src/biogeochem/EDBGCDynMod.F90 +++ /dev/null @@ -1,370 +0,0 @@ -module EDBGCDynMod - -! This module creates a pathway to call the belowground biogeochemistry code as driven by the fates vegetation model -! but bypassing the aboveground CN vegetation code. It is modeled after the CNDriverMod in its call sequence and -! functionality. - - use shr_kind_mod , only : r8 => shr_kind_r8 - use clm_varctl , only : use_c13, use_c14, use_fates - use decompMod , only : bounds_type - use perf_mod , only : t_startf, t_stopf - use shr_log_mod , only : errMsg => shr_log_errMsg - use abortutils , only : endrun - use SoilBiogeochemDecompCascadeConType , only : no_soil_decomp, mimics_decomp, century_decomp, decomp_method - use CNVegCarbonStateType , only : cnveg_carbonstate_type - use CNVegCarbonFluxType , only : cnveg_carbonflux_type - use SoilBiogeochemStateType , only : soilbiogeochem_state_type - use SoilBiogeochemCarbonStateType , only : soilbiogeochem_carbonstate_type - use SoilBiogeochemCarbonFluxType , only : soilbiogeochem_carbonflux_type - use SoilBiogeochemNitrogenStateType , only : soilbiogeochem_nitrogenstate_type - use SoilBiogeochemNitrogenFluxType , only : soilbiogeochem_nitrogenflux_type - use CanopyStateType , only : canopystate_type - use SoilStateType , only : soilstate_type - use SoilHydrologyType , only : soilhydrology_type - use TemperatureType , only : temperature_type - use WaterFluxBulkType , only : waterfluxbulk_type - use ActiveLayerMod , only : active_layer_type - use atm2lndType , only : atm2lnd_type - use SoilStateType , only : soilstate_type - use ch4Mod , only : ch4_type - use CLMFatesInterfaceMod , only : hlm_fates_interface_type - - implicit none - - ! public :: EDBGCDynInit ! BGC dynamics: initialization - public :: EDBGCDyn ! BGC Dynamics - public :: EDBGCDynSummary ! BGC dynamics: summary - - character(len=*), parameter, private :: sourcefile = & - __FILE__ - -contains - - - !----------------------------------------------------------------------- - subroutine EDBGCDyn(bounds, & - num_soilc, filter_soilc, num_soilp, filter_soilp, num_pcropp, filter_pcropp, doalb, & - cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & - soilbiogeochem_state_inst, clm_fates, & - soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst, & - c13_soilbiogeochem_carbonstate_inst, c13_soilbiogeochem_carbonflux_inst, & - c14_soilbiogeochem_carbonstate_inst, c14_soilbiogeochem_carbonflux_inst, & - active_layer_inst, atm2lnd_inst, waterfluxbulk_inst, & - canopystate_inst, soilstate_inst, temperature_inst, crop_inst, ch4_inst) - ! - ! !DESCRIPTION: - - ! - ! !USES: - use clm_varpar , only: nlevgrnd, nlevdecomp_full - use clm_varpar , only: nlevdecomp, ndecomp_cascade_transitions, ndecomp_pools - use subgridAveMod , only: p2c - use CropType , only: crop_type - use CNNDynamicsMod , only: CNNDeposition,CNNFixation, CNNFert, CNSoyfix - use CNMRespMod , only: CNMResp - use CNPhenologyMod , only: CNPhenology - use CNGRespMod , only: CNGResp - use CNCIsoFluxMod , only: CIsoFlux1, CIsoFlux2, CIsoFlux2h, CIsoFlux3 - use CNC14DecayMod , only: C14Decay - use CNCStateUpdate1Mod , only: CStateUpdate1,CStateUpdate0 - use CNCStateUpdate2Mod , only: CStateUpdate2, CStateUpdate2h - use CNCStateUpdate3Mod , only: CStateUpdate3 - use CNNStateUpdate1Mod , only: NStateUpdate1 - use CNNStateUpdate2Mod , only: NStateUpdate2, NStateUpdate2h - use CNGapMortalityMod , only: CNGapMortality - use SoilBiogeochemDecompCascadeMIMICSMod, only: decomp_rates_mimics - use SoilBiogeochemDecompCascadeBGCMod , only: decomp_rate_constants_bgc - use SoilBiogeochemDecompMod , only: SoilBiogeochemDecomp - use SoilBiogeochemLittVertTranspMod , only: SoilBiogeochemLittVertTransp - use SoilBiogeochemPotentialMod , only: SoilBiogeochemPotential - use SoilBiogeochemVerticalProfileMod , only: SoilBiogeochemVerticalProfile - use SoilBiogeochemNitrifDenitrifMod , only: SoilBiogeochemNitrifDenitrif - use SoilBiogeochemNStateUpdate1Mod , only: SoilBiogeochemNStateUpdate1 - ! - ! !ARGUMENTS: - type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches - integer , intent(in) :: num_pcropp ! number of prog. crop patches in filter - integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches - logical , intent(in) :: doalb ! true = surface albedo calculation time step - type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst - type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst - type(soilbiogeochem_state_type) , intent(inout) :: soilbiogeochem_state_inst - type(soilbiogeochem_carbonflux_type) , intent(inout) :: soilbiogeochem_carbonflux_inst - type(soilbiogeochem_carbonstate_type) , intent(inout) :: soilbiogeochem_carbonstate_inst - type(soilbiogeochem_carbonflux_type) , intent(inout) :: c13_soilbiogeochem_carbonflux_inst - type(soilbiogeochem_carbonstate_type) , intent(inout) :: c13_soilbiogeochem_carbonstate_inst - type(soilbiogeochem_carbonflux_type) , intent(inout) :: c14_soilbiogeochem_carbonflux_inst - type(soilbiogeochem_carbonstate_type) , intent(inout) :: c14_soilbiogeochem_carbonstate_inst - type(soilbiogeochem_nitrogenflux_type) , intent(inout) :: soilbiogeochem_nitrogenflux_inst - type(soilbiogeochem_nitrogenstate_type) , intent(inout) :: soilbiogeochem_nitrogenstate_inst - type(active_layer_type) , intent(in) :: active_layer_inst - type(atm2lnd_type) , intent(in) :: atm2lnd_inst - type(waterfluxbulk_type) , intent(in) :: waterfluxbulk_inst - type(canopystate_type) , intent(in) :: canopystate_inst - type(soilstate_type) , intent(in) :: soilstate_inst - type(temperature_type) , intent(inout) :: temperature_inst - type(crop_type) , intent(in) :: crop_inst - type(ch4_type) , intent(in) :: ch4_inst - type(hlm_fates_interface_type) , intent(inout) :: clm_fates - ! - ! !LOCAL VARIABLES: - real(r8):: cn_decomp_pools(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_pools) - real(r8):: p_decomp_cpool_loss(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_cascade_transitions) !potential C loss from one pool to another - real(r8):: pmnf_decomp_cascade(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_cascade_transitions) !potential mineral N flux, from one pool to another - real(r8):: p_decomp_npool_to_din(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_cascade_transitions) ! potential flux to dissolved inorganic N - real(r8):: p_decomp_cn_gain(bounds%begc:bounds%endc,1:nlevdecomp,1:ndecomp_pools) ! C:N ratio of the flux gained by the receiver pool - integer :: begc,endc - !----------------------------------------------------------------------- - - begc = bounds%begc; endc = bounds%endc - - associate( & - laisun => canopystate_inst%laisun_patch , & ! Input: [real(r8) (:) ] sunlit projected leaf area index - laisha => canopystate_inst%laisha_patch , & ! Input: [real(r8) (:) ] shaded projected leaf area index - frac_veg_nosno => canopystate_inst%frac_veg_nosno_patch , & ! Input: [integer (:) ] fraction of vegetation not covered by snow (0 OR 1) [-] - frac_veg_nosno_alb => canopystate_inst%frac_veg_nosno_alb_patch , & ! Output: [integer (:) ] frac of vegetation not covered by snow [-] - tlai => canopystate_inst%tlai_patch , & ! Input: [real(r8) (:) ] one-sided leaf area index, no burying by snow - tsai => canopystate_inst%tsai_patch , & ! Input: [real(r8) (:) ] one-sided stem area index, no burying by snow - elai => canopystate_inst%elai_patch , & ! Output: [real(r8) (:) ] one-sided leaf area index with burying by snow - esai => canopystate_inst%esai_patch , & ! Output: [real(r8) (:) ] one-sided stem area index with burying by snow - htop => canopystate_inst%htop_patch , & ! Output: [real(r8) (:) ] canopy top (m) - hbot => canopystate_inst%hbot_patch & ! Output: [real(r8) (:) ] canopy bottom (m) - ) - - ! -------------------------------------------------- - ! zero the column-level C and N fluxes - ! -------------------------------------------------- - - if ( decomp_method /= no_soil_decomp )then - call t_startf('SoilBGCZero') - - call soilbiogeochem_carbonflux_inst%SetValues( & - num_soilc, filter_soilc, 0._r8) - if ( use_c13 ) then - call c13_soilbiogeochem_carbonflux_inst%SetValues( & - num_soilc, filter_soilc, 0._r8) - end if - if ( use_c14 ) then - call c14_soilbiogeochem_carbonflux_inst%SetValues( & - num_soilc, filter_soilc, 0._r8) - end if - - call t_stopf('SoilBGCZero') - end if - - ! -------------------------------------------------- - ! Nitrogen Deposition, Fixation and Respiration - ! -------------------------------------------------- - - ! call t_startf('CNDeposition') - ! call CNNDeposition(bounds, & - ! atm2lnd_inst, soilbiogeochem_nitrogenflux_inst) - ! call t_stopf('CNDeposition') - - - ! if (crop_prog) then - ! call CNNFert(bounds, num_soilc,filter_soilc, & - ! cnveg_nitrogenflux_inst, soilbiogeochem_nitrogenflux_inst) - - ! call CNSoyfix (bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & - ! waterstate_inst, crop_inst, cnveg_state_inst, cnveg_nitrogenflux_inst , & - ! soilbiogeochem_state_inst, soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst) - ! end if - - !-------------------------------------------- - ! Soil Biogeochemistry - !-------------------------------------------- - - if (decomp_method == century_decomp) then - call decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & - soilstate_inst, temperature_inst, ch4_inst, soilbiogeochem_carbonflux_inst) - else if (decomp_method == mimics_decomp) then - call decomp_rates_mimics(bounds, num_soilc, filter_soilc, & - num_soilp, filter_soilp, clm_fates, & - soilstate_inst, temperature_inst, cnveg_carbonflux_inst, ch4_inst, & - soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst) - end if - - if ( decomp_method /= no_soil_decomp )then - ! calculate potential decomp rates and total immobilization demand (previously inlined in CNDecompAlloc) - call SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & - soilbiogeochem_state_inst, soilbiogeochem_carbonstate_inst, soilbiogeochem_carbonflux_inst, & - soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & - cn_decomp_pools=cn_decomp_pools(begc:endc,1:nlevdecomp,1:ndecomp_pools), & - p_decomp_cpool_loss=p_decomp_cpool_loss(begc:endc,1:nlevdecomp,1:ndecomp_cascade_transitions), & - p_decomp_cn_gain=p_decomp_cn_gain(begc:endc,1:nlevdecomp,1:ndecomp_pools), & - pmnf_decomp_cascade=pmnf_decomp_cascade(begc:endc,1:nlevdecomp,1:ndecomp_cascade_transitions), & - p_decomp_npool_to_din=p_decomp_npool_to_din(begc:endc,1:nlevdecomp,1:ndecomp_cascade_transitions)) - end if - - !-------------------------------------------- - ! Resolve the competition between plants and soil heterotrophs - ! for available soil mineral N resource - !-------------------------------------------- - ! will add this back in when integrtating hte nutirent cycles - - - !-------------------------------------------- - ! Calculate litter and soil decomposition rate - !-------------------------------------------- - - ! Calculation of actual immobilization and decomp rates, following - ! resolution of plant/heterotroph competition for mineral N (previously inlined in CNDecompAllocation in CNDecompMod) - - if ( decomp_method /= no_soil_decomp )then - call t_startf('SoilBiogeochemDecomp') - - call SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, & - soilbiogeochem_state_inst, soilbiogeochem_carbonstate_inst, soilbiogeochem_carbonflux_inst, & - soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & - cn_decomp_pools=cn_decomp_pools(begc:endc,1:nlevdecomp,1:ndecomp_pools), & - p_decomp_cpool_loss=p_decomp_cpool_loss(begc:endc,1:nlevdecomp,1:ndecomp_cascade_transitions), & - pmnf_decomp_cascade=pmnf_decomp_cascade(begc:endc,1:nlevdecomp,1:ndecomp_cascade_transitions), & - p_decomp_npool_to_din=p_decomp_npool_to_din(begc:endc,1:nlevdecomp,1:ndecomp_cascade_transitions)) - - call t_stopf('SoilBiogeochemDecomp') - - - !-------------------------------------------- - ! Update1 - !-------------------------------------------- - - call t_startf('BNGCUpdate1') - - - ! Update all prognostic carbon state variables (except for gap-phase mortality and fire fluxes) - call CStateUpdate1( num_soilc, filter_soilc, num_soilp, filter_soilp, & - crop_inst, cnveg_carbonflux_inst, cnveg_carbonstate_inst, & - soilbiogeochem_carbonflux_inst, dribble_crophrv_xsmrpool_2atm=.False.) - - call t_stopf('BNGCUpdate1') - - !-------------------------------------------- - ! Calculate vertical mixing of soil and litter pools - !-------------------------------------------- - - call t_startf('SoilBiogeochemLittVertTransp') - - call SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & - active_layer_inst, soilbiogeochem_state_inst, & - soilbiogeochem_carbonstate_inst, soilbiogeochem_carbonflux_inst, & - c13_soilbiogeochem_carbonstate_inst, c13_soilbiogeochem_carbonflux_inst, & - c14_soilbiogeochem_carbonstate_inst, c14_soilbiogeochem_carbonflux_inst, & - soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst) - - call t_stopf('SoilBiogeochemLittVertTransp') - end if - - ! Wood product fluxes will eventually be added to FATES-CLM. However - ! it is likely this will be implemented during or after we break away from - ! using this module. This module and the current coupling stategy bypasses - ! a number of processes in CLM, which includes the wood product modules. - ! Therefore the following call is a placeholder so that the wood-product - ! wrapper code can be copied from here and applied at the right place when the time comes. - ! RGK 06-2022 - - !call FatesWrapWoodProducts(bounds, num_soilc, filter_soilc,c_products_inst) - !call t_startf('CNWoodProducts') - !call c_products_inst%UpdateProducts(bounds, & - ! num_soilp, filter_soilp, & - ! dwt_wood_product_gain_patch = cnveg_carbonflux_inst%dwt_wood_productc_gain_patch(begp:endp), & - ! wood_harvest_patch = cnveg_carbonflux_inst%wood_harvestc_patch(begp:endp), & - ! dwt_crop_product_gain_patch = cnveg_carbonflux_inst%dwt_crop_productc_gain_patch(begp:endp), & - ! crop_harvest_to_cropprod_patch = cnveg_carbonflux_inst%crop_harvestc_to_cropprodc_patch(begp:endp)) - !call t_stopf('CNWoodProducts') - - - end associate - - end subroutine EDBGCDyn - - - !----------------------------------------------------------------------- - subroutine EDBGCDynSummary(bounds, num_soilc, filter_soilc, num_soilp, filter_soilp, & - soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & - c13_soilbiogeochem_carbonflux_inst, c13_soilbiogeochem_carbonstate_inst, & - c14_soilbiogeochem_carbonflux_inst, c14_soilbiogeochem_carbonstate_inst, & - soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst, & - nc) - ! - ! !DESCRIPTION: - ! Call to all CN and SoilBiogeochem summary routines - ! also aggregate production and decomposition fluxes to whole-ecosystem balance fluxes - ! - ! !USES: - use clm_varpar , only: ndecomp_cascade_transitions - use CNPrecisionControlMod , only: CNPrecisionControl - use SoilBiogeochemPrecisionControlMod , only: SoilBiogeochemPrecisionControl - ! - ! !ARGUMENTS: - type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches - type(soilbiogeochem_carbonflux_type) , intent(inout) :: soilbiogeochem_carbonflux_inst - type(soilbiogeochem_carbonstate_type) , intent(inout) :: soilbiogeochem_carbonstate_inst - type(soilbiogeochem_carbonflux_type) , intent(inout) :: c13_soilbiogeochem_carbonflux_inst - type(soilbiogeochem_carbonstate_type) , intent(inout) :: c13_soilbiogeochem_carbonstate_inst - type(soilbiogeochem_carbonflux_type) , intent(inout) :: c14_soilbiogeochem_carbonflux_inst - type(soilbiogeochem_carbonstate_type) , intent(inout) :: c14_soilbiogeochem_carbonstate_inst - type(soilbiogeochem_nitrogenflux_type) , intent(inout) :: soilbiogeochem_nitrogenflux_inst - type(soilbiogeochem_nitrogenstate_type) , intent(inout) :: soilbiogeochem_nitrogenstate_inst - integer , intent(in) :: nc ! thread index - ! - ! !LOCAL VARIABLES: - integer :: begc,endc - !----------------------------------------------------------------------- - - begc = bounds%begc; endc= bounds%endc - - ! Call to all summary routines - - call t_startf('BGCsum') - - ! Set controls on very low values in critical state variables - - call SoilBiogeochemPrecisionControl(num_soilc, filter_soilc, & - soilbiogeochem_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst, & - c14_soilbiogeochem_carbonstate_inst,soilbiogeochem_nitrogenstate_inst) - - ! Note - all summary updates to cnveg_carbonstate_inst and cnveg_carbonflux_inst are done in - ! soilbiogeochem_carbonstate_inst%summary and CNVeg_carbonstate_inst%summary - - ! ---------------------------------------------- - ! soilbiogeochem carbon/nitrogen state summary - ! ---------------------------------------------- - - call soilbiogeochem_carbonstate_inst%summary(bounds, num_soilc, filter_soilc) - if ( use_c13 ) then - call c13_soilbiogeochem_carbonstate_inst%summary(bounds, num_soilc, filter_soilc) - end if - if ( use_c14 ) then - call c14_soilbiogeochem_carbonstate_inst%summary(bounds, num_soilc, filter_soilc) - end if - ! call soilbiogeochem_nitrogenstate_inst%summary(bounds, num_soilc, filter_soilc) - - ! ---------------------------------------------- - ! soilbiogeochem carbon/nitrogen flux summary - ! ---------------------------------------------- - - call soilbiogeochem_carbonflux_inst%Summary(bounds, num_soilc, filter_soilc) - if ( use_c13 ) then - call c13_soilbiogeochem_carbonflux_inst%Summary(bounds, num_soilc, filter_soilc) - end if - if ( use_c14 ) then - call c14_soilbiogeochem_carbonflux_inst%Summary(bounds, num_soilc, filter_soilc) - end if - ! call soilbiogeochem_nitrogenflux_inst%Summary(bounds, num_soilc, filter_soilc) - - - call t_stopf('BGCsum') - - end subroutine EDBGCDynSummary - -end module EDBGCDynMod diff --git a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 index e7c16f15c7..6f8632658d 100644 --- a/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 +++ b/src/biogeochem/NutrientCompetitionFlexibleCNMod.F90 @@ -1365,7 +1365,6 @@ subroutine calc_plant_nitrogen_demand(this, bounds, & frootn => cnveg_nitrogenstate_inst%frootn_patch , & ! Input: [real(r8) (:) ] (gN/m2) fine root N livestemn_to_retransn => cnveg_nitrogenflux_inst%livestemn_to_retransn_patch,& ! Output: [real(r8) (:) ] sminn_vr => soilbiogeochem_nitrogenstate_inst%sminn_vr_col , & ! Input: [real(r8) (:,:) ] (gN/m3) soil mineral N - btran => energyflux_inst%btran_patch , & ! Input: [real(r8) (:) ] transpiration wetness factor (0 to 1) t_scalar => soilbiogeochem_carbonflux_inst%t_scalar_col & ! Input: [real(r8) (:,:) ] soil temperature scalar for decomp ) diff --git a/src/biogeochem/VOCEmissionMod.F90 b/src/biogeochem/VOCEmissionMod.F90 index f1865af3b7..a1722a08b9 100644 --- a/src/biogeochem/VOCEmissionMod.F90 +++ b/src/biogeochem/VOCEmissionMod.F90 @@ -84,14 +84,17 @@ module VOCEmissionMod !------------------------------------------------------------------------ subroutine Init(this, bounds) - use clm_varctl , only : use_fates, use_fates_sp + use clm_varctl, only : use_fates, use_fates_nocomp class(vocemis_type) :: this type(bounds_type), intent(in) :: bounds + if ( shr_megan_mechcomps_n > 0) then - if ( use_fates .and. (.not. use_fates_sp) ) then - call endrun( msg='ERROR: MEGAN currently does NOT work with FATES outside of FATES-SP mode (see github issue #115)'//& - errMsg(sourcefile, __LINE__)) + if (use_fates) then + if (.not. use_fates_nocomp) then + call endrun( msg='ERROR: MEGAN currently only works with when FATES is in SP and/or NOCOMP mode '//& + errMsg(sourcefile, __LINE__)) + end if end if call this%InitAllocate(bounds) call this%InitHistory(bounds) diff --git a/src/biogeochem/ch4Mod.F90 b/src/biogeochem/ch4Mod.F90 index afb8af351b..f199e7913f 100644 --- a/src/biogeochem/ch4Mod.F90 +++ b/src/biogeochem/ch4Mod.F90 @@ -17,7 +17,7 @@ module ch4Mod use clm_varcon , only : catomw, s_con, d_con_w, d_con_g, c_h_inv, kh_theta, kh_tbase use landunit_varcon , only : istsoil, istcrop, istdlak use clm_time_manager , only : get_step_size_real, get_nstep - use clm_varctl , only : iulog, use_cn, use_nitrif_denitrif, use_lch4, use_cn, use_fates + use clm_varctl , only : iulog, use_cn, use_nitrif_denitrif, use_lch4, use_fates_bgc use abortutils , only : endrun use decompMod , only : bounds_type, subgrid_level_gridcell, subgrid_level_column use atm2lndType , only : atm2lnd_type @@ -2521,7 +2521,7 @@ subroutine ch4_prod (bounds, num_methc, filter_methc, num_methp, & end if end do - if(use_fates) then + if(use_fates_bgc) then nc = bounds%clump_index do s = 1,clm_fates%fates(nc)%nsites c = clm_fates%f2hmap(nc)%fcolumn(s) @@ -2544,7 +2544,7 @@ subroutine ch4_prod (bounds, num_methc, filter_methc, num_methp, & if (.not. lake) then - if (use_cn .or. use_fates) then + if (use_cn .or. use_fates_bgc) then ! Use soil heterotrophic respiration (based on Wania) base_decomp = (somhr(c)+lithr(c)) / catomw ! Convert from gC to molC @@ -2567,7 +2567,7 @@ subroutine ch4_prod (bounds, num_methc, filter_methc, num_methp, & else call endrun(msg=' ERROR: No source for decomp rate in CH4Prod.'//& ' CH4 model currently requires CN or FATES.'//errMsg(sourcefile, __LINE__)) - end if ! use_cn + end if ! use_cn or use_fates_bgc ! For sensitivity studies base_decomp = base_decomp * cnscalefactor diff --git a/src/biogeochem/test/CMakeLists.txt b/src/biogeochem/test/CMakeLists.txt index ad91c7c995..81fe9bbaf0 100644 --- a/src/biogeochem/test/CMakeLists.txt +++ b/src/biogeochem/test/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(Species_test) add_subdirectory(CNVegComputeSeed_test) add_subdirectory(CNPhenology_test) +add_subdirectory(Latbaset_test) diff --git a/src/biogeochem/test/CNPhenology_test/CMakeLists.txt b/src/biogeochem/test/CNPhenology_test/CMakeLists.txt index f265e12ab0..2367c86612 100644 --- a/src/biogeochem/test/CNPhenology_test/CMakeLists.txt +++ b/src/biogeochem/test/CNPhenology_test/CMakeLists.txt @@ -1,7 +1,6 @@ set (pfunit_sources test_CNPhenology.pf) -create_pFUnit_test(CNPhenology test_CNPhenology_exe - "${pfunit_sources}" "") - -target_link_libraries(test_CNPhenology_exe clm csm_share esmf_wrf_timemgr) +add_pfunit_ctest(CNPhenology + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf b/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf index c93f15cb8f..9e06bc74e2 100644 --- a/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf +++ b/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf @@ -2,7 +2,7 @@ module test_CNPhenology ! Tests of CNPhenologyMod - use pfunit_mod + use funit use unittestSubgridMod use CNPhenologyMod use unittestTimeManagerMod, only : unittest_timemgr_setup, unittest_timemgr_teardown @@ -293,6 +293,212 @@ contains end do end subroutine check_crit_dayl_dependsonlatnveg + @Test + subroutine test_get_swindow_startend(this) + use clm_time_manager, only : get_curr_days_per_year + class(TestCNPhenology), intent(inout) :: this + integer, dimension(3), parameter :: rx_starts = (/1, 150, -1/) + integer, dimension(3), parameter :: rx_ends = (/45, 180, -1/) + integer, parameter :: param_start = 200 + integer, parameter :: param_end = 250 + integer :: w, start_w, end_w + + call get_swindow(1, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(1, w) + @assertEqual(1, start_w) + @assertEqual(45, end_w) + + call get_swindow(45, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(1, w) + @assertEqual(1, start_w) + @assertEqual(45, end_w) + + call get_swindow(149, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(2, w) + @assertEqual(150, start_w) + @assertEqual(180, end_w) + + call get_swindow(175, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(2, w) + @assertEqual(150, start_w) + @assertEqual(180, end_w) + + call get_swindow(229, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(1, w) + @assertEqual(1, start_w) + @assertEqual(45, end_w) + + call get_swindow(get_curr_days_per_year(), rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(1, w) + @assertEqual(1, start_w) + @assertEqual(45, end_w) + end subroutine test_get_swindow_startend + + @Test + subroutine test_get_swindow_endstart(this) + use clm_time_manager, only : get_curr_days_per_year + class(TestCNPhenology), intent(inout) :: this + integer, dimension(3), parameter :: rx_starts = (/360, 150, -1/) + integer, dimension(3), parameter :: rx_ends = (/45, 180, -1/) + integer, parameter :: param_start = 200 + integer, parameter :: param_end = 250 + integer :: w, start_w, end_w + + call get_swindow(1, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(1, w) + @assertEqual(360, start_w) + @assertEqual(45, end_w) + + call get_swindow(45, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(1, w) + @assertEqual(start_w, 360) + @assertEqual(end_w, 45) + + call get_swindow(149, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(2, w) + @assertEqual(150, start_w) + @assertEqual(180, end_w) + + call get_swindow(175, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(2, w) + @assertEqual(150, start_w) + @assertEqual(180, end_w) + + call get_swindow(229, rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(1, w) + @assertEqual(360, start_w) + @assertEqual(45, end_w) + + call get_swindow(get_curr_days_per_year(), rx_starts, rx_ends, param_start, param_end, w, start_w, end_w) + @assertEqual(1, w) + @assertEqual(360, start_w) + @assertEqual(45, end_w) + end subroutine test_get_swindow_endstart + + @Test + subroutine test_was_sown_in_this_window_startend(this) + use clm_time_manager , only : is_doy_in_interval + class(TestCNPhenology), intent(inout) :: this + integer, parameter :: sowing_window_startdate = 84 + integer, parameter :: sowing_window_enddate = 205 + integer :: jday, idop + + jday = 100 + + idop = 90 + @assertTrue(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + ! With jday 100 and idop 90 (as well as idop 100 below), if the existing value of sown_in_this_window is false, then even though it LOOKS like it was sown in the current window, it must have been in a previous year. If it had been sown in this window this year, sown_in_this_window would be true. + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + idop = 100 + @assertTrue(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + idop = 101 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + idop = 120 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + idop = 360 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + jday = 300 + + idop = 90 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + idop = 360 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + end subroutine test_was_sown_in_this_window_startend + + @Test + subroutine test_was_sown_in_this_window_endstart(this) + use clm_time_manager , only : is_doy_in_interval + class(TestCNPhenology), intent(inout) :: this + integer, parameter :: sowing_window_startdate = 205 + integer, parameter :: sowing_window_enddate = 84 + integer :: jday, idop + + jday = 300 + + idop = 60 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + idop = 205 + @assertTrue(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + ! With jday 300 and idop 205 (as well as jday 70 idop 60 below), if the existing value of sown_in_this_window is false, then even though it LOOKS like it was sown in the current window, it must have been in a previous year. If it had been sown in the actual current window, sown_in_this_window would be true. + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + idop = 300 + @assertTrue(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + idop = 301 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + jday = 70 + + idop = 60 + @assertTrue(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + idop = 75 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + idop = 301 + @assertTrue(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + end subroutine test_was_sown_in_this_window_endstart + + @Test + subroutine test_was_sown_in_this_window_sameday(this) + use clm_time_manager , only : is_doy_in_interval + class(TestCNPhenology), intent(inout) :: this + integer, parameter :: sowing_window_startdate = 205 + integer, parameter :: sowing_window_enddate = 205 + integer :: jday, idop + + ! If today == start == end, we trust whatever the current value of sown_in_this_window is. + jday = 205 + idop = 205 + ! If it's false, then even if idop == jday, it that idop value must be left over from planting in a PREVIOUS year. + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + ! The ONLY way was_sown_in_this_window() should return true is if today == start == end == idop AND the current value is true. + @assertTrue(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + ! There is no other situation where was_sown_in_this_window() should return true. + + jday = 300 + idop = 60 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + idop = 205 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + idop = 300 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + idop = 301 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + jday = 70 + + idop = 60 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + + idop = 75 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + + idop = 301 + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .true.)) + @assertFalse(was_sown_in_this_window(sowing_window_startdate, sowing_window_enddate, jday, idop, .false.)) + end subroutine test_was_sown_in_this_window_sameday end module test_CNPhenology diff --git a/src/biogeochem/test/CNVegComputeSeed_test/CMakeLists.txt b/src/biogeochem/test/CNVegComputeSeed_test/CMakeLists.txt index 35173a6dc3..dd6bdba723 100644 --- a/src/biogeochem/test/CNVegComputeSeed_test/CMakeLists.txt +++ b/src/biogeochem/test/CNVegComputeSeed_test/CMakeLists.txt @@ -1,7 +1,6 @@ set (pfunit_sources test_ComputeSeedAmounts.pf) -create_pFUnit_test(CNVegComputeSeed test_CNVegComputeSeed_exe - "${pfunit_sources}" "") - -target_link_libraries(test_CNVegComputeSeed_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(CNVegComputeSeed + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeochem/test/CNVegComputeSeed_test/test_ComputeSeedAmounts.pf b/src/biogeochem/test/CNVegComputeSeed_test/test_ComputeSeedAmounts.pf index f5e8aeff9b..be87b4c989 100644 --- a/src/biogeochem/test/CNVegComputeSeed_test/test_ComputeSeedAmounts.pf +++ b/src/biogeochem/test/CNVegComputeSeed_test/test_ComputeSeedAmounts.pf @@ -2,7 +2,7 @@ module test_ComputeSeedAmounts ! Tests of CNVegComputeSeedMod: ComputeSeedAmounts - use pfunit_mod + use funit use CNVegComputeSeedMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/biogeochem/test/Latbaset_test/CMakeLists.txt b/src/biogeochem/test/Latbaset_test/CMakeLists.txt new file mode 100644 index 0000000000..217fc7233c --- /dev/null +++ b/src/biogeochem/test/Latbaset_test/CMakeLists.txt @@ -0,0 +1,6 @@ +set (pfunit_sources + test_Latbaset.pf) + +add_pfunit_ctest(CropTypeLatbaset + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeochem/test/Latbaset_test/test_Latbaset.pf b/src/biogeochem/test/Latbaset_test/test_Latbaset.pf new file mode 100644 index 0000000000..ebb5bfa5e4 --- /dev/null +++ b/src/biogeochem/test/Latbaset_test/test_Latbaset.pf @@ -0,0 +1,119 @@ +module test_Latbaset + + ! Tests of CropType module: latbaset + + use funit + use shr_kind_mod , only : r8 => shr_kind_r8 + use unittestSubgridMod + use unittestSimpleSubgridSetupsMod + use unittestFilterBuilderMod + use CropType, only : latbaset + + implicit none + + @TestCase + type, extends(TestCase) :: TestLatbaset + contains + procedure :: setUp + procedure :: tearDown + end type TestLatbaset + + real(r8) :: baset + real(r8) :: latdeg + real(r8) :: baset_latvary_intercept + real(r8) :: baset_latvary_slope + real(r8) :: expected + +contains + + subroutine setUp(this) + class(TestLatbaset), intent(inout) :: this + end subroutine setUp + + subroutine tearDown(this) + class(TestLatbaset), intent(inout) :: this + + call unittest_subgrid_teardown() + end subroutine tearDown + + real(r8) function latbaset_max_lat(intercept, slope) + real(r8), intent(in) :: intercept + real(r8), intent(in) :: slope + + latbaset_max_lat = intercept / slope + end function latbaset_max_lat + + @Test + subroutine too_far_north(this) + class(TestLatbaset), intent(inout) :: this + + baset = 5._r8 + baset_latvary_intercept = 8.7_r8 + baset_latvary_slope = 0.5_r8 + latdeg = 10._r8 + latbaset_max_lat(baset_latvary_intercept, baset_latvary_slope) + + @assertEqual(baset, latbaset(baset, latdeg, baset_latvary_intercept, baset_latvary_slope)) + end subroutine too_far_north + + @Test + subroutine too_far_south(this) + class(TestLatbaset), intent(inout) :: this + + baset = 5._r8 + baset_latvary_intercept = 8.7_r8 + baset_latvary_slope = 0.5_r8 + latdeg = -10._r8 - latbaset_max_lat(baset_latvary_intercept, baset_latvary_slope) + + @assertEqual(baset, latbaset(baset, latdeg, baset_latvary_intercept, baset_latvary_slope)) + end subroutine too_far_south + + @Test + subroutine at_northern_limit(this) + class(TestLatbaset), intent(inout) :: this + + baset = 5._r8 + baset_latvary_intercept = 12._r8 + baset_latvary_slope = 0.4_r8 + latdeg = latbaset_max_lat(baset_latvary_intercept, baset_latvary_slope) + + @assertEqual(baset, latbaset(baset, latdeg, baset_latvary_intercept, baset_latvary_slope)) + end subroutine at_northern_limit + + @Test + subroutine at_southern_limit(this) + class(TestLatbaset), intent(inout) :: this + + baset = 5._r8 + baset_latvary_intercept = 12._r8 + baset_latvary_slope = 0.4_r8 + latdeg = -latbaset_max_lat(baset_latvary_intercept, baset_latvary_slope) + + @assertEqual(baset, latbaset(baset, latdeg, baset_latvary_intercept, baset_latvary_slope)) + end subroutine at_southern_limit + + @Test + subroutine in_nh(this) + class(TestLatbaset), intent(inout) :: this + + baset = 5._r8 + latdeg = 10._r8 + baset_latvary_intercept = 13._r8 + baset_latvary_slope = 0.3_r8 + + @assertEqual(15._r8, latbaset(baset, latdeg, baset_latvary_intercept, baset_latvary_slope)) + end subroutine in_nh + + @Test + subroutine in_sh(this) + class(TestLatbaset), intent(inout) :: this + + baset = 5._r8 + latdeg = -10._r8 + baset_latvary_intercept = 13._r8 + baset_latvary_slope = 0.3_r8 + + @assertEqual(15._r8, latbaset(baset, latdeg, baset_latvary_intercept, baset_latvary_slope)) + end subroutine in_sh + +end module test_Latbaset + diff --git a/src/biogeochem/test/Species_test/CMakeLists.txt b/src/biogeochem/test/Species_test/CMakeLists.txt index 3a0bc4b50c..14d091d1ef 100644 --- a/src/biogeochem/test/Species_test/CMakeLists.txt +++ b/src/biogeochem/test/Species_test/CMakeLists.txt @@ -2,7 +2,6 @@ set (pfunit_sources test_SpeciesNonIsotope.pf test_SpeciesIsotope.pf) -create_pFUnit_test(Species test_Species_exe - "${pfunit_sources}" "") - -target_link_libraries(test_Species_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(Species + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share) diff --git a/src/biogeochem/test/Species_test/test_SpeciesIsotope.pf b/src/biogeochem/test/Species_test/test_SpeciesIsotope.pf index 8199b1c92a..6017d9a866 100644 --- a/src/biogeochem/test/Species_test/test_SpeciesIsotope.pf +++ b/src/biogeochem/test/Species_test/test_SpeciesIsotope.pf @@ -2,7 +2,7 @@ module test_SpeciesIsotope ! Tests of SpeciesIsotopeType - use pfunit_mod + use funit use SpeciesIsotopeType use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/biogeochem/test/Species_test/test_SpeciesNonIsotope.pf b/src/biogeochem/test/Species_test/test_SpeciesNonIsotope.pf index e9e4b9fac5..fdcc204db1 100644 --- a/src/biogeochem/test/Species_test/test_SpeciesNonIsotope.pf +++ b/src/biogeochem/test/Species_test/test_SpeciesNonIsotope.pf @@ -2,7 +2,7 @@ module test_SpeciesNonIsotope ! Tests of SpeciesNonIsotopeType - use pfunit_mod + use funit use SpeciesNonIsotopeType use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/biogeophys/AerosolMod.F90 b/src/biogeophys/AerosolMod.F90 index f0e0c3fa88..39ade89fb0 100644 --- a/src/biogeophys/AerosolMod.F90 +++ b/src/biogeophys/AerosolMod.F90 @@ -15,6 +15,7 @@ module AerosolMod use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type use ColumnType , only : col use abortutils , only : endrun + use CLM_varctl , only : snicar_use_aerosol ! ! !PUBLIC TYPES: implicit none @@ -25,9 +26,6 @@ module AerosolMod public :: AerosolFluxes ! ! !PUBLIC DATA MEMBERS: - real(r8), public, parameter :: snw_rds_min = 54.526_r8 ! minimum allowed snow effective radius (also cold "fresh snow" value) [microns] - real(r8), public :: fresh_snw_rds_max = 204.526_r8 ! maximum warm fresh snow effective radius [microns] - ! type, public :: aerosol_type real(r8), pointer, public :: mss_bcpho_col(:,:) ! mass of hydrophobic BC in snow (col,lyr) [kg] real(r8), pointer, public :: mss_bcphi_col(:,:) ! mass of hydrophillic BC in snow (col,lyr) [kg] @@ -92,7 +90,6 @@ module AerosolMod procedure, private :: InitAllocate procedure, private :: InitHistory procedure, private :: InitCold - procedure, private :: InitReadNML end type aerosol_type @@ -112,7 +109,6 @@ subroutine Init(this, bounds, NLFilename) call this%InitAllocate(bounds) call this%InitHistory(bounds) call this%InitCold(bounds) - call this%InitReadNML(NLFilename) end subroutine Init @@ -293,58 +289,6 @@ subroutine InitCold(this, bounds) end subroutine InitCold !----------------------------------------------------------------------- - subroutine InitReadNML(this, NLFilename) - ! - ! !USES: - ! !USES: - use fileutils , only : getavu, relavu, opnfil - use shr_nl_mod , only : shr_nl_find_group_name - use spmdMod , only : masterproc, mpicom - use shr_mpi_mod , only : shr_mpi_bcast - use clm_varctl , only : iulog - ! - ! !ARGUMENTS: - class(aerosol_type) :: this - character(len=*), intent(in) :: NLFilename ! Input namelist filename - ! - ! !LOCAL VARIABLES: - !----------------------------------------------------------------------- - integer :: ierr ! error code - integer :: unitn ! unit for namelist file - - character(len=*), parameter :: subname = 'Aerosol::InitReadNML' - character(len=*), parameter :: nmlname = 'aerosol' - !----------------------------------------------------------------------- - namelist/aerosol/ fresh_snw_rds_max - - if (masterproc) then - unitn = getavu() - write(iulog,*) 'Read in '//nmlname//' namelist' - call opnfil (NLFilename, unitn, 'F') - call shr_nl_find_group_name(unitn, nmlname, status=ierr) - if (ierr == 0) then - read(unitn, nml=aerosol, iostat=ierr) - if (ierr /= 0) then - call endrun(msg="ERROR reading "//nmlname//" namelist "//errmsg(sourcefile, __LINE__)) - end if - else - call endrun(msg="ERROR could NOT find "//nmlname//" namelist "//errmsg(sourcefile, __LINE__)) - end if - call relavu( unitn ) - end if - - call shr_mpi_bcast (fresh_snw_rds_max , mpicom) - - if (masterproc) then - write(iulog,*) ' ' - write(iulog,*) nmlname//' settings:' - write(iulog,nml=aerosol) - write(iulog,*) ' ' - end if - - end subroutine InitReadNML - - !------------------------------------------------------------------------ subroutine Restart(this, bounds, ncid, flag, & h2osoi_ice_col, h2osoi_liq_col) ! @@ -806,6 +750,32 @@ subroutine AerosolFluxes(bounds, num_snowc, filter_snowc, & forc_aer(g,13) + forc_aer(g,14) end do + ! if turn off aerosol effect in snow, zero out deposition flux + if (.not. snicar_use_aerosol) then + do c = bounds%begc,bounds%endc + + flx_bc_dep_dry(c) = 0._r8 + flx_bc_dep_wet(c) = 0._r8 + flx_bc_dep_phi(c) = 0._r8 + flx_bc_dep_pho(c) = 0._r8 + flx_bc_dep(c) = 0._r8 + flx_oc_dep_dry(c) = 0._r8 + flx_oc_dep_wet(c) = 0._r8 + flx_oc_dep_phi(c) = 0._r8 + flx_oc_dep_pho(c) = 0._r8 + flx_oc_dep(c) = 0._r8 + flx_dst_dep_wet1(c) = 0._r8 + flx_dst_dep_dry1(c) = 0._r8 + flx_dst_dep_wet2(c) = 0._r8 + flx_dst_dep_dry2(c) = 0._r8 + flx_dst_dep_wet3(c) = 0._r8 + flx_dst_dep_dry3(c) = 0._r8 + flx_dst_dep_wet4(c) = 0._r8 + flx_dst_dep_dry4(c) = 0._r8 + flx_dst_dep(c) = 0._r8 + end do + end if + ! aerosol deposition fluxes into top layer ! This is done after the inter-layer fluxes so that some aerosol ! is in the top layer after deposition, and is not immediately diff --git a/src/biogeophys/BareGroundFluxesMod.F90 b/src/biogeophys/BareGroundFluxesMod.F90 index e68e56841e..7db214065d 100644 --- a/src/biogeophys/BareGroundFluxesMod.F90 +++ b/src/biogeophys/BareGroundFluxesMod.F90 @@ -82,7 +82,8 @@ subroutine BareGroundFluxes(bounds, num_noexposedvegp, filter_noexposedvegp, & use shr_const_mod , only : SHR_CONST_RGAS use clm_varpar , only : nlevgrnd use clm_varcon , only : cpair, vkc, grav, denice, denh2o - use clm_varctl , only : use_lch4 + use clm_varcon , only : beta_param, nu_param, meier_param3 + use clm_varctl , only : use_lch4, z0param_method use landunit_varcon , only : istsoil, istcrop use QSatMod , only : QSat use SurfaceResistanceMod , only : do_soilevap_beta,do_soil_resistance_sl14 @@ -90,6 +91,8 @@ subroutine BareGroundFluxes(bounds, num_noexposedvegp, filter_noexposedvegp, & Wet_Bulb, Wet_BulbS, HeatIndex, AppTemp, & swbgt, hmdex, dis_coi, dis_coiS, THIndex, & SwampCoolEff, KtoC, VaporPres + use CanopyStateType , only : canopystate_type + ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -138,9 +141,6 @@ subroutine BareGroundFluxes(bounds, num_noexposedvegp, filter_noexposedvegp, & real(r8) :: raih ! temporary variable [kg/m2/s] real(r8) :: raiw ! temporary variable [kg/m2/s] real(r8) :: fm(bounds%begp:bounds%endp) ! needed for BGC only to diagnose 10m wind speed - real(r8) :: z0mg_patch(bounds%begp:bounds%endp) - real(r8) :: z0hg_patch(bounds%begp:bounds%endp) - real(r8) :: z0qg_patch(bounds%begp:bounds%endp) real(r8) :: e_ref2m ! 2 m height surface saturated vapor pressure [Pa] real(r8) :: qsat_ref2m ! 2 m height surface saturated specific humidity [kg/kg] real(r8) :: www ! surface soil wetness [-] @@ -190,8 +190,12 @@ subroutine BareGroundFluxes(bounds, num_noexposedvegp, filter_noexposedvegp, & forc_th => atm2lnd_inst%forc_th_downscaled_col , & ! Input: [real(r8) (:) ] atmospheric potential temperature (Kelvin) forc_t => atm2lnd_inst%forc_t_downscaled_col , & ! Input: [real(r8) (:) ] atmospheric temperature (Kelvin) forc_pbot => atm2lnd_inst%forc_pbot_downscaled_col , & ! Input: [real(r8) (:) ] atmospheric pressure (Pa) - forc_rho => atm2lnd_inst%forc_rho_downscaled_col , & ! Input: [real(r8) (:) ] density (kg/m**3) - forc_q => wateratm2lndbulk_inst%forc_q_downscaled_col , & ! Input: [real(r8) (:) ] atmospheric specific humidity (kg/kg) + forc_rho => atm2lnd_inst%forc_rho_downscaled_col , & ! Input: [real(r8) (:) ] density (kg/m**3) + forc_hgt_t => atm2lnd_inst%forc_hgt_t_grc , & ! Input: [real(r8) (:) ] observational height of temperature [m] + forc_hgt_u => atm2lnd_inst%forc_hgt_u_grc , & ! Input: [real(r8) (:) ] observational height of wind [m] + forc_hgt_q => atm2lnd_inst%forc_hgt_q_grc , & ! Input: [real(r8) (:) ] observational height of specific humidity [m] + + forc_q => wateratm2lndbulk_inst%forc_q_downscaled_col , & ! Input: [real(r8) (:) ] atmospheric specific humidity (kg/kg) watsat => soilstate_inst%watsat_col , & ! Input: [real(r8) (:,:) ] volumetric soil water at saturation (porosity) soilbeta => soilstate_inst%soilbeta_col , & ! Input: [real(r8) (:) ] soil wetness relative to field capacity @@ -235,16 +239,22 @@ subroutine BareGroundFluxes(bounds, num_noexposedvegp, filter_noexposedvegp, & rh_ref2m_r => waterdiagnosticbulk_inst%rh_ref2m_r_patch , & ! Output: [real(r8) (:) ] Rural 2 m height surface relative humidity (%) rh_ref2m => waterdiagnosticbulk_inst%rh_ref2m_patch , & ! Output: [real(r8) (:) ] 2 m height surface relative humidity (%) - forc_hgt_u_patch => frictionvel_inst%forc_hgt_u_patch , & ! Input: - displa => canopystate_inst%displa_patch , & ! Input: [real(r8) (:) ] displacement height (m) + forc_hgt_u_patch => frictionvel_inst%forc_hgt_u_patch , & ! Output: [real(r8) (:) ] observational height of wind at patch level [m] + forc_hgt_t_patch => frictionvel_inst%forc_hgt_t_patch , & ! Output: [real(r8) (:) ] observational height of temperature at patch level [m] + forc_hgt_q_patch => frictionvel_inst%forc_hgt_q_patch , & ! Output: [real(r8) (:) ] observational height of specific humidity at patch level [m] u10_clm => frictionvel_inst%u10_clm_patch , & ! Input: [real(r8) (:) ] 10 m height winds (m/s) zetamax => frictionvel_inst%zetamaxstable , & ! Input: [real(r8) ] max zeta value under stable conditions + zeta => frictionvel_inst%zeta_patch , & ! Output: [real(r8) (:) ] dimensionless stability parameter z0mg_col => frictionvel_inst%z0mg_col , & ! Output: [real(r8) (:) ] roughness length, momentum [m] z0hg_col => frictionvel_inst%z0hg_col , & ! Output: [real(r8) (:) ] roughness length, sensible heat [m] z0qg_col => frictionvel_inst%z0qg_col , & ! Output: [real(r8) (:) ] roughness length, latent heat [m] - z0mv => frictionvel_inst%z0mv_patch , & ! Output: [real(r8) (:) ] roughness length over vegetation, momentum [m] - z0hv => frictionvel_inst%z0hv_patch , & ! Output: [real(r8) (:) ] roughness length over vegetation, sensible heat [m] - z0qv => frictionvel_inst%z0qv_patch , & ! Output: [real(r8) (:) ] roughness length over vegetation, latent heat [m] + z0mg_patch => frictionvel_inst%z0mg_patch , & ! Output: [real(r8) (:) ] patch roughness length, momentum [m] + z0hg_patch => frictionvel_inst%z0hg_patch , & ! Output: [real(r8) (:) ] patch roughness length, sensible heat [m] + z0qg_patch => frictionvel_inst%z0qg_patch , & ! Output: [real(r8) (:) ] patch roughness length, latent heat [m] + z0mv => frictionvel_inst%z0mv_patch , & ! Output: [real(r8) (:) ] roughness length over vegetation, momentum [m] + z0hv => frictionvel_inst%z0hv_patch , & ! Output: [real(r8) (:) ] roughness length over vegetation, sensible heat [m] + z0qv => frictionvel_inst%z0qv_patch , & ! Output: [real(r8) (:) ] roughness length over vegetation, latent heat [m] + kbm1 => frictionvel_inst%kbm1_patch , & ! Output: [real(r8) (:) ] natural logarithm of z0mg_p/z0hg_p [-] ram1 => frictionvel_inst%ram1_patch , & ! Output: [real(r8) (:) ] aerodynamical resistance (s/m) num_iter => frictionvel_inst%num_iter_patch , & ! Output: [real(r8) (:) ] number of iterations htvp => energyflux_inst%htvp_col , & ! Input: [real(r8) (:) ] latent heat of evaporation (/sublimation) [J/kg] @@ -259,6 +269,8 @@ subroutine BareGroundFluxes(bounds, num_noexposedvegp, filter_noexposedvegp, & rssun => photosyns_inst%rssun_patch , & ! Output: [real(r8) (:) ] leaf sunlit stomatal resistance (s/m) (output from Photosynthesis) rssha => photosyns_inst%rssha_patch , & ! Output: [real(r8) (:) ] leaf shaded stomatal resistance (s/m) (output from Photosynthesis) + displa => canopystate_inst%displa_patch , & ! Output: [real(r8) (:) ] displacement height (m) + begp => bounds%begp , & endp => bounds%endp & ) @@ -337,20 +349,37 @@ subroutine BareGroundFluxes(bounds, num_noexposedvegp, filter_noexposedvegp, & tstar = temp1(p)*dth(p) qstar = temp2(p)*dqh(p) - z0hg_patch(p) = z0mg_patch(p) / exp(params_inst%a_coef * (ustar(p) * z0mg_patch(p) / 1.5e-5_r8)**params_inst%a_exp) + + select case (z0param_method) + case ('ZengWang2007') + z0hg_patch(p) = z0mg_patch(p) / exp(params_inst%a_coef * (ustar(p) * z0mg_patch(p) / nu_param)**params_inst%a_exp) + case ('Meier2022') + + ! After Yang et al. (2008) + ! (...)**0.5 = sqrt(...) and (...)**0.25 = sqrt(sqrt(...)) + ! likely more efficient to calculate as exponents + z0hg_patch(p) = meier_param3 * nu_param / ustar(p) * exp( -beta_param * ustar(p)**(0.5_r8) * (abs(tstar))**(0.25_r8)) + + end select + z0qg_patch(p) = z0hg_patch(p) + ! Update the forcing heights for new roughness lengths + forc_hgt_u_patch(p) = forc_hgt_u(g) + z0mg_patch(p) + displa(p) + forc_hgt_t_patch(p) = forc_hgt_t(g) + z0hg_patch(p) + displa(p) + forc_hgt_q_patch(p) = forc_hgt_q(g) + z0qg_patch(p) + displa(p) + thvstar = tstar*(1._r8+0.61_r8*forc_q(c)) + 0.61_r8*forc_th(c)*qstar - zeta = zldis(p)*vkc*grav*thvstar/(ustar(p)**2*thv(c)) + zeta(p) = zldis(p)*vkc*grav*thvstar/(ustar(p)**2*thv(c)) - if (zeta >= 0._r8) then !stable - zeta = min(zetamax,max(zeta,0.01_r8)) + if (zeta(p) >= 0._r8) then !stable + zeta(p) = min(zetamax,max(zeta(p),0.01_r8)) um(p) = max(ur(p),0.1_r8) else !unstable - zeta = max(-100._r8,min(zeta,-0.01_r8)) + zeta(p) = max(-100._r8,min(zeta(p),-0.01_r8)) wc = beta(c)*(-grav*ustar(p)*thvstar*zii(c)/thv(c))**0.333_r8 um(p) = sqrt(ur(p)*ur(p) + wc*wc) end if - obu(p) = zldis(p)/zeta + obu(p) = zldis(p)/zeta(p) num_iter(p) = iter end do @@ -441,6 +470,8 @@ subroutine BareGroundFluxes(bounds, num_noexposedvegp, filter_noexposedvegp, & t_ref2m_r(p) = t_ref2m(p) end if + kbm1(p) = log(z0mg_patch(p) / z0hg_patch(p)) + ! Copy local patch ground roughness back to column arrays for history output which ! uses the column arrays. z0mg is unchanged so only need to copy z0hg and z0qg diff --git a/src/biogeophys/BiogeophysPreFluxCalcsMod.F90 b/src/biogeophys/BiogeophysPreFluxCalcsMod.F90 index 2aa56de927..574af6f782 100644 --- a/src/biogeophys/BiogeophysPreFluxCalcsMod.F90 +++ b/src/biogeophys/BiogeophysPreFluxCalcsMod.F90 @@ -10,14 +10,15 @@ module BiogeophysPreFluxCalcsMod #include "shr_assert.h" use shr_kind_mod , only : r8 => shr_kind_r8 use shr_log_mod , only : errMsg => shr_log_errMsg + use abortutils , only : endrun use decompMod , only : bounds_type use PatchType , only : patch use ColumnType , only : col use LandunitType , only : lun use clm_varcon , only : spval use clm_varpar , only : nlevgrnd, nlevsno, nlevurb, nlevmaxurbgrnd - use clm_varctl , only : use_fates - use pftconMod , only : pftcon + use clm_varctl , only : use_fates, z0param_method, iulog + use pftconMod , only : pftcon, noveg use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall use landunit_varcon , only : istsoil, istcrop, istice use clm_varcon , only : hvap, hsub @@ -32,6 +33,8 @@ module BiogeophysPreFluxCalcsMod use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type use WaterStateBulkType , only : waterstatebulk_type use SurfaceResistanceMod , only : calc_soilevap_resis + use WaterFluxBulkType , only : waterfluxbulk_type + ! ! !PUBLIC TYPES: implicit none @@ -57,7 +60,7 @@ subroutine BiogeophysPreFluxCalcs(bounds, & num_urbanc, filter_urbanc, & clm_fates, atm2lnd_inst, canopystate_inst, energyflux_inst, frictionvel_inst, & soilstate_inst, temperature_inst, & - wateratm2lndbulk_inst, waterdiagnosticbulk_inst, waterstatebulk_inst) + wateratm2lndbulk_inst, waterdiagnosticbulk_inst, waterstatebulk_inst, waterfluxbulk_inst) ! ! !DESCRIPTION: ! Do various calculations that need to happen before the main biogeophysics flux calculations @@ -80,6 +83,7 @@ subroutine BiogeophysPreFluxCalcs(bounds, & type(wateratm2lndbulk_type) , intent(in) :: wateratm2lndbulk_inst type(waterdiagnosticbulk_type) , intent(in) :: waterdiagnosticbulk_inst type(waterstatebulk_type) , intent(in) :: waterstatebulk_inst + type(waterfluxbulk_type) , intent(in) :: waterfluxbulk_inst ! ! !LOCAL VARIABLES: integer :: fp, p @@ -88,12 +92,13 @@ subroutine BiogeophysPreFluxCalcs(bounds, & !----------------------------------------------------------------------- call SetZ0mDisp(bounds, num_nolakep, filter_nolakep, & - clm_fates, canopystate_inst) + clm_fates, frictionvel_inst, canopystate_inst) call frictionvel_inst%SetRoughnessLengthsAndForcHeightsNonLake(bounds, & num_nolakec, filter_nolakec, & num_nolakep, filter_nolakep, & - atm2lnd_inst, waterdiagnosticbulk_inst, canopystate_inst) + atm2lnd_inst, waterdiagnosticbulk_inst, canopystate_inst, & + waterfluxbulk_inst) call CalcInitialTemperatureAndEnergyVars(bounds, & num_nolakec, filter_nolakec, & @@ -115,25 +120,34 @@ end subroutine BiogeophysPreFluxCalcs !----------------------------------------------------------------------- subroutine SetZ0mDisp(bounds, num_nolakep, filter_nolakep, & - clm_fates, canopystate_inst) + clm_fates, frictionvel_inst, canopystate_inst) ! ! !DESCRIPTION: ! Set z0m and displa ! + ! !USES: + use clm_time_manager, only : is_first_step, get_nstep, is_beg_curr_year + use clm_varcon , only : cd1_param + use decompMod , only : subgrid_level_patch + use BalanceCheckMod , only : GetBalanceCheckSkipSteps ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_nolakep ! number of column non-lake points in patch filter integer , intent(in) :: filter_nolakep(:) ! patch filter for non-lake points type(hlm_fates_interface_type) , intent(in) :: clm_fates + type(frictionvel_type) , intent(in) :: frictionvel_inst type(canopystate_type) , intent(inout) :: canopystate_inst ! ! !LOCAL VARIABLES: - integer :: fp, p + integer :: fp, p, c character(len=*), parameter :: subname = 'SetZ0mDisp' + real(r8) :: U_ustar ! wind at canopy height divided by friction velocity (unitless) + !----------------------------------------------------------------------- associate( & + z0mg => frictionvel_inst%z0mg_col , & ! Input: [real(r8) (:) ] roughness length of ground, momentum [m] htop => canopystate_inst%htop_patch , & ! Input: [real(r8) (:) ] canopy top (m) z0m => canopystate_inst%z0m_patch , & ! Output: [real(r8) (:) ] momentum roughness length (m) displa => canopystate_inst%displa_patch & ! Output: [real(r8) (:) ] displacement height (m) @@ -152,10 +166,53 @@ subroutine SetZ0mDisp(bounds, num_nolakep, filter_nolakep, & do fp = 1, num_nolakep p = filter_nolakep(fp) + c = patch%column(p) if( .not.(patch%is_fates(p))) then - z0m(p) = pftcon%z0mr(patch%itype(p)) * htop(p) - displa(p) = pftcon%displar(patch%itype(p)) * htop(p) + select case (z0param_method) + case ('ZengWang2007') + + z0m(p) = pftcon%z0mr(patch%itype(p)) * htop(p) + displa(p) = pftcon%displar(patch%itype(p)) * htop(p) + + case ('Meier2022') + + ! Don't set on first few steps of a simulation, since htop isn't set yet, need to wait until after first do_alb time + if ( is_first_step() .or. get_nstep() <= GetBalanceCheckSkipSteps()-1 ) then + z0m(p) = 0._r8 + displa(p) = 0._r8 + cycle + ! If a crop type and it's the start of the year, htop gets reset to + ! zero... + else if ( is_beg_curr_year() .and. pftcon%crop(patch%itype(p)) /= 0.0_r8 )then + z0m(p) = 0._r8 + displa(p) = 0._r8 + end if + + if (patch%itype(p) == noveg) then + z0m(p) = 0._r8 + displa(p) = 0._r8 + + else + ! Compute as if elai+esai = LAImax in CanopyFluxes + displa(p) = htop(p) * (1._r8 - (1._r8 - exp(-(cd1_param * (pftcon%z0v_LAImax(patch%itype(p))))**0.5_r8)) & + / (cd1_param*(pftcon%z0v_LAImax(patch%itype(p)) ))**0.5_r8) + + U_ustar = 4._r8 * (pftcon%z0v_Cs(patch%itype(p)) + pftcon%z0v_Cr(patch%itype(p)) * (pftcon%z0v_LAImax(patch%itype(p))) & + / 2._r8)**(-0.5_r8) / (pftcon%z0v_LAImax(patch%itype(p))) / pftcon%z0v_c(patch%itype(p)) + + if ( htop(p) <= 1.e-10_r8 )then + z0m(p) = z0mg(c) + else + z0m(p) = htop(p) * (1._r8 - displa(p) / htop(p)) * exp(-0.4_r8 * U_ustar + & + log(pftcon%z0v_cw(patch%itype(p))) - 1._r8 + pftcon%z0v_cw(patch%itype(p))**(-1._r8)) + end if + + end if + + + end select + end if end do diff --git a/src/biogeophys/CanopyFluxesMod.F90 b/src/biogeophys/CanopyFluxesMod.F90 index 0dc9bbf797..f152e761eb 100644 --- a/src/biogeophys/CanopyFluxesMod.F90 +++ b/src/biogeophys/CanopyFluxesMod.F90 @@ -14,7 +14,7 @@ module CanopyFluxesMod use shr_log_mod , only : errMsg => shr_log_errMsg use abortutils , only : endrun use clm_varctl , only : iulog, use_cn, use_lch4, use_c13, use_c14, use_cndv, use_fates, & - use_luna, use_hydrstress, use_biomass_heat_storage + use_luna, use_hydrstress, use_biomass_heat_storage, z0param_method use clm_varpar , only : nlevgrnd, nlevsno, nlevcan, mxpft use pftconMod , only : pftcon use decompMod , only : bounds_type, subgrid_level_patch @@ -47,6 +47,7 @@ module CanopyFluxesMod use EDTypesMod , only : ed_site_type use SoilWaterRetentionCurveMod, only : soil_water_retention_curve_type use LunaMod , only : Update_Photosynthesis_Capacity, Acc24_Climate_LUNA,Acc240_Climate_LUNA,Clear24_Climate_LUNA + use NumericsMod , only : truncate_small_values ! ! !PUBLIC TYPES: implicit none @@ -114,6 +115,7 @@ subroutine CanopyFluxesReadNML(NLFilename) namelist /canopyfluxes_inparm/ use_biomass_heat_storage namelist /canopyfluxes_inparm/ itmax_canopy_fluxes + ! Initialize options to default values, in case they are not specified in ! the namelist @@ -228,6 +230,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, use clm_varcon , only : denh2o, tfrz, tlsai_crit, alpha_aero use clm_varcon , only : c14ratio, spval use clm_varcon , only : c_water, c_dry_biomass, c_to_b + use clm_varcon , only : nu_param, cd1_param use perf_mod , only : t_startf, t_stopf use QSatMod , only : QSat use CLMFatesInterfaceMod, only : hlm_fates_interface_type @@ -237,6 +240,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, SwampCoolEff, KtoC, VaporPres use SoilWaterRetentionCurveMod, only : soil_water_retention_curve_type use LunaMod , only : is_time_to_run_LUNA + ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -378,6 +382,9 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, integer :: index ! patch index for error real(r8) :: egvf ! effective green vegetation fraction real(r8) :: lt ! elai+esai + real(r8) :: U_ustar ! wind at canopy height divided by friction velocity (unitless) + real(r8) :: U_ustar_ini ! initial guess of wind at canopy height divided by friction velocity (unitless) + real(r8) :: U_ustar_prev ! wind at canopy height divided by friction velocity from the previous iteration (unitless) real(r8) :: ri ! stability parameter for under canopy air (unitless) real(r8) :: csoilb ! turbulent transfer coefficient over bare soil (unitless) real(r8) :: ricsoilc ! modified transfer coefficient under dense canopy (unitless) @@ -416,6 +423,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, real(r8) :: uuc(bounds%begp:bounds%endp) ! undercanopy windspeed real(r8) :: carea_stem ! cross-sectional area of stem real(r8) :: dlrad_leaf ! Downward longwave radition from leaf + real(r8) :: snocan_baseline(bounds%begp:bounds%endp) ! baseline of snocan for use in truncate_small_values ! Indices for raw and rah integer, parameter :: above_canopy = 1 ! Above canopy @@ -449,9 +457,17 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, slatop => pftcon%slatop , & ! SLA at top of canopy [m^2/gC] fbw => pftcon%fbw , & ! Input: fraction of biomass that is water nstem => pftcon%nstem , & ! Input: stem number density (#ind/m2) + woody => pftcon%woody , & ! Input: woody flag rstem_per_dbh => pftcon%rstem_per_dbh , & ! Input: stem resistance per stem diameter (s/m**2) wood_density => pftcon%wood_density , & ! Input: dry wood density (kg/m3) + z0v_Cr => pftcon%z0v_Cr , & ! Input: roughness-element drag coefficient for Raupach92 parameterization (-) + z0v_Cs => pftcon%z0v_Cs , & ! Input: substrate-element drag coefficient for Raupach92 parameterization (-) + z0v_c => pftcon%z0v_c , & ! Input: c parameter for Raupach92 parameterization (-) + z0v_cw => pftcon%z0v_cw , & ! Input: roughness sublayer depth coefficient for Raupach92 parameterization (-) + z0v_LAIoff => pftcon%z0v_LAIoff , & ! Input: leaf area index offset for Raupach92 parameterization (-) + z0v_LAImax => pftcon%z0v_LAImax , & ! Input: onset of over-sheltering for Raupach92 parameterization (-) + forc_lwrad => atm2lnd_inst%forc_lwrad_downscaled_col , & ! Input: [real(r8) (:) ] downward infrared (longwave) radiation (W/m**2) forc_q => wateratm2lndbulk_inst%forc_q_downscaled_col , & ! Input: [real(r8) (:) ] atmospheric specific humidity (kg/kg) forc_pbot => atm2lnd_inst%forc_pbot_downscaled_col , & ! Input: [real(r8) (:) ] atmospheric pressure (Pa) @@ -516,7 +532,12 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, soilbeta => soilstate_inst%soilbeta_col , & ! Input: [real(r8) (:) ] soil wetness relative to field capacity u10_clm => frictionvel_inst%u10_clm_patch , & ! Input: [real(r8) (:) ] 10 m height winds (m/s) - forc_hgt_u_patch => frictionvel_inst%forc_hgt_u_patch , & ! Input: [real(r8) (:) ] observational height of wind at patch level [m] + forc_hgt_t => atm2lnd_inst%forc_hgt_t_grc , & ! Input: [real(r8) (:) ] observational height of temperature [m] + forc_hgt_u => atm2lnd_inst%forc_hgt_u_grc , & ! Input: [real(r8) (:) ] observational height of wind [m] + forc_hgt_q => atm2lnd_inst%forc_hgt_q_grc , & ! Input: [real(r8) (:) ] observational height of specific humidity [m] + forc_hgt_t_patch => frictionvel_inst%forc_hgt_t_patch , & ! Output: [real(r8) (:) ] observational height of temperature at patch level [m] + forc_hgt_q_patch => frictionvel_inst%forc_hgt_q_patch , & ! Output: [real(r8) (:) ] observational height of specific humidity at patch level [m] + forc_hgt_u_patch => frictionvel_inst%forc_hgt_u_patch , & ! Output: [real(r8) (:) ] observational height of wind at patch level [m] z0mg => frictionvel_inst%z0mg_col , & ! Input: [real(r8) (:) ] roughness length of ground, momentum [m] zetamax => frictionvel_inst%zetamaxstable , & ! Input: [real(r8) ] max zeta value under stable conditions ram1 => frictionvel_inst%ram1_patch , & ! Output: [real(r8) (:) ] aerodynamical resistance (s/m) @@ -865,13 +886,51 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, do f = 1, fn p = filterp(f) c = patch%column(p) + g = patch%gridcell(p) + + select case (z0param_method) + case ('ZengWang2007') + lt = min(elai(p)+esai(p), tlsai_crit) + egvf =(1._r8 - alpha_aero * exp(-lt)) / (1._r8 - alpha_aero * exp(-tlsai_crit)) + displa(p) = egvf * displa(p) + z0mv(p) = exp(egvf * log(z0mv(p)) + (1._r8 - egvf) * log(z0mg(c))) + + case ('Meier2022') + lt = max(1.e-5_r8, elai(p) + esai(p)) + displa(p) = htop(p) * (1._r8 - (1._r8 - exp(-(cd1_param * lt)**0.5_r8)) / (cd1_param*lt)**0.5_r8) + + lt = min(lt,z0v_LAImax(patch%itype(p))) + delt = 2._r8 + ! Reminder that (...)**(-0.5) = 1 / sqrt(...) + U_ustar_ini = (z0v_Cs(patch%itype(p)) + z0v_Cr(patch%itype(p)) * lt * 0.5_r8)**(-0.5_r8) & + *z0v_c(patch%itype(p)) * lt * 0.25_r8 + U_ustar = U_ustar_ini + + do while (delt > 1.e-4_r8) + U_ustar_prev = U_ustar + U_ustar = U_ustar_ini * exp(U_ustar_prev) + delt = abs(U_ustar - U_ustar_prev) + end do + + U_ustar = 4._r8 * U_ustar / lt / z0v_c(patch%itype(p)) + + z0mv(p) = htop(p) * (1._r8 - displa(p) / htop(p)) * exp(-vkc * U_ustar + & + log(z0v_cw(patch%itype(p))) - 1._r8 + z0v_cw(patch%itype(p))**(-1._r8)) + + + case default + write(iulog,*) 'ERROR: unknown z0para_method: ', z0param_method + call endrun(msg = 'unknown z0param_method', additional_msg = errMsg(sourcefile, __LINE__)) + end select + + z0hv(p) = z0mv(p) + z0qv(p) = z0mv(p) + + ! Update the forcing heights + forc_hgt_u_patch(p) = forc_hgt_u(g) + z0mv(p) + displa(p) + forc_hgt_t_patch(p) = forc_hgt_t(g) + z0hv(p) + displa(p) + forc_hgt_q_patch(p) = forc_hgt_q(g) + z0qv(p) + displa(p) - lt = min(elai(p)+esai(p), tlsai_crit) - egvf =(1._r8 - alpha_aero * exp(-lt)) / (1._r8 - alpha_aero * exp(-tlsai_crit)) - displa(p) = egvf * displa(p) - z0mv(p) = exp(egvf * log(z0mv(p)) + (1._r8 - egvf) * log(z0mg(c))) - z0hv(p) = z0mv(p) - z0qv(p) = z0mv(p) end do found = .false. @@ -1007,7 +1066,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, ! changed by K.Sakaguchi from here ! transfer coefficient over bare soil is changed to a local variable ! just for readability of the code (from line 680) - csoilb = vkc / (params_inst%a_coef * (z0mg(c) * uaf(p) / 1.5e-5_r8)**params_inst%a_exp) + csoilb = vkc / (params_inst%a_coef * (z0mg(c) * uaf(p) / nu_param)**params_inst%a_exp) !compute the stability parameter for ricsoilc ("S" in Sakaguchi&Zeng,2008) @@ -1329,16 +1388,29 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, zeta(p) = zldis(p)*vkc*grav*thvstar/(ustar(p)**2*thv(c)) if (zeta(p) >= 0._r8) then !stable - ! remove stability cap when biomass heat storage is active - if(use_biomass_heat_storage) then - zeta(p) = min(100._r8,max(zeta(p),0.01_r8)) - else - zeta(p) = min(zetamax,max(zeta(p),0.01_r8)) - endif + zeta(p) = min(zetamax,max(zeta(p),0.01_r8)) um(p) = max(ur(p),0.1_r8) else !unstable zeta(p) = max(-100._r8,min(zeta(p),-0.01_r8)) - wc = beta*(-grav*ustar(p)*thvstar*zii/thv(c))**0.333_r8 + if ( ustar(p)*thvstar > 0.0d00 )then + write(iulog,*) 'ustar*thvstar is positive and has to be negative' + write(iulog,*) 'p = ', p + write(iulog,*) '-grav*ustar(p)*thvstar*zii/thv(c) = ', -grav*ustar(p)*thvstar*zii/thv(c) + write(iulog,*) 'ustar = ', ustar(p) + write(iulog,*) 'thvstar = ', thvstar + write(iulog,*) 'thv = ', thv(c) + write(iulog,*) 'displa= ', displa(p) + write(iulog,*) 'z0mg= ', z0mg(c) + write(iulog,*) 'zeta= ', zeta(p) + write(iulog,*) 'temp1= ', temp1(p) + write(iulog,*) 'dth= ', dth(p) + write(iulog,*) 'rah(above)= ', rah(p,above_canopy) + write(iulog,*) 'rah(below)= ', rah(p,below_canopy) + !call endrun(decomp_index=p, clmlevel=namep, msg=errmsg(sourcefile, __LINE__)) + wc = 0.0_r8 + else + wc = beta*(-grav*ustar(p)*thvstar*zii/thv(c))**0.333_r8 + end if um(p) = sqrt(ur(p)*ur(p)+wc*wc) end if obu(p) = zldis(p)/zeta(p) @@ -1405,8 +1477,9 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, dt_stem(p) = 0._r8 endif + dhsdt_canopy(p) = dt_stem(p)*cp_stem(p)/dtime & - +(t_veg(p)-tl_ini(p))*cp_leaf(p)/dtime + + (t_veg(p)-tl_ini(p))*cp_leaf(p)/dtime t_stem(p) = t_stem(p) + dt_stem(p) else @@ -1525,6 +1598,9 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, cgrndl(p) = cgrndl(p) + forc_rho(c)*wtgq(p)*wtalq(p)*dqgdT(c) cgrnd(p) = cgrnds(p) + cgrndl(p)*htvp(c) + ! save before updating + snocan_baseline(p) = snocan(p) + ! Update dew accumulation (kg/m2) if (t_veg(p) > tfrz ) then ! above freezing, update accumulation in liqcan if ((qflx_evap_veg(p)-qflx_tran_veg(p))*dtime > liqcan(p)) then ! all liq evap @@ -1542,6 +1618,12 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, end if end do + + ! Remove snocan that got reduced by more than a factor of rel_epsilon + ! snocan < rel_epsilon * snocan_baseline will be set to zero + ! See NumericsMod for rel_epsilon value + call truncate_small_values(fn, filterp, begp, endp, & + snocan_baseline(begp:endp), snocan(begp:endp)) if ( use_fates ) then @@ -1654,6 +1736,7 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, fn = fn + 1 filterp(fn) = p end if + end do do f = 1, fn diff --git a/src/biogeophys/CanopyStateType.F90 b/src/biogeophys/CanopyStateType.F90 index a94baaa319..313f7a83f3 100644 --- a/src/biogeophys/CanopyStateType.F90 +++ b/src/biogeophys/CanopyStateType.F90 @@ -225,9 +225,7 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='canopy top', & ptr_patch=this%htop_patch) endif - - - endif !fates or CN + endif if(use_fates_sp)then this%tlai_hist_patch(begp:endp) = spval diff --git a/src/biogeophys/FrictionVelocityMod.F90 b/src/biogeophys/FrictionVelocityMod.F90 index 21a09fbc13..7cea2a22f9 100644 --- a/src/biogeophys/FrictionVelocityMod.F90 +++ b/src/biogeophys/FrictionVelocityMod.F90 @@ -11,8 +11,9 @@ module FrictionVelocityMod use shr_log_mod , only : errMsg => shr_log_errMsg use shr_const_mod , only : SHR_CONST_PI use decompMod , only : bounds_type + use abortutils , only : endrun use clm_varcon , only : spval - use clm_varctl , only : use_cn, use_luna + use clm_varctl , only : use_cn, use_luna, z0param_method, use_z0m_snowmelt use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch @@ -22,6 +23,7 @@ module FrictionVelocityMod use atm2lndType , only : atm2lnd_type use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type use CanopyStateType , only : canopystate_type + use WaterFluxBulkType , only : waterfluxbulk_type ! ! !PUBLIC TYPES: implicit none @@ -35,6 +37,7 @@ module FrictionVelocityMod real(r8), public :: zetamaxstable = -999._r8 ! Max value zeta ("height" used in Monin-Obukhov theory) can go to under stable conditions real(r8) :: zsno = -999._r8 ! Momentum roughness length for snow (m) real(r8) :: zlnd = -999._r8 ! Momentum roughness length for soil, glacier, wetland (m) + real(r8) :: zglc = -999._r8 ! Momentum roughness length for glacier (only used with z0param_method = 'Meier2022') (m) ! Roughness length/resistance for friction velocity calculation @@ -52,7 +55,12 @@ module FrictionVelocityMod real(r8), pointer, public :: z0mv_patch (:) ! patch roughness length over vegetation, momentum [m] real(r8), pointer, public :: z0hv_patch (:) ! patch roughness length over vegetation, sensible heat [m] real(r8), pointer, public :: z0qv_patch (:) ! patch roughness length over vegetation, latent heat [m] - real(r8), pointer, public :: z0mg_col (:) ! col roughness length over ground, momentum [m] + real(r8), pointer, public :: z0mg_patch (:) ! patch roughness length over ground, momentum [m] + real(r8), pointer, public :: z0hg_patch (:) ! patch roughness length over ground, sensible heat [m] + real(r8), pointer, public :: z0qg_patch (:) ! patch roughness length over ground, latent heat [m] + real(r8), pointer, public :: kbm1_patch (:) ! natural logarithm of z0mg_p/z0hg_p [-] + real(r8), pointer, public :: z0mg_col (:) ! col roughness length over ground, momentum [m] + real(r8), pointer, public :: z0mg_2D_col (:) ! 2-D field of input col roughness length over ground, momentum [m] real(r8), pointer, public :: z0hg_col (:) ! col roughness length over ground, sensible heat [m] real(r8), pointer, public :: z0qg_col (:) ! col roughness length over ground, latent heat [m] ! variables to add history output from CanopyFluxesMod @@ -149,7 +157,12 @@ subroutine InitAllocate(this, bounds) allocate(this%z0mv_patch (begp:endp)) ; this%z0mv_patch (:) = nan allocate(this%z0hv_patch (begp:endp)) ; this%z0hv_patch (:) = nan allocate(this%z0qv_patch (begp:endp)) ; this%z0qv_patch (:) = nan + allocate(this%z0mg_patch (begp:endp)) ; this%z0mg_patch (:) = nan + allocate(this%z0hg_patch (begp:endp)) ; this%z0hg_patch (:) = nan + allocate(this%z0qg_patch (begp:endp)) ; this%z0qg_patch (:) = nan + allocate(this%kbm1_patch (begp:endp)) ; this%kbm1_patch (:) = nan allocate(this%z0mg_col (begc:endc)) ; this%z0mg_col (:) = nan + allocate(this%z0mg_2D_col (begc:endc)) ; this%z0mg_2D_col (:) = nan allocate(this%z0qg_col (begc:endc)) ; this%z0qg_col (:) = nan allocate(this%z0hg_col (begc:endc)) ; this%z0hg_col (:) = nan allocate(this%rah1_patch (begp:endp)) ; this%rah1_patch (:) = nan @@ -299,20 +312,36 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='Z0HV', units='m', & avgflag='A', long_name='roughness length over vegetation, sensible heat', & ptr_patch=this%z0hv_patch, default='inactive', l2g_scale_type='veg') - end if - if (use_cn) then this%z0mv_patch(begp:endp) = spval call hist_addfld1d (fname='Z0MV', units='m', & avgflag='A', long_name='roughness length over vegetation, momentum', & ptr_patch=this%z0mv_patch, default='inactive', l2g_scale_type='veg') - end if - if (use_cn) then this%z0qv_patch(begp:endp) = spval call hist_addfld1d (fname='Z0QV', units='m', & avgflag='A', long_name='roughness length over vegetation, latent heat', & ptr_patch=this%z0qv_patch, default='inactive', l2g_scale_type='veg') + + this%z0hg_patch(begp:endp) = spval + call hist_addfld1d (fname='Z0HG_P', units='m', & + avgflag='A', long_name='patch roughness length over ground, sensible heat', & + ptr_patch=this%z0hg_patch, default='inactive') + + this%z0mg_patch(begp:endp) = spval + call hist_addfld1d (fname='Z0MG_P', units='m', & + avgflag='A', long_name='patch roughness length over ground, momentum', & + ptr_patch=this%z0mg_patch, default='inactive') + + this%z0qg_patch(begp:endp) = spval + call hist_addfld1d (fname='Z0QG_P', units='m', & + avgflag='A', long_name='patch roughness length over ground, latent heat', & + ptr_patch=this%z0qg_patch, default='inactive') + + this%kbm1_patch(begp:endp) = spval + call hist_addfld1d (fname='KBM1', units='unitless', & + avgflag='A', long_name='natural logarithm of Z0MG_P/Z0HG_P', & + ptr_patch=this%kbm1_patch, default='inactive') end if if (use_luna) then @@ -374,6 +403,11 @@ subroutine ReadParams( this, params_ncid ) ! Momentum roughness length for soil, glacier, wetland (m) call readNcdioScalar(params_ncid, 'zlnd', subname, this%zlnd) + ! Separated roughness length for glacier if z0param_method == 'Meier2022' + if (z0param_method == 'Meier2022') then + call readNcdioScalar(params_ncid, 'zglc', subname, this%zglc) + end if + end subroutine ReadParams !------------------------------------------------------------------------ @@ -424,7 +458,6 @@ subroutine ReadNamelist( this, NLFilename ) use shr_mpi_mod , only : shr_mpi_bcast use clm_varctl , only : iulog use shr_log_mod , only : errMsg => shr_log_errMsg - use abortutils , only : endrun ! ! !ARGUMENTS: class(frictionvel_type), intent(inout) :: this @@ -477,11 +510,13 @@ end subroutine ReadNamelist !----------------------------------------------------------------------- subroutine SetRoughnessLengthsAndForcHeightsNonLake(this, bounds, & num_nolakec, filter_nolakec, num_nolakep, filter_nolakep, & - atm2lnd_inst, waterdiagnosticbulk_inst, canopystate_inst) + atm2lnd_inst, waterdiagnosticbulk_inst, canopystate_inst, waterfluxbulk_inst) ! ! !DESCRIPTION: ! Set roughness lengths and forcing heights for non-lake points ! + ! !USES: + use clm_varcon , only : rpi, b1_param, b4_param, meier_param1, meier_param2 ! !ARGUMENTS: class(frictionvel_type) , intent(inout) :: this type(bounds_type) , intent(in) :: bounds @@ -492,6 +527,7 @@ subroutine SetRoughnessLengthsAndForcHeightsNonLake(this, bounds, & type(atm2lnd_type) , intent(in) :: atm2lnd_inst type(waterdiagnosticbulk_type) , intent(in) :: waterdiagnosticbulk_inst type(canopystate_type) , intent(in) :: canopystate_inst + type(waterfluxbulk_type) , intent(in) :: waterfluxbulk_inst ! ! !LOCAL VARIABLES: integer :: fc, c @@ -505,6 +541,10 @@ subroutine SetRoughnessLengthsAndForcHeightsNonLake(this, bounds, & z0mv => this%z0mv_patch , & ! Output: [real(r8) (:) ] roughness length over vegetation, momentum [m] z0hv => this%z0hv_patch , & ! Output: [real(r8) (:) ] roughness length over vegetation, sensible heat [m] z0qv => this%z0qv_patch , & ! Output: [real(r8) (:) ] roughness length over vegetation, latent heat [m] + z0mg_p => this%z0mg_patch , & ! Output: [real(r8) (:) ] patch roughness length over ground, momentum [m] + z0hg_p => this%z0hg_patch , & ! Output: [real(r8) (:) ] patch roughness length over ground, sensible heat [m] + z0qg_p => this%z0qg_patch , & ! Output: [real(r8) (:) ] patch roughness length over ground, latent heat [m] + kbm1 => this%kbm1_patch , & ! Output: [real(r8) (:) ] natural logarithm of z0mg_p/z0hg_p [-] z0hg => this%z0hg_col , & ! Output: [real(r8) (:) ] roughness length over ground, sensible heat [m] z0mg => this%z0mg_col , & ! Output: [real(r8) (:) ] roughness length over ground, momentum [m] z0qg => this%z0qg_col , & ! Output: [real(r8) (:) ] roughness length over ground, latent heat [m] @@ -516,12 +556,14 @@ subroutine SetRoughnessLengthsAndForcHeightsNonLake(this, bounds, & frac_veg_nosno => canopystate_inst%frac_veg_nosno_patch , & ! Input: [integer (:) ] fraction of vegetation not covered by snow (0 OR 1) [-] frac_sno => waterdiagnosticbulk_inst%frac_sno_col , & ! Input: [real(r8) (:) ] fraction of ground covered by snow (0 to 1) + snomelt_accum => waterdiagnosticbulk_inst%snomelt_accum_col , & ! Input: [real(r8) (:) ] accumulated col snow melt for z0m calculation (m H2O) urbpoi => lun%urbpoi , & ! Input: [logical (:) ] true => landunit is an urban point z_0_town => lun%z_0_town , & ! Input: [real(r8) (:) ] momentum roughness length of urban landunit (m) z_d_town => lun%z_d_town , & ! Input: [real(r8) (:) ] displacement height of urban landunit (m) forc_hgt_t => atm2lnd_inst%forc_hgt_t_grc , & ! Input: [real(r8) (:) ] observational height of temperature [m] forc_hgt_u => atm2lnd_inst%forc_hgt_u_grc , & ! Input: [real(r8) (:) ] observational height of wind [m] - forc_hgt_q => atm2lnd_inst%forc_hgt_q_grc & ! Input: [real(r8) (:) ] observational height of specific humidity [m] + forc_hgt_q => atm2lnd_inst%forc_hgt_q_grc , & ! Input: [real(r8) (:) ] observational height of specific humidity [m] + z0mg_2D => this%z0mg_2D_col & ! Input: [real(r8) (:) ] 2-D field of input col roughness length over ground, momentum [m] ) do fc = 1, num_nolakec @@ -529,13 +571,37 @@ subroutine SetRoughnessLengthsAndForcHeightsNonLake(this, bounds, & ! Ground roughness lengths over non-lake columns (includes bare ground, ground ! underneath canopy, wetlands, etc.) - if (frac_sno(c) > 0._r8) then - z0mg(c) = this%zsno - else - z0mg(c) = this%zlnd - end if + + select case (z0param_method) + case ('ZengWang2007') + if (frac_sno(c) > 0._r8) then + z0mg(c) = this%zsno + else + z0mg(c) = this%zlnd + end if + case ('Meier2022') ! Bare ground and ice have a different value + l = col%landunit(c) + if (frac_sno(c) > 0._r8) then ! Do snow first because ice could be snow-covered + if(use_z0m_snowmelt) then + if ( snomelt_accum(c) < 1.e-5_r8 )then + z0mg(c) = exp(-b1_param * rpi * 0.5_r8 + b4_param) * 1.e-3_r8 + else + z0mg(c) = exp(b1_param * (atan((log10(snomelt_accum(c)) + meier_param1) / meier_param2)) + b4_param) * 1.e-3_r8 + end if + else + z0mg(c) = this%zsno + end if + else if (lun%itype(l) == istice) then + z0mg(c) = this%zglc + else + z0mg(c) = this%zlnd + end if + end select + z0hg(c) = z0mg(c) ! initial set only z0qg(c) = z0mg(c) ! initial set only + + end do do fp = 1,num_nolakep @@ -545,6 +611,13 @@ subroutine SetRoughnessLengthsAndForcHeightsNonLake(this, bounds, & z0mv(p) = z0m(p) z0hv(p) = z0mv(p) z0qv(p) = z0mv(p) + + ! Set to arbitrary value (will be overwritten by respective modules + z0mg_p(p) = spval + z0hg_p(p) = spval + z0qg_p(p) = spval + kbm1(p) = spval + end do ! Make forcing height a patch-level quantity that is the atmospheric forcing @@ -557,17 +630,17 @@ subroutine SetRoughnessLengthsAndForcHeightsNonLake(this, bounds, & if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then if (frac_veg_nosno(p) == 0) then forc_hgt_u_patch(p) = forc_hgt_u(g) + z0mg(c) + displa(p) - forc_hgt_t_patch(p) = forc_hgt_t(g) + z0mg(c) + displa(p) - forc_hgt_q_patch(p) = forc_hgt_q(g) + z0mg(c) + displa(p) + forc_hgt_t_patch(p) = forc_hgt_t(g) + z0hg(c) + displa(p) + forc_hgt_q_patch(p) = forc_hgt_q(g) + z0qg(c) + displa(p) else - forc_hgt_u_patch(p) = forc_hgt_u(g) + z0m(p) + displa(p) - forc_hgt_t_patch(p) = forc_hgt_t(g) + z0m(p) + displa(p) - forc_hgt_q_patch(p) = forc_hgt_q(g) + z0m(p) + displa(p) + forc_hgt_u_patch(p) = forc_hgt_u(g) + z0mv(p) + displa(p) + forc_hgt_t_patch(p) = forc_hgt_t(g) + z0hv(p) + displa(p) + forc_hgt_q_patch(p) = forc_hgt_q(g) + z0qv(p) + displa(p) end if else if (lun%itype(l) == istwet .or. lun%itype(l) == istice) then - forc_hgt_u_patch(p) = forc_hgt_u(g) + z0mg(c) - forc_hgt_t_patch(p) = forc_hgt_t(g) + z0mg(c) - forc_hgt_q_patch(p) = forc_hgt_q(g) + z0mg(c) + forc_hgt_u_patch(p) = forc_hgt_u(g) + z0mg(c) + displa(p) + forc_hgt_t_patch(p) = forc_hgt_t(g) + z0hg(c) + displa(p) + forc_hgt_q_patch(p) = forc_hgt_q(g) + z0qg(c) + displa(p) else if (urbpoi(l)) then forc_hgt_u_patch(p) = forc_hgt_u(g) + z_0_town(l) + z_d_town(l) forc_hgt_t_patch(p) = forc_hgt_t(g) + z_0_town(l) + z_d_town(l) @@ -677,10 +750,13 @@ subroutine FrictionVelocity(this, lbn, ubn, fn, filtern, & real(r8) , intent(in) :: ur ( lbn: ) ! wind speed at reference height [m/s] [lbn:ubn] real(r8) , intent(in) :: um ( lbn: ) ! wind speed including the stablity effect [m/s] [lbn:ubn] real(r8) , intent(out) :: ustar ( lbn: ) ! friction velocity [m/s] [lbn:ubn] - real(r8) , intent(out) :: temp1 ( lbn: ) ! relation for potential temperature profile [lbn:ubn] - real(r8) , intent(out) :: temp12m ( lbn: ) ! relation for potential temperature profile applied at 2-m [lbn:ubn] - real(r8) , intent(out) :: temp2 ( lbn: ) ! relation for specific humidity profile [lbn:ubn] - real(r8) , intent(out) :: temp22m ( lbn: ) ! relation for specific humidity profile applied at 2-m [lbn:ubn] + ! temp1, temp12m, temp2, temp22m are "inout" rather than "out" to + ! prevent returning nan when the code returns from this subroutine + ! before assigning values to these variables + real(r8) , intent(inout) :: temp1 ( lbn: ) ! relation for potential temperature profile [lbn:ubn] + real(r8) , intent(inout) :: temp12m ( lbn: ) ! relation for potential temperature profile applied at 2-m [lbn:ubn] + real(r8) , intent(inout) :: temp2 ( lbn: ) ! relation for specific humidity profile [lbn:ubn] + real(r8) , intent(inout) :: temp22m ( lbn: ) ! relation for specific humidity profile applied at 2-m [lbn:ubn] real(r8) , intent(inout) :: fm ( lbn: ) ! diagnose 10m wind (DUST only) [lbn:ubn] logical , intent(in), optional :: landunit_index ! optional argument that defines landunit or pft level ! diff --git a/src/biogeophys/HumanIndexMod.F90 b/src/biogeophys/HumanIndexMod.F90 index 9c3bc319e8..17a1ef968e 100644 --- a/src/biogeophys/HumanIndexMod.F90 +++ b/src/biogeophys/HumanIndexMod.F90 @@ -16,6 +16,9 @@ module HumanIndexMod ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 use decompMod , only : bounds_type + use abortutils , only : endrun + use clm_varctl , only : iulog + use shr_log_mod , only : errMsg => shr_log_errMsg ! !PUBLIC TYPES: implicit none save @@ -500,13 +503,10 @@ subroutine HumanIndexReadNML( NLFilename ) ! ! !USES: use shr_mpi_mod , only : shr_mpi_bcast - use abortutils , only : endrun use spmdMod , only : masterproc, mpicom use fileutils , only : getavu, relavu, opnfil use shr_nl_mod , only : shr_nl_find_group_name use shr_mpi_mod , only : shr_mpi_bcast - use clm_varctl , only : iulog - use shr_log_mod , only : errMsg => shr_log_errMsg ! ! !ARGUMENTS: implicit none @@ -1014,6 +1014,14 @@ subroutine Wet_BulbS (Tc_6,rh,wbt) ! !LOCAL VARIABLES: !EOP ! + if ( rh < 0.0d00 )then + write(iulog,*) 'rh = ', rh + call endrun(msg="ERROR RH is negative "//errmsg(sourcefile, __LINE__)) + else if ( rh > 100.d00 )then + write(iulog,*) 'rh = ', rh + call endrun(msg="ERROR RH is greater than a hundred "//errmsg(sourcefile, __LINE__)) + end if + wbt = Tc_6 * atan(0.151977_r8*sqrt(rh + 8.313659_r8)) + & atan(Tc_6+rh) - atan(rh-1.676331_r8) + & 0.00391838_r8*rh**(3._r8/2._r8)*atan(0.023101_r8*rh) - & diff --git a/src/biogeophys/LakeFluxesMod.F90 b/src/biogeophys/LakeFluxesMod.F90 index 4528d9dfb2..fb5f723839 100644 --- a/src/biogeophys/LakeFluxesMod.F90 +++ b/src/biogeophys/LakeFluxesMod.F90 @@ -37,6 +37,7 @@ module LakeFluxesMod real(r8) :: a_coef ! Drag coefficient under less dense canopy (unitless) real(r8) :: a_exp ! Drag exponent under less dense canopy (unitless) real(r8) :: zsno ! Momentum roughness length for snow (m) + real(r8) :: zglc ! Momentum roughness length for ice (m) real(r8) :: wind_min ! Minimum wind speed at the atmospheric forcing height (m/s) end type params_type type(params_type), private :: params_inst @@ -49,6 +50,7 @@ subroutine readParams( ncid ) ! !USES: use ncdio_pio, only: file_desc_t use paramUtilMod, only: readNcdioScalar + use clm_varctl, only: z0param_method ! ! !ARGUMENTS: implicit none @@ -64,6 +66,11 @@ subroutine readParams( ncid ) call readNcdioScalar(ncid, 'a_exp', subname, params_inst%a_exp) ! Momentum roughness length for snow (m) call readNcdioScalar(ncid, 'zsno', subname, params_inst%zsno) + + if (z0param_method == 'Meier2022') then + ! Momentum roughness length for ice (m) + call readNcdioScalar(ncid, 'zglc', subname, params_inst%zglc) + end if ! Minimum wind speed at the atmospheric forcing height (m/s) call readNcdioScalar(ncid, 'wind_min', subname, params_inst%wind_min) @@ -85,8 +92,10 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, ! !USES: use clm_varpar , only : nlevlak use clm_varcon , only : hvap, hsub, hfus, cpair, cpliq, tkwat, tkice, tkair - use clm_varcon , only : sb, vkc, grav, denh2o, tfrz, spval - use clm_varctl , only : use_lch4 + use clm_varcon , only : sb, vkc, grav, denh2o, tfrz, spval, rpi + use clm_varcon , only : beta_param, nu_param, b1_param, b4_param + use clm_varcon , only : meier_param1, meier_param2, meier_param3 + use clm_varctl , only : use_lch4, z0param_method, use_z0m_snowmelt use LakeCon , only : betavis, z0frzlake, tdmax, emg_lake use LakeCon , only : lake_use_old_fcrit_minz0 use LakeCon , only : minz0lake, cur0, cus, curm, fcrit @@ -118,6 +127,10 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, real(r8), pointer :: z0mg_col(:) ! roughness length over ground, momentum [m] real(r8), pointer :: z0hg_col(:) ! roughness length over ground, sensible heat [m] real(r8), pointer :: z0qg_col(:) ! roughness length over ground, latent heat [m] + real(r8), pointer :: z0mg(:) ! patch roughness length over ground, momentum [m] + real(r8), pointer :: z0hg(:) ! patch roughness length over ground, sensible heat [m] + real(r8), pointer :: z0qg(:) ! patch roughness length over ground, latent heat [m] + real(r8), pointer :: kbm1(:) ! natural logarithm of z0mg_p/z0hg_p [-] integer , parameter :: niters = 4 ! maximum number of iterations for surface temperature real(r8), parameter :: beta1 = 1._r8 ! coefficient of convective velocity (in computing W_*) [-] real(r8), parameter :: zii = 1000._r8 ! convective boundary height [m] @@ -159,12 +172,8 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, real(r8) :: ur(bounds%begp:bounds%endp) ! wind speed at reference height [m/s] real(r8) :: ustar(bounds%begp:bounds%endp) ! friction velocity [m/s] real(r8) :: wc ! convective velocity [m/s] - real(r8) :: zeta ! dimensionless height used in Monin-Obukhov theory real(r8) :: zldis(bounds%begp:bounds%endp) ! reference height "minus" zero displacement height [m] real(r8) :: displa(bounds%begp:bounds%endp) ! displacement (always zero) [m] - real(r8) :: z0mg(bounds%begp:bounds%endp) ! roughness length over ground, momentum [m] - real(r8) :: z0hg(bounds%begp:bounds%endp) ! roughness length over ground, sensible heat [m] - real(r8) :: z0qg(bounds%begp:bounds%endp) ! roughness length over ground, latent heat [m] real(r8) :: u2m ! 2 m wind speed (m/s) real(r8) :: fm(bounds%begp:bounds%endp) ! needed for BGC only to diagnose 10m wind speed real(r8) :: bw ! partial density of water (ice + liquid) @@ -219,7 +228,8 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, lakefetch => lakestate_inst%lakefetch_col , & ! Input: [real(r8) (:) ] lake fetch from surface data (m) h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Input: [real(r8) (:,:) ] liquid water (kg/m2) - h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice lens (kg/m2) + h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice lens (kg/m2) + snomelt_accum => waterdiagnosticbulk_inst%snomelt_accum_col , & ! Input: [real(r8) (:) ] accumulated col snow melt for z0m calculation (m H2O) t_skin_patch => temperature_inst%t_skin_patch , & ! Output: [real(r8) (:) ] patch skin temperature (K) t_lake => temperature_inst%t_lake_col , & ! Input: [real(r8) (:,:) ] lake temperature (Kelvin) @@ -230,6 +240,7 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, forc_hgt_t_patch => frictionvel_inst%forc_hgt_t_patch , & ! Input: [real(r8) (:) ] observational height of temperature at pft level [m] forc_hgt_q_patch => frictionvel_inst%forc_hgt_q_patch , & ! Input: [real(r8) (:) ] observational height of specific humidity at pft level [m] zetamax => frictionvel_inst%zetamaxstable , & ! Input: [real(r8) ] max zeta value under stable conditions + zeta => frictionvel_inst%zeta_patch , & ! Output: [real(r8) (:) ] dimensionless stability parameter ram1 => frictionvel_inst%ram1_patch , & ! Output: [real(r8) (:) ] aerodynamical resistance (s/m) q_ref2m => waterdiagnosticbulk_inst%q_ref2m_patch , & ! Output: [real(r8) (:) ] 2 m height surface specific humidity (kg/kg) @@ -270,7 +281,6 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, taux => energyflux_inst%taux_patch , & ! Output: [real(r8) (:) ] wind (shear) stress: e-w (kg/m/s**2) tauy => energyflux_inst%tauy_patch , & ! Output: [real(r8) (:) ] wind (shear) stress: n-s (kg/m/s**2) dhsdt_canopy => energyflux_inst%dhsdt_canopy_patch , & ! Output: [real(r8) (:) ] change in heat storage of stem (W/m**2) [+ to atm] - ks => lakestate_inst%ks_col , & ! Output: [real(r8) (:) ] coefficient passed to LakeTemperature ws => lakestate_inst%ws_col , & ! Output: [real(r8) (:) ] surface friction velocity (m/s) betaprime => lakestate_inst%betaprime_col , & ! Output: [real(r8) (:) ] fraction of solar rad absorbed at surface: equal to NIR fraction @@ -286,7 +296,10 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, z0mg_col => frictionvel_inst%z0mg_col z0hg_col => frictionvel_inst%z0hg_col z0qg_col => frictionvel_inst%z0qg_col - + z0mg => frictionvel_inst%z0mg_patch + z0hg => frictionvel_inst%z0hg_patch + z0qg => frictionvel_inst%z0qg_patch + kbm1 => frictionvel_inst%kbm1_patch kva0temp = 20._r8 + tfrz do fp = 1, num_lakep @@ -324,20 +337,45 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, z0qg(p) = max(z0qg(p), minz0lake) z0hg(p) = max(z0hg(p), minz0lake) else if (snl(c) == 0) then ! frozen lake with ice - z0mg(p) = z0frzlake - z0hg(p) = z0mg(p) / exp(params_inst%a_coef * (ust_lake(c) * z0mg(p) / 1.5e-5_r8)**params_inst%a_exp) ! Consistent with BareGroundFluxes + select case (z0param_method) + case ('Meier2022') + z0mg(p) = params_inst%zglc + + + z0hg(p) = meier_param3 * nu_param / ust_lake(c) ! For initial guess assume tstar = 0 + + case ('ZengWang2007') + z0mg(p) = z0frzlake + z0hg(p) = z0mg(p) / exp(params_inst%a_coef * (ust_lake(c) * z0mg(p) / nu_param)**params_inst%a_exp) ! Consistent with BareGroundFluxes + end select z0qg(p) = z0hg(p) else ! use roughness over snow as in Biogeophysics1 - z0mg(p) = params_inst%zsno - z0hg(p) = z0mg(p) / exp(params_inst%a_coef * (ust_lake(c) * z0mg(p) / 1.5e-5_r8)**params_inst%a_exp) ! Consistent with BareGroundFluxes + if(use_z0m_snowmelt) then + if ( snomelt_accum(c) < 1.e-5_r8 ) then + z0mg(p) = exp(-b1_param * rpi * 0.5_r8 + b4_param) * 1.e-3_r8 + else + z0mg(p) = exp(b1_param * (atan((log10(snomelt_accum(c)) + meier_param1) / meier_param2)) + b4_param) * 1.e-3_r8 + end if + else + z0mg(p) = params_inst%zsno + end if + + select case (z0param_method) + case ('Meier2022') + z0hg(p) = meier_param3 * nu_param / ust_lake(c) ! For initial guess assume tstar = 0 + case ('ZengWang2007') + z0hg(p) = z0mg(p) / exp(params_inst%a_coef * (ust_lake(c) * z0mg(p) / nu_param)**params_inst%a_exp) ! Consistent with BareGroundFluxes + end select + z0qg(p) = z0hg(p) end if ! Surface temperature and fluxes + ! Update forcing heights for updated roughness lengths forc_hgt_u_patch(p) = forc_hgt_u(g) + z0mg(p) - forc_hgt_t_patch(p) = forc_hgt_t(g) + z0mg(p) - forc_hgt_q_patch(p) = forc_hgt_q(g) + z0mg(p) + forc_hgt_t_patch(p) = forc_hgt_t(g) + z0hg(p) + forc_hgt_q_patch(p) = forc_hgt_q(g) + z0qg(p) ! Find top layer jtop(c) = snl(c) + 1 @@ -379,6 +417,7 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, g = patch%gridcell(p) dhsdt_canopy(p) = 0.0_r8 + nmozsgn(p) = 0 obuold(p) = 0._r8 displa(p) = 0._r8 @@ -500,17 +539,17 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, qstar = temp2(p)*dqh(p) thvstar=tstar*(1._r8+0.61_r8*forc_q(c)) + 0.61_r8*forc_th(c)*qstar - zeta=zldis(p)*vkc * grav*thvstar/(ustar(p)**2*thv(c)) + zeta(p)=zldis(p)*vkc * grav*thvstar/(ustar(p)**2*thv(c)) - if (zeta >= 0._r8) then !stable - zeta = min(zetamax,max(zeta,0.01_r8)) + if (zeta(p) >= 0._r8) then !stable + zeta(p) = min(zetamax,max(zeta(p),0.01_r8)) um(p) = max(ur(p),0.1_r8) else !unstable - zeta = max(-100._r8,min(zeta,-0.01_r8)) + zeta(p) = max(-100._r8,min(zeta(p),-0.01_r8)) wc = beta1*(-grav*ustar(p)*thvstar*zii/thv(c))**0.333_r8 um(p) = sqrt(ur(p)*ur(p)+wc*wc) end if - obu(p) = zldis(p)/zeta + obu(p) = zldis(p)/zeta(p) if (obuold(p)*obu(p) < 0._r8) nmozsgn(p) = nmozsgn(p)+1 @@ -545,15 +584,45 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, z0hg(p) = max(z0hg(p), minz0lake) else if (snl(c) == 0) then ! in case it was above freezing and now below freezing - z0mg(p) = z0frzlake - z0hg(p) = z0mg(p) / exp(params_inst%a_coef * (ustar(p) * z0mg(p) / 1.5e-5_r8)**params_inst%a_exp) ! Consistent with BareGroundFluxes - z0qg(p) = z0hg(p) + select case (z0param_method) + case ('Meier2022') + z0mg(p) = params_inst%zglc + ! (...)**0.5 = sqrt(...) and (...)**0.25 = sqrt(sqrt(...)) + ! likely more efficient to calculate as exponents + z0hg(p) = meier_param3 * nu_param / ustar(p) * exp( -beta_param * ustar(p)**(0.5_r8) * (abs(tstar))**(0.25_r8)) ! Consistent with BareGroundFluxes + + case ('ZengWang2007') + z0mg(p) = z0frzlake + z0hg(p) = z0mg(p) / exp(params_inst%a_coef * (ustar(p) * z0mg(p) / nu_param)**params_inst%a_exp) ! Consistent with BareGroundFluxes + end select + z0qg(p) = z0hg(p) else ! Snow layers - ! z0mg won't have changed - z0hg(p) = z0mg(p) / exp(params_inst%a_coef * (ustar(p) * z0mg(p) / 1.5e-5_r8)**params_inst%a_exp) ! Consistent with BareGroundFluxes + if(use_z0m_snowmelt) then + if ( snomelt_accum(c) < 1.e-5_r8 )then + z0mg(p) = exp(-b1_param * rpi * 0.5_r8 + b4_param) * 1.e-3_r8 + else + z0mg(p) = exp(b1_param * (atan((log10(snomelt_accum(c)) + meier_param1) / meier_param2)) + b4_param) * 1.e-3_r8 + end if + end if + + select case (z0param_method) + case ('Meier2022') + ! (...)**0.5 = sqrt(...) and (...)**0.25 = sqrt(sqrt(...)) + ! likely more efficient to calculate as exponents + z0hg(p) = meier_param3 * nu_param / ustar(p) * exp( -beta_param * ustar(p)**(0.5_r8) * (abs(tstar))**(0.25_r8)) ! Consistent with BareGroundFluxes + + case ('ZengWang2007') + z0hg(p) = z0mg(p) / exp(params_inst%a_coef * (ustar(p) * z0mg(p) / nu_param)**params_inst%a_exp) ! Consistent with BareGroundFluxes + end select + z0qg(p) = z0hg(p) end if + ! Update forcing heights for updated roughness lengths + forc_hgt_u_patch(p) = forc_hgt_u(g) + z0mg(p) + forc_hgt_t_patch(p) = forc_hgt_t(g) + z0hg(p) + forc_hgt_q_patch(p) = forc_hgt_q(g) + z0qg(p) + end do ! end of filtered pft loop iter = iter + 1 @@ -690,6 +759,7 @@ subroutine LakeFluxes(bounds, num_lakec, filter_lakec, num_lakep, filter_lakep, z0hg_col(c) = z0hg(p) z0qg_col(c) = z0qg(p) ust_lake(c) = ustar(p) + kbm1(p) = log(z0mg(p) / z0hg(p)) end do diff --git a/src/biogeophys/LakeHydrologyMod.F90 b/src/biogeophys/LakeHydrologyMod.F90 index f6f83d8956..c1e69cbb13 100644 --- a/src/biogeophys/LakeHydrologyMod.F90 +++ b/src/biogeophys/LakeHydrologyMod.F90 @@ -351,6 +351,10 @@ subroutine LakeHydrology(bounds, & end if if (h2osno_temp > 0._r8) then ! Assume that snow bulk density remains the same as before + ! NOTE (SSR, 2023-11-08): Small h2osno_temp can cause unrealistically high snow depths: see https://github.com/ESCOMP/CTSM/issues/2227. Suggested fix there is to replace this line with + ! snow_depth(c) = h2osno_no_layers(c) * min (snow_depth(c)/h2osno_temp, 1._r8/50._r8) + ! where 50 kg/m3 is suggested as a lower limit for snow density. + ! As this bug seemingly has never been encountered in CTSM, we are not yet implementing the fix. snow_depth(c) = snow_depth(c) * h2osno_no_layers(c) / h2osno_temp else ! Assume a constant snow bulk density = 250. diff --git a/src/biogeophys/PhotosynthesisMod.F90 b/src/biogeophys/PhotosynthesisMod.F90 index 5b2c68a0fb..6176668f19 100644 --- a/src/biogeophys/PhotosynthesisMod.F90 +++ b/src/biogeophys/PhotosynthesisMod.F90 @@ -2266,7 +2266,10 @@ subroutine hybrid(x0, p, iv, c, gb_mol, je, cair, oair, lmr_z, par_z,& real(r8), intent(in) :: cair ! Atmospheric CO2 partial pressure (Pa) real(r8), intent(in) :: oair ! Atmospheric O2 partial pressure (Pa) integer, intent(in) :: p, iv, c ! pft, c3/c4, and column index - real(r8), intent(out) :: gs_mol ! leaf stomatal conductance (umol H2O/m**2/s) + ! gs_mol is "inout" rather than "out" to + ! prevent returning nan when the code returns from this subroutine + ! before assigning a value to this variable + real(r8), intent(inout) :: gs_mol ! leaf stomatal conductance (umol H2O/m**2/s) integer, intent(out) :: iter !number of iterations used, for record only type(atm2lnd_type) , intent(in) :: atm2lnd_inst type(photosyns_type), intent(inout) :: photosyns_inst @@ -2378,7 +2381,10 @@ subroutine brent(x, x1,x2,f1, f2, tol, ip, iv, ic, gb_mol, je, cair, oair,& real(r8), intent(in) :: oair ! Atmospheric O2 partial pressure (Pa) real(r8), intent(in) :: rh_can ! inside canopy relative humidity integer, intent(in) :: ip, iv, ic ! pft, c3/c4, and column index - real(r8), intent(out) :: gs_mol ! leaf stomatal conductance (umol H2O/m**2/s) + ! gs_mol is "inout" rather than "out" to + ! prevent returning nan when the code returns from this subroutine + ! before assigning a value to this variable + real(r8), intent(inout) :: gs_mol ! leaf stomatal conductance (umol H2O/m**2/s) type(atm2lnd_type) , intent(in) :: atm2lnd_inst type(photosyns_type), intent(inout) :: photosyns_inst ! @@ -2568,7 +2574,10 @@ subroutine ci_func(ci, fval, p, iv, c, gb_mol, je, cair, oair, lmr_z, par_z,& real(r8) , intent(in) :: rh_can ! canopy air realtive humidity integer , intent(in) :: p, iv, c ! pft, vegetation type and column indexes real(r8) , intent(out) :: fval ! return function of the value f(ci) - real(r8) , intent(out) :: gs_mol ! leaf stomatal conductance (umol H2O/m**2/s) + ! gs_mol is "inout" rather than "out" to + ! prevent returning nan when the code returns from this subroutine + ! before assigning a value to this variable + real(r8) , intent(inout) :: gs_mol ! leaf stomatal conductance (umol H2O/m**2/s) type(atm2lnd_type) , intent(in) :: atm2lnd_inst type(photosyns_type) , intent(inout) :: photosyns_inst ! @@ -2706,7 +2715,6 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & use clm_varpar , only : nlevsoi use pftconMod , only : nbrdlf_dcd_tmp_shrub, npcropmin use ColumnType , only : col - use shr_infnan_mod , only : shr_infnan_isnan ! ! !ARGUMENTS: @@ -2724,7 +2732,7 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & real(r8) , intent(in) :: leafn( bounds%begp: ) ! leaf N (gN/m2) real(r8) , intent(out) :: bsun( bounds%begp: ) ! sunlit canopy transpiration wetness factor (0 to 1) real(r8) , intent(out) :: bsha( bounds%begp: ) ! shaded canopy transpiration wetness factor (0 to 1) - real(r8) , intent(out) :: btran( bounds%begp: ) ! transpiration wetness factor (0 to 1) [pft] + real(r8) , intent(inout) :: btran( bounds%begp: ) ! transpiration wetness factor (0 to 1) [pft] real(r8) , intent(in) :: froot_carbon( bounds%begp: ) ! fine root carbon (gC/m2) [pft] real(r8) , intent(in) :: croot_carbon( bounds%begp: ) ! live coarse root carbon (gC/m2) [pft] @@ -3477,6 +3485,7 @@ subroutine PhotosynthesisHydraulicStress ( bounds, fn, filterp, & else gsminsun = nan gsminsha = nan + call endrun( 'ERROR:: Photosynthesis::PhotosynthesisHydraulicStress must choose stomatalcond_mtd method' ) end if call calcstress(p,c,vegwp(p,:),bsun(p),bsha(p),gb_mol(p),gsminsun, gsminsha, & qsatl(p),qaf(p), atm2lnd_inst,canopystate_inst,waterdiagnosticbulk_inst, & @@ -4064,8 +4073,11 @@ subroutine brent_PHS(xsun, x1sun, x2sun, f1sun, f2sun, xsha, x1sha, x2sha, f1sha real(r8), intent(in) :: lmr_z_sun, lmr_z_sha ! canopy layer: leaf maintenance respiration rate (umol CO2/m**2/s) real(r8), intent(in) :: par_z_sun, par_z_sha ! par absorbed per unit lai for canopy layer (w/m**2) real(r8), intent(in) :: rh_can ! inside canopy relative humidity - real(r8), intent(out) :: gs_mol_sun ! sunlit leaf stomatal conductance (umol H2O/m**2/s) - real(r8), intent(out) :: gs_mol_sha ! shaded leaf stomatal conductance (umol H2O/m**2/s) + ! gs_mol_s* are "inout" rather than "out" to + ! prevent returning nan when the code returns from this subroutine + ! before assigning values to these variables + real(r8), intent(inout) :: gs_mol_sun ! sunlit leaf stomatal conductance (umol H2O/m**2/s) + real(r8), intent(inout) :: gs_mol_sha ! shaded leaf stomatal conductance (umol H2O/m**2/s) real(r8), intent(inout) :: bsun ! sunlit canopy transpiration wetness factor (0 to 1) real(r8), intent(inout) :: bsha ! shaded canopy transpiration wetness factor (0 to 1) real(r8), intent(in) :: qsatl ! leaf specific humidity [kg/kg] @@ -4344,6 +4356,7 @@ subroutine ci_func_PHS(x,cisun, cisha, fvalsun, fvalsha, p, iv, c, bsun, bsha, b gs_mol_sun = bbb(p) else gs_mol_sun = nan + call endrun( 'ERROR:: Photosynthesis::ci_func_PHS must choose stomatalcond_mtd method' ) end if gs_mol_sun = max( bsun*gs_mol_sun, 1._r8) fvalsun = 0._r8 ! really tho? zqz @@ -4355,6 +4368,7 @@ subroutine ci_func_PHS(x,cisun, cisha, fvalsun, fvalsha, p, iv, c, bsun, bsha, b gs_mol_sha = bbb(p) else gs_mol_sha = nan + call endrun( 'ERROR:: Photosynthesis::ci_func_PHS must choose stomatalcond_mtd method' ) end if gs_mol_sha = max( bsha*gs_mol_sha, 1._r8) fvalsha = 0._r8 diff --git a/src/biogeophys/SnowHydrologyMod.F90 b/src/biogeophys/SnowHydrologyMod.F90 index 242f6ac359..9fb1a52dbc 100644 --- a/src/biogeophys/SnowHydrologyMod.F90 +++ b/src/biogeophys/SnowHydrologyMod.F90 @@ -79,7 +79,14 @@ module SnowHydrologyMod real(r8) :: rho_max ! Wind drift compaction / maximum density (kg/m3) real(r8) :: tau_ref ! Wind drift compaction / reference time (48*3600) (s) real(r8) :: scvng_fct_mlt_sf ! Scaling factor modifying scavenging factors for BC, OC, and dust species inclusion in meltwater (-) + real(r8) :: scvng_fct_mlt_bcphi ! scavenging factor for hydrophillic BC inclusion in meltwater [frc] + real(r8) :: scvng_fct_mlt_bcpho ! scavenging factor for hydrophobic BC inclusion in meltwater [frc] + real(r8) :: scvng_fct_mlt_dst1 ! scavenging factor for dust species 1 inclusion in meltwater [frc] + real(r8) :: scvng_fct_mlt_dst2 ! scavenging factor for dust species 2 inclusion in meltwater [frc] + real(r8) :: scvng_fct_mlt_dst3 ! scavenging factor for dust species 3 inclusion in meltwater [frc] + real(r8) :: scvng_fct_mlt_dst4 ! scavenging factor for dust species 4 inclusion in meltwater [frc] real(r8) :: ceta ! Overburden compaction constant (kg/m3) + real(r8) :: snw_rds_min ! minimum allowed snow effective radius (also cold "fresh snow" value) [microns] end type params_type type(params_type), private :: params_inst @@ -120,14 +127,8 @@ module SnowHydrologyMod ! 7= dust species 3 ! 8= dust species 4 ! - real(r8), public, parameter :: scvng_fct_mlt_bcphi = 0.20_r8 ! scavenging factor for hydrophillic BC inclusion in meltwater [frc] - real(r8), public, parameter :: scvng_fct_mlt_bcpho = 0.03_r8 ! scavenging factor for hydrophobic BC inclusion in meltwater [frc] real(r8), public, parameter :: scvng_fct_mlt_ocphi = 0.20_r8 ! scavenging factor for hydrophillic OC inclusion in meltwater [frc] real(r8), public, parameter :: scvng_fct_mlt_ocpho = 0.03_r8 ! scavenging factor for hydrophobic OC inclusion in meltwater [frc] - real(r8), public, parameter :: scvng_fct_mlt_dst1 = 0.02_r8 ! scavenging factor for dust species 1 inclusion in meltwater [frc] - real(r8), public, parameter :: scvng_fct_mlt_dst2 = 0.02_r8 ! scavenging factor for dust species 2 inclusion in meltwater [frc] - real(r8), public, parameter :: scvng_fct_mlt_dst3 = 0.01_r8 ! scavenging factor for dust species 3 inclusion in meltwater [frc] - real(r8), public, parameter :: scvng_fct_mlt_dst4 = 0.01_r8 ! scavenging factor for dust species 4 inclusion in meltwater [frc] ! The following are public for the sake of unit testing integer, parameter, public :: LoTmpDnsSlater2017 = 2 ! For temperature below -15C use equation from Slater 2017 @@ -316,8 +317,22 @@ subroutine readParams( ncid ) call readNcdioScalar(ncid, 'tau_ref', subname, params_inst%tau_ref) ! Scaling factor modifying scavenging factors for BC, OC, and dust species inclusion in meltwater (-) call readNcdioScalar(ncid, 'scvng_fct_mlt_sf', subname, params_inst%scvng_fct_mlt_sf) + ! scavenging factor for hydrophillic BC inclusion in meltwater [frc] + call readNcdioScalar(ncid, 'scvng_fct_mlt_bcphi', subname, params_inst%scvng_fct_mlt_bcphi) + ! scavenging factor for hydrophobic BC inclusion in meltwater [frc] + call readNcdioScalar(ncid, 'scvng_fct_mlt_bcpho', subname, params_inst%scvng_fct_mlt_bcpho) + ! scavenging factor for dust species 1 inclusion in meltwater [frc] + call readNcdioScalar(ncid, 'scvng_fct_mlt_dst1', subname, params_inst%scvng_fct_mlt_dst1) + ! scavenging factor for dust species 2 inclusion in meltwater [frc] + call readNcdioScalar(ncid, 'scvng_fct_mlt_dst2', subname, params_inst%scvng_fct_mlt_dst2) + ! scavenging factor for dust species 3 inclusion in meltwater [frc] + call readNcdioScalar(ncid, 'scvng_fct_mlt_dst3', subname, params_inst%scvng_fct_mlt_dst3) + ! scavenging factor for dust species 4 inclusion in meltwater [frc] + call readNcdioScalar(ncid, 'scvng_fct_mlt_dst4', subname, params_inst%scvng_fct_mlt_dst4) ! Overburden compaction constant (kg/m3) call readNcdioScalar(ncid, 'ceta', subname, params_inst%ceta) + ! minimum allowed snow effective radius (also cold "fresh snow" value) [microns] + call readNcdioScalar(ncid, 'snw_rds_min', subname, params_inst%snw_rds_min) end subroutine readParams @@ -383,7 +398,8 @@ subroutine UpdateQuantitiesForNewSnow(bounds, num_c, filter_c, & swe_old = b_waterdiagnostic_inst%swe_old_col(begc:endc,:), & frac_sno = b_waterdiagnostic_inst%frac_sno_col(begc:endc), & frac_sno_eff = b_waterdiagnostic_inst%frac_sno_eff_col(begc:endc), & - snow_depth = b_waterdiagnostic_inst%snow_depth_col(begc:endc)) + snow_depth = b_waterdiagnostic_inst%snow_depth_col(begc:endc), & + snomelt_accum = b_waterdiagnostic_inst%snomelt_accum_col(begc:endc)) do i = water_inst%bulk_and_tracers_beg, water_inst%bulk_and_tracers_end associate(w => water_inst%bulk_and_tracers(i)) @@ -407,7 +423,7 @@ subroutine BulkDiag_NewSnowDiagnostics(bounds, num_c, filter_c, & scf_method, & dtime, lun_itype_col, urbpoi, snl, bifall, h2osno_total, h2osoi_ice, h2osoi_liq, & qflx_snow_grnd, qflx_snow_drain, & - dz, int_snow, swe_old, frac_sno, frac_sno_eff, snow_depth) + dz, int_snow, swe_old, frac_sno, frac_sno_eff, snow_depth, snomelt_accum) ! ! !DESCRIPTION: ! Update various snow-related diagnostic quantities to account for new snow @@ -434,6 +450,7 @@ subroutine BulkDiag_NewSnowDiagnostics(bounds, num_c, filter_c, & real(r8) , intent(inout) :: frac_sno( bounds%begc: ) ! fraction of ground covered by snow (0 to 1) real(r8) , intent(inout) :: frac_sno_eff( bounds%begc: ) ! eff. fraction of ground covered by snow (0 to 1) real(r8) , intent(inout) :: snow_depth( bounds%begc: ) ! snow height (m) + real(r8) , intent(inout) :: snomelt_accum( bounds%begc: ) ! accumulated col snow melt for z0m calculation (m H2O) ! ! !LOCAL VARIABLES: integer :: fc, c @@ -462,6 +479,7 @@ subroutine BulkDiag_NewSnowDiagnostics(bounds, num_c, filter_c, & SHR_ASSERT_FL((ubound(frac_sno, 1) == bounds%endc), sourcefile, __LINE__) SHR_ASSERT_FL((ubound(frac_sno_eff, 1) == bounds%endc), sourcefile, __LINE__) SHR_ASSERT_FL((ubound(snow_depth, 1) == bounds%endc), sourcefile, __LINE__) + SHR_ASSERT_FL((ubound(snomelt_accum, 1) == bounds%endc), sourcefile, __LINE__) associate( & begc => bounds%begc, & @@ -488,6 +506,7 @@ subroutine BulkDiag_NewSnowDiagnostics(bounds, num_c, filter_c, & ! all snow falls on ground, no snow on h2osfc (note that qflx_snow_h2osfc is ! currently set to 0 always in CanopyHydrologyMod) newsnow(c) = qflx_snow_grnd(c) * dtime + snomelt_accum(c) = max(0._r8, snomelt_accum(c) - newsnow(c) * 1.e-3_r8) ! update int_snow int_snow(c) = max(int_snow(c),h2osno_total(c)) !h2osno_total could be larger due to frost @@ -805,6 +824,7 @@ subroutine InitializeExplicitSnowPack(bounds, num_c, filter_c, & h2osno_no_layers = w%waterstate_inst%h2osno_no_layers_col(begc:endc), & h2osoi_ice = w%waterstate_inst%h2osoi_ice_col(begc:endc,:), & h2osoi_liq = w%waterstate_inst%h2osoi_liq_col(begc:endc,:)) + end associate end do @@ -818,7 +838,8 @@ subroutine InitializeExplicitSnowPack(bounds, num_c, filter_c, & dz = col%dz(begc:endc,:), & z = col%z(begc:endc,:), & t_soisno = temperature_inst%t_soisno_col(begc:endc,:), & - frac_iceold = b_waterdiagnostic_inst%frac_iceold_col(begc:endc,:)) + frac_iceold = b_waterdiagnostic_inst%frac_iceold_col(begc:endc,:), & + snomelt_accum = b_waterdiagnostic_inst%snomelt_accum_col(begc:endc)) ! intitialize SNICAR variables for fresh snow: call aerosol_inst%ResetFilter( & @@ -932,7 +953,7 @@ end subroutine UpdateState_InitializeSnowPack !----------------------------------------------------------------------- subroutine Bulk_InitializeSnowPack(bounds, snowpack_initialized_filterc, & - forc_t, snow_depth, snl, zi, dz, z, t_soisno, frac_iceold) + forc_t, snow_depth, snl, zi, dz, z, t_soisno, frac_iceold, snomelt_accum) ! ! !DESCRIPTION: ! Initialize an explicit snow pack in columns where this is warranted based on snow depth @@ -952,6 +973,7 @@ subroutine Bulk_InitializeSnowPack(bounds, snowpack_initialized_filterc, & real(r8) , intent(inout) :: z( bounds%begc: , -nlevsno+1: ) ! layer depth (m) real(r8) , intent(inout) :: t_soisno( bounds%begc: , -nlevsno+1: ) ! soil temperature (Kelvin) real(r8) , intent(inout) :: frac_iceold( bounds%begc: , -nlevsno+1: ) ! fraction of ice relative to the tot water + real(r8) , intent(inout) :: snomelt_accum( bounds%begc: ) ! accumulated col snow melt for z0m calculation (m H2O) ! ! !LOCAL VARIABLES: integer :: fc, c @@ -967,6 +989,7 @@ subroutine Bulk_InitializeSnowPack(bounds, snowpack_initialized_filterc, & SHR_ASSERT_ALL_FL((ubound(z) == [bounds%endc, nlevmaxurbgrnd]), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(t_soisno) == [bounds%endc, nlevmaxurbgrnd]), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(frac_iceold) == [bounds%endc, nlevgrnd]), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(snomelt_accum, 1) == bounds%endc), sourcefile, __LINE__) do fc = 1, snowpack_initialized_filterc%num c = snowpack_initialized_filterc%indices(fc) @@ -982,6 +1005,8 @@ subroutine Bulk_InitializeSnowPack(bounds, snowpack_initialized_filterc, & ! This value of frac_iceold makes sense together with the state initialization: ! h2osoi_ice is non-zero, while h2osoi_liq is zero. frac_iceold(c,0) = 1._r8 + + snomelt_accum(c) = 0._r8 end do end subroutine Bulk_InitializeSnowPack @@ -1576,7 +1601,7 @@ subroutine CalcAndApplyAerosolFluxes(bounds, num_snowc, filter_snowc, & ! BCPHI: ! 1. flux with meltwater: qout_bc_phi(c) = qflx_snow_percolation(c,j)*params_inst%scvng_fct_mlt_sf* & - scvng_fct_mlt_bcphi*(mss_bcphi(c,j)/mss_liqice) + params_inst%scvng_fct_mlt_bcphi*(mss_bcphi(c,j)/mss_liqice) if (qout_bc_phi(c)*dtime > mss_bcphi(c,j)) then qout_bc_phi(c) = mss_bcphi(c,j)/dtime mss_bcphi(c,j) = 0._r8 @@ -1588,7 +1613,7 @@ subroutine CalcAndApplyAerosolFluxes(bounds, num_snowc, filter_snowc, & ! BCPHO: ! 1. flux with meltwater: qout_bc_pho(c) = qflx_snow_percolation(c,j)*params_inst%scvng_fct_mlt_sf* & - scvng_fct_mlt_bcpho*(mss_bcpho(c,j)/mss_liqice) + params_inst%scvng_fct_mlt_bcpho*(mss_bcpho(c,j)/mss_liqice) if (qout_bc_pho(c)*dtime > mss_bcpho(c,j)) then qout_bc_pho(c) = mss_bcpho(c,j)/dtime mss_bcpho(c,j) = 0._r8 @@ -1624,7 +1649,7 @@ subroutine CalcAndApplyAerosolFluxes(bounds, num_snowc, filter_snowc, & ! DUST 1: ! 1. flux with meltwater: qout_dst1(c) = qflx_snow_percolation(c,j)*params_inst%scvng_fct_mlt_sf* & - scvng_fct_mlt_dst1*(mss_dst1(c,j)/mss_liqice) + params_inst%scvng_fct_mlt_dst1*(mss_dst1(c,j)/mss_liqice) if (qout_dst1(c)*dtime > mss_dst1(c,j)) then qout_dst1(c) = mss_dst1(c,j)/dtime mss_dst1(c,j) = 0._r8 @@ -1636,7 +1661,7 @@ subroutine CalcAndApplyAerosolFluxes(bounds, num_snowc, filter_snowc, & ! DUST 2: ! 1. flux with meltwater: qout_dst2(c) = qflx_snow_percolation(c,j)*params_inst%scvng_fct_mlt_sf* & - scvng_fct_mlt_dst2*(mss_dst2(c,j)/mss_liqice) + params_inst%scvng_fct_mlt_dst2*(mss_dst2(c,j)/mss_liqice) if (qout_dst2(c)*dtime > mss_dst2(c,j)) then qout_dst2(c) = mss_dst2(c,j)/dtime mss_dst2(c,j) = 0._r8 @@ -1648,7 +1673,7 @@ subroutine CalcAndApplyAerosolFluxes(bounds, num_snowc, filter_snowc, & ! DUST 3: ! 1. flux with meltwater: qout_dst3(c) = qflx_snow_percolation(c,j)*params_inst%scvng_fct_mlt_sf* & - scvng_fct_mlt_dst3*(mss_dst3(c,j)/mss_liqice) + params_inst%scvng_fct_mlt_dst3*(mss_dst3(c,j)/mss_liqice) if (qout_dst3(c)*dtime > mss_dst3(c,j)) then qout_dst3(c) = mss_dst3(c,j)/dtime mss_dst3(c,j) = 0._r8 @@ -1660,7 +1685,7 @@ subroutine CalcAndApplyAerosolFluxes(bounds, num_snowc, filter_snowc, & ! DUST 4: ! 1. flux with meltwater: qout_dst4(c) = qflx_snow_percolation(c,j)*params_inst%scvng_fct_mlt_sf* & - scvng_fct_mlt_dst4*(mss_dst4(c,j)/mss_liqice) + params_inst%scvng_fct_mlt_dst4*(mss_dst4(c,j)/mss_liqice) if (qout_dst4(c)*dtime > mss_dst4(c,j)) then qout_dst4(c) = mss_dst4(c,j)/dtime mss_dst4(c,j) = 0._r8 @@ -3926,7 +3951,6 @@ function MassWeightedSnowRadius( rds1, rds2, swtot, zwtot ) result(mass_weighted ! Calculate the mass weighted snow radius when two layers are combined ! ! !USES: - use AerosolMod , only : snw_rds_min use SnowSnicarMod, only : snw_rds_max implicit none ! !ARGUMENTS: @@ -3941,8 +3965,8 @@ function MassWeightedSnowRadius( rds1, rds2, swtot, zwtot ) result(mass_weighted if ( mass_weighted_snowradius > snw_rds_max ) then mass_weighted_snowradius = snw_rds_max - else if ( mass_weighted_snowradius < snw_rds_min ) then - mass_weighted_snowradius = snw_rds_min + else if ( mass_weighted_snowradius < params_inst%snw_rds_min ) then + mass_weighted_snowradius = params_inst%snw_rds_min end if end function MassWeightedSnowRadius diff --git a/src/biogeophys/SnowSnicarMod.F90 b/src/biogeophys/SnowSnicarMod.F90 index 77cc5b53d6..5bc1c61edb 100644 --- a/src/biogeophys/SnowSnicarMod.F90 +++ b/src/biogeophys/SnowSnicarMod.F90 @@ -11,12 +11,13 @@ module SnowSnicarMod use shr_kind_mod , only : r8 => shr_kind_r8 use shr_sys_mod , only : shr_sys_flush use shr_log_mod , only : errMsg => shr_log_errMsg - use clm_varctl , only : iulog + use clm_varctl , only : iulog, snicar_numrad_snw, & + snicar_snw_shape, snicar_snobc_intmix, & + snicar_snodst_intmix, do_sno_oc use clm_varcon , only : tfrz use shr_const_mod , only : SHR_CONST_RHOICE use abortutils , only : endrun use decompMod , only : bounds_type, subgrid_level_column - use AerosolMod , only : snw_rds_min use atm2lndType , only : atm2lnd_type use WaterStateBulkType , only : waterstatebulk_type use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type @@ -40,40 +41,38 @@ module SnowSnicarMod real(r8) :: snw_rds_refrz ! Effective radius of re-frozen snow (microns) real(r8) :: C2_liq_Brun89 ! Constant for liquid water grain growth [m3 s-1], ! from Brun89: corrected for LWC in units of percent + real(r8) :: fresh_snw_rds_max ! maximum warm fresh snow effective radius [microns] + real(r8) :: snw_rds_min ! minimum allowed snow effective radius (also cold "fresh snow" value) [microns] end type params_type type(params_type), private :: params_inst ! ! !PUBLIC DATA MEMBERS: integer, public, parameter :: sno_nbr_aer = 8 ! number of aerosol species in snowpack ! (indices described above) [nbr] - logical, public, parameter :: DO_SNO_OC = .false. ! parameter to include organic carbon (OC) - ! in snowpack radiative calculations logical, public, parameter :: DO_SNO_AER = .true. ! parameter to include aerosols in snowpack radiative calculations ! !PRIVATE DATA MEMBERS: - integer, parameter :: numrad_snw = 5 ! number of spectral bands used in snow model [nbr] - integer, parameter :: nir_bnd_bgn = 2 ! first band index in near-IR spectrum [idx] - integer, parameter :: nir_bnd_end = 5 ! ending near-IR band index [idx] + integer, parameter :: default_number_bands = 5 ! currently the only alternative is 480 bands + integer, parameter :: highest_default_band = 5 + integer, parameter :: sec_highest_default_band = 4 + integer, parameter :: high_number_bands = 480 integer, parameter :: idx_Mie_snw_mx = 1471 ! number of effective radius indices used in Mie lookup table [idx] - integer, parameter :: idx_T_max = 11 ! maxiumum temperature index used in aging lookup table [idx] + integer, parameter :: idx_T_max = 11 ! maximum temperature index used in aging lookup table [idx] integer, parameter :: idx_T_min = 1 ! minimum temperature index used in aging lookup table [idx] - integer, parameter :: idx_Tgrd_max = 31 ! maxiumum temperature gradient index used in aging lookup table [idx] + integer, parameter :: idx_Tgrd_max = 31 ! maximum temperature gradient index used in aging lookup table [idx] integer, parameter :: idx_Tgrd_min = 1 ! minimum temperature gradient index used in aging lookup table [idx] - integer, parameter :: idx_rhos_max = 8 ! maxiumum snow density index used in aging lookup table [idx] + integer, parameter :: idx_rhos_max = 8 ! maximum snow density index used in aging lookup table [idx] integer, parameter :: idx_rhos_min = 1 ! minimum snow density index used in aging lookup table [idx] integer, parameter :: snw_rds_max_tbl = 1500 ! maximum effective radius defined in Mie lookup table [microns] integer, parameter :: snw_rds_min_tbl = 30 ! minimium effective radius defined in Mie lookup table [microns] - integer, parameter :: snw_rds_min_int = nint(snw_rds_min) ! minimum allowed snow effective radius as integer [microns] real(r8), parameter :: snw_rds_max = 1500._r8 ! maximum allowed snow effective radius [microns] real(r8), parameter :: min_snw = 1.0E-30_r8 ! minimum snow mass required for SNICAR RT calculation [kg m-2] - !real(r8), parameter :: C1_liq_Brun89 = 1.28E-17_r8 ! constant for liquid water grain growth [m3 s-1], - ! from Brun89 real(r8), parameter :: C1_liq_Brun89 = 0._r8 ! constant for liquid water grain growth [m3 s-1], - ! from Brun89: zeroed to accomodate dry snow aging + ! from Brun89: zeroed to accomodate dry snow aging, was 1.28E-17_r8 real(r8), parameter :: tim_cns_bc_rmv = 2.2E-8_r8 ! time constant for removal of BC in snow on sea-ice ! [s-1] (50% mass removal/year) @@ -90,66 +89,71 @@ module SnowSnicarMod ! (idx_Mie_snw_mx is number of snow radii with defined parameters (i.e. from 30um to 1500um)) ! direct-beam weighted ice optical properties - real(r8) :: ss_alb_snw_drc(idx_Mie_snw_mx,numrad_snw) - real(r8) :: asm_prm_snw_drc(idx_Mie_snw_mx,numrad_snw) - real(r8) :: ext_cff_mss_snw_drc(idx_Mie_snw_mx,numrad_snw) + real(r8), allocatable :: ss_alb_snw_drc(:,:) ! (idx_Mie_snw_mx, numrad_snw) + real(r8), allocatable :: asm_prm_snw_drc(:,:) + real(r8), allocatable :: ext_cff_mss_snw_drc(:,:) ! diffuse radiation weighted ice optical properties - real(r8) :: ss_alb_snw_dfs(idx_Mie_snw_mx,numrad_snw) - real(r8) :: asm_prm_snw_dfs(idx_Mie_snw_mx,numrad_snw) - real(r8) :: ext_cff_mss_snw_dfs(idx_Mie_snw_mx,numrad_snw) + real(r8), allocatable :: ss_alb_snw_dfs(:,:) ! (idx_Mie_snw_mx, numrad_snw) + real(r8), allocatable :: asm_prm_snw_dfs(:,:) + real(r8), allocatable :: ext_cff_mss_snw_dfs(:,:) - ! hydrophiliic BC - real(r8) :: ss_alb_bc1(numrad_snw) - real(r8) :: asm_prm_bc1(numrad_snw) - real(r8) :: ext_cff_mss_bc1(numrad_snw) + ! hydrophilic BC + real(r8), allocatable :: ss_alb_bc_hphil(:) ! (numrad_snw) + real(r8), allocatable :: asm_prm_bc_hphil(:) + real(r8), allocatable :: ext_cff_mss_bc_hphil(:) ! hydrophobic BC - real(r8) :: ss_alb_bc2(numrad_snw) - real(r8) :: asm_prm_bc2(numrad_snw) - real(r8) :: ext_cff_mss_bc2(numrad_snw) - - ! hydrophobic OC - real(r8) :: ss_alb_oc1(numrad_snw) - real(r8) :: asm_prm_oc1(numrad_snw) - real(r8) :: ext_cff_mss_oc1(numrad_snw) + real(r8), allocatable :: ss_alb_bc_hphob(:) ! (numrad_snw) + real(r8), allocatable :: asm_prm_bc_hphob(:) + real(r8), allocatable :: ext_cff_mss_bc_hphob(:) ! hydrophilic OC - real(r8) :: ss_alb_oc2(numrad_snw) - real(r8) :: asm_prm_oc2(numrad_snw) - real(r8) :: ext_cff_mss_oc2(numrad_snw) + real(r8), allocatable :: ss_alb_oc_hphil(:) ! (numrad_snw) + real(r8), allocatable :: asm_prm_oc_hphil(:) + real(r8), allocatable :: ext_cff_mss_oc_hphil(:) + + ! hydrophobic OC + real(r8), allocatable :: ss_alb_oc_hphob(:) ! (numrad_snw) + real(r8), allocatable :: asm_prm_oc_hphob(:) + real(r8), allocatable :: ext_cff_mss_oc_hphob(:) ! dust species 1: - real(r8) :: ss_alb_dst1(numrad_snw) - real(r8) :: asm_prm_dst1(numrad_snw) - real(r8) :: ext_cff_mss_dst1(numrad_snw) + real(r8), allocatable :: ss_alb_dst1(:) ! (numrad_snw) + real(r8), allocatable :: asm_prm_dst1(:) + real(r8), allocatable :: ext_cff_mss_dst1(:) ! dust species 2: - real(r8) :: ss_alb_dst2(numrad_snw) - real(r8) :: asm_prm_dst2(numrad_snw) - real(r8) :: ext_cff_mss_dst2(numrad_snw) + real(r8), allocatable :: ss_alb_dst2(:) ! (numrad_snw) + real(r8), allocatable :: asm_prm_dst2(:) + real(r8), allocatable :: ext_cff_mss_dst2(:) ! dust species 3: - real(r8) :: ss_alb_dst3(numrad_snw) - real(r8) :: asm_prm_dst3(numrad_snw) - real(r8) :: ext_cff_mss_dst3(numrad_snw) + real(r8), allocatable :: ss_alb_dst3(:) ! (numrad_snw) + real(r8), allocatable :: asm_prm_dst3(:) + real(r8), allocatable :: ext_cff_mss_dst3(:) ! dust species 4: - real(r8) :: ss_alb_dst4(numrad_snw) - real(r8) :: asm_prm_dst4(numrad_snw) - real(r8) :: ext_cff_mss_dst4(numrad_snw) + real(r8), allocatable :: ss_alb_dst4(:) ! (numrad_snw) + real(r8), allocatable :: asm_prm_dst4(:) + real(r8), allocatable :: ext_cff_mss_dst4(:) + + ! downward solar radiation spectral weights for 5-band or 480-band + real(r8), allocatable :: flx_wgt_dir(:) ! (numrad_snw) ! direct + real(r8), allocatable :: flx_wgt_dif(:) ! (numrad_snw) ! diffuse ! best-fit parameters for snow aging defined over: ! 11 temperatures from 225 to 273 K ! 31 temperature gradients from 0 to 300 K/m ! 8 snow densities from 0 to 350 kg/m3 ! (arrays declared here, but are set in iniTimeConst) - real(r8), pointer :: snowage_tau(:,:,:) ! (idx_rhos_max,idx_Tgrd_max,idx_T_max) - real(r8), pointer :: snowage_kappa(:,:,:) ! (idx_rhos_max,idx_Tgrd_max,idx_T_max) - real(r8), pointer :: snowage_drdt0(:,:,:) ! idx_rhos_max,idx_Tgrd_max,idx_T_max) + real(r8), allocatable :: snowage_tau(:,:,:) ! (idx_rhos_max, idx_Tgrd_max, idx_T_max) + real(r8), allocatable :: snowage_kappa(:,:,:) + real(r8), allocatable :: snowage_drdt0(:,:,:) ! ! !REVISION HISTORY: - ! Created by Mark Flanner + ! Created by Mark Flanner (Univ. of Michigan) + ! Updated by Cenlin He (NCAR) based on Flanner et al. 2021 GMD character(len=*), parameter, private :: sourcefile = & __FILE__ @@ -178,11 +182,15 @@ subroutine readParams( ncid ) call readNcdioScalar(ncid, 'snw_rds_refrz', subname, params_inst%snw_rds_refrz) ! constant for liquid water grain growth [m3 s-1], from Brun89: corrected for LWC in units of percent call readNcdioScalar(ncid, 'C2_liq_Brun89', subname, params_inst%C2_liq_Brun89) + ! maximum warm fresh snow effective radius [microns] + call readNcdioScalar(ncid, 'fresh_snw_rds_max', subname, params_inst%fresh_snw_rds_max) + ! minimum allowed snow effective radius (also cold "fresh snow" value) [microns] + call readNcdioScalar(ncid, 'snw_rds_min', subname, params_inst%snw_rds_min) end subroutine readParams !----------------------------------------------------------------------- - subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + subroutine SNICAR_RT (bounds, num_nourbanc, filter_nourbanc, & coszen, flg_slr_in, h2osno_liq, h2osno_ice, h2osno_total, snw_rds, & mss_cnc_aer_in, albsfc, albout, flx_abs, waterdiagnosticbulk_inst) ! @@ -204,13 +212,25 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & ! Present-day climate forcing and response from black carbon in snow, ! J. Geophys. Res., 112, D11202, doi: 10.1029/2006JD008003 ! + ! Updated radiative transfer solver: + ! + ! The multi-layer solution for multiple-scattering used here is from: + ! Briegleb, P. and Light, B.: A Delta-Eddington mutiple scattering + ! parameterization for solar radiation in the sea ice component of the + ! community climate system model, 2007. + ! + ! The implementation of the SNICAR-AD model in CLM is described in: + ! Dang et al.2019, Inter-comparison and improvement of 2-stream shortwave + ! radiative transfer models for unified treatment of cryospheric surfaces + ! in ESMs; and Flanner et al. 2021, SNICAR-ADv3: a community tool for modeling + ! spectral snow albedo + ! ! !USES: - use clm_varpar , only : nlevsno, numrad + use clm_varpar , only : nlevsno, numrad, ivis, inir use clm_time_manager , only : get_nstep use shr_const_mod , only : SHR_CONST_PI ! ! !ARGUMENTS: - integer , intent(in) :: flg_snw_ice ! flag: =1 when called from CLM, =2 when called from CSIM type (bounds_type), intent(in) :: bounds integer , intent(in) :: num_nourbanc ! number of columns in non-urban filter integer , intent(in) :: filter_nourbanc(:) ! column filter for non-urban points @@ -229,17 +249,19 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & ! !LOCAL VARIABLES: ! ! variables for snow radiative transfer calculations + integer :: nir_bnd_bgn ! first band index in near-IR spectrum [idx] + integer :: nir_bnd_end ! ending near-IR band index [idx] ! Local variables representing single-column values of arrays: integer :: snl_lcl ! negative number of snow layers [nbr] integer :: snw_rds_lcl(-nlevsno+1:0) ! snow effective radius [m^-6] - real(r8):: flx_slrd_lcl(1:numrad_snw) ! direct beam incident irradiance [W/m2] (set to 1) - real(r8):: flx_slri_lcl(1:numrad_snw) ! diffuse incident irradiance [W/m2] (set to 1) + real(r8):: flx_slrd_lcl(1:snicar_numrad_snw) ! direct beam incident irradiance [W/m2] (set to 1) + real(r8):: flx_slri_lcl(1:snicar_numrad_snw) ! diffuse incident irradiance [W/m2] (set to 1) real(r8):: mss_cnc_aer_lcl(-nlevsno+1:0,1:sno_nbr_aer) ! aerosol mass concentration (lyr,aer_nbr) [kg/kg] real(r8):: h2osno_lcl ! total column snow mass [kg/m2] real(r8):: h2osno_liq_lcl(-nlevsno+1:0) ! liquid water mass [kg/m2] real(r8):: h2osno_ice_lcl(-nlevsno+1:0) ! ice mass [kg/m2] - real(r8):: albsfc_lcl(1:numrad_snw) ! albedo of underlying surface [frc] + real(r8):: albsfc_lcl(1:snicar_numrad_snw) ! albedo of underlying surface [frc] real(r8):: ss_alb_snw_lcl(-nlevsno+1:0) ! single-scatter albedo of ice grains (lyr) [frc] real(r8):: asm_prm_snw_lcl(-nlevsno+1:0) ! asymmetry parameter of ice grains (lyr) [frc] real(r8):: ext_cff_mss_snw_lcl(-nlevsno+1:0) ! mass extinction coefficient of ice grains (lyr) [m2/kg] @@ -247,25 +269,21 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & real(r8):: asm_prm_aer_lcl(sno_nbr_aer) ! asymmetry parameter of aerosol species (aer_nbr) [frc] real(r8):: ext_cff_mss_aer_lcl(sno_nbr_aer) ! mass extinction coefficient of aerosol species (aer_nbr) [m2/kg] - ! Other local variables integer :: APRX_TYP ! two-stream approximation type ! (1=Eddington, 2=Quadrature, 3=Hemispheric Mean) [nbr] integer :: DELTA ! flag to use Delta approximation (Joseph, 1976) ! (1= use, 0= don't use) - real(r8):: flx_wgt(1:numrad_snw) ! weights applied to spectral bands, - ! specific to direct and diffuse cases (bnd) [frc] - + real(r8):: flx_wgt(1:snicar_numrad_snw) ! weights applied to spectral bands, + ! specific to direct and diffuse cases (bnd) [frc] integer :: flg_nosnl ! flag: =1 if there is snow, but zero snow layers, ! =0 if at least 1 snow layer [flg] integer :: trip ! flag: =1 to redo RT calculation if result is unrealistic integer :: flg_dover ! defines conditions for RT redo (explained below) - real(r8):: albedo ! temporary snow albedo [frc] real(r8):: flx_sum ! temporary summation variable for NIR weighting - real(r8):: albout_lcl(numrad_snw) ! snow albedo by band [frc] - real(r8):: flx_abs_lcl(-nlevsno+1:1,numrad_snw)! absorbed flux per unit incident flux at top of snowpack (lyr,bnd) [frc] - + real(r8):: albout_lcl(snicar_numrad_snw) ! snow albedo by band [frc] + real(r8):: flx_abs_lcl(-nlevsno+1:1,snicar_numrad_snw)! absorbed flux per unit incident flux at top of snowpack (lyr,bnd) [frc] real(r8):: L_snw(-nlevsno+1:0) ! h2o mass (liquid+solid) in snow layer (lyr) [kg/m2] real(r8):: tau_snw(-nlevsno+1:0) ! snow optical depth (lyr) [unitless] real(r8):: L_aer(-nlevsno+1:0,sno_nbr_aer) ! aerosol mass in snow layer (lyr,nbr_aer) [kg/m2] @@ -274,7 +292,6 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & real(r8):: tau_clm(-nlevsno+1:0) ! column optical depth from layer bottom to snowpack top (lyr) [unitless] real(r8):: omega_sum ! temporary summation of single-scatter albedo of all aerosols [frc] real(r8):: g_sum ! temporary summation of asymmetry parameter of all aerosols [frc] - real(r8):: tau(-nlevsno+1:0) ! weighted optical depth of snow+aerosol layer (lyr) [unitless] real(r8):: omega(-nlevsno+1:0) ! weighted single-scatter albedo of snow+aerosol layer (lyr) [frc] real(r8):: g(-nlevsno+1:0) ! weighted asymmetry parameter of snow+aerosol layer (lyr) [frc] @@ -283,10 +300,9 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & real(r8):: omega_star(-nlevsno+1:0) ! transformed (i.e. Delta-Eddington) SSA of snow+aerosol layer (lyr) [frc] real(r8):: g_star(-nlevsno+1:0) ! transformed (i.e. Delta-Eddington) asymmetry paramater of snow+aerosol layer ! (lyr) [frc] - integer :: nstep ! current timestep [nbr] (debugging only) integer :: g_idx, c_idx, l_idx ! gridcell, column, and landunit indices [idx] - integer :: bnd_idx ! spectral band index (1 <= bnd_idx <= numrad_snw) [idx] + integer :: bnd_idx ! spectral band index (1 <= bnd_idx <= snicar_numrad_snw) [idx] integer :: rds_idx ! snow effective radius index for retrieving ! Mie parameters from lookup table [idx] integer :: snl_btm ! index of bottom snow layer (0) [idx] @@ -295,8 +311,7 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & integer :: i ! layer index [idx] integer :: j ! aerosol number index [idx] integer :: n ! tridiagonal matrix index [idx] - integer :: m ! secondary layer index [idx] - + integer :: m ! secondary layer index [idx] real(r8):: F_direct(-nlevsno+1:0) ! direct-beam radiation at bottom of layer interface (lyr) [W/m^2] real(r8):: F_net(-nlevsno+1:0) ! net radiative flux at bottom of layer interface (lyr) [W/m^2] real(r8):: F_abs(-nlevsno+1:0) ! net absorbed radiative energy (lyr) [W/m^2] @@ -307,13 +322,14 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & real(r8):: energy_sum ! sum of all energy terms; should be 0.0 [W/m^2] real(r8):: F_direct_btm ! direct-beam radiation at bottom of snowpack [W/m^2] real(r8):: mu_not ! cosine of solar zenith angle (used locally) [frc] - integer :: err_idx ! counter for number of times through error loop [nbr] real(r8):: lat_coord ! gridcell latitude (debugging only) real(r8):: lon_coord ! gridcell longitude (debugging only) integer :: sfctype ! underlying surface type (debugging only) real(r8):: pi ! 3.1415... + !----------------------------------------------------------------------- + ! variables used for Toon et al. 1989 2-stream solver (Flanner et al. 2007): ! intermediate variables for radiative transfer approximation: real(r8):: gamma1(-nlevsno+1:0) ! two-stream coefficient from Toon et al. (lyr) [unitless] real(r8):: gamma2(-nlevsno+1:0) ! two-stream coefficient from Toon et al. (lyr) [unitless] @@ -338,6 +354,252 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & real(r8):: DS(-2*nlevsno+1:0) ! tri-diag intermediate variable from Toon et al. (2*lyr) real(r8):: X(-2*nlevsno+1:0) ! tri-diag intermediate variable from Toon et al. (2*lyr) real(r8):: Y(-2*nlevsno+1:0) ! tri-diag intermediate variable from Toon et al. (2*lyr) + + !----------------------------------------------------------------------- + ! variables used for Adding-doubling 2-stream solver based on SNICAR-ADv3 version + ! (Dang et al. 2019; Flanner et al. 2021) + real(r8):: trndir(-nlevsno+1:1) ! solar beam down transmission from top + real(r8):: trntdr(-nlevsno+1:1) ! total transmission to direct beam for layers above + real(r8):: trndif(-nlevsno+1:1) ! diffuse transmission to diffuse beam for layers above + real(r8):: rupdir(-nlevsno+1:1) ! reflectivity to direct radiation for layers below + real(r8):: rupdif(-nlevsno+1:1) ! reflectivity to diffuse radiation for layers below + real(r8):: rdndif(-nlevsno+1:1) ! reflectivity to diffuse radiation for layers above + real(r8):: dfdir(-nlevsno+1:1) ! down-up flux at interface due to direct beam at top surface + real(r8):: dfdif(-nlevsno+1:1) ! down-up flux at interface due to diffuse beam at top surface + real(r8):: dftmp(-nlevsno+1:1) ! temporary variable for down-up flux at interface + real(r8):: rdir(-nlevsno+1:0) ! layer reflectivity to direct radiation + real(r8):: rdif_a(-nlevsno+1:0) ! layer reflectivity to diffuse radiation from above + real(r8):: rdif_b(-nlevsno+1:0) ! layer reflectivity to diffuse radiation from below + real(r8):: tdir(-nlevsno+1:0) ! layer transmission to direct radiation (solar beam + diffuse) + real(r8):: tdif_a(-nlevsno+1:0) ! layer transmission to diffuse radiation from above + real(r8):: tdif_b(-nlevsno+1:0) ! layer transmission to diffuse radiation from below + real(r8):: trnlay(-nlevsno+1:0) ! solar beam transm for layer (direct beam only) + real(r8):: ts ! layer delta-scaled extinction optical depth + real(r8):: ws ! layer delta-scaled single scattering albedo + real(r8):: gs ! layer delta-scaled asymmetry parameter + real(r8):: extins ! extinction + real(r8):: alp ! temporary for alpha + real(r8):: gam ! temporary for agamm + real(r8):: amg ! alp - gam + real(r8):: apg ! alp + gam + real(r8):: ue ! temporary for u + real(r8):: refk ! interface multiple scattering + real(r8):: refkp1 ! interface multiple scattering for k+1 + real(r8):: refkm1 ! interface multiple scattering for k-1 + real(r8):: tdrrdir ! direct tran times layer direct ref + real(r8):: tdndif ! total down diffuse = tot tran - direct tran + real(r8):: taus ! scaled extinction optical depth + real(r8):: omgs ! scaled single particle scattering albedo + real(r8):: asys ! scaled asymmetry parameter + real(r8):: lm ! temporary for el + real(r8):: mu ! cosine solar zenith for either snow or water + real(r8):: ne ! temporary for n + real(r8):: R1 ! perpendicular polarization reflection amplitude + real(r8):: R2 ! parallel polarization reflection amplitude + real(r8):: T1 ! perpendicular polarization transmission amplitude + real(r8):: T2 ! parallel polarization transmission amplitude + real(r8):: Rf_dir_a ! fresnel reflection to direct radiation + real(r8):: Tf_dir_a ! fresnel transmission to direct radiation + real(r8):: Rf_dif_a ! fresnel reflection to diff radiation from above + real(r8):: Rf_dif_b ! fresnel reflection to diff radiation from below + real(r8):: Tf_dif_a ! fresnel transmission to diff radiation from above + real(r8):: Tf_dif_b ! fresnel transmission to diff radiation from below + real(r8):: gwt ! gaussian weight + real(r8):: swt ! sum of weights + real(r8):: trn ! layer transmission + real(r8):: rdr ! rdir for gaussian integration + real(r8):: tdr ! tdir for gaussian integration + real(r8):: smr ! accumulator for rdif gaussian integration + real(r8):: smt ! accumulator for tdif gaussian integration + real(r8):: exp_min ! minimum exponential value + + integer :: ng ! gaussian integration index + integer, parameter :: ngmax = 8 ! max gaussian integration index + real(r8), parameter :: difgauspt(ngmax) = & ! Gaussian integration angles (radians) + (/ 0.9894009_r8, 0.9445750_r8, & + 0.8656312_r8, 0.7554044_r8, & + 0.6178762_r8, 0.4580168_r8, & + 0.2816036_r8, 0.0950125_r8/) + real(r8), parameter :: difgauswt(ngmax) = & ! Gaussian integration coefficients/weights + (/ 0.0271525_r8, 0.0622535_r8, & + 0.0951585_r8, 0.1246290_r8, & + 0.1495960_r8, 0.1691565_r8, & + 0.1826034_r8, 0.1894506_r8/) + + integer :: snl_btm_itf ! index of bottom snow layer interfaces (1) [idx] + ! constants used in algorithm + real(r8), parameter :: c0 = 0.0_r8 + real(r8), parameter :: c1 = 1.0_r8 + real(r8), parameter :: c3 = 3.0_r8 + real(r8), parameter :: c4 = 4.0_r8 + real(r8), parameter :: c6 = 6.0_r8 + real(r8), parameter :: cp01 = 0.01_r8 + real(r8), parameter :: cp5 = 0.5_r8 + real(r8), parameter :: cp75 = 0.75_r8 + real(r8), parameter :: c1p5 = 1.5_r8 + real(r8), parameter :: trmin = 0.001_r8 + real(r8), parameter :: argmax = 10.0_r8 ! maximum argument of exponential + ! constants and coefficients used for SZA parameterization + real(r8), parameter :: sza_a0 = 0.085730_r8 + real(r8), parameter :: sza_a1 = -0.630883_r8 + real(r8), parameter :: sza_a2 = 1.303723_r8 + real(r8), parameter :: sza_b0 = 1.467291_r8 + real(r8), parameter :: sza_b1 = -3.338043_r8 + real(r8), parameter :: sza_b2 = 6.807489_r8 + real(r8), parameter :: puny = 1.0e-11_r8 + real(r8), parameter :: mu_75 = 0.2588_r8 ! cosine of 75 degree + real(r8):: sza_c1 ! coefficient, SZA parameteirzation + real(r8):: sza_c0 ! coefficient, SZA parameterization + real(r8):: sza_factor ! factor used to adjust NIR direct albedo + real(r8):: flx_sza_adjust ! direct NIR flux adjustment from sza_factor + real(r8):: mu0 ! incident solar zenith angle + + !----------------------------------------------------------------------- + ! variables used for nonspherical snow grain treatment (He et al. 2017 J of Climate): + character(len=15) :: sno_shp(-nlevsno+1:0) ! Snow shape type: sphere, spheroid, hexagonal plate, koch snowflake + ! currently only assuming same shapes for all snow layers + real(r8) :: sno_fs(-nlevsno+1:0) ! Snow shape factor: ratio of nonspherical grain effective radii to that of equal-volume sphere + ! only activated when snicar_snw_shape is nonspherical + ! 0=use recommended default value (He et al. 2017); + ! others(01.2um, no BC-snow int mixing effect) + real(r8), parameter :: bcint_wvl(sixteen_bands+1) = & ! Parameterization band (0.2-1.2um) for BC-induced enhancement in snow 1-omega + (/ 0.20_r8, 0.25_r8, 0.30_r8, 0.33_r8, 0.36_r8, 0.40_r8, 0.44_r8, 0.48_r8, & + 0.52_r8, 0.57_r8, 0.64_r8, 0.69_r8, 0.75_r8, 0.78_r8, 0.87_r8, 1._r8, 1.2_r8 /) + real(r8), parameter :: bcint_d0(sixteen_bands) = & ! Parameterization coefficients at each band center wavelength + (/ 2.48045_r8 , 4.70305_r8 , 4.68619_r8 , 4.67369_r8 , 4.65040_r8 , & + 2.40364_r8 , 7.95408E-1_r8, 2.92745E-1_r8, 8.63396E-2_r8, 2.76299E-2_r8, & + 1.40864E-2_r8, 8.65705E-3_r8, 6.12971E-3_r8, 4.45697E-3_r8, 3.06648E-2_r8, & + 7.96544E-1_r8 /) + real(r8), parameter :: bcint_d1(sixteen_bands) = & ! Parameterization coefficients at each band center wavelength + (/ 9.77209E-1_r8, 9.73317E-1_r8, 9.79650E-1_r8, 9.84579E-1_r8, 9.93537E-1_r8, & + 9.95955E-1_r8, 9.95218E-1_r8, 9.74284E-1_r8, 9.81193E-1_r8, 9.81239E-1_r8, & + 9.55515E-1_r8, 9.10491E-1_r8, 8.74196E-1_r8, 8.27238E-1_r8, 4.82870E-1_r8, & + 4.36649E-2_r8 /) + real(r8), parameter :: bcint_d2(sixteen_bands) = & ! Parameterization coefficients at each band center wavelength + (/ 3.95960E-1_r8, 2.04820E-1_r8, 2.07410E-1_r8, 2.09390E-1_r8, 2.13030E-1_r8, & + 4.18570E-1_r8, 1.29682_r8 , 3.75514_r8 , 1.27372E+1_r8, 3.93293E+1_r8, & + 8.78918E+1_r8, 1.86969E+2_r8, 3.45600E+2_r8, 7.08637E+2_r8, 1.41067E+3_r8, & + 2.57288E+2_r8 /) + real(r8), parameter :: den_bc = 1.7_r8 ! BC particle density (g/cm3) + real(r8), parameter :: den_bc_target = 1.49_r8 ! target BC particle density (g/cm3) used in BC MAC adjustment + real(r8), parameter :: Re_bc = 0.045_r8 ! target BC effective radius (um) used in BC MAC adjustment + real(r8), parameter :: radius_1 = 0.1_r8 ! used with Re_bc (um) + real(r8), parameter :: radius_2 = 0.05_r8 ! used with Re_bc (um) + ! Eq. 1a,1b and Table S1 in He et al. 2018 GRL + ! Parameterization coefficients for BC size adjustment in BC-snow int mix + integer, parameter :: three_bands = 3 + real(r8), parameter :: bcint_m(three_bands) = (/ -0.8724_r8, -0.1866_r8, -0.0046_r8 /) + real(r8), parameter :: bcint_n(three_bands) = (/ -0.0072_r8, -0.1918_r8, -0.5177_r8 /) + + real(r8) :: bcint_m_tmp ! temporary of bcint_m + real(r8) :: bcint_n_tmp ! temporary of bcint_n + real(r8) :: bcint_dd ! intermediate parameter + real(r8) :: bcint_dd2 ! intermediate parameter + real(r8) :: bcint_f ! intermediate parameter + real(r8) :: enh_omg_bcint_intp ! BC-induced enhancement in snow 1-omega (logscale) interpolated to CLM wavelength + real(r8) :: enh_omg_bcint_intp2 ! BC-induced enhancement in snow 1-omega interpolated to CLM wavelength + real(r8) :: wvl_doint ! wavelength doing BC-snow int mixing (<=1.2um) + integer :: ibb ! loop index + + !----------------------------------------------------------------------- + ! variables used for dust-snow internal mixing (He et al. 2019 JAMES): + real(r8) :: enh_omg_dstint ! dust-induced enhancement in snow single-scattering co-albedo (1-omega) + integer, parameter :: size_bins = 6 + real(r8) :: enh_omg_dstint_tmp(size_bins) ! temporary dust-induced enhancement in snow 1-omega + real(r8) :: enh_omg_dstint_tmp2(size_bins) ! temporary dust-induced enhancement in snow 1-omega + real(r8) :: dstint_wvl_ct(size_bins) ! Parameterization band center wavelength (um) + ! initialize for dust-snow internal mixing + ! Eq. 1 and Table 1 in He et al. 2019 JAMES (wavelength>1.2um, no dust-snow int mixing effect) + real(r8), parameter :: dstint_wvl(size_bins+1) = & ! Parameterization band (0.2-1.2um) for dust-induced enhancement in snow 1-omega + (/ 0.2_r8, 0.2632_r8, 0.3448_r8, 0.4415_r8, 0.625_r8, 0.7782_r8, 1.2422_r8/) + real(r8), parameter :: dstint_a1(size_bins) = & ! Parameterization coefficients at each band center wavelength + (/ -2.1307E+1_r8, -1.5815E+1_r8, -9.2880_r8 , 1.1115_r8 , 1.0307_r8 , 1.0185_r8 /) + real(r8), parameter :: dstint_a2(size_bins) = & ! Parameterization coefficients at each band center wavelength + (/ 1.1746E+2_r8, 9.3241E+1_r8, 4.0605E+1_r8, 3.7389E-1_r8, 1.4800E-2_r8, 2.8921E-4_r8 /) + real(r8), parameter :: dstint_a3(size_bins) = & ! Parameterization coefficients at each band center wavelength + (/ 9.9701E-1_r8, 9.9781E-1_r8, 9.9848E-1_r8, 1.0035_r8 , 1.0024_r8 , 1.0356_r8 /) + + real(r8) :: enh_omg_dstint_intp ! dust-induced enhancement in snow 1-omega (logscale) interpolated to CLM wavelength + real(r8) :: enh_omg_dstint_intp2 ! dust-induced enhancement in snow 1-omega interpolated to CLM wavelength + real(r8) :: tot_dst_snw_conc ! total dust content in snow across all size bins (ppm=ug/g) + integer :: idb ! loop index + + real(r8), parameter :: enh_omg_max = 1.e5_r8 ! reasonable maximum value for enh_omg_[bc,dst]int_intp2 + + ! unit conversions + real(r8), parameter :: kg_kg_to_ppm = 1.e6_r8 ! kg/kg to ppm + real(r8), parameter :: kg_to_ug = 1.e9_r8 ! kg to micrograms + + character(len=*), parameter :: subname = 'SNICAR_RT' !----------------------------------------------------------------------- ! Enforce expected array sizes @@ -353,10 +615,36 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & associate(& snl => col%snl , & ! Input: [integer (:)] negative number of snow layers (col) [nbr] - frac_sno => waterdiagnosticbulk_inst%frac_sno_eff_col & ! Input: [real(r8) (:)] fraction of ground covered by snow (0 to 1) ) + ! initialize parameter and + ! SNICAR/CLM snow band center wavelength (um) + allocate(wvl_ct(snicar_numrad_snw)) + select case (snicar_numrad_snw) + case (default_number_bands) + nir_bnd_bgn = 2 + wvl_ct(:) = (/ 0.5_r8, 0.85_r8, 1.1_r8, 1.35_r8, 3.25_r8 /) ! 5-band + case (high_number_bands) + nir_bnd_bgn = 51 + do igb = 1, snicar_numrad_snw + wvl_ct(igb) = 0.205_r8 + 0.01_r8 * (igb - 1._r8) ! 480-band + enddo + case default + write(iulog,*) subname//' ERROR: unknown snicar_numrad_snw value: ', snicar_numrad_snw + call endrun(msg=errMsg(sourcefile, __LINE__)) + end select + nir_bnd_end = snicar_numrad_snw + + ! initialize for nonspherical snow grains + sno_shp(:) = snicar_snw_shape ! currently only assuming same shapes for all snow layers + sno_fs(:) = 0._r8 + sno_AR(:) = 0._r8 + + g_wvl_ct(1:seven_bands) = g_wvl(2:seven_bands+1) * 0.5_r8 + g_wvl(1:seven_bands) * 0.5_r8 + dstint_wvl_ct(1:size_bins) = dstint_wvl(2:size_bins+1) * 0.5_r8 + dstint_wvl(1:size_bins) * 0.5_r8 + bcint_wvl_ct(1:sixteen_bands) = bcint_wvl(2:sixteen_bands+1) * 0.5_r8 + bcint_wvl(1:sixteen_bands) * 0.5_r8 + ! Define constants pi = SHR_CONST_PI @@ -367,24 +655,17 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & nstep = get_nstep() ! Loop over all non-urban columns - ! (when called from CSIM, there is only one column) do fc = 1,num_nourbanc c_idx = filter_nourbanc(fc) - ! Zero absorbed radiative fluxes: do i=-nlevsno+1,1,1 - flx_abs_lcl(:,:) = 0._r8 + flx_abs_lcl(i,:) = 0._r8 flx_abs(c_idx,i,:) = 0._r8 enddo ! set snow/ice mass to be used for RT: - if (flg_snw_ice == 1) then - h2osno_lcl = h2osno_total(c_idx) - else - h2osno_lcl = h2osno_ice(c_idx,0) - endif - + h2osno_lcl = h2osno_total(c_idx) ! Qualifier for computing snow RT: ! 1) sunlight from atmosphere model @@ -393,47 +674,32 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & if ((coszen(c_idx) > 0._r8) .and. (h2osno_lcl > min_snw)) then ! Set variables specific to CLM - if (flg_snw_ice == 1) then - ! If there is snow, but zero snow layers, we must create a layer locally. - ! This layer is presumed to have the fresh snow effective radius. - if (snl(c_idx) > -1) then - flg_nosnl = 1 - snl_lcl = -1 - h2osno_ice_lcl(0) = h2osno_lcl - h2osno_liq_lcl(0) = 0._r8 - snw_rds_lcl(0) = snw_rds_min_int - else - flg_nosnl = 0 - snl_lcl = snl(c_idx) - h2osno_liq_lcl(:) = h2osno_liq(c_idx,:) - h2osno_ice_lcl(:) = h2osno_ice(c_idx,:) - snw_rds_lcl(:) = snw_rds(c_idx,:) - endif - - snl_btm = 0 - snl_top = snl_lcl+1 + ! If there is snow, but zero snow layers, we must create a layer locally. + ! This layer is presumed to have the fresh snow effective radius. + if (snl(c_idx) > -1) then + flg_nosnl = 1 + snl_lcl = -1 + h2osno_ice_lcl(0) = h2osno_lcl + h2osno_liq_lcl(0) = 0._r8 + snw_rds_lcl(0) = nint(params_inst%snw_rds_min) + else + flg_nosnl = 0 + snl_lcl = snl(c_idx) + h2osno_liq_lcl(:) = h2osno_liq(c_idx,:) + h2osno_ice_lcl(:) = h2osno_ice(c_idx,:) + snw_rds_lcl(:) = snw_rds(c_idx,:) + endif - ! for debugging only - l_idx = col%landunit(c_idx) - g_idx = col%gridcell(c_idx) - sfctype = lun%itype(l_idx) - lat_coord = grc%latdeg(g_idx) - lon_coord = grc%londeg(g_idx) + snl_btm = 0 + snl_top = snl_lcl+1 + ! for debugging only + l_idx = col%landunit(c_idx) + g_idx = col%gridcell(c_idx) + sfctype = lun%itype(l_idx) + lat_coord = grc%latdeg(g_idx) + lon_coord = grc%londeg(g_idx) - ! Set variables specific to CSIM - else - flg_nosnl = 0 - snl_lcl = -1 - h2osno_liq_lcl(:) = h2osno_liq(c_idx,:) - h2osno_ice_lcl(:) = h2osno_ice(c_idx,:) - snw_rds_lcl(:) = snw_rds(c_idx,:) - snl_btm = 0 - snl_top = 0 - sfctype = -1 - lat_coord = -90 - lon_coord = 0 - endif ! Set local aerosol array do j=1,sno_nbr_aer @@ -442,16 +708,15 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & ! Set spectral underlying surface albedos to their corresponding VIS or NIR albedos - albsfc_lcl(1) = albsfc(c_idx,1) - albsfc_lcl(nir_bnd_bgn:nir_bnd_end) = albsfc(c_idx,2) - + albsfc_lcl(1:(nir_bnd_bgn-1)) = albsfc(c_idx,ivis) + albsfc_lcl(nir_bnd_bgn:nir_bnd_end) = albsfc(c_idx,inir) + ! Error check for snow grain size: do i=snl_top,snl_btm,1 if ((snw_rds_lcl(i) < snw_rds_min_tbl) .or. (snw_rds_lcl(i) > snw_rds_max_tbl)) then write (iulog,*) "SNICAR ERROR: snow grain radius of ", snw_rds_lcl(i), " out of bounds." write (iulog,*) "NSTEP= ", nstep - write (iulog,*) "flg_snw_ice= ", flg_snw_ice write (iulog,*) "column: ", c_idx, " level: ", i, " snl(c)= ", snl_lcl write (iulog,*) "lat= ", lat_coord, " lon= ", lon_coord write (iulog,*) "h2osno_total(c)= ", h2osno_lcl @@ -459,6 +724,7 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & endif enddo + ! Incident flux weighting parameters ! - sum of all VIS bands must equal 1 ! - sum of all NIR bands must equal 1 @@ -470,102 +736,37 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & ! Band 4: 1.2-1.5um (NIR) ! Band 5: 1.5-5.0um (NIR) ! + ! Hyperspectral (10-nm) bands (480-band case) + ! Bands 1~50 : 0.2-0.7um (VIS) + ! Bands 51~480: 0.7~5.0um (NIR) + ! ! The following weights are appropriate for surface-incident flux in a mid-latitude winter atmosphere ! - ! 3-band weights - if (numrad_snw==3) then - ! Direct: - if (flg_slr_in == 1) then - flx_wgt(1) = 1._r8 - flx_wgt(2) = 0.66628670195247_r8 - flx_wgt(3) = 0.33371329804753_r8 - ! Diffuse: - elseif (flg_slr_in == 2) then - flx_wgt(1) = 1._r8 - flx_wgt(2) = 0.77887652162877_r8 - flx_wgt(3) = 0.22112347837123_r8 - endif - - ! 5-band weights - elseif(numrad_snw==5) then - ! Direct: - if (flg_slr_in == 1) then - flx_wgt(1) = 1._r8 - flx_wgt(2) = 0.49352158521175_r8 - flx_wgt(3) = 0.18099494230665_r8 - flx_wgt(4) = 0.12094898498813_r8 - flx_wgt(5) = 0.20453448749347_r8 - ! Diffuse: - elseif (flg_slr_in == 2) then - flx_wgt(1) = 1._r8 - flx_wgt(2) = 0.58581507618433_r8 - flx_wgt(3) = 0.20156903770812_r8 - flx_wgt(4) = 0.10917889346386_r8 - flx_wgt(5) = 0.10343699264369_r8 - endif +! ! works for both 5-band & 480-band, flux weights directly read from input data + ! Direct: + if (flg_slr_in == 1) then + flx_wgt(1:snicar_numrad_snw) = flx_wgt_dir(1:snicar_numrad_snw) ! VIS or NIR band sum is already normalized to 1.0 in input data + ! Diffuse: + elseif (flg_slr_in == 2) then + flx_wgt(1:snicar_numrad_snw) = flx_wgt_dif(1:snicar_numrad_snw) ! VIS or NIR band sum is already normalized to 1.0 in input data endif + exp_min = exp(-argmax) + ! Loop over snow spectral bands - do bnd_idx = 1,numrad_snw + do bnd_idx = 1,snicar_numrad_snw + ! flg_dover is not used since this algorithm is stable for mu_not > 0.01 + ! mu_not is cosine solar zenith angle above the fresnel level; make + ! sure mu_not is large enough for stable and meaningful radiation + ! solution: .01 is like sun just touching horizon with its lower edge + ! equivalent to mu0 in sea-ice shortwave model ice_shortwave.F90 + mu_not = max(coszen(c_idx), cp01) - mu_not = coszen(c_idx) ! must set here, because of error handling - flg_dover = 1 ! default is to redo - err_idx = 0 ! number of times through loop + flg_dover = 1 ! default is to redo + err_idx = 0 ! number of times through loop do while (flg_dover > 0) - ! DEFAULT APPROXIMATIONS: - ! VIS: Delta-Eddington - ! NIR (all): Delta-Hemispheric Mean - ! WARNING: DO NOT USE DELTA-EDDINGTON FOR NIR DIFFUSE - this sometimes results in negative albedo - ! - ! ERROR CONDITIONS: - ! Conditions which cause "trip", resulting in redo of RT approximation: - ! 1. negative absorbed flux - ! 2. total absorbed flux greater than incident flux - ! 3. negative albedo - ! NOTE: These errors have only been encountered in spectral bands 4 and 5 - ! - ! ERROR HANDLING - ! 1st error (flg_dover=2): switch approximation (Edd->HM or HM->Edd) - ! 2nd error (flg_dover=3): change zenith angle by 0.02 (this happens about 1 in 10^6 cases) - ! 3rd error (flg_dover=4): switch approximation with new zenith - ! Subsequent errors: repeatedly change zenith and approximations... - - if (bnd_idx == 1) then - if (flg_dover == 2) then - APRX_TYP = 3 - elseif (flg_dover == 3) then - APRX_TYP = 1 - if (coszen(c_idx) > 0.5_r8) then - mu_not = mu_not - 0.02_r8 - else - mu_not = mu_not + 0.02_r8 - endif - elseif (flg_dover == 4) then - APRX_TYP = 3 - else - APRX_TYP = 1 - endif - - else - if (flg_dover == 2) then - APRX_TYP = 1 - elseif (flg_dover == 3) then - APRX_TYP = 3 - if (coszen(c_idx) > 0.5_r8) then - mu_not = mu_not - 0.02_r8 - else - mu_not = mu_not + 0.02_r8 - endif - elseif (flg_dover == 4) then - APRX_TYP = 1 - else - APRX_TYP = 3 - endif - - endif - ! Set direct or diffuse incident irradiance to 1 ! (This has to be within the bnd loop because mu_not is adjusted in rare cases) if (flg_slr_in == 1) then @@ -578,81 +779,238 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & ! Pre-emptive error handling: aerosols can reap havoc on these absorptive bands. ! Since extremely high soot concentrations have a negligible effect on these bands, zero them. - if ( (numrad_snw == 5).and.((bnd_idx == 5).or.(bnd_idx == 4)) ) then + if (snicar_numrad_snw == default_number_bands .and. (bnd_idx == highest_default_band .or. bnd_idx == sec_highest_default_band)) then mss_cnc_aer_lcl(:,:) = 0._r8 endif - if ( (numrad_snw == 3).and.(bnd_idx == 3) ) then + if ( (snicar_numrad_snw == high_number_bands).and.(bnd_idx > 100) ) then ! >1.2um mss_cnc_aer_lcl(:,:) = 0._r8 endif - ! Define local Mie parameters based on snow grain size and aerosol species, - ! retrieved from a lookup table. + + !--------------------------- Start snow & aerosol optics -------------------------------- + ! Define local Mie parameters based on snow grain size and aerosol species retrieved from a lookup table. + + ! Spherical snow: single-scatter albedo, mass extinction coefficient, asymmetry factor if (flg_slr_in == 1) then do i=snl_top,snl_btm,1 rds_idx = snw_rds_lcl(i) - snw_rds_min_tbl + 1 ! snow optical properties (direct radiation) ss_alb_snw_lcl(i) = ss_alb_snw_drc(rds_idx,bnd_idx) - asm_prm_snw_lcl(i) = asm_prm_snw_drc(rds_idx,bnd_idx) ext_cff_mss_snw_lcl(i) = ext_cff_mss_snw_drc(rds_idx,bnd_idx) + if (sno_shp(i) == 'sphere') asm_prm_snw_lcl(i) = asm_prm_snw_drc(rds_idx,bnd_idx) enddo elseif (flg_slr_in == 2) then do i=snl_top,snl_btm,1 rds_idx = snw_rds_lcl(i) - snw_rds_min_tbl + 1 ! snow optical properties (diffuse radiation) ss_alb_snw_lcl(i) = ss_alb_snw_dfs(rds_idx,bnd_idx) - asm_prm_snw_lcl(i) = asm_prm_snw_dfs(rds_idx,bnd_idx) ext_cff_mss_snw_lcl(i) = ext_cff_mss_snw_dfs(rds_idx,bnd_idx) + if (sno_shp(i) == 'sphere') asm_prm_snw_lcl(i) = asm_prm_snw_dfs(rds_idx,bnd_idx) enddo endif - ! aerosol species 1 optical properties - ss_alb_aer_lcl(1) = ss_alb_bc1(bnd_idx) - asm_prm_aer_lcl(1) = asm_prm_bc1(bnd_idx) - ext_cff_mss_aer_lcl(1) = ext_cff_mss_bc1(bnd_idx) - - ! aerosol species 2 optical properties - ss_alb_aer_lcl(2) = ss_alb_bc2(bnd_idx) - asm_prm_aer_lcl(2) = asm_prm_bc2(bnd_idx) - ext_cff_mss_aer_lcl(2) = ext_cff_mss_bc2(bnd_idx) - - ! aerosol species 3 optical properties - ss_alb_aer_lcl(3) = ss_alb_oc1(bnd_idx) - asm_prm_aer_lcl(3) = asm_prm_oc1(bnd_idx) - ext_cff_mss_aer_lcl(3) = ext_cff_mss_oc1(bnd_idx) - - ! aerosol species 4 optical properties - ss_alb_aer_lcl(4) = ss_alb_oc2(bnd_idx) - asm_prm_aer_lcl(4) = asm_prm_oc2(bnd_idx) - ext_cff_mss_aer_lcl(4) = ext_cff_mss_oc2(bnd_idx) - - ! aerosol species 5 optical properties - ss_alb_aer_lcl(5) = ss_alb_dst1(bnd_idx) - asm_prm_aer_lcl(5) = asm_prm_dst1(bnd_idx) - ext_cff_mss_aer_lcl(5) = ext_cff_mss_dst1(bnd_idx) - - ! aerosol species 6 optical properties - ss_alb_aer_lcl(6) = ss_alb_dst2(bnd_idx) - asm_prm_aer_lcl(6) = asm_prm_dst2(bnd_idx) - ext_cff_mss_aer_lcl(6) = ext_cff_mss_dst2(bnd_idx) - - ! aerosol species 7 optical properties - ss_alb_aer_lcl(7) = ss_alb_dst3(bnd_idx) - asm_prm_aer_lcl(7) = asm_prm_dst3(bnd_idx) - ext_cff_mss_aer_lcl(7) = ext_cff_mss_dst3(bnd_idx) + ! Nonspherical snow: shape-dependent asymmetry factors + do i=snl_top,snl_btm,1 - ! aerosol species 8 optical properties - ss_alb_aer_lcl(8) = ss_alb_dst4(bnd_idx) - asm_prm_aer_lcl(8) = asm_prm_dst4(bnd_idx) - ext_cff_mss_aer_lcl(8) = ext_cff_mss_dst4(bnd_idx) + select case (sno_shp(i)) + case ('spheroid') + diam_ice = 2._r8 * snw_rds_lcl(i) ! unit: microns + if (sno_fs(i) == 0._r8) then + fs_sphd = fs_sphd_default ! default; He et al. (2017), Table 1 + else + fs_sphd = sno_fs(i) ! user specified value + endif + fs_hex = fs_hex_ref ! reference shape factor + if (sno_AR(i) == 0._r8) then + AR_tmp = AR_tmp_default_1 ! default; He et al. (2017), Table 1 + else + AR_tmp = sno_AR(i) ! user specified value + endif + do igb = 1, seven_bands + g_ice_Cg_tmp(igb) = g_b0(igb) * ((fs_sphd/fs_hex)**g_b1(igb)) * (diam_ice**g_b2(igb)) ! Eq.7, He et al. (2017) + gg_ice_F07_tmp(igb) = g_F07_c0(igb) + g_F07_c1(igb) * AR_tmp + g_F07_c2(igb) * (AR_tmp * AR_tmp) ! Eqn. 3.1 in Fu (2007) + enddo + + case ('hexagonal_plate') + diam_ice = 2._r8 * snw_rds_lcl(i) ! unit: microns + if (sno_fs(i) == 0._r8) then + fs_hex0 = fs_hex_ref ! default; He et al. (2017), Table 1 + else + fs_hex0 = sno_fs(i) ! user specified value + endif + fs_hex = fs_hex_ref ! reference shape factor + if (sno_AR(i) == 0._r8) then + AR_tmp = AR_tmp_default_2 ! default; He et al. (2017), Table 1 + else + AR_tmp = sno_AR(i) ! user specified value + endif + do igb = 1, seven_bands + g_ice_Cg_tmp(igb) = g_b0(igb) * ((fs_hex0/fs_hex)**g_b1(igb)) * (diam_ice**g_b2(igb)) ! Eq.7, He et al. (2017) + gg_ice_F07_tmp(igb) = g_F07_p0(igb) + g_F07_p1(igb) * log(AR_tmp) + g_F07_p2(igb) * (log(AR_tmp) * log(AR_tmp)) ! Eqn. 3.3 in Fu (2007) + enddo + + case ('koch_snowflake') + diam_ice = 2._r8 * snw_rds_lcl(i) / 0.544_r8 ! unit: microns + if (sno_fs(i) == 0._r8) then + fs_koch = fs_koch_default ! default; He et al. (2017), Table 1 + else + fs_koch = sno_fs(i) ! user specified value + endif + fs_hex = fs_hex_ref ! reference shape factor + if (sno_AR(i) == 0._r8) then + AR_tmp = AR_tmp_default_2 ! default; He et al. (2017), Table 1 + else + AR_tmp = sno_AR(i) ! user specified value + endif + do igb = 1, seven_bands + g_ice_Cg_tmp(igb) = g_b0(igb) * ((fs_koch/fs_hex)**g_b1(igb)) * (diam_ice**g_b2(igb)) ! Eq.7, He et al. (2017) + gg_ice_F07_tmp(igb) = g_F07_p0(igb) + g_F07_p1(igb) * log(AR_tmp) + g_F07_p2(igb) * (log(AR_tmp) * log(AR_tmp)) ! Eqn. 3.3 in Fu (2007) + enddo + + case ('sphere') + ! DO NOTHING + case default + write(iulog,*) subname//' ERROR: unknown sno_shp for i: ', sno_shp(i), i + call endrun(msg=errMsg(sourcefile, __LINE__)) + end select + + ! compute nonspherical snow asymmetry factor + if (sno_shp(i) /= 'sphere') then + ! 7 wavelength bands for g_ice to be interpolated into targeted SNICAR bands here + ! use the piecewise linear interpolation subroutine created at the end of this module + ! tests showed the piecewise linear interpolation has similar results as pchip interpolation + call piecewise_linear_interp1d(seven_bands, g_wvl_ct, g_ice_Cg_tmp, wvl_ct(bnd_idx), g_Cg_intp) + call piecewise_linear_interp1d(seven_bands, g_wvl_ct, gg_ice_F07_tmp, wvl_ct(bnd_idx), gg_F07_intp) + g_ice_F07 = gg_F07_intp + 0.5_r8 * (1._r8 - gg_F07_intp) / ss_alb_snw_lcl(i) ! Eq.2.2 in Fu (2007) + asm_prm_snw_lcl(i) = g_ice_F07 * g_Cg_intp ! Eq.6, He et al. (2017) + endif + asm_prm_snw_lcl(i) = min(0.99_r8, asm_prm_snw_lcl(i)) !avoid unreasonable values (rarely occur in large-size spheroid cases) + + enddo ! snow layer loop + + ! aerosol species 2 optical properties, hydrophobic BC + ss_alb_aer_lcl(2) = ss_alb_bc_hphob(bnd_idx) + asm_prm_aer_lcl(2) = asm_prm_bc_hphob(bnd_idx) + ext_cff_mss_aer_lcl(2) = ext_cff_mss_bc_hphob(bnd_idx) + ! aerosol species 3 optical properties, hydrophilic OC + ss_alb_aer_lcl(3) = ss_alb_oc_hphil(bnd_idx) + asm_prm_aer_lcl(3) = asm_prm_oc_hphil(bnd_idx) + ext_cff_mss_aer_lcl(3) = ext_cff_mss_oc_hphil(bnd_idx) + ! aerosol species 4 optical properties, hydrophobic OC + ss_alb_aer_lcl(4) = ss_alb_oc_hphob(bnd_idx) + asm_prm_aer_lcl(4) = asm_prm_oc_hphob(bnd_idx) + ext_cff_mss_aer_lcl(4) = ext_cff_mss_oc_hphob(bnd_idx) + + ! Optics for BC/dust-snow external mixing: + ! aerosol species 1 optical properties, hydrophilic BC + ss_alb_aer_lcl(1) = ss_alb_bc_hphil(bnd_idx) + asm_prm_aer_lcl(1) = asm_prm_bc_hphil(bnd_idx) + ext_cff_mss_aer_lcl(1) = ext_cff_mss_bc_hphil(bnd_idx) + ! aerosol species 5 optical properties, dust size1 + ss_alb_aer_lcl(5) = ss_alb_dst1(bnd_idx) + asm_prm_aer_lcl(5) = asm_prm_dst1(bnd_idx) + ext_cff_mss_aer_lcl(5) = ext_cff_mss_dst1(bnd_idx) + ! aerosol species 6 optical properties, dust size2 + ss_alb_aer_lcl(6) = ss_alb_dst2(bnd_idx) + asm_prm_aer_lcl(6) = asm_prm_dst2(bnd_idx) + ext_cff_mss_aer_lcl(6) = ext_cff_mss_dst2(bnd_idx) + ! aerosol species 7 optical properties, dust size3 + ss_alb_aer_lcl(7) = ss_alb_dst3(bnd_idx) + asm_prm_aer_lcl(7) = asm_prm_dst3(bnd_idx) + ext_cff_mss_aer_lcl(7) = ext_cff_mss_dst3(bnd_idx) + ! aerosol species 8 optical properties, dust size4 + ss_alb_aer_lcl(8) = ss_alb_dst4(bnd_idx) + asm_prm_aer_lcl(8) = asm_prm_dst4(bnd_idx) + ext_cff_mss_aer_lcl(8) = ext_cff_mss_dst4(bnd_idx) ! 1. snow and aerosol layer column mass (L_snw, L_aer [kg/m^2]) ! 2. optical Depths (tau_snw, tau_aer) ! 3. weighted Mie properties (tau, omega, g) + wvl_doint = wvl_ct(bnd_idx) + ! Weighted Mie parameters of each layer do i=snl_top,snl_btm,1 + + ! Start BC/dust-snow internal mixing for wavelength<=1.2um + if (wvl_doint <= 1.2_r8) then + + ! BC-snow internal mixing applied to hydrophilic BC if activated + ! BC-snow internal mixing primarily affect snow single-scattering albedo + if ( snicar_snobc_intmix .and. (mss_cnc_aer_lcl(i,1) > 0._r8) ) then + ! result from Eq.8b in He et al.(2017) is based on BC Re=0.1um & + ! MAC=6.81 m2/g (@550 nm) & BC density=1.7g/cm3 (den_bc). + ! To be consistent with Bond et al. 2006 recommeded value (BC MAC=7.5 m2/g @550nm) + ! we made adjustments on BC size & density as follows to get MAC=7.5m2/g: + ! (1) We use BC Re=0.045um [geometric mean diameter=0.06um (Dentener et al.2006, + ! Yu and Luo,2009) & geometric std=1.5 (Flanner et al.2007;Aoki et al., 2011)]. + ! (2) We tune BC density from 1.7 to 1.49 g/cm3 (den_bc_target) (Aoki et al., 2011). + ! These adjustments also lead to consistent results with Flanner et al. 2012 (ACP) lookup table + ! for BC-snow internal mixing enhancement in albedo reduction (He et al. 2018 ACP) + do ibb=1,sixteen_bands + enh_omg_bcint_tmp(ibb) = bcint_d0(ibb) * & + ( (mss_cnc_aer_lcl(i,1) * kg_to_ug * den_bc / den_bc_target + bcint_d2(ibb))**bcint_d1(ibb) ) + ! adjust enhancment factor for BC effective size from 0.1um to Re_bc (He et al. 2018 GRL Eqs.1a,1b) + if (ibb < 3) then ! near-UV + bcint_m_tmp = bcint_m(1) + bcint_n_tmp = bcint_n(1) + else if (ibb >= 3 .and. ibb <= 11) then ! visible + bcint_m_tmp = bcint_m(2) + bcint_n_tmp = bcint_n(2) + else ! ibb > 11, NIR + bcint_m_tmp = bcint_m(3) + bcint_n_tmp = bcint_n(3) + endif + bcint_dd = (Re_bc / radius_2)**bcint_m_tmp + bcint_dd2 = (radius_1 / radius_2)**bcint_m_tmp + bcint_f = (Re_bc / radius_1)**bcint_n_tmp + + enh_omg_bcint_tmp2(ibb)=LOG10(max(1._r8,bcint_dd*((enh_omg_bcint_tmp(ibb)/bcint_dd2)**bcint_f))) + enddo + ! piecewise linear interpolate into targeted SNICAR bands in a logscale space + call piecewise_linear_interp1d(sixteen_bands,bcint_wvl_ct,enh_omg_bcint_tmp2,wvl_doint,enh_omg_bcint_intp) + ! update snow single-scattering albedo + enh_omg_bcint_intp2 = 10._r8 ** enh_omg_bcint_intp + enh_omg_bcint_intp2 = min(enh_omg_max, max(enh_omg_bcint_intp2, 1._r8)) ! constrain enhancement to a reasonable range + ss_alb_snw_lcl(i) = 1._r8 - (1._r8 - ss_alb_snw_lcl(i)) * enh_omg_bcint_intp2 + ss_alb_snw_lcl(i) = max(0.5_r8, min(ss_alb_snw_lcl(i),1._r8)) + ! reset hydrophilic BC property to 0 since it is accounted by updated snow ss_alb above + ss_alb_aer_lcl(1) = 0.0 + asm_prm_aer_lcl(1) = 0.0 + ext_cff_mss_aer_lcl(1) = 0.0 + endif ! end if BC-snow mixing type + + ! Dust-snow internal mixing applied to all size bins if activated + ! Dust-snow internal mixing primarily affect snow single-scattering albedo + ! default optics of externally mixed dust at 4 size bins based on effective + ! radius of 1.38um and sigma=2.0 with truncation to each size bin (Flanner et al. 2021 GMD) + ! parameterized dust-snow int mix results based on effective radius of 1.1um and sigma=2.0 + ! from (He et al. 2019 JAMES). Thus, the parameterization can be approximately applied to + ! all dust size bins here. + tot_dst_snw_conc = (mss_cnc_aer_lcl(i,5) + mss_cnc_aer_lcl(i,6) + & + mss_cnc_aer_lcl(i,7) + mss_cnc_aer_lcl(i,8)) * kg_kg_to_ppm + if ( snicar_snodst_intmix .and. (tot_dst_snw_conc > 0._r8) ) then + do idb=1, size_bins + enh_omg_dstint_tmp(idb) = dstint_a1(idb)+dstint_a2(idb)*(tot_dst_snw_conc**dstint_a3(idb)) + enh_omg_dstint_tmp2(idb) = LOG10(max(enh_omg_dstint_tmp(idb),1._r8)) + enddo + ! piecewise linear interpolate into targeted SNICAR bands in a logscale space + call piecewise_linear_interp1d(size_bins,dstint_wvl_ct,enh_omg_dstint_tmp2,wvl_doint,enh_omg_dstint_intp) + ! update snow single-scattering albedo + enh_omg_dstint_intp2 = 10._r8 ** enh_omg_dstint_intp + enh_omg_dstint_intp2 = min(enh_omg_max, max(enh_omg_dstint_intp2, 1._r8)) ! constrain enhancement to a reasonable range + ss_alb_snw_lcl(i) = 1._r8 - (1._r8 - ss_alb_snw_lcl(i)) * enh_omg_dstint_intp2 + ss_alb_snw_lcl(i) = max(0.5_r8, min(ss_alb_snw_lcl(i),1._r8)) + ! reset all dust optics to zero since it is accounted by updated snow ss_alb above + ss_alb_aer_lcl(5:8) = 0._r8 + asm_prm_aer_lcl(5:8) = 0._r8 + ext_cff_mss_aer_lcl(5:8) = 0._r8 + endif ! end if dust-snow internal mixing + + endif ! end if BC/dust-snow internal mixing (bands<1.2um) + L_snw(i) = h2osno_ice_lcl(i)+h2osno_liq_lcl(i) tau_snw(i) = L_snw(i)*ext_cff_mss_snw_lcl(i) @@ -674,14 +1032,15 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & tau(i) = tau_sum + tau_snw(i) omega(i) = (1/tau(i))*(omega_sum+(ss_alb_snw_lcl(i)*tau_snw(i))) g(i) = (1/(tau(i)*omega(i)))*(g_sum+ (asm_prm_snw_lcl(i)*ss_alb_snw_lcl(i)*tau_snw(i))) - enddo + + enddo ! end do snow layers ! DELTA transformations, if requested if (DELTA == 1) then do i=snl_top,snl_btm,1 g_star(i) = g(i)/(1+g(i)) - omega_star(i) = ((1-(g(i)**2))*omega(i)) / (1-(omega(i)*(g(i)**2))) - tau_star(i) = (1-(omega(i)*(g(i)**2)))*tau(i) + omega_star(i) = (1._r8 - g(i) * g(i)) * omega(i) / (1._r8 - omega(i) * (g(i) * g(i))) + tau_star(i) = (1._r8 - omega(i) * (g(i) * g(i))) * tau(i) enddo else do i=snl_top,snl_btm,1 @@ -690,247 +1049,283 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & tau_star(i) = tau(i) enddo endif - - ! Total column optical depth: - ! tau_clm(i) = total optical depth above the bottom of layer i - tau_clm(snl_top) = 0._r8 - do i=snl_top+1,snl_btm,1 - tau_clm(i) = tau_clm(i-1)+tau_star(i-1) - enddo - - ! Direct radiation at bottom of snowpack: - F_direct_btm = albsfc_lcl(bnd_idx)*mu_not * & - exp(-(tau_clm(snl_btm)+tau_star(snl_btm))/mu_not)*pi*flx_slrd_lcl(bnd_idx) - - ! Intermediates - ! Gamma values are approximation-specific. - - ! Eddington - if (APRX_TYP==1) then - do i=snl_top,snl_btm,1 - gamma1(i) = (7._r8-(omega_star(i)*(4._r8+(3._r8*g_star(i)))))/4._r8 - gamma2(i) = -(1._r8-(omega_star(i)*(4._r8-(3._r8*g_star(i)))))/4._r8 - gamma3(i) = (2._r8-(3._r8*g_star(i)*mu_not))/4._r8 - gamma4(i) = 1._r8-gamma3(i) - mu_one = 0.5_r8 - enddo - - ! Quadrature - elseif (APRX_TYP==2) then - do i=snl_top,snl_btm,1 - gamma1(i) = (3._r8**0.5)*(2._r8-(omega_star(i)*(1._r8+g_star(i))))/2._r8 - gamma2(i) = omega_star(i)*(3._r8**0.5)*(1._r8-g_star(i))/2._r8 - gamma3(i) = (1._r8-((3._r8**0.5)*g_star(i)*mu_not))/2._r8 - gamma4(i) = 1._r8-gamma3(i) - mu_one = 1._r8/(3._r8**0.5_r8) - enddo - - ! Hemispheric Mean - elseif (APRX_TYP==3) then - do i=snl_top,snl_btm,1 - gamma1(i) = 2._r8 - (omega_star(i)*(1._r8+g_star(i))) - gamma2(i) = omega_star(i)*(1-g_star(i)) - gamma3(i) = (1._r8-((3._r8**0.5_r8)*g_star(i)*mu_not))/2._r8 - gamma4(i) = 1._r8-gamma3(i) - mu_one = 0.5_r8 - enddo - endif - - ! Intermediates for tri-diagonal solution - do i=snl_top,snl_btm,1 - lambda(i) = sqrt(abs((gamma1(i)**2) - (gamma2(i)**2))) - GAMMA(i) = gamma2(i)/(gamma1(i)+lambda(i)) - - e1(i) = 1+(GAMMA(i)*exp(-lambda(i)*tau_star(i))) - e2(i) = 1-(GAMMA(i)*exp(-lambda(i)*tau_star(i))) - e3(i) = GAMMA(i) + exp(-lambda(i)*tau_star(i)) - e4(i) = GAMMA(i) - exp(-lambda(i)*tau_star(i)) - enddo !enddo over snow layers - - - ! Intermediates for tri-diagonal solution - do i=snl_top,snl_btm,1 - if (flg_slr_in == 1) then - - C_pls_btm(i) = (omega_star(i)*pi*flx_slrd_lcl(bnd_idx)* & - exp(-(tau_clm(i)+tau_star(i))/mu_not)* & - (((gamma1(i)-(1/mu_not))*gamma3(i))+ & - (gamma4(i)*gamma2(i))))/((lambda(i)**2)-(1/(mu_not**2))) - - C_mns_btm(i) = (omega_star(i)*pi*flx_slrd_lcl(bnd_idx)* & - exp(-(tau_clm(i)+tau_star(i))/mu_not)* & - (((gamma1(i)+(1/mu_not))*gamma4(i))+ & - (gamma2(i)*gamma3(i))))/((lambda(i)**2)-(1/(mu_not**2))) - - C_pls_top(i) = (omega_star(i)*pi*flx_slrd_lcl(bnd_idx)* & - exp(-tau_clm(i)/mu_not)*(((gamma1(i)-(1/mu_not))* & - gamma3(i))+(gamma4(i)*gamma2(i))))/((lambda(i)**2)-(1/(mu_not**2))) - - C_mns_top(i) = (omega_star(i)*pi*flx_slrd_lcl(bnd_idx)* & - exp(-tau_clm(i)/mu_not)*(((gamma1(i)+(1/mu_not))* & - gamma4(i))+(gamma2(i)*gamma3(i))))/((lambda(i)**2)-(1/(mu_not**2))) - - else - C_pls_btm(i) = 0._r8 - C_mns_btm(i) = 0._r8 - C_pls_top(i) = 0._r8 - C_mns_top(i) = 0._r8 - endif - enddo - - ! Coefficients for tridiaganol matrix solution - do i=2*snl_lcl+1,0,1 - - !Boundary values for i=1 and i=2*snl_lcl, specifics for i=odd and i=even - if (i==(2*snl_lcl+1)) then - A(i) = 0._r8 - B(i) = e1(snl_top) - D(i) = -e2(snl_top) - E(i) = flx_slri_lcl(bnd_idx)-C_mns_top(snl_top) - - elseif(i==0) then - A(i) = e1(snl_btm)-(albsfc_lcl(bnd_idx)*e3(snl_btm)) - B(i) = e2(snl_btm)-(albsfc_lcl(bnd_idx)*e4(snl_btm)) - D(i) = 0._r8 - E(i) = F_direct_btm-C_pls_btm(snl_btm)+(albsfc_lcl(bnd_idx)*C_mns_btm(snl_btm)) - - elseif(mod(i,2)==-1) then ! If odd and i>=3 (n=1 for i=3) - n=floor(i/2.0) - A(i) = (e2(n)*e3(n))-(e4(n)*e1(n)) - B(i) = (e1(n)*e1(n+1))-(e3(n)*e3(n+1)) - D(i) = (e3(n)*e4(n+1))-(e1(n)*e2(n+1)) - E(i) = (e3(n)*(C_pls_top(n+1)-C_pls_btm(n)))+(e1(n)*(C_mns_btm(n)-C_mns_top(n+1))) - - elseif(mod(i,2)==0) then ! If even and i<=2*snl_lcl - n=(i/2) - A(i) = (e2(n+1)*e1(n))-(e3(n)*e4(n+1)) - B(i) = (e2(n)*e2(n+1))-(e4(n)*e4(n+1)) - D(i) = (e1(n+1)*e4(n+1))-(e2(n+1)*e3(n+1)) - E(i) = (e2(n+1)*(C_pls_top(n+1)-C_pls_btm(n)))+(e4(n+1)*(C_mns_top(n+1)-C_mns_btm(n))) - endif - enddo - - AS(0) = A(0)/B(0) - DS(0) = E(0)/B(0) - - do i=-1,(2*snl_lcl+1),-1 - X(i) = 1/(B(i)-(D(i)*AS(i+1))) - AS(i) = A(i)*X(i) - DS(i) = (E(i)-(D(i)*DS(i+1)))*X(i) - enddo - - Y(2*snl_lcl+1) = DS(2*snl_lcl+1) - do i=(2*snl_lcl+2),0,1 - Y(i) = DS(i)-(AS(i)*Y(i-1)) - enddo - - ! Downward direct-beam and net flux (F_net) at the base of each layer: - do i=snl_top,snl_btm,1 - F_direct(i) = mu_not*pi*flx_slrd_lcl(bnd_idx)*exp(-(tau_clm(i)+tau_star(i))/mu_not) - F_net(i) = (Y(2*i-1)*(e1(i)-e3(i))) + (Y(2*i)*(e2(i)-e4(i))) + & - C_pls_btm(i) - C_mns_btm(i) - F_direct(i) - enddo - - ! Upward flux at snowpack top: - F_sfc_pls = (Y(2*snl_lcl+1)*(exp(-lambda(snl_top)*tau_star(snl_top))+ & - GAMMA(snl_top))) + (Y(2*snl_lcl+2)*(exp(-lambda(snl_top)* & - tau_star(snl_top))-GAMMA(snl_top))) + C_pls_top(snl_top) - - ! Net flux at bottom = absorbed radiation by underlying surface: - F_btm_net = -F_net(snl_btm) - - - ! Bulk column albedo and surface net flux - albedo = F_sfc_pls/((mu_not*pi*flx_slrd_lcl(bnd_idx))+flx_slri_lcl(bnd_idx)) - F_sfc_net = F_sfc_pls - ((mu_not*pi*flx_slrd_lcl(bnd_idx))+flx_slri_lcl(bnd_idx)) - - trip = 0 - ! Absorbed flux in each layer - do i=snl_top,snl_btm,1 - if(i==snl_top) then - F_abs(i) = F_net(i)-F_sfc_net - else - F_abs(i) = F_net(i)-F_net(i-1) - endif - flx_abs_lcl(i,bnd_idx) = F_abs(i) - - - ! ERROR check: negative absorption - if (flx_abs_lcl(i,bnd_idx) < -0.00001_r8) then - trip = 1 - endif - enddo - - flx_abs_lcl(1,bnd_idx) = F_btm_net - - if (flg_nosnl == 1) then - ! If there are no snow layers (but still snow), all absorbed energy must be in top soil layer - !flx_abs_lcl(:,bnd_idx) = 0._r8 - !flx_abs_lcl(1,bnd_idx) = F_abs(0) + F_btm_net - - ! changed on 20070408: - ! OK to put absorbed energy in the fictitous snow layer because routine SurfaceRadiation - ! handles the case of no snow layers. Then, if a snow layer is addded between now and - ! SurfaceRadiation (called in CanopyHydrology), absorbed energy will be properly distributed. - flx_abs_lcl(0,bnd_idx) = F_abs(0) - flx_abs_lcl(1,bnd_idx) = F_btm_net - - endif - - !Underflow check (we've already tripped the error condition above) - do i=snl_top,1,1 - if (flx_abs_lcl(i,bnd_idx) < 0._r8) then - flx_abs_lcl(i,bnd_idx) = 0._r8 - endif - enddo - - F_abs_sum = 0._r8 - do i=snl_top,snl_btm,1 - F_abs_sum = F_abs_sum + F_abs(i) - enddo - - - !ERROR check: absorption greater than incident flux - ! (should make condition more generic than "1._r8") - if (F_abs_sum > 1._r8) then - trip = 1 - endif - - !ERROR check: - if ((albedo < 0._r8).and.(trip==0)) then - trip = 1 - endif - - ! Set conditions for redoing RT calculation - if ((trip == 1).and.(flg_dover == 1)) then - flg_dover = 2 - elseif ((trip == 1).and.(flg_dover == 2)) then - flg_dover = 3 - elseif ((trip == 1).and.(flg_dover == 3)) then - flg_dover = 4 - elseif((trip == 1).and.(flg_dover == 4).and.(err_idx < 20)) then - flg_dover = 3 - err_idx = err_idx + 1 - elseif((trip == 1).and.(flg_dover == 4).and.(err_idx >= 20)) then - flg_dover = 0 - write(iulog,*) "SNICAR ERROR: FOUND A WORMHOLE. STUCK IN INFINITE LOOP! Called from: ", flg_snw_ice - write(iulog,*) "SNICAR STATS: snw_rds(0)= ", snw_rds(c_idx,0) - write(iulog,*) "SNICAR STATS: L_snw(0)= ", L_snw(0) - write(iulog,*) "SNICAR STATS: h2osno= ", h2osno_lcl, " snl= ", snl_lcl - write(iulog,*) "SNICAR STATS: soot1(0)= ", mss_cnc_aer_lcl(0,1) - write(iulog,*) "SNICAR STATS: soot2(0)= ", mss_cnc_aer_lcl(0,2) - write(iulog,*) "SNICAR STATS: dust1(0)= ", mss_cnc_aer_lcl(0,3) - write(iulog,*) "SNICAR STATS: dust2(0)= ", mss_cnc_aer_lcl(0,4) - write(iulog,*) "SNICAR STATS: dust3(0)= ", mss_cnc_aer_lcl(0,5) - write(iulog,*) "SNICAR STATS: dust4(0)= ", mss_cnc_aer_lcl(0,6) - l_idx = col%landunit(c_idx) - write(iulog,*) "column index: ", c_idx - write(iulog,*) "landunit type", lun%itype(l_idx) - write(iulog,*) "frac_sno: ", frac_sno(c_idx) - call endrun(subgrid_index=c_idx, subgrid_level=subgrid_level_column, msg=errmsg(sourcefile, __LINE__)) - else - flg_dover = 0 - endif + !--------------------------- End of snow & aerosol optics -------------------------------- + + !--------------------------- Start Adding-doubling RT solver -------------------------------- + + ! Given input vertical profiles of optical properties, evaluate the + ! monochromatic Delta-Eddington adding-doubling solution + + ! trndir, trntdr, trndif, rupdir, rupdif, rdndif are variables at the layer interface, + ! for snow with layers from snl_top to snl_btm there are snl_top to snl_btm+1 layer interface + snl_btm_itf = snl_btm + 1 + + ! initialization for layer interface + do i = snl_top,snl_btm_itf,1 + trndir(i) = c0 + trntdr(i) = c0 + trndif(i) = c0 + rupdir(i) = c0 + rupdif(i) = c0 + rdndif(i) = c0 + enddo + ! initialize top interface of top layer + trndir(snl_top) = c1 + trntdr(snl_top) = c1 + trndif(snl_top) = c1 + rdndif(snl_top) = c0 + + ! begin main level loop for snow layer interfaces except for the very bottom + do i = snl_top,snl_btm,1 + + ! initialize all layer apparent optical properties to 0 + rdir (i) = c0 + rdif_a(i) = c0 + rdif_b(i) = c0 + tdir (i) = c0 + tdif_a(i) = c0 + tdif_b(i) = c0 + trnlay(i) = c0 + + ! compute next layer Delta-eddington solution only if total transmission + ! of radiation to the interface just above the layer exceeds trmin. + if (trntdr(i) > trmin ) then + + ! delta-transformed single-scattering properties of this layer + ts = tau_star(i) + ws = omega_star(i) + gs = g_star(i) + + ! Delta-Eddington solution expressions, Eq. 50: Briegleb and Light 2007 + lm = sqrt(c3*(c1-ws)*(c1 - ws*gs)) + ue = c1p5*(c1 - ws*gs)/lm + extins = max(exp_min, exp(-lm*ts)) + ne = ((ue+c1)*(ue+c1)/extins) - ((ue-c1)*(ue-c1)*extins) + + ! first calculation of rdif, tdif using Delta-Eddington formulas + ! Eq.: Briegleb 1992; alpha and gamma for direct radiation + rdif_a(i) = (ue * ue - c1) * (c1 / extins - extins) / ne + tdif_a(i) = c4*ue/ne + + ! evaluate rdir,tdir for direct beam + trnlay(i) = max(exp_min, exp(-ts/mu_not)) + + ! Delta-Eddington solution expressions + ! Eq. 50: Briegleb and Light 2007; alpha and gamma for direct radiation + alp = cp75*ws*mu_not*((c1 + gs*(c1-ws))/(c1 - lm*lm*mu_not*mu_not)) + gam = cp5*ws*((c1 + c3*gs*(c1-ws)*mu_not*mu_not)/(c1-lm*lm*mu_not*mu_not)) + apg = alp + gam + amg = alp - gam + rdir(i) = apg*rdif_a(i) + amg*(tdif_a(i)*trnlay(i) - c1) + tdir(i) = apg*tdif_a(i) + (amg* rdif_a(i)-apg+c1)*trnlay(i) + + ! recalculate rdif,tdif using direct angular integration over rdir,tdir, + ! since Delta-Eddington rdif formula is not well-behaved (it is usually + ! biased low and can even be negative); use ngmax angles and gaussian + ! integration for most accuracy: + R1 = rdif_a(i) ! use R1 as temporary + T1 = tdif_a(i) ! use T1 as temporary + swt = c0 + smr = c0 + smt = c0 + ! gaussian angles for the AD integral + do ng=1,ngmax + mu = difgauspt(ng) + gwt = difgauswt(ng) + swt = swt + mu*gwt + trn = max(exp_min, exp(-ts/mu)) + alp = cp75*ws*mu*((c1 + gs*(c1-ws))/(c1 - lm*lm*mu*mu)) + gam = cp5*ws*((c1 + c3*gs*(c1-ws)*mu*mu)/(c1-lm*lm*mu*mu)) + apg = alp + gam + amg = alp - gam + rdr = apg*R1 + amg*T1*trn - amg + tdr = apg*T1 + amg*R1*trn - apg*trn + trn + smr = smr + mu*rdr*gwt + smt = smt + mu*tdr*gwt + enddo ! ng + rdif_a(i) = smr/swt + tdif_a(i) = smt/swt + + ! homogeneous layer + rdif_b(i) = rdif_a(i) + tdif_b(i) = tdif_a(i) + + endif ! trntdr(k) > trmin + + ! Calculate the solar beam transmission, total transmission, and + ! reflectivity for diffuse radiation from below at interface i, + ! the top of the current layer k: + ! + ! layers interface + ! + ! --------------------- i-1 + ! i-1 + ! --------------------- i + ! i + ! --------------------- + + trndir(i+1) = trndir(i)*trnlay(i) ! solar beam transmission from top + refkm1 = c1/(c1 - rdndif(i)*rdif_a(i)) ! interface multiple scattering for i-1 + tdrrdir = trndir(i)*rdir(i) ! direct tran times layer direct ref + tdndif = trntdr(i) - trndir(i) ! total down diffuse = tot tran - direct tran + trntdr(i+1) = trndir(i)*tdir(i) + & ! total transmission to direct beam for layers above + (tdndif + tdrrdir*rdndif(i))*refkm1*tdif_a(i) + ! Eq. B4; Briegleb and Light 2007 + rdndif(i+1) = rdif_b(i) + & ! reflectivity to diffuse radiation for layers above + (tdif_b(i)*rdndif(i)*refkm1*tdif_a(i)) + trndif(i+1) = trndif(i)*refkm1*tdif_a(i) ! diffuse transmission to diffuse beam for layers above + + enddo ! end i main level loop + + ! compute reflectivity to direct and diffuse radiation for layers + ! below by adding succesive layers starting from the underlying + ! ground and working upwards: + ! + ! layers interface + ! + ! --------------------- i + ! i + ! --------------------- i+1 + ! i+1 + ! --------------------- + + ! set the underlying ground albedo == albedo of near-IR + ! unless bnd_idx < nir_bnd_bgn, for visible + rupdir(snl_btm_itf) = albsfc(c_idx,inir) + rupdif(snl_btm_itf) = albsfc(c_idx,inir) + if (bnd_idx < nir_bnd_bgn) then + rupdir(snl_btm_itf) = albsfc(c_idx,ivis) + rupdif(snl_btm_itf) = albsfc(c_idx,ivis) + endif + + do i=snl_btm,snl_top,-1 + ! interface scattering Eq. B5; Briegleb and Light 2007 + refkp1 = c1/( c1 - rdif_b(i)*rupdif(i+1)) + ! dir from top layer plus exp tran ref from lower layer, interface + ! scattered and tran thru top layer from below, plus diff tran ref + ! from lower layer with interface scattering tran thru top from below + rupdir(i) = rdir(i) & + + ( trnlay(i) *rupdir(i+1) & + + (tdir(i)-trnlay(i))*rupdif(i+1) ) * refkp1 * tdif_b(i) + ! dif from top layer from above, plus dif tran upwards reflected and + ! interface scattered which tran top from below + rupdif(i) = rdif_a(i) + tdif_a(i)*rupdif(i+1)*refkp1*tdif_b(i) + enddo ! i + + ! net flux (down-up) at each layer interface from the + ! snow top (i = snl_top) to bottom interface above land (i = snl_btm_itf) + ! the interface reflectivities and transmissivities required + ! to evaluate interface fluxes are returned from solution_dEdd; + ! now compute up and down fluxes for each interface, using the + ! combined layer properties at each interface: + ! + ! layers interface + ! + ! --------------------- i + ! i + ! --------------------- + + do i = snl_top, snl_btm_itf + ! interface scattering, Eq. 52; Briegleb and Light 2007 + refk = c1/(c1 - rdndif(i)*rupdif(i)) + ! dir tran ref from below times interface scattering, plus diff + ! tran and ref from below times interface scattering + ! fdirup(i) = (trndir(i)*rupdir(i) + & + ! (trntdr(i)-trndir(i)) & + ! *rupdif(i))*refk + ! dir tran plus total diff trans times interface scattering plus + ! dir tran with up dir ref and down dif ref times interface scattering + ! fdirdn(i) = trndir(i) + (trntdr(i) & + ! - trndir(i) + trndir(i) & + ! *rupdir(i)*rdndif(i))*refk + ! diffuse tran ref from below times interface scattering + ! fdifup(i) = trndif(i)*rupdif(i)*refk + ! diffuse tran times interface scattering + ! fdifdn(i) = trndif(i)*refk + + ! netflux, down - up + ! dfdir = fdirdn - fdirup + dfdir(i) = trndir(i) & + + (trntdr(i)-trndir(i)) * (c1 - rupdif(i)) * refk & + - trndir(i)*rupdir(i) * (c1 - rdndif(i)) * refk + if (dfdir(i) < puny) dfdir(i) = c0 + ! dfdif = fdifdn - fdifup + dfdif(i) = trndif(i) * (c1 - rupdif(i)) * refk + if (dfdif(i) < puny) dfdif(i) = c0 + enddo ! k + + ! SNICAR_AD_RT is called twice for direct and diffuse incident fluxes + ! direct incident + if (flg_slr_in == 1) then + albedo = rupdir(snl_top) + dftmp = dfdir + refk = c1/(c1 - rdndif(snl_top)*rupdif(snl_top)) + F_sfc_pls = (trndir(snl_top)*rupdir(snl_top) + & + (trntdr(snl_top)-trndir(snl_top)) & + *rupdif(snl_top))*refk + !diffuse incident + else + albedo = rupdif(snl_top) + dftmp = dfdif + refk = c1/(c1 - rdndif(snl_top)*rupdif(snl_top)) + F_sfc_pls = trndif(snl_top)*rupdif(snl_top)*refk + endif + + ! Absorbed flux in each layer + do i=snl_top,snl_btm,1 + F_abs(i) = dftmp(i)-dftmp(i+1) + flx_abs_lcl(i,bnd_idx) = F_abs(i) + + ! ERROR check: negative absorption + if (flx_abs_lcl(i,bnd_idx) < -0.00001_r8) then + write (iulog,"(a,e13.6,a,i6,a,i6)") "SNICAR ERROR: negative absoption : ", & + flx_abs_lcl(i,bnd_idx), " at timestep: ", nstep, " at column: ", c_idx + write(iulog,*) "SNICAR_AD STATS: snw_rds(0)= ", snw_rds(c_idx,0) + write(iulog,*) "SNICAR_AD STATS: L_snw(0)= ", L_snw(0) + write(iulog,*) "SNICAR_AD STATS: h2osno= ", h2osno_lcl, " snl= ", snl_lcl + write(iulog,*) "SNICAR_AD STATS: soot1(0)= ", mss_cnc_aer_lcl(0,1) + write(iulog,*) "SNICAR_AD STATS: soot2(0)= ", mss_cnc_aer_lcl(0,2) + write(iulog,*) "SNICAR_AD STATS: dust1(0)= ", mss_cnc_aer_lcl(0,3) + write(iulog,*) "SNICAR_AD STATS: dust2(0)= ", mss_cnc_aer_lcl(0,4) + write(iulog,*) "SNICAR_AD STATS: dust3(0)= ", mss_cnc_aer_lcl(0,5) + write(iulog,*) "SNICAR_AD STATS: dust4(0)= ", mss_cnc_aer_lcl(0,6) + call endrun(subgrid_index=c_idx, subgrid_level=subgrid_level_column, msg=errmsg(sourcefile, __LINE__)) + endif + enddo + + ! absobed flux by the underlying ground + F_btm_net = dftmp(snl_btm_itf) + + ! note here, snl_btm_itf = 1 by snow column set up in CLM + flx_abs_lcl(1,bnd_idx) = F_btm_net + + if (flg_nosnl == 1) then + ! If there are no snow layers (but still snow), all absorbed energy must be in top soil layer + !flx_abs_lcl(:,bnd_idx) = 0._r8 + !flx_abs_lcl(1,bnd_idx) = F_abs(0) + F_btm_net + + ! changed on 20070408: + ! OK to put absorbed energy in the fictitous snow layer because routine SurfaceRadiation + ! handles the case of no snow layers. Then, if a snow layer is addded between now and + ! SurfaceRadiation (called in CanopyHydrology), absorbed energy will be properly distributed. + flx_abs_lcl(0,bnd_idx) = F_abs(0) + flx_abs_lcl(1,bnd_idx) = F_btm_net + endif + + !Underflow check (we've already tripped the error condition above) + do i=snl_top,1,1 + flx_abs_lcl(i,bnd_idx) = max(0._r8, flx_abs_lcl(i,bnd_idx)) + enddo + + F_abs_sum = 0._r8 + do i=snl_top,snl_btm,1 + F_abs_sum = F_abs_sum + F_abs(i) + enddo + + ! no need to repeat calculations for adding-doubling solver + flg_dover = 0 + + !--------------------------- End of Adding-doubling RT solver -------------------------------- enddo !enddo while (flg_dover > 0) @@ -940,12 +1335,20 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & if (abs(energy_sum) > 0.00001_r8) then write (iulog,"(a,e12.6,a,i6,a,i6)") "SNICAR ERROR: Energy conservation error of : ", energy_sum, & " at timestep: ", nstep, " at column: ", c_idx + write(iulog,*) "F_abs_sum: ",F_abs_sum + write(iulog,*) "F_btm_net: ",F_btm_net + write(iulog,*) "F_sfc_pls: ",F_sfc_pls + write(iulog,*) "mu_not*pi*flx_slrd_lcl(bnd_idx): ", mu_not*pi*flx_slrd_lcl(bnd_idx) + write(iulog,*) "flx_slri_lcl(bnd_idx)", flx_slri_lcl(bnd_idx) + write(iulog,*) "bnd_idx", bnd_idx + write(iulog,*) "F_abs", F_abs + write(iulog,*) "albedo", albedo call endrun(subgrid_index=c_idx, subgrid_level=subgrid_level_column, msg=errmsg(sourcefile, __LINE__)) endif albout_lcl(bnd_idx) = albedo - ! Check that albedo is less than 1 + ! Fail if albedo > 1 if (albout_lcl(bnd_idx) > 1.0) then write (iulog,*) "SNICAR ERROR: Albedo > 1.0 at c: ", c_idx, " NSTEP= ",nstep @@ -981,32 +1384,74 @@ subroutine SNICAR_RT (flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & ! Weight output NIR albedo appropriately - albout(c_idx,1) = albout_lcl(1) - flx_sum = 0._r8 - do bnd_idx= nir_bnd_bgn,nir_bnd_end - flx_sum = flx_sum + flx_wgt(bnd_idx)*albout_lcl(bnd_idx) + select case (snicar_numrad_snw) + case (default_number_bands) ! 5-band case + ! VIS band + albout(c_idx,ivis) = albout_lcl(ivis) + case (high_number_bands) ! 480-band case + ! average for VIS band + flx_sum = 0._r8 + do bnd_idx= 1, (nir_bnd_bgn-1) + flx_sum = flx_sum + flx_wgt(bnd_idx)*albout_lcl(bnd_idx) + end do + albout(c_idx,ivis) = flx_sum / sum(flx_wgt(1:(nir_bnd_bgn-1))) + end select + + ! average for NIR band (5 or 480-band case) + flx_sum = 0._r8 + do bnd_idx = nir_bnd_bgn, nir_bnd_end + flx_sum = flx_sum + flx_wgt(bnd_idx) * albout_lcl(bnd_idx) end do - albout(c_idx,2) = flx_sum / sum(flx_wgt(nir_bnd_bgn:nir_bnd_end)) + albout(c_idx,inir) = flx_sum / sum(flx_wgt(nir_bnd_bgn:nir_bnd_end)) ! Weight output NIR absorbed layer fluxes (flx_abs) appropriately - flx_abs(c_idx,:,1) = flx_abs_lcl(:,1) - do i=snl_top,1,1 + select case (snicar_numrad_snw) + case (default_number_bands) ! 5-band case + ! VIS band + flx_abs(c_idx,:,1) = flx_abs_lcl(:,1) + case (high_number_bands) ! 480-band case + ! average for VIS band + do i=snl_top,1,1 + flx_sum = 0._r8 + do bnd_idx= 1,(nir_bnd_bgn-1) + flx_sum = flx_sum + flx_wgt(bnd_idx)*flx_abs_lcl(i,bnd_idx) + enddo + flx_abs(c_idx,i,ivis) = flx_sum / sum(flx_wgt(1:(nir_bnd_bgn-1))) + end do + end select + + ! average for NIR band (5 or 480-band case) + do i = snl_top, 1, 1 flx_sum = 0._r8 - do bnd_idx= nir_bnd_bgn,nir_bnd_end - flx_sum = flx_sum + flx_wgt(bnd_idx)*flx_abs_lcl(i,bnd_idx) - enddo - flx_abs(c_idx,i,2) = flx_sum / sum(flx_wgt(nir_bnd_bgn:nir_bnd_end)) + do bnd_idx = nir_bnd_bgn, nir_bnd_end + flx_sum = flx_sum + flx_wgt(bnd_idx) * flx_abs_lcl(i,bnd_idx) + end do + flx_abs(c_idx,i,inir) = flx_sum / sum(flx_wgt(nir_bnd_bgn:nir_bnd_end)) end do - ! If snow < minimum_snow, but > 0, and there is sun, set albedo to underlying surface albedo + ! high solar zenith angle adjustment for Adding-doubling solver results + ! near-IR direct albedo/absorption adjustment for high solar zenith angles + ! solar zenith angle parameterization + ! calculate the scaling factor for NIR direct albedo if SZA>75 degree + if ((mu_not < mu_75) .and. (flg_slr_in == 1)) then + sza_c1 = sza_a0 + sza_a1 * mu_not + sza_a2 * (mu_not * mu_not) + sza_c0 = sza_b0 + sza_b1 * mu_not + sza_b2 * (mu_not * mu_not) + sza_factor = sza_c1 * (log10(snw_rds_lcl(snl_top) * c1) - c6) + sza_c0 + flx_sza_adjust = albout(c_idx,inir) * (sza_factor-c1) * sum(flx_wgt(nir_bnd_bgn:nir_bnd_end)) + albout(c_idx,inir) = albout(c_idx,inir) * sza_factor + flx_abs(c_idx,snl_top,inir) = flx_abs(c_idx,snl_top,inir) - flx_sza_adjust + endif + + + ! If snow < minimum_snow, but > 0, and there is sun, set albedo to underlying surface albedo elseif ( (coszen(c_idx) > 0._r8) .and. (h2osno_lcl < min_snw) .and. (h2osno_lcl > 0._r8) ) then - albout(c_idx,1) = albsfc(c_idx,1) - albout(c_idx,2) = albsfc(c_idx,2) + albout(c_idx,ivis) = albsfc(c_idx,ivis) + albout(c_idx,inir) = albsfc(c_idx,inir) - ! There is either zero snow, or no sun + ! There is either zero snow, or no sun else - albout(c_idx,1) = 0._r8 - albout(c_idx,2) = 0._r8 + albout(c_idx,ivis) = 0._r8 + albout(c_idx,inir) = 0._r8 endif ! if column has snow and coszen > 0 enddo ! loop over all columns @@ -1056,7 +1501,7 @@ subroutine SnowAge_grain(bounds, & ! !USES: use clm_time_manager , only : get_step_size_real, get_nstep use clm_varpar , only : nlevsno - use clm_varcon , only : spval + use clm_varcon, only: spval, secsphr use shr_const_mod , only : SHR_CONST_RHOICE, SHR_CONST_PI ! ! !ARGUMENTS: @@ -1167,30 +1612,18 @@ subroutine SnowAge_grain(bounds, & ! make sure rhos doesn't drop below 50 (see rhos_idx below) rhos=max(50._r8,rhos) - ! best-fit table indecies + ! best-fit table indices T_idx = nint((t_soisno(c_idx,i)-223) / 5) + 1 Tgrd_idx = nint(dTdz(c_idx,i) / 10) + 1 rhos_idx = nint((rhos-50) / 50) + 1 - ! boundary check: - if (T_idx < idx_T_min) then - T_idx = idx_T_min - endif - if (T_idx > idx_T_max) then - T_idx = idx_T_max - endif - if (Tgrd_idx < idx_Tgrd_min) then - Tgrd_idx = idx_Tgrd_min - endif - if (Tgrd_idx > idx_Tgrd_max) then - Tgrd_idx = idx_Tgrd_max - endif - if (rhos_idx < idx_rhos_min) then - rhos_idx = idx_rhos_min - endif - if (rhos_idx > idx_rhos_max) then - rhos_idx = idx_rhos_max - endif + ! boundary checks + T_idx = max(T_idx, idx_T_min) + T_idx = min(T_idx, idx_T_max) + Tgrd_idx = max(Tgrd_idx, idx_Tgrd_min) + Tgrd_idx = min(Tgrd_idx, idx_Tgrd_max) + rhos_idx = max(rhos_idx, idx_rhos_min) + rhos_idx = min(rhos_idx, idx_rhos_max) ! best-fit parameters bst_tau = snowage_tau(rhos_idx,Tgrd_idx,T_idx) @@ -1199,13 +1632,11 @@ subroutine SnowAge_grain(bounds, & !LvK extra boundary check, to prevent when using old restart file with lower snw_rds_min than current run - if (snw_rds(c_idx,i) < snw_rds_min) then - snw_rds(c_idx,i) = snw_rds_min - endif + snw_rds(c_idx,i) = max(snw_rds(c_idx,i), params_inst%snw_rds_min) ! change in snow effective radius, using best-fit parameters - dr_fresh = snw_rds(c_idx,i)-snw_rds_min - dr = (bst_drdt0*(bst_tau/(dr_fresh+bst_tau))**(1/bst_kappa)) * (dtime/3600) + dr_fresh = snw_rds(c_idx,i) - params_inst%snw_rds_min + dr = (bst_drdt0 * (bst_tau / (dr_fresh + bst_tau))**(1._r8 / bst_kappa)) * (dtime / secsphr) ! !********** 2. WET SNOW AGING *********** @@ -1220,7 +1651,7 @@ subroutine SnowAge_grain(bounds, & !dr_wet = 1E6_r8*(dtime*(C1_liq_Brun89 + C2_liq_Brun89*(frc_liq**(3))) / (4*SHR_CONST_PI*(snw_rds(c_idx,i)/1E6)**(2))) !simplified, units of microns: dr_wet = 1E18_r8*(dtime*(params_inst%C2_liq_Brun89*(frc_liq**(3))) / & - (4*SHR_CONST_PI*snw_rds(c_idx,i)**(2))) + (4._r8 * SHR_CONST_PI * (snw_rds(c_idx,i) * snw_rds(c_idx,i)))) dr = dr + dr_wet @@ -1274,13 +1705,8 @@ subroutine SnowAge_grain(bounds, & !********** 5. CHECK BOUNDARIES *********** ! ! boundary check - if (snw_rds(c_idx,i) < snw_rds_min) then - snw_rds(c_idx,i) = snw_rds_min - endif - - if (snw_rds(c_idx,i) > snw_rds_max) then - snw_rds(c_idx,i) = snw_rds_max - end if + snw_rds(c_idx,i) = max(snw_rds(c_idx,i), params_inst%snw_rds_min) + snw_rds(c_idx,i) = min(snw_rds(c_idx,i), snw_rds_max) ! set top layer variables for history files if (i == snl_top) then @@ -1298,7 +1724,7 @@ subroutine SnowAge_grain(bounds, & do fc = 1, num_nosnowc c_idx = filter_nosnowc(fc) if (h2osno_no_layers(c_idx) > 0._r8) then - snw_rds(c_idx,0) = snw_rds_min + snw_rds(c_idx,0) = params_inst%snw_rds_min endif enddo @@ -1322,7 +1748,7 @@ real(r8) function FreshSnowRadius(c_idx, atm2lnd_inst) ! Author: Leo VanKampenhout ! ! !USES: - use AerosolMod , only : fresh_snw_rds_max + ! ! !ARGUMENTS: integer, intent(in) :: c_idx ! column index type(atm2lnd_type) , intent(in) :: atm2lnd_inst ! Forcing from atmosphere @@ -1331,16 +1757,17 @@ real(r8) function FreshSnowRadius(c_idx, atm2lnd_inst) !----------------------------------------------------------------------- real(r8), parameter :: tmin = tfrz - 30._r8 ! start of linear ramp real(r8), parameter :: tmax = tfrz - 0._r8 ! end of linear ramp - real(r8), parameter :: gs_min = snw_rds_min ! minimum value - real(r8) :: gs_max ! maximum value + real(r8) :: gs_min ! minimum value + real(r8) :: gs_max ! maximum value associate( & forc_t => atm2lnd_inst%forc_t_downscaled_col & ! Input: [real(r8) (:) ] atmospheric temperature (Kelvin) ) - if ( fresh_snw_rds_max <= snw_rds_min )then - FreshSnowRadius = snw_rds_min + if ( params_inst%fresh_snw_rds_max <= params_inst%snw_rds_min )then + FreshSnowRadius = params_inst%snw_rds_min else - gs_max = fresh_snw_rds_max + gs_max = params_inst%fresh_snw_rds_max + gs_min = params_inst%snw_rds_min if (forc_t(c_idx) < tmin) then FreshSnowRadius = gs_min @@ -1362,7 +1789,8 @@ end function FreshSnowRadius subroutine SnowOptics_init( ) use fileutils , only : getfil - use CLM_varctl , only : fsnowoptics + use CLM_varctl , only : fsnowoptics, snicar_numrad_snw + use CLM_varctl , only : snicar_solarspec, snicar_dust_optics use spmdMod , only : masterproc use ncdio_pio , only : file_desc_t, ncd_io, ncd_pio_openfile, ncd_pio_closefile @@ -1370,64 +1798,334 @@ subroutine SnowOptics_init( ) character(len=256) :: locfn ! local filename character(len= 32) :: subname = 'SnowOptics_init' ! subroutine name integer :: ier ! error status + logical :: readv ! has variable been read in or not + character(len=100) :: errCode = '-Error reading fsnowoptics file:' + character(len=100) :: tString ! temp. var for reading + character(len=3) :: short_case_dust_opt ! subset of tString + character(len=3) :: short_case_solarspec ! subset of tString ! - ! Open optics file: - if(masterproc) write(iulog,*) 'Attempting to read snow optical properties .....' + ! Initialize optical variables + allocate(ss_alb_snw_drc(idx_Mie_snw_mx,snicar_numrad_snw)) + allocate(asm_prm_snw_drc(idx_Mie_snw_mx,snicar_numrad_snw)) + allocate(ext_cff_mss_snw_drc(idx_Mie_snw_mx,snicar_numrad_snw)) + allocate(ss_alb_snw_dfs(idx_Mie_snw_mx,snicar_numrad_snw)) + allocate(asm_prm_snw_dfs(idx_Mie_snw_mx,snicar_numrad_snw)) + allocate(ext_cff_mss_snw_dfs(idx_Mie_snw_mx,snicar_numrad_snw)) + allocate(ss_alb_bc_hphil(snicar_numrad_snw)) + allocate(asm_prm_bc_hphil(snicar_numrad_snw)) + allocate(ext_cff_mss_bc_hphil(snicar_numrad_snw)) + allocate(ss_alb_bc_hphob(snicar_numrad_snw)) + allocate(asm_prm_bc_hphob(snicar_numrad_snw)) + allocate(ext_cff_mss_bc_hphob(snicar_numrad_snw)) + allocate(ss_alb_oc_hphil(snicar_numrad_snw)) + allocate(asm_prm_oc_hphil(snicar_numrad_snw)) + allocate(ext_cff_mss_oc_hphil(snicar_numrad_snw)) + allocate(ss_alb_oc_hphob(snicar_numrad_snw)) + allocate(asm_prm_oc_hphob(snicar_numrad_snw)) + allocate(ext_cff_mss_oc_hphob(snicar_numrad_snw)) + allocate(ss_alb_dst1(snicar_numrad_snw)) + allocate(asm_prm_dst1(snicar_numrad_snw)) + allocate(ext_cff_mss_dst1(snicar_numrad_snw)) + allocate(ss_alb_dst2(snicar_numrad_snw)) + allocate(asm_prm_dst2(snicar_numrad_snw)) + allocate(ext_cff_mss_dst2(snicar_numrad_snw)) + allocate(ss_alb_dst3(snicar_numrad_snw)) + allocate(asm_prm_dst3(snicar_numrad_snw)) + allocate(ext_cff_mss_dst3(snicar_numrad_snw)) + allocate(ss_alb_dst4(snicar_numrad_snw)) + allocate(asm_prm_dst4(snicar_numrad_snw)) + allocate(ext_cff_mss_dst4(snicar_numrad_snw)) + allocate(flx_wgt_dir(snicar_numrad_snw)) + allocate(flx_wgt_dif(snicar_numrad_snw)) + + if (masterproc) write(iulog,*) 'Attempting to read snow optical properties...' call getfil (fsnowoptics, locfn, 0) call ncd_pio_openfile(ncid, locfn, 0) if(masterproc) write(iulog,*) subname,trim(fsnowoptics) - ! direct-beam snow Mie parameters: - call ncd_io('ss_alb_ice_drc', ss_alb_snw_drc, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_ice_drc',asm_prm_snw_drc, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_ice_drc', ext_cff_mss_snw_drc, 'read', ncid, posNOTonfile=.true.) - - ! diffuse snow Mie parameters - call ncd_io( 'ss_alb_ice_dfs', ss_alb_snw_dfs, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_ice_dfs', asm_prm_snw_dfs, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_ice_dfs', ext_cff_mss_snw_dfs, 'read', ncid, posNOTonfile=.true.) - - ! BC species 1 Mie parameters - call ncd_io( 'ss_alb_bcphil', ss_alb_bc1, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_bcphil', asm_prm_bc1, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_bcphil', ext_cff_mss_bc1, 'read', ncid, posNOTonfile=.true.) - - ! BC species 2 Mie parameters - call ncd_io( 'ss_alb_bcphob', ss_alb_bc2, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_bcphob', asm_prm_bc2, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_bcphob', ext_cff_mss_bc2, 'read', ncid, posNOTonfile=.true.) - - ! OC species 1 Mie parameters - call ncd_io( 'ss_alb_ocphil', ss_alb_oc1, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_ocphil', asm_prm_oc1, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_ocphil', ext_cff_mss_oc1, 'read', ncid, posNOTonfile=.true.) - - ! OC species 2 Mie parameters - call ncd_io( 'ss_alb_ocphob', ss_alb_oc2, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_ocphob', asm_prm_oc2, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_ocphob', ext_cff_mss_oc2, 'read', ncid, posNOTonfile=.true.) - - ! dust species 1 Mie parameters - call ncd_io( 'ss_alb_dust01', ss_alb_dst1, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_dust01', asm_prm_dst1, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_dust01', ext_cff_mss_dst1, 'read', ncid, posNOTonfile=.true.) - - ! dust species 2 Mie parameters - call ncd_io( 'ss_alb_dust02', ss_alb_dst2, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_dust02', asm_prm_dst2, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_dust02', ext_cff_mss_dst2, 'read', ncid, posNOTonfile=.true.) - - ! dust species 3 Mie parameters - call ncd_io( 'ss_alb_dust03', ss_alb_dst3, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_dust03', asm_prm_dst3, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_dust03', ext_cff_mss_dst3, 'read', ncid, posNOTonfile=.true.) - - ! dust species 4 Mie parameters - call ncd_io( 'ss_alb_dust04', ss_alb_dst4, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'asm_prm_dust04', asm_prm_dst4, 'read', ncid, posNOTonfile=.true.) - call ncd_io( 'ext_cff_mss_dust04', ext_cff_mss_dst4, 'read', ncid, posNOTonfile=.true.) - + select case (snicar_solarspec) + case ('mid_latitude_winter') ! mid-latitude winter spectrum + short_case_solarspec = 'mlw' + case ('mid_latitude_summer') ! mid-latitude summer spectrum + short_case_solarspec = 'mls' + case ('sub_arctic_winter') ! sub-Arctic winter spectrum + short_case_solarspec = 'saw' + case ('sub_arctic_summer') ! sub-Arctic summer spectrum + short_case_solarspec = 'sas' + case ('summit_greenland_summer') ! Summit,Greenland,summer spectrum + short_case_solarspec = 'smm' + case ('high_mountain_summer') ! High Mountain summer spectrum + short_case_solarspec = 'hmn' + case default + write(iulog,*) subname//' ERROR: unknown snicar_solarspec: ', snicar_solarspec + call endrun(msg=errMsg(sourcefile, __LINE__)) + end select + + select case (snicar_dust_optics) ! dust optical properties + case ('sahara') ! Saharan dust (Balkanski et al., 2007, central hematite) + short_case_dust_opt = 'sah' + case ('san_juan_mtns_colorado') ! San Juan Mountains, CO (Skiles et al, 2017) + short_case_dust_opt = 'col' + case ('greenland') ! Greenland (Polashenski et al., 2015, central absorptivity) + short_case_dust_opt = 'gre' + case default + write(iulog,*) subname//' ERROR: unknown snicar_dust_optics: ', snicar_dust_optics + call endrun(msg=errMsg(sourcefile, __LINE__)) + end select + + !--------------------- for 5-band data + select case (snicar_numrad_snw) + case (default_number_bands) ! 5-band case + + ! The argument posNOTonfile=.true. is used here because this is a non-spatial file. + ! This argument is relevant when running single_column. + ! flux weights/spectrum + tString = 'flx_wgt_dir5_'//short_case_solarspec + call ncd_io(trim(tString), flx_wgt_dir, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'flx_wgt_dif5_'//short_case_solarspec + call ncd_io(trim(tString), flx_wgt_dif, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! + ! THIS NOTE APPLIES TO ALL THE call ncd_io LINES BELOW WHERE + ! bcphob AND ocphob GET ASSIGNED TO VARIABLES SUFFIXED bc_hphil/oc_hphil: + ! + ! Assumption (1) applies here, in the input section. + ! Assumption (2) applies later, in the snicar code. + ! + ! 1) In this section, hydrophillic particles behave like hydrophobic + ! particles. We assume bc_hphil/oc_hphil to have the same optics as bc_hphob/oc_hphob + ! because sulfate coating on the bc_hphil/oc_hphil surface is assumed to be + ! dissolved into the hydrometeo (i.e, snow grain here) during the + ! deposition process. This is different from the assumption made in + ! prior model versions, where bc_hphil/oc_hphil was coated by undissolved + ! sulfate. + ! 2) Later, in the snicar code, if the bc-snow internal mixing option + ! is on, bc_hphil/oc_hphil (internally mixed within the snow grain) will be + ! treated differently than bc_hphob/oc_hphob (mixed externally or outside the + ! snow grain). + ! + ! BC species 1 Mie parameters, uncoated BC, same as bc_hphob before BC-snow internal mixing + tString = 'ss_alb_bcphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_bc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_bcphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), asm_prm_bc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_bcphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_bc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! BC species 2 Mie parameters, uncoated BC + tString = 'ss_alb_bcphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_bc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_bcphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), asm_prm_bc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_bcphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_bc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! OC species 1 Mie parameters, uncoated OC, same as oc_hphob before OC-snow internal mixing + tString = 'ss_alb_ocphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_oc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_ocphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), asm_prm_oc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_ocphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_oc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! OC species 2 Mie parameters, uncoated OC + tString = 'ss_alb_ocphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_oc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_ocphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), asm_prm_oc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_ocphob_dif_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_oc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! ice refractive index (Picard et al., 2016) + tString = 'ss_alb_ice_pic16_dir_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_snw_drc, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_ice_pic16_dir_'//short_case_solarspec + call ncd_io(trim(tString),asm_prm_snw_drc, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_ice_pic16_dir_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_snw_drc, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ss_alb_ice_pic16_dif_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_snw_dfs, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_ice_pic16_dif_'//short_case_solarspec + call ncd_io(trim(tString),asm_prm_snw_dfs, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_ice_pic16_dif_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_snw_dfs, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + + ! dust species 1 Mie parameters + tString = 'ss_alb_dust01_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_dst1, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_dust01_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), asm_prm_dst1, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_dust01_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_dst1, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! dust species 2 Mie parameters + tString = 'ss_alb_dust02_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_dst2, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_dust02_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), asm_prm_dst2, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_dust02_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_dst2, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! dust species 3 Mie parameters + tString = 'ss_alb_dust03_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_dst3, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_dust03_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), asm_prm_dst3, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_dust03_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_dst3, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! dust species 4 Mie parameters + tString = 'ss_alb_dust04_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), ss_alb_dst4, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_dust04_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), asm_prm_dst4, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_dust04_'//short_case_dust_opt//'_dif_'//short_case_solarspec + call ncd_io(trim(tString), ext_cff_mss_dst4, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + + !-------------------- for 480-band data + case (high_number_bands) + + ! BC species 1 Mie parameters, uncoated BC, same as bc_hphob before BC-snow internal mixing + tString = 'ss_alb_bcphob' + call ncd_io(trim(tString), ss_alb_bc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_bcphob' + call ncd_io(trim(tString), asm_prm_bc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_bcphob' + call ncd_io(trim(tString), ext_cff_mss_bc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! BC species 2 Mie parameters, uncoated BC + tString = 'ss_alb_bcphob' + call ncd_io(trim(tString), ss_alb_bc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_bcphob' + call ncd_io(trim(tString), asm_prm_bc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_bcphob' + call ncd_io(trim(tString), ext_cff_mss_bc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! OC species 1 Mie parameters, uncoated OC, same as oc_hphob before OC-snow internal mixing + tString = 'ss_alb_ocphob' + call ncd_io(trim(tString), ss_alb_oc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_ocphob' + call ncd_io(trim(tString), asm_prm_oc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_ocphob' + call ncd_io(trim(tString), ext_cff_mss_oc_hphil, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! OC species 2 Mie parameters, uncoated OC + tString = 'ss_alb_ocphob' + call ncd_io(trim(tString), ss_alb_oc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_ocphob' + call ncd_io(trim(tString), asm_prm_oc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_ocphob' + call ncd_io(trim(tString), ext_cff_mss_oc_hphob, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + + ! snow optical properties derived from different ice refractive index dataset + ! same value for direct and diffuse due to high spectral res without spectra averaging in database (Picard et al., 2016) + tString = 'ss_alb_ice_pic16' + call ncd_io(trim(tString), ss_alb_snw_drc, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_ice_pic16' + call ncd_io(trim(tString), asm_prm_snw_drc, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_ice_pic16' + call ncd_io(trim(tString), ext_cff_mss_snw_drc, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ss_alb_ice_pic16' + call ncd_io(trim(tString), ss_alb_snw_dfs, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_ice_pic16' + call ncd_io(trim(tString), asm_prm_snw_dfs, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_ice_pic16' + call ncd_io(trim(tString), ext_cff_mss_snw_dfs, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + + ! dust optical properties + ! dust species 1 Mie parameters + tString = 'ss_alb_dust01_'//short_case_dust_opt + call ncd_io(trim(tString), ss_alb_dst1, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_dust01_'//short_case_dust_opt + call ncd_io(trim(tString), asm_prm_dst1, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_dust01_'//short_case_dust_opt + call ncd_io(trim(tString), ext_cff_mss_dst1, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! dust species 2 Mie parameters + tString = 'ss_alb_dust02_'//short_case_dust_opt + call ncd_io(trim(tString), ss_alb_dst2, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_dust02_'//short_case_dust_opt + call ncd_io(trim(tString), asm_prm_dst2, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_dust02_'//short_case_dust_opt + call ncd_io(trim(tString), ext_cff_mss_dst2, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! dust species 3 Mie parameters + tString = 'ss_alb_dust03_'//short_case_dust_opt + call ncd_io(trim(tString), ss_alb_dst3, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_dust03_'//short_case_dust_opt + call ncd_io(trim(tString), asm_prm_dst3, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_dust03_'//short_case_dust_opt + call ncd_io(trim(tString), ext_cff_mss_dst3, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + ! dust species 4 Mie parameters + tString = 'ss_alb_dust04_'//short_case_dust_opt + call ncd_io(trim(tString), ss_alb_dst4, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'asm_prm_dust04_'//short_case_dust_opt + call ncd_io(trim(tString), asm_prm_dst4, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'ext_cff_mss_dust04_'//short_case_dust_opt + call ncd_io(trim(tString), ext_cff_mss_dst4, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + + ! downward solar radiation spectral weights for 480-band + tString = 'flx_wgt_dir480_'//short_case_solarspec + call ncd_io(trim(tString), flx_wgt_dir, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'flx_wgt_dif480_'//short_case_solarspec + call ncd_io(trim(tString), flx_wgt_dif, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + + case default + write(iulog,*) subname//' ERROR: unknown snicar_numrad_snw: ', snicar_numrad_snw + call endrun(msg=errMsg(sourcefile, __LINE__)) + end select call ncd_pio_closefile(ncid) if (masterproc) then @@ -1440,20 +2138,20 @@ subroutine SnowOptics_init( ) write (iulog,*) 'SNICAR: Mie single scatter albedos for diffuse ice, rds=100um: ', & ss_alb_snw_dfs(71,1), ss_alb_snw_dfs(71,2), ss_alb_snw_dfs(71,3), & ss_alb_snw_dfs(71,4), ss_alb_snw_dfs(71,5) - if (DO_SNO_OC) then + if (do_sno_oc) then write (iulog,*) 'SNICAR: Including OC aerosols from snow radiative transfer calculations' else write (iulog,*) 'SNICAR: Excluding OC aerosols from snow radiative transfer calculations' endif write (iulog,*) 'SNICAR: Mie single scatter albedos for hydrophillic BC: ', & - ss_alb_bc1(1), ss_alb_bc1(2), ss_alb_bc1(3), ss_alb_bc1(4), ss_alb_bc1(5) + ss_alb_bc_hphil(1), ss_alb_bc_hphil(2), ss_alb_bc_hphil(3), ss_alb_bc_hphil(4), ss_alb_bc_hphil(5) write (iulog,*) 'SNICAR: Mie single scatter albedos for hydrophobic BC: ', & - ss_alb_bc2(1), ss_alb_bc2(2), ss_alb_bc2(3), ss_alb_bc2(4), ss_alb_bc2(5) - if (DO_SNO_OC) then + ss_alb_bc_hphob(1), ss_alb_bc_hphob(2), ss_alb_bc_hphob(3), ss_alb_bc_hphob(4), ss_alb_bc_hphob(5) + if (do_sno_oc) then write (iulog,*) 'SNICAR: Mie single scatter albedos for hydrophillic OC: ', & - ss_alb_oc1(1), ss_alb_oc1(2), ss_alb_oc1(3), ss_alb_oc1(4), ss_alb_oc1(5) + ss_alb_oc_hphil(1), ss_alb_oc_hphil(2), ss_alb_oc_hphil(3), ss_alb_oc_hphil(4), ss_alb_oc_hphil(5) write (iulog,*) 'SNICAR: Mie single scatter albedos for hydrophobic OC: ', & - ss_alb_oc2(1), ss_alb_oc2(2), ss_alb_oc2(3), ss_alb_oc2(4), ss_alb_oc2(5) + ss_alb_oc_hphob(1), ss_alb_oc_hphob(2), ss_alb_oc_hphob(3), ss_alb_oc_hphob(4), ss_alb_oc_hphob(5) endif write (iulog,*) 'SNICAR: Mie single scatter albedos for dust species 1: ', & ss_alb_dst1(1), ss_alb_dst1(2), ss_alb_dst1(3), ss_alb_dst1(4), ss_alb_dst1(5) @@ -1480,6 +2178,9 @@ subroutine SnowAge_init( ) character(len= 32) :: subname = 'SnowOptics_init' ! subroutine name integer :: varid ! netCDF id's integer :: ier ! error status + logical :: readv ! has variable been read in or not + character(len=100) :: errCode = '-Error reading snow aging parameters:' + character(len=100) :: tString ! temp. var for reading ! Open snow aging (effective radius evolution) file: allocate(snowage_tau(idx_rhos_max,idx_Tgrd_max,idx_T_max)) @@ -1493,9 +2194,15 @@ subroutine SnowAge_init( ) ! snow aging parameters - call ncd_io('tau', snowage_tau, 'read', ncid, posNOTonfile=.true.) - call ncd_io('kappa', snowage_kappa, 'read', ncid, posNOTonfile=.true.) - call ncd_io('drdsdt0', snowage_drdt0, 'read', ncid, posNOTonfile=.true.) + tString = 'tau' + call ncd_io(trim(tString), snowage_tau, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'kappa' + call ncd_io(trim(tString), snowage_kappa, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + tString = 'drdsdt0' + call ncd_io(trim(tString), snowage_drdt0, 'read', ncid, readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) call ncd_pio_closefile(ncid) if (masterproc) then @@ -1509,5 +2216,55 @@ subroutine SnowAge_init( ) endif end subroutine SnowAge_init - + + !----------------------------------------------------------------------- + subroutine piecewise_linear_interp1d(nd, xd, yd, xi, yi) + + ! piecewise linear interpolation method for 1-dimensional data + ! original author: John Burkardt, Florida State University, 09/22/2012 + ! Licencing: Original code distributed under the GNU LGPL license + ! Original code: https://people.sc.fsu.edu/~jburkardt/f77_src/pwl_interp_1d/pwl_interp_1d.f + ! Added and modified by Cenlin He (NCAR), 01/27/2022 + + implicit none + + integer , intent(in) :: nd ! number of data points of (xd) + real(r8), intent(in) :: xd(1:nd) ! x-value of data points + real(r8), intent(in) :: yd(1:nd) ! y-value of data points + real(r8), intent(in) :: xi ! x-value for to-be-interpolated point + real(r8), intent(out) :: yi ! the interpolated value at xi + + ! local variables + integer :: i, k ! loop index + real(r8) :: t + + yi = 0._r8 + + ! if only one data point + if ( nd == 1 ) then + yi = yd(1) + return + endif + + ! if multiple data points + if ( xi < xd(1) ) then ! extrapolate + t = ( xi - xd(1) ) / ( xd(2) - xd(1) ) + yi = (1._r8 - t) * yd(1) + t * yd(2) + elseif ( xi > xd(nd) ) then ! extrapolate + t = ( xi - xd(nd-1) ) / ( xd(nd) - xd(nd-1) ) + yi = (1._r8 - t) * yd(nd-1) + t * yd(nd) + else ! piecsewise interpolate + do k = 2, nd + if ( (xd(k-1) <= xi) .and. (xi <= xd(k)) ) then + t = ( xi - xd(k-1) ) / ( xd(k) - xd(k-1) ) + yi = (1._r8 - t) * yd(k-1) + t * yd(k) + exit + endif + enddo + endif + + return + + end subroutine piecewise_linear_interp1d + end module SnowSnicarMod diff --git a/src/biogeophys/SoilFluxesMod.F90 b/src/biogeophys/SoilFluxesMod.F90 index 5f4030c6e1..44e6d0e1cd 100644 --- a/src/biogeophys/SoilFluxesMod.F90 +++ b/src/biogeophys/SoilFluxesMod.F90 @@ -11,7 +11,7 @@ module SoilFluxesMod use abortutils , only : endrun use perf_mod , only : t_startf, t_stopf use clm_varctl , only : iulog - use clm_varpar , only : nlevsno, nlevgrnd, nlevurb, max_patch_per_col + use clm_varpar , only : nlevsno, nlevgrnd, nlevurb use atm2lndType , only : atm2lnd_type use CanopyStateType , only : canopystate_type use EnergyFluxType , only : energyflux_type @@ -205,6 +205,74 @@ subroutine SoilFluxes (bounds, num_urbanl, filter_urbanl, & endif end do + ! Partition evaporation into liquid and solid + do fp = 1, num_nolakep + p = filter_nolakep(fp) + c = patch%column(p) + l = patch%landunit(p) + j = col%snl(c)+1 + + qflx_liqevap_from_top_layer(p) = 0._r8 + qflx_solidevap_from_top_layer(p) = 0._r8 + qflx_soliddew_to_top_layer(p) = 0._r8 + qflx_liqdew_to_top_layer(p) = 0._r8 + + ! Partition the evaporation from snow/soil surface into liquid evaporation, + ! solid evaporation (sublimation), liquid dew, or solid dew. Note that the variables + ! affected here are all related to the snow subgrid patch only because of the use of qflx_ev_snow. + ! In the situations where there are snow layers or there is snow without an explicit snow layer, + ! the partitioned variables will represent the components of snow evaporation + ! (qflx_ev_snow = qflx_liqevap_from_top_layer + qflx_solidevap_from_top_layer + ! - qflx_liqdew_to_top_layer - qflx_soliddew_to_top_layer). + ! In the case of no snow, qflx_ev_snow has already been set equal to qflx_ev_soil (the evaporation + ! from the subgrid soil patch) and the partitioned variables will then represent evaporation from the + ! subgrid soil patch. + ! In the case of urban columns (and lake columns - see LakeHydrologyMod), there are no subgrid + ! patches and qflx_evap_soi is used. qflx_evap_soi = qflx_liqevap_from_top_layer + qflx_solidevap_from_top_layer + ! - qflx_liqdew_to_top_layer - qflx_soliddew_to_top_layer. + if (.not. lun%urbpoi(l)) then + if (qflx_ev_snow(p) >= 0._r8) then + ! for evaporation partitioning between liquid evap and ice sublimation, + ! use the ratio of liquid to (liquid+ice) in the top layer to determine split + if ((h2osoi_liq(c,j)+h2osoi_ice(c,j)) > 0._r8) then + qflx_liqevap_from_top_layer(p) = max(qflx_ev_snow(p)*(h2osoi_liq(c,j)/ & + (h2osoi_liq(c,j)+h2osoi_ice(c,j))), 0._r8) + else + qflx_liqevap_from_top_layer(p) = 0._r8 + end if + qflx_solidevap_from_top_layer(p) = qflx_ev_snow(p) - qflx_liqevap_from_top_layer(p) + else + if (t_grnd(c) < tfrz) then + qflx_soliddew_to_top_layer(p) = abs(qflx_ev_snow(p)) + else + qflx_liqdew_to_top_layer(p) = abs(qflx_ev_snow(p)) + end if + end if + + else ! Urban columns + + if (qflx_evap_soi(p) >= 0._r8) then + ! for evaporation partitioning between liquid evap and ice sublimation, + ! use the ratio of liquid to (liquid+ice) in the top layer to determine split + if ((h2osoi_liq(c,j)+h2osoi_ice(c,j)) > 0._r8) then + qflx_liqevap_from_top_layer(p) = max(qflx_evap_soi(p)*(h2osoi_liq(c,j)/ & + (h2osoi_liq(c,j)+h2osoi_ice(c,j))), 0._r8) + else + qflx_liqevap_from_top_layer(p) = 0._r8 + end if + qflx_solidevap_from_top_layer(p) = qflx_evap_soi(p) - qflx_liqevap_from_top_layer(p) + else + if (t_grnd(c) < tfrz) then + qflx_soliddew_to_top_layer(p) = abs(qflx_evap_soi(p)) + else + qflx_liqdew_to_top_layer(p) = abs(qflx_evap_soi(p)) + end if + end if + + end if + + end do + ! Constrain evaporation from snow to be <= available moisture do fp = 1,num_nolakep p = filter_nolakep(fp) @@ -221,6 +289,8 @@ subroutine SoilFluxes (bounds, num_urbanl, filter_urbanl, & evaporation_demand = qflx_ev_snow(p) qflx_ev_snow(p) = evaporation_limit qflx_evap_soi(p) = qflx_evap_soi(p) - frac_sno_eff(c)*(evaporation_demand - evaporation_limit) + qflx_liqevap_from_top_layer(p) = max(h2osoi_liq(c,j)/(frac_sno_eff(c)*dtime), 0._r8) + qflx_solidevap_from_top_layer(p) = max(h2osoi_ice(c,j)/(frac_sno_eff(c)*dtime), 0._r8) ! conserve total energy flux eflx_sh_grnd(p) = eflx_sh_grnd(p) + frac_sno_eff(c)*(evaporation_demand - evaporation_limit)*htvp(c) endif @@ -235,11 +305,27 @@ subroutine SoilFluxes (bounds, num_urbanl, filter_urbanl, & evaporation_demand = qflx_evap_soi(p) qflx_evap_soi(p) = evaporation_limit qflx_ev_snow(p) = qflx_evap_soi(p) + qflx_liqevap_from_top_layer(p) = max(h2osoi_liq(c,j)/dtime, 0._r8) + qflx_solidevap_from_top_layer(p) = max(h2osoi_ice(c,j)/dtime, 0._r8) ! conserve total energy flux eflx_sh_grnd(p) = eflx_sh_grnd(p) +(evaporation_demand -evaporation_limit)*htvp(c) endif endif + ! limit only solid evaporation (sublimation) from top soil layer + ! (liquid evaporation from soil should not be limited) + if (j==1 .and. frac_h2osfc(c) < 1._r8) then + evaporation_limit = h2osoi_ice(c,j)/(dtime*(1._r8 - frac_h2osfc(c))) + if (qflx_solidevap_from_top_layer(p) >= evaporation_limit) then + evaporation_demand = qflx_solidevap_from_top_layer(p) + qflx_solidevap_from_top_layer(p) & + = evaporation_limit + qflx_liqevap_from_top_layer(p) & + = qflx_liqevap_from_top_layer(p) & + + (evaporation_demand - evaporation_limit) + endif + endif + enddo call t_stopf('bgp2_loop_1') @@ -299,79 +385,6 @@ subroutine SoilFluxes (bounds, num_urbanl, filter_urbanl, & eflx_sh_tot_u(p)= eflx_sh_tot(p) end if - qflx_liqevap_from_top_layer(p) = 0._r8 - qflx_solidevap_from_top_layer(p) = 0._r8 - qflx_soliddew_to_top_layer(p) = 0._r8 - qflx_liqdew_to_top_layer(p) = 0._r8 - - ! Partition the evaporation from snow/soil surface into liquid evaporation, - ! solid evaporation (sublimation), liquid dew, or solid dew. Note that the variables - ! affected here are all related to the snow subgrid patch only because of the use of qflx_ev_snow. - ! In the situations where there are snow layers or there is snow without an explicit snow layer, - ! the partitioned variables will represent the components of snow evaporation - ! (qflx_ev_snow = qflx_liqevap_from_top_layer + qflx_solidevap_from_top_layer - ! - qflx_liqdew_to_top_layer - qflx_soliddew_to_top_layer). - ! In the case of no snow, qflx_ev_snow has already been set equal to qflx_ev_soil (the evaporation - ! from the subgrid soil patch) and the partitioned variables will then represent evaporation from the - ! subgrid soil patch. - ! In the case of urban columns (and lake columns - see LakeHydrologyMod), there are no subgrid - ! patches and qflx_evap_soi is used. qflx_evap_soi = qflx_liqevap_from_top_layer + qflx_solidevap_from_top_layer - ! - qflx_liqdew_to_top_layer - qflx_soliddew_to_top_layer. - if (.not. lun%urbpoi(l)) then - if (qflx_ev_snow(p) >= 0._r8) then - ! for evaporation partitioning between liquid evap and ice sublimation, - ! use the ratio of liquid to (liquid+ice) in the top layer to determine split - if ((h2osoi_liq(c,j)+h2osoi_ice(c,j)) > 0._r8) then - qflx_liqevap_from_top_layer(p) = max(qflx_ev_snow(p)*(h2osoi_liq(c,j)/ & - (h2osoi_liq(c,j)+h2osoi_ice(c,j))), 0._r8) - else - qflx_liqevap_from_top_layer(p) = 0._r8 - end if - qflx_solidevap_from_top_layer(p) = qflx_ev_snow(p) - qflx_liqevap_from_top_layer(p) - else - if (t_grnd(c) < tfrz) then - qflx_soliddew_to_top_layer(p) = abs(qflx_ev_snow(p)) - else - qflx_liqdew_to_top_layer(p) = abs(qflx_ev_snow(p)) - end if - end if - - else ! Urban columns - - if (qflx_evap_soi(p) >= 0._r8) then - ! for evaporation partitioning between liquid evap and ice sublimation, - ! use the ratio of liquid to (liquid+ice) in the top layer to determine split - if ((h2osoi_liq(c,j)+h2osoi_ice(c,j)) > 0._r8) then - qflx_liqevap_from_top_layer(p) = max(qflx_evap_soi(p)*(h2osoi_liq(c,j)/ & - (h2osoi_liq(c,j)+h2osoi_ice(c,j))), 0._r8) - else - qflx_liqevap_from_top_layer(p) = 0._r8 - end if - qflx_solidevap_from_top_layer(p) = qflx_evap_soi(p) - qflx_liqevap_from_top_layer(p) - else - if (t_grnd(c) < tfrz) then - qflx_soliddew_to_top_layer(p) = abs(qflx_evap_soi(p)) - else - qflx_liqdew_to_top_layer(p) = abs(qflx_evap_soi(p)) - end if - end if - - end if - - ! limit only solid evaporation (sublimation) from top soil layer - ! (liquid evaporation from soil should not be limited) - if (j==1 .and. frac_h2osfc(c) < 1._r8) then - evaporation_limit = h2osoi_ice(c,j)/(dtime*(1._r8 - frac_h2osfc(c))) - if (qflx_solidevap_from_top_layer(p) >= evaporation_limit) then - evaporation_demand = qflx_solidevap_from_top_layer(p) - qflx_solidevap_from_top_layer(p) & - = evaporation_limit - qflx_liqevap_from_top_layer(p) & - = qflx_liqevap_from_top_layer(p) & - + (evaporation_demand - evaporation_limit) - endif - endif - ! Variables needed by history tape qflx_evap_can(p) = qflx_evap_veg(p) - qflx_tran_veg(p) diff --git a/src/biogeophys/SoilHydrologyMod.F90 b/src/biogeophys/SoilHydrologyMod.F90 index 5cdffba4ff..4bc6a784de 100644 --- a/src/biogeophys/SoilHydrologyMod.F90 +++ b/src/biogeophys/SoilHydrologyMod.F90 @@ -179,6 +179,7 @@ subroutine SetSoilWaterFractions(bounds, num_hydrologyc, filter_hydrologyc, & integer :: j, fc, c real(r8) :: vol_ice(bounds%begc:bounds%endc,1:nlevsoi) !partial volume of ice lens in layer real(r8) :: icefrac_orig ! original formulation for icefrac + real(r8) :: dz_ext(bounds%begc:bounds%endc,1:nlevsoi) character(len=*), parameter :: subname = 'SetSoilWaterFractions' !----------------------------------------------------------------------- @@ -189,8 +190,9 @@ subroutine SetSoilWaterFractions(bounds, num_hydrologyc, filter_hydrologyc, & watsat => soilstate_inst%watsat_col , & ! Input: [real(r8) (:,:) ] volumetric soil water at saturation (porosity) eff_porosity => soilstate_inst%eff_porosity_col , & ! Output: [real(r8) (:,:) ] effective porosity = porosity - vol_ice - h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Input: [real(r8) (:,:) ] liquid water (kg/m2) - h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice water (kg/m2) + h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Input: [real(r8) (:,:) ] liquid water (kg/m2) + h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice water (kg/m2) + excess_ice => waterstatebulk_inst%excess_ice_col , & ! Input: [real(r8) (:,:) ] excess ice (kg/m2) origflag => soilhydrology_inst%origflag , & ! Input: logical icefrac => soilhydrology_inst%icefrac_col , & ! Output: [real(r8) (:,:) ] @@ -203,7 +205,8 @@ subroutine SetSoilWaterFractions(bounds, num_hydrologyc, filter_hydrologyc, & ! Porosity of soil, partial volume of ice and liquid, fraction of ice in each layer, ! fractional impermeability - vol_ice(c,j) = min(watsat(c,j), h2osoi_ice(c,j)/(dz(c,j)*denice)) + dz_ext(c,j) = dz(c,j) + excess_ice(c,j)/denice ! extended layer thickness, should be good for all the columns + vol_ice(c,j) = min(watsat(c,j), (h2osoi_ice(c,j) + excess_ice(c,j))/(dz_ext(c,j)*denice)) eff_porosity(c,j) = max(0.01_r8,watsat(c,j)-vol_ice(c,j)) icefrac(c,j) = min(1._r8,vol_ice(c,j)/watsat(c,j)) diff --git a/src/biogeophys/SoilTemperatureMod.F90 b/src/biogeophys/SoilTemperatureMod.F90 index 513413e8a9..0dc8876d24 100644 --- a/src/biogeophys/SoilTemperatureMod.F90 +++ b/src/biogeophys/SoilTemperatureMod.F90 @@ -47,7 +47,7 @@ module SoilTemperatureMod ! o The thermal conductivity of soil is computed from ! the algorithm of Johansen (as reported by Farouki 1981), and the ! conductivity of snow is from the formulation used in - ! SNTHERM (Jordan 1991). + ! Sturm (1997) or Jordan (1991) p. 18 depending on namelist option. ! o Boundary conditions: ! F = Rnet - Hg - LEg (top), F= 0 (base of the soil column). ! o Soil / snow temperature is predicted from heat conduction @@ -88,7 +88,8 @@ module SoilTemperatureMod contains !----------------------------------------------------------------------- - subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter_urbanc, num_nolakec, filter_nolakec, & + subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter_urbanc, & + num_nolakep, filter_nolakep, num_nolakec, filter_nolakec, & atm2lnd_inst, urbanparams_inst, canopystate_inst, waterstatebulk_inst, waterdiagnosticbulk_inst, waterfluxbulk_inst,& solarabs_inst, soilstate_inst, energyflux_inst, temperature_inst, urbantv_inst) ! @@ -99,7 +100,7 @@ subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter ! o The thermal conductivity of soil is computed from ! the algorithm of Johansen (as reported by Farouki 1981), and the ! conductivity of snow is from the formulation used in - ! SNTHERM (Jordan 1991). + ! Sturm (1997) or Jordan (1991) p. 18 depending on namelist option. ! o Boundary conditions: ! F = Rnet - Hg - LEg (top), F= 0 (base of the soil column). ! o Soil / snow temperature is predicted from heat conduction @@ -114,8 +115,8 @@ subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter ! !USES: use clm_time_manager , only : get_step_size_real use clm_varpar , only : nlevsno, nlevgrnd, nlevurb, nlevmaxurbgrnd - use clm_varctl , only : iulog - use clm_varcon , only : cnfac, cpice, cpliq, denh2o + use clm_varctl , only : iulog, use_excess_ice + use clm_varcon , only : cnfac, cpice, cpliq, denh2o, denice use landunit_varcon , only : istsoil, istcrop use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall, icol_road_perv, icol_road_imperv use BandDiagonalMod , only : BandDiagonal @@ -124,6 +125,8 @@ subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds + integer , intent(in) :: num_nolakep ! number of non-lake points in patch filter + integer , intent(in) :: filter_nolakep(:) ! patch filter for non-lake points integer , intent(in) :: num_nolakec ! number of column non-lake points in column filter integer , intent(in) :: filter_nolakec(:) ! column filter for non-lake points integer , intent(in) :: num_urbanl ! number of urban landunits in clump @@ -143,8 +146,8 @@ subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter type(temperature_type) , intent(inout) :: temperature_inst ! ! !LOCAL VARIABLES: - integer :: j,c,l,g,pi ! indices - integer :: fc ! lake filtered column indices + integer :: j,c,l,g ! indices + integer :: fc, fp ! lake filtered column & patch indices integer :: fl ! urban filtered landunit indices integer :: jtop(bounds%begc:bounds%endc) ! top level at each column real(r8) :: dtime ! land model time step (sec) @@ -171,6 +174,10 @@ subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter real(r8) :: hs_top_snow(bounds%begc:bounds%endc) ! heat flux on top snow layer [W/m2] real(r8) :: hs_h2osfc(bounds%begc:bounds%endc) ! heat flux on standing water [W/m2] integer :: jbot(bounds%begc:bounds%endc) ! bottom level at each column + real(r8) :: dz_0(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd) ! original layer thickness [m] + real(r8) :: z_0(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd) ! original layer depth [m] + real(r8) :: zi_0(bounds%begc:bounds%endc,-nlevsno+0:nlevmaxurbgrnd) ! original layer interface level bellow layer "z" [m] + !----------------------------------------------------------------------- associate( & @@ -194,6 +201,7 @@ subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter frac_sno_eff => waterdiagnosticbulk_inst%frac_sno_eff_col , & ! Input: [real(r8) (:) ] eff. fraction of ground covered by snow (0 to 1) snow_depth => waterdiagnosticbulk_inst%snow_depth_col , & ! Input: [real(r8) (:) ] snow height (m) h2osfc => waterstatebulk_inst%h2osfc_col , & ! Input: [real(r8) (:) ] surface water (mm) + excess_ice => waterstatebulk_inst%excess_ice_col , & ! Input: [real(r8) (:,:) ] excess ice (kg/m2) (new) (1:nlevgrnd) frac_h2osfc => waterdiagnosticbulk_inst%frac_h2osfc_col , & ! Input: [real(r8) (:) ] fraction of ground covered by surface water (0 to 1) @@ -271,6 +279,29 @@ subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter endif end do + + !-------------------------------------------------------------- + ! Vertical coordinates adjustment for excess ice calculations + !-------------------------------------------------------------- + if ( use_excess_ice ) then + ! Save original soil depth to get put them back in et the end + dz_0(begc:endc,-nlevsno+1:nlevmaxurbgrnd) = dz(begc:endc,-nlevsno+1:nlevmaxurbgrnd) + zi_0(begc:endc,-nlevsno+0:nlevmaxurbgrnd) = zi(begc:endc,-nlevsno+0:nlevmaxurbgrnd) + z_0(begc:endc,-nlevsno+1:nlevmaxurbgrnd) = z(begc:endc,-nlevsno+1:nlevmaxurbgrnd) + ! Adjust column depth for excess ice thickness + do fc = 1, num_nolakec + c = filter_nolakec(fc) + l = col%landunit(c) + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + dz(c,1:nlevmaxurbgrnd) = dz(c,1:nlevmaxurbgrnd) + excess_ice(c,1:nlevmaxurbgrnd) / denice ! add extra layer thickness + do j = 1, nlevmaxurbgrnd ! if excess ice amount dropped to zero there will be no adjustment + zi(c,j) = zi(c,j) + sum(excess_ice(c,1:j)) / denice + z(c,j) = (zi(c,j-1) + zi(c,j)) * 0.5_r8 + end do + end if + end do + end if + !------------------------------------------------------ ! Compute ground surface and soil temperatures !------------------------------------------------------ @@ -288,7 +319,8 @@ subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter ! Added a patches loop here to get the average of hs and dhsdT over ! all Patches on the column. Precalculate the terms that do not depend on PFT. - call ComputeGroundHeatFluxAndDeriv(bounds, num_nolakec, filter_nolakec, & + call ComputeGroundHeatFluxAndDeriv(bounds, & + num_nolakep, filter_nolakep, num_nolakec, filter_nolakec, & hs_h2osfc( begc:endc ), & hs_top_snow( begc:endc ), & hs_soil( begc:endc ), & @@ -488,6 +520,24 @@ subroutine SoilTemperature(bounds, num_urbanl, filter_urbanl, num_urbanc, filter dhsdT(bounds%begc:bounds%endc), & soilstate_inst, waterstatebulk_inst, waterdiagnosticbulk_inst, waterfluxbulk_inst, energyflux_inst, temperature_inst) + !-------------------------------------------------------------- + ! Vertical coordinates adjustment for excess ice calculations + !-------------------------------------------------------------- + ! bringing back the soil depth to the original state + if (use_excess_ice) then + ! Adjust column depth for excess ice thickness + do fc = 1, num_nolakec + c = filter_nolakec(fc) + l = col%landunit(c) + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + dz(c,1:nlevmaxurbgrnd)=dz_0(c,1:nlevmaxurbgrnd) + zi(c,1:nlevmaxurbgrnd)=zi_0(c,1:nlevmaxurbgrnd) + z(c,1:nlevmaxurbgrnd)=z_0(c,1:nlevmaxurbgrnd) + end if + end do + end if + + if ( IsProgBuildTemp() )then call BuildingTemperature(bounds, num_urbanl, filter_urbanl, num_nolakec, filter_nolakec, & tk(bounds%begc:bounds%endc, :), urbanparams_inst, & @@ -561,18 +611,20 @@ subroutine SoilThermProp (bounds, num_urbanc, filter_urbanc, num_nolakec, filter ! ! (2) The thermal conductivity of soil is computed from the algorithm of ! Johansen (as reported by Farouki 1981), and of snow is from the - ! formulation used in SNTHERM (Jordan 1991). + ! formulation used in Sturm (1997) or Jordan (1991) p. 18 depending on + ! namelist option. ! The thermal conductivities at the interfaces between two neighboring ! layers (j, j+1) are derived from an assumption that the flux across ! the interface is equal to that from the node j to the interface and the ! flux from the interface to the node j+1. ! ! !USES: + use shr_log_mod , only : errMsg => shr_log_errMsg use clm_varpar , only : nlevsno, nlevgrnd, nlevurb, nlevsoi, nlevmaxurbgrnd use clm_varcon , only : denh2o, denice, tfrz, tkwat, tkice, tkair, cpice, cpliq, thk_bedrock, csol_bedrock use landunit_varcon , only : istice, istwet use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall, icol_road_perv, icol_road_imperv - use clm_varctl , only : iulog + use clm_varctl , only : iulog, snow_thermal_cond_method ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -597,6 +649,8 @@ subroutine SoilThermProp (bounds, num_urbanc, filter_urbanc, num_nolakec, filter real(r8) :: fl ! volume fraction of liquid or unfrozen water to total water real(r8) :: satw ! relative total water content of soil. real(r8) :: zh2osfc + + character(len=*),parameter :: subname = 'SoilThermProp' !----------------------------------------------------------------------- call t_startf( 'SoilThermProp' ) @@ -628,6 +682,7 @@ subroutine SoilThermProp (bounds, num_urbanc, filter_urbanc, num_nolakec, filter h2osno_no_layers => waterstatebulk_inst%h2osno_no_layers_col , & ! Input: [real(r8) (:) ] snow not resolved into layers (mm H2O) h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Input: [real(r8) (:,:) ] liquid water (kg/m2) h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice lens (kg/m2) + excess_ice => waterstatebulk_inst%excess_ice_col , & ! Input: [real(r8) (:,:) ] excess ice lenses (kg/m2) (new) (1:nlevgrnd) bw => waterdiagnosticbulk_inst%bw_col , & ! Output: [real(r8) (:,:) ] partial density of water in the snow pack (ice + liquid) [kg/m3] tkmg => soilstate_inst%tkmg_col , & ! Input: [real(r8) (:,:) ] thermal conductivity, soil minerals [W/m-K] @@ -654,7 +709,7 @@ subroutine SoilThermProp (bounds, num_urbanc, filter_urbanc, num_nolakec, filter col%itype(c) /= icol_roof .and. col%itype(c) /= icol_road_imperv) .or. & (col%itype(c) == icol_road_imperv .and. j > nlev_improad(l))) then - satw = (h2osoi_liq(c,j)/denh2o + h2osoi_ice(c,j)/denice)/(dz(c,j)*watsat(c,j)) + satw = (h2osoi_liq(c,j)/denh2o + h2osoi_ice(c,j)/denice +excess_ice(c,j)/denice)/(dz(c,j)*watsat(c,j)) satw = min(1._r8, satw) if (satw > .1e-6_r8) then if (t_soisno(c,j) >= tfrz) then ! Unfrozen soil @@ -663,7 +718,7 @@ subroutine SoilThermProp (bounds, num_urbanc, filter_urbanc, num_nolakec, filter dke = satw end if fl = (h2osoi_liq(c,j)/(denh2o*dz(c,j))) / (h2osoi_liq(c,j)/(denh2o*dz(c,j)) + & - h2osoi_ice(c,j)/(denice*dz(c,j))) + h2osoi_ice(c,j)/(denice*dz(c,j))+excess_ice(c,j)/(denice*dz(c,j))) dksat = tkmg(c,j)*tkwat**(fl*watsat(c,j))*tkice**((1._r8-fl)*watsat(c,j)) thk(c,j) = dke*dksat + (1._r8-dke)*tkdry(c,j) else @@ -683,11 +738,27 @@ subroutine SoilThermProp (bounds, num_urbanc, filter_urbanc, num_nolakec, filter endif endif - ! Thermal conductivity of snow, which from Jordan (1991) pp. 18 + ! Thermal conductivity of snow ! Only examine levels from snl(c)+1 -> 0 where snl(c) < 1 if (snl(c)+1 < 1 .AND. (j >= snl(c)+1) .AND. (j <= 0)) then bw(c,j) = (h2osoi_ice(c,j)+h2osoi_liq(c,j))/(frac_sno(c)*dz(c,j)) - thk(c,j) = tkair + (7.75e-5_r8 *bw(c,j) + 1.105e-6_r8*bw(c,j)*bw(c,j))*(tkice-tkair) + select case (snow_thermal_cond_method) + case ('Jordan1991') + thk(c,j) = tkair + (7.75e-5_r8 *bw(c,j) + 1.105e-6_r8*bw(c,j)*bw(c,j))*(tkice-tkair) + case ('Sturm1997') + ! Implemented by Vicky Dutch (VRD), Nick Rutter, and + ! Leanne Wake (LMW) + ! https://tc.copernicus.org/articles/16/4201/2022/ + ! Code provided by Adrien Dams to Will Wieder + if (bw(c,j) <= 156) then !LMW or 0.156 ? + thk(c,j) = 0.023 + 0.234*(bw(c,j)/1000) !LMW - units changed by VRD + else !LMW + thk(c,j) = 0.138 - 1.01*(bw(c,j)/1000) +(3.233*((bw(c,j)/1000)*(bw(c,j)/1000))) ! LMW Sturm I think + end if + case default + write(iulog,*) subname//' ERROR: unknown snow_thermal_cond_method value: ', snow_thermal_cond_method + call endrun(msg=errMsg(sourcefile, __LINE__)) + end select end if end do @@ -756,7 +827,8 @@ subroutine SoilThermProp (bounds, num_urbanc, filter_urbanc, num_nolakec, filter .and. col%itype(c) /= icol_sunwall .and. col%itype(c) /= icol_shadewall .and. & col%itype(c) /= icol_roof .and. col%itype(c) /= icol_road_imperv) .or. & (col%itype(c) == icol_road_imperv .and. j > nlev_improad(l))) then - cv(c,j) = csol(c,j)*(1._r8-watsat(c,j))*dz(c,j) + (h2osoi_ice(c,j)*cpice + h2osoi_liq(c,j)*cpliq) + cv(c,j) = csol(c,j)*(1._r8-watsat(c,j))*dz(c,j) + (h2osoi_ice(c,j)*cpice + & + h2osoi_liq(c,j)*cpliq) + excess_ice(c,j)*cpice if (j > nbedrock(c)) cv(c,j) = csol_bedrock*dz(c,j) else if (lun%itype(l) == istwet) then cv(c,j) = (h2osoi_ice(c,j)*cpice + h2osoi_liq(c,j)*cpliq) @@ -1057,7 +1129,7 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & use clm_time_manager , only : get_step_size_real use clm_varpar , only : nlevsno, nlevgrnd, nlevurb, nlevmaxurbgrnd use clm_varctl , only : iulog - use clm_varcon , only : tfrz, hfus, grav + use clm_varcon , only : tfrz, hfus, grav, denice use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall, icol_road_perv use landunit_varcon , only : istsoil, istcrop, istice ! @@ -1081,13 +1153,17 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & real(r8) :: temp1 !temporary variables [kg/m2] real(r8) :: hm(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd) !energy residual [W/m2] real(r8) :: xm(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd) !melting or freezing within a time step [kg/m2] + real(r8) :: xm2(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd) !additional melting or freezing within a time step [kg/m2] (needed for excess ice melt) real(r8) :: wmass0(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd)!initial mass of ice and liquid (kg/m2) real(r8) :: wice0 (bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd)!initial mass of ice (kg/m2) real(r8) :: wliq0 (bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd)!initial mass of liquid (kg/m2) real(r8) :: supercool(bounds%begc:bounds%endc,nlevmaxurbgrnd) !supercooled water in soil (kg/m2) - real(r8) :: propor !proportionality constant (-) + real(r8) :: propor !proportionality constant (-) real(r8) :: tinc(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd) !t(n+1)-t(n) [K] - real(r8) :: smp !frozen water potential (mm) + real(r8) :: smp !frozen water potential (mm) + real(r8) :: wexice0(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd) !initial mass of excess_ice at the timestep (kg/m2) + + !----------------------------------------------------------------------- call t_startf( 'PhaseChangebeta' ) @@ -1106,14 +1182,17 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & frac_sno_eff => waterdiagnosticbulk_inst%frac_sno_eff_col , & ! Input: [real(r8) (:) ] eff. fraction of ground covered by snow (0 to 1) frac_h2osfc => waterdiagnosticbulk_inst%frac_h2osfc_col , & ! Input: [real(r8) (:) ] fraction of ground covered by surface water (0 to 1) snow_depth => waterdiagnosticbulk_inst%snow_depth_col , & ! Input: [real(r8) (:) ] snow height (m) + exice_subs_col => waterdiagnosticbulk_inst%exice_subs_col , & ! Output: [real(r8) (:,:) ] per layer subsidence due to excess ice melt (mm/s) h2osno_no_layers => waterstatebulk_inst%h2osno_no_layers_col , & ! Output: [real(r8) (:) ] snow not resolved into layers (mm H2O) - h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Output: [real(r8) (:,:) ] liquid water (kg/m2) (new) - h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Output: [real(r8) (:,:) ] ice lens (kg/m2) (new) + h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Output: [real(r8) (:,:) ] liquid water (kg/m2) (new) + h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Output: [real(r8) (:,:) ] ice lens (kg/m2) (new) + excess_ice => waterstatebulk_inst%excess_ice_col , & ! Input: [real(r8) (:,:) ] excess ice (kg/m2) (new) (1:nlevgrnd) qflx_snow_drain => waterfluxbulk_inst%qflx_snow_drain_col , & ! Output: [real(r8) (:) ] drainage from snow pack qflx_snofrz_lyr => waterfluxbulk_inst%qflx_snofrz_lyr_col , & ! Output: [real(r8) (:,:) ] snow freezing rate (positive definite) (col,lyr) [kg m-2 s-1] qflx_snofrz => waterfluxbulk_inst%qflx_snofrz_col , & ! Output: [real(r8) (:) ] column-integrated snow freezing rate (positive definite) [kg m-2 s-1] qflx_snomelt => waterfluxbulk_inst%qflx_snomelt_col , & ! Output: [real(r8) (:) ] snow melt (mm H2O /s) + snomelt_accum => waterdiagnosticbulk_inst%snomelt_accum_col , & ! Output: [real(r8) (:) ] accumulated snow melt (m) qflx_snomelt_lyr => waterfluxbulk_inst%qflx_snomelt_lyr_col , & ! Output: [real(r8) (:) ] snow melt in each layer (mm H2O /s) eflx_snomelt => energyflux_inst%eflx_snomelt_col , & ! Output: [real(r8) (:) ] snow melt heat flux (W/m**2) @@ -1152,9 +1231,14 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & imelt(c,j) = 0 hm(c,j) = 0._r8 xm(c,j) = 0._r8 + xm2(c,j) = 0._r8 wice0(c,j) = h2osoi_ice(c,j) wliq0(c,j) = h2osoi_liq(c,j) - wmass0(c,j) = h2osoi_ice(c,j) + h2osoi_liq(c,j) + wexice0(c,j) = excess_ice(c,j) + wmass0(c,j) = h2osoi_ice(c,j) + h2osoi_liq(c,j) + wexice0(c,j) + if (j >= 1) then + exice_subs_col(c,j) = 0._r8 + endif endif ! end of snow layer if-block if (j <= 0) then @@ -1173,7 +1257,7 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & ! Melting identification ! If ice exists above melt point, melt some to liquid. - if (h2osoi_ice(c,j) > 0._r8 .AND. t_soisno(c,j) > tfrz) then + if (h2osoi_ice(c,j) > 0._r8 .and. t_soisno(c,j) > tfrz) then imelt(c,j) = 1 ! tinc(c,j) = t_soisno(c,j) - tfrz tinc(c,j) = tfrz - t_soisno(c,j) @@ -1211,6 +1295,13 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & t_soisno(c,j) = tfrz endif + ! melt excess ice after normal ice + if (excess_ice(c,j) > 0._r8 .AND. t_soisno(c,j) > tfrz) then + imelt(c,j) = 1 + tinc(c,j) = tfrz - t_soisno(c,j) + t_soisno(c,j) = tfrz + endif + ! from Zhao (1997) and Koren (1999) supercool(c,j) = 0.0_r8 if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop .or. col%itype(c) == icol_road_perv) then @@ -1325,23 +1416,31 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & endif heatr = 0._r8 - if (xm(c,j) > 0._r8) then + if (xm(c,j) > 0._r8) then !if there is excess heat to melt the ice h2osoi_ice(c,j) = max(0._r8, wice0(c,j)-xm(c,j)) heatr = hm(c,j) - hfus*(wice0(c,j)-h2osoi_ice(c,j))/dtime + xm2(c,j) = xm(c,j) - h2osoi_ice(c,j) !excess ice melting + if (h2osoi_ice(c,j) == 0._r8) then ! this might be redundant + if (excess_ice(c,j) >= 0._r8 .and. xm2(c,j)>0._r8 .and. j>=2) then ! if there is excess ice to melt + excess_ice(c,j) = max(0._r8,wexice0(c,j) - xm2(c,j)) + heatr = hm(c,j) - hfus * (wexice0(c,j)-excess_ice(c,j)+wice0(c,j)-h2osoi_ice(c,j)) / dtime + endif + endif !end of excess ice block else if (xm(c,j) < 0._r8) then if (j <= 0) then h2osoi_ice(c,j) = min(wmass0(c,j), wice0(c,j)-xm(c,j)) ! snow else - if (wmass0(c,j) < supercool(c,j)) then + if (wmass0(c,j) - wexice0(c,j) < supercool(c,j)) then ! even if excess ice is present, it cannot refreeze h2osoi_ice(c,j) = 0._r8 else - h2osoi_ice(c,j) = min(wmass0(c,j) - supercool(c,j),wice0(c,j)-xm(c,j)) + h2osoi_ice(c,j) = min(wmass0(c,j) - wexice0(c,j) - supercool(c,j),wice0(c,j)-xm(c,j)) endif endif heatr = hm(c,j) - hfus*(wice0(c,j)-h2osoi_ice(c,j))/dtime endif - h2osoi_liq(c,j) = max(0._r8,wmass0(c,j)-h2osoi_ice(c,j)) + h2osoi_liq(c,j) = max(0._r8,wmass0(c,j)-h2osoi_ice(c,j)-excess_ice(c,j)) !melted excess ice is added to the respective soil layers + if (abs(heatr) > 0._r8) then if (j == snl(c)+1) then @@ -1373,14 +1472,18 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & endif ! end of heatr > 0 if-block if (j >= 1) then - xmf(c) = xmf(c) + hfus*(wice0(c,j)-h2osoi_ice(c,j))/dtime + xmf(c) = xmf(c) + hfus*(wice0(c,j)-h2osoi_ice(c,j))/dtime + & + hfus*(wexice0(c,j)-excess_ice(c,j))/dtime + ! subsidence calculation + exice_subs_col(c,j) = max(0._r8, (wexice0(c,j)-excess_ice(c,j))/denice) else xmf(c) = xmf(c) + hfus*(wice0(c,j)-h2osoi_ice(c,j))/dtime endif - + if (imelt(c,j) == 1 .AND. j < 1) then qflx_snomelt_lyr(c,j) = max(0._r8,(wice0(c,j)-h2osoi_ice(c,j)))/dtime qflx_snomelt(c) = qflx_snomelt(c) + qflx_snomelt_lyr(c,j) + snomelt_accum(c) = snomelt_accum(c) + qflx_snomelt_lyr(c,j) * dtime * 1.e-3_r8 endif ! layer freezing mass flux (positive): @@ -1398,6 +1501,7 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & end do ! end of column-loop enddo ! end of level-loop + ! Needed for history file output do fc = 1,num_nolakec @@ -1417,7 +1521,8 @@ subroutine Phasechange_beta (bounds, num_nolakec, filter_nolakec, dhsdT, & end subroutine Phasechange_beta !----------------------------------------------------------------------- - subroutine ComputeGroundHeatFluxAndDeriv(bounds, num_nolakec, filter_nolakec, & + subroutine ComputeGroundHeatFluxAndDeriv(bounds, & + num_nolakep, filter_nolakep, num_nolakec, filter_nolakec, & hs_h2osfc, hs_top_snow, hs_soil, hs_top, dhsdT, sabg_lyr_col, & atm2lnd_inst, urbanparams_inst, canopystate_inst, waterdiagnosticbulk_inst, & waterfluxbulk_inst, solarabs_inst, energyflux_inst, temperature_inst) @@ -1433,12 +1538,14 @@ subroutine ComputeGroundHeatFluxAndDeriv(bounds, num_nolakec, filter_nolakec, & ! !USES: use clm_varcon , only : sb, hvap use column_varcon , only : icol_road_perv, icol_road_imperv - use clm_varpar , only : nlevsno, max_patch_per_col + use clm_varpar , only : nlevsno use UrbanParamsType, only : IsSimpleBuildTemp, IsProgBuildTemp ! ! !ARGUMENTS: implicit none type(bounds_type) , intent(in) :: bounds ! bounds + integer , intent(in) :: num_nolakep ! number of non-lake points in patch filter + integer , intent(in) :: filter_nolakep( : ) ! patch filter for non-lake points integer , intent(in) :: num_nolakec ! number of column non-lake points in column filter integer , intent(in) :: filter_nolakec( : ) ! column filter for non-lake points real(r8) , intent(out) :: hs_h2osfc( bounds%begc: ) ! heat flux on standing water [W/m2] @@ -1457,8 +1564,8 @@ subroutine ComputeGroundHeatFluxAndDeriv(bounds, num_nolakec, filter_nolakec, & type(temperature_type) , intent(in) :: temperature_inst ! ! !LOCAL VARIABLES: - integer :: j,c,p,l,g,pi ! indices - integer :: fc ! lake filtered column indices + integer :: j,c,p,l,g ! indices + integer :: fc, fp ! lake filtered column and patch indices real(r8) :: hs(bounds%begc:bounds%endc) ! net energy flux into the surface (w/m2) real(r8) :: lwrad_emit(bounds%begc:bounds%endc) ! emitted longwave radiation real(r8) :: dlwrad_emit(bounds%begc:bounds%endc) ! time derivative of emitted longwave radiation @@ -1550,79 +1657,71 @@ subroutine ComputeGroundHeatFluxAndDeriv(bounds, num_nolakec, filter_nolakec, & hs_h2osfc(begc:endc) = 0._r8 hs(begc:endc) = 0._r8 dhsdT(begc:endc) = 0._r8 - do pi = 1,max_patch_per_col - do fc = 1,num_nolakec - c = filter_nolakec(fc) - if ( pi <= col%npatches(c) ) then - p = col%patchi(c) + pi - 1 - l = patch%landunit(p) - g = patch%gridcell(p) - - if (patch%active(p)) then - if (.not. lun%urbpoi(l)) then - eflx_gnet(p) = sabg(p) + dlrad(p) & - + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) - lwrad_emit(c) & - - (eflx_sh_grnd(p)+qflx_evap_soi(p)*htvp(c)) - ! save sabg for balancecheck, in case frac_sno is set to zero later - sabg_chk(p) = frac_sno_eff(c) * sabg_snow(p) + (1._r8 - frac_sno_eff(c) ) * sabg_soil(p) - - eflx_gnet_snow = sabg_snow(p) + dlrad(p) & - + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) - lwrad_emit_snow(c) & - - (eflx_sh_snow(p)+qflx_ev_snow(p)*htvp(c)) - - eflx_gnet_soil = sabg_soil(p) + dlrad(p) & - + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) - lwrad_emit_soil(c) & - - (eflx_sh_soil(p)+qflx_ev_soil(p)*htvp(c)) - - eflx_gnet_h2osfc = sabg_soil(p) + dlrad(p) & - + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) - lwrad_emit_h2osfc(c) & - - (eflx_sh_h2osfc(p)+qflx_ev_h2osfc(p)*htvp(c)) - else - ! For urban columns we use the net longwave radiation (eflx_lwrad_net) because of - ! interactions between urban columns. - - ! All wasteheat and traffic flux goes into canyon floor - if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - ! Note that we divide the following landunit variables by 1-wtlunit_roof which - ! essentially converts the flux from W/m2 of urban area to W/m2 of canyon floor area - eflx_wasteheat_patch(p) = eflx_wasteheat(l)/(1._r8-lun%wtlunit_roof(l)) - if ( IsSimpleBuildTemp() ) then - eflx_ventilation_patch(p) = 0._r8 - else if ( IsProgBuildTemp() ) then - eflx_ventilation_patch(p) = eflx_ventilation(l)/(1._r8-lun%wtlunit_roof(l)) - end if - eflx_heat_from_ac_patch(p) = eflx_heat_from_ac(l)/(1._r8-lun%wtlunit_roof(l)) - eflx_traffic_patch(p) = eflx_traffic(l)/(1._r8-lun%wtlunit_roof(l)) - else - eflx_wasteheat_patch(p) = 0._r8 - eflx_ventilation_patch(p) = 0._r8 - eflx_heat_from_ac_patch(p) = 0._r8 - eflx_traffic_patch(p) = 0._r8 - end if - ! Include transpiration term because needed for previous road - ! and include wasteheat and traffic flux - eflx_gnet(p) = sabg(p) + dlrad(p) & - - eflx_lwrad_net(p) & - - (eflx_sh_grnd(p) + qflx_evap_soi(p)*htvp(c) + qflx_tran_veg(p)*hvap) & - + eflx_wasteheat_patch(p) + eflx_heat_from_ac_patch(p) + eflx_traffic_patch(p) & - + eflx_ventilation_patch(p) - if ( IsSimpleBuildTemp() ) then - eflx_anthro(p) = eflx_wasteheat_patch(p) + eflx_traffic_patch(p) - end if - eflx_gnet_snow = eflx_gnet(p) - eflx_gnet_soil = eflx_gnet(p) - eflx_gnet_h2osfc = eflx_gnet(p) - end if - dgnetdT(p) = - cgrnd(p) - dlwrad_emit(c) - hs(c) = hs(c) + eflx_gnet(p) * patch%wtcol(p) - dhsdT(c) = dhsdT(c) + dgnetdT(p) * patch%wtcol(p) - ! separate surface fluxes for soil/snow - hs_soil(c) = hs_soil(c) + eflx_gnet_soil * patch%wtcol(p) - hs_h2osfc(c) = hs_h2osfc(c) + eflx_gnet_h2osfc * patch%wtcol(p) - + do fp = 1,num_nolakep + p = filter_nolakep(fp) + c = patch%column(p) + l = patch%landunit(p) + + if (.not. lun%urbpoi(l)) then + eflx_gnet(p) = sabg(p) + dlrad(p) & + + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) - lwrad_emit(c) & + - (eflx_sh_grnd(p)+qflx_evap_soi(p)*htvp(c)) + ! save sabg for balancecheck, in case frac_sno is set to zero later + sabg_chk(p) = frac_sno_eff(c) * sabg_snow(p) + (1._r8 - frac_sno_eff(c) ) * sabg_soil(p) + + eflx_gnet_snow = sabg_snow(p) + dlrad(p) & + + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) - lwrad_emit_snow(c) & + - (eflx_sh_snow(p)+qflx_ev_snow(p)*htvp(c)) + + eflx_gnet_soil = sabg_soil(p) + dlrad(p) & + + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) - lwrad_emit_soil(c) & + - (eflx_sh_soil(p)+qflx_ev_soil(p)*htvp(c)) + + eflx_gnet_h2osfc = sabg_soil(p) + dlrad(p) & + + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) - lwrad_emit_h2osfc(c) & + - (eflx_sh_h2osfc(p)+qflx_ev_h2osfc(p)*htvp(c)) + else + ! For urban columns we use the net longwave radiation (eflx_lwrad_net) because of + ! interactions between urban columns. + + ! All wasteheat and traffic flux goes into canyon floor + if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then + ! Note that we divide the following landunit variables by 1-wtlunit_roof which + ! essentially converts the flux from W/m2 of urban area to W/m2 of canyon floor area + eflx_wasteheat_patch(p) = eflx_wasteheat(l)/(1._r8-lun%wtlunit_roof(l)) + if ( IsSimpleBuildTemp() ) then + eflx_ventilation_patch(p) = 0._r8 + else if ( IsProgBuildTemp() ) then + eflx_ventilation_patch(p) = eflx_ventilation(l)/(1._r8-lun%wtlunit_roof(l)) end if + eflx_heat_from_ac_patch(p) = eflx_heat_from_ac(l)/(1._r8-lun%wtlunit_roof(l)) + eflx_traffic_patch(p) = eflx_traffic(l)/(1._r8-lun%wtlunit_roof(l)) + else + eflx_wasteheat_patch(p) = 0._r8 + eflx_ventilation_patch(p) = 0._r8 + eflx_heat_from_ac_patch(p) = 0._r8 + eflx_traffic_patch(p) = 0._r8 end if - end do + ! Include transpiration term because needed for previous road + ! and include wasteheat and traffic flux + eflx_gnet(p) = sabg(p) + dlrad(p) & + - eflx_lwrad_net(p) & + - (eflx_sh_grnd(p) + qflx_evap_soi(p)*htvp(c) + qflx_tran_veg(p)*hvap) & + + eflx_wasteheat_patch(p) + eflx_heat_from_ac_patch(p) + eflx_traffic_patch(p) & + + eflx_ventilation_patch(p) + if ( IsSimpleBuildTemp() ) then + eflx_anthro(p) = eflx_wasteheat_patch(p) + eflx_traffic_patch(p) + end if + eflx_gnet_snow = eflx_gnet(p) + eflx_gnet_soil = eflx_gnet(p) + eflx_gnet_h2osfc = eflx_gnet(p) + end if + dgnetdT(p) = - cgrnd(p) - dlwrad_emit(c) + hs(c) = hs(c) + eflx_gnet(p) * patch%wtcol(p) + dhsdT(c) = dhsdT(c) + dgnetdT(p) * patch%wtcol(p) + ! separate surface fluxes for soil/snow + hs_soil(c) = hs_soil(c) + eflx_gnet_soil * patch%wtcol(p) + hs_h2osfc(c) = hs_h2osfc(c) + eflx_gnet_h2osfc * patch%wtcol(p) end do ! Additional calculations with SNICAR: @@ -1639,44 +1738,38 @@ subroutine ComputeGroundHeatFluxAndDeriv(bounds, num_nolakec, filter_nolakec, & hs_top(begc:endc) = 0._r8 hs_top_snow(begc:endc) = 0._r8 - do pi = 1,max_patch_per_col - do fc = 1,num_nolakec - c = filter_nolakec(fc) - lyr_top = snl(c) + 1 - if ( pi <= col%npatches(c) ) then - p = col%patchi(c) + pi - 1 - if (patch%active(p)) then - g = patch%gridcell(p) - l = patch%landunit(p) - if (.not. lun%urbpoi(l)) then + do fp = 1,num_nolakep + p = filter_nolakep(fp) + c = patch%column(p) + l = patch%landunit(p) - eflx_gnet_top = sabg_lyr(p,lyr_top) + dlrad(p) + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) & - - lwrad_emit(c) - (eflx_sh_grnd(p)+qflx_evap_soi(p)*htvp(c)) + lyr_top = snl(c) + 1 - hs_top(c) = hs_top(c) + eflx_gnet_top*patch%wtcol(p) + if (.not. lun%urbpoi(l)) then - eflx_gnet_snow = sabg_lyr(p,lyr_top) + dlrad(p) + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) & - - lwrad_emit_snow(c) - (eflx_sh_snow(p)+qflx_ev_snow(p)*htvp(c)) + eflx_gnet_top = sabg_lyr(p,lyr_top) + dlrad(p) + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) & + - lwrad_emit(c) - (eflx_sh_grnd(p)+qflx_evap_soi(p)*htvp(c)) - eflx_gnet_soil = sabg_lyr(p,lyr_top) + dlrad(p) + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) & - - lwrad_emit_soil(c) - (eflx_sh_soil(p)+qflx_ev_soil(p)*htvp(c)) + hs_top(c) = hs_top(c) + eflx_gnet_top*patch%wtcol(p) - hs_top_snow(c) = hs_top_snow(c) + eflx_gnet_snow*patch%wtcol(p) + eflx_gnet_snow = sabg_lyr(p,lyr_top) + dlrad(p) + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) & + - lwrad_emit_snow(c) - (eflx_sh_snow(p)+qflx_ev_snow(p)*htvp(c)) - do j = lyr_top,1,1 - sabg_lyr_col(c,j) = sabg_lyr_col(c,j) + sabg_lyr(p,j) * patch%wtcol(p) - enddo - else + eflx_gnet_soil = sabg_lyr(p,lyr_top) + dlrad(p) + (1._r8-frac_veg_nosno(p))*emg(c)*forc_lwrad(c) & + - lwrad_emit_soil(c) - (eflx_sh_soil(p)+qflx_ev_soil(p)*htvp(c)) - hs_top(c) = hs_top(c) + eflx_gnet(p)*patch%wtcol(p) - hs_top_snow(c) = hs_top_snow(c) + eflx_gnet(p)*patch%wtcol(p) - sabg_lyr_col(c,lyr_top) = sabg_lyr_col(c,lyr_top) + sabg(p) * patch%wtcol(p) + hs_top_snow(c) = hs_top_snow(c) + eflx_gnet_snow*patch%wtcol(p) - endif - endif + do j = lyr_top,1,1 + sabg_lyr_col(c,j) = sabg_lyr_col(c,j) + sabg_lyr(p,j) * patch%wtcol(p) + enddo + else - endif - enddo + hs_top(c) = hs_top(c) + eflx_gnet(p)*patch%wtcol(p) + hs_top_snow(c) = hs_top_snow(c) + eflx_gnet(p)*patch%wtcol(p) + sabg_lyr_col(c,lyr_top) = sabg_lyr_col(c,lyr_top) + sabg(p) * patch%wtcol(p) + + endif enddo end associate diff --git a/src/biogeophys/SoilWaterMovementMod.F90 b/src/biogeophys/SoilWaterMovementMod.F90 index 70da14a713..b1487e2779 100644 --- a/src/biogeophys/SoilWaterMovementMod.F90 +++ b/src/biogeophys/SoilWaterMovementMod.F90 @@ -380,7 +380,7 @@ subroutine BaseflowSink(bounds, num_hydrologyc, & !USES: use decompMod , only : bounds_type use shr_kind_mod , only : r8 => shr_kind_r8 - use clm_varpar , only : nlevsoi, max_patch_per_col + use clm_varpar , only : nlevsoi use SoilStateType , only : soilstate_type use WaterFluxBulkType , only : waterfluxbulk_type use PatchType , only : patch @@ -484,7 +484,7 @@ subroutine soilwater_zengdecker2009(bounds, num_hydrologyc, filter_hydrologyc, & use decompMod , only : bounds_type use clm_varcon , only : grav,hfus,tfrz use clm_varcon , only : denh2o, denice - use clm_varpar , only : nlevsoi, max_patch_per_col, nlevgrnd + use clm_varpar , only : nlevsoi, nlevgrnd use clm_time_manager , only : get_step_size_real, get_nstep use column_varcon , only : icol_roof, icol_road_imperv use clm_varctl , only : use_flexibleCN, use_hydrstress diff --git a/src/biogeophys/SoilWaterPlantSinkMod.F90 b/src/biogeophys/SoilWaterPlantSinkMod.F90 index 115e1cab76..2d9c1a03c6 100644 --- a/src/biogeophys/SoilWaterPlantSinkMod.F90 +++ b/src/biogeophys/SoilWaterPlantSinkMod.F90 @@ -149,7 +149,6 @@ subroutine Compute_EffecRootFrac_And_VertTranSink_HydStress_Roads(bounds, & use SoilStateType , only : soilstate_type use WaterFluxBulkType , only : waterfluxbulk_type use clm_varpar , only : nlevsoi - use clm_varpar , only : max_patch_per_col use PatchType , only : patch use ColumnType , only : col @@ -199,30 +198,25 @@ subroutine Compute_EffecRootFrac_And_VertTranSink_HydStress_Roads(bounds, & end do end do - do pi = 1,max_patch_per_col - do j = 1,nlevsoi - do fc = 1, num_filterc - c = filterc(fc) - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 - if (patch%active(p)) then - rootr_col(c,j) = rootr_col(c,j) + rootr_patch(p,j) * & - qflx_tran_veg_patch(p) * patch%wtcol(p) - end if - end if - end do - end do + do j = 1,nlevsoi do fc = 1, num_filterc c = filterc(fc) - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 + do p = col%patchi(c), col%patchi(c) + col%npatches(c) - 1 if (patch%active(p)) then - temp(c) = temp(c) + qflx_tran_veg_patch(p) * patch%wtcol(p) + rootr_col(c,j) = rootr_col(c,j) + rootr_patch(p,j) * & + qflx_tran_veg_patch(p) * patch%wtcol(p) end if + end do + end do + end do + do fc = 1, num_filterc + c = filterc(fc) + do p = col%patchi(c), col%patchi(c) + col%npatches(c) - 1 + if (patch%active(p)) then + temp(c) = temp(c) + qflx_tran_veg_patch(p) * patch%wtcol(p) end if end do end do - do j = 1, nlevsoi do fc = 1, num_filterc @@ -248,7 +242,6 @@ subroutine Compute_EffecRootFrac_And_VertTranSink_HydStress( bounds, & !USES: use decompMod , only : bounds_type use clm_varpar , only : nlevsoi - use clm_varpar , only : max_patch_per_col use SoilStateType , only : soilstate_type use WaterFluxBulkType , only : waterfluxbulk_type use CanopyStateType , only : canopystate_type @@ -308,21 +301,18 @@ subroutine Compute_EffecRootFrac_And_VertTranSink_HydStress( bounds, & do j = 1, nlevsoi grav2 = z(c,j) * 1000._r8 temp(c) = 0._r8 - do pi = 1,max_patch_per_col - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 - if (j == 1) then - qflx_hydr_redist_patch(p) = 0._r8 - end if - if (patch%active(p).and.frac_veg_nosno(p)>0) then - if (patch%wtcol(p) > 0._r8) then - patchflux = k_soil_root(p,j) * (smp(c,j) - vegwp(p,4) - grav2) - if (patchflux <0) then - qflx_hydr_redist_patch(p) = qflx_hydr_redist_patch(p) + patchflux - end if - temp(c) = temp(c) + patchflux * patch%wtcol(p) - endif - end if + do p = col%patchi(c), col%patchi(c) + col%npatches(c) - 1 + if (j == 1) then + qflx_hydr_redist_patch(p) = 0._r8 + end if + if (patch%active(p).and.frac_veg_nosno(p)>0) then + if (patch%wtcol(p) > 0._r8) then + patchflux = k_soil_root(p,j) * (smp(c,j) - vegwp(p,4) - grav2) + if (patchflux <0) then + qflx_hydr_redist_patch(p) = qflx_hydr_redist_patch(p) + patchflux + end if + temp(c) = temp(c) + patchflux * patch%wtcol(p) + endif end if end do qflx_rootsoi_col(c,j)= temp(c) @@ -351,7 +341,7 @@ subroutine Compute_EffecRootFrac_And_VertTranSink_Default(bounds, num_filterc, & !USES: use decompMod , only : bounds_type use shr_kind_mod , only : r8 => shr_kind_r8 - use clm_varpar , only : nlevsoi, max_patch_per_col + use clm_varpar , only : nlevsoi use SoilStateType , only : soilstate_type use WaterFluxBulkType , only : waterfluxbulk_type use PatchType , only : patch @@ -399,26 +389,22 @@ subroutine Compute_EffecRootFrac_And_VertTranSink_Default(bounds, num_filterc, & end do end do - do pi = 1,max_patch_per_col - do j = 1,nlevsoi - do fc = 1, num_filterc - c = filterc(fc) - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 - if (patch%active(p)) then - rootr_col(c,j) = rootr_col(c,j) + rootr_patch(p,j) * & - qflx_tran_veg_patch(p) * patch%wtcol(p) - end if - end if - end do - end do + do j = 1,nlevsoi do fc = 1, num_filterc c = filterc(fc) - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 + do p = col%patchi(c), col%patchi(c) + col%npatches(c) - 1 if (patch%active(p)) then - temp(c) = temp(c) + qflx_tran_veg_patch(p) * patch%wtcol(p) + rootr_col(c,j) = rootr_col(c,j) + rootr_patch(p,j) * & + qflx_tran_veg_patch(p) * patch%wtcol(p) end if + end do + end do + end do + do fc = 1, num_filterc + c = filterc(fc) + do p = col%patchi(c), col%patchi(c) + col%npatches(c) - 1 + if (patch%active(p)) then + temp(c) = temp(c) + qflx_tran_veg_patch(p) * patch%wtcol(p) end if end do end do diff --git a/src/biogeophys/SolarAbsorbedType.F90 b/src/biogeophys/SolarAbsorbedType.F90 index d42a072b06..d1941f68cc 100644 --- a/src/biogeophys/SolarAbsorbedType.F90 +++ b/src/biogeophys/SolarAbsorbedType.F90 @@ -168,7 +168,7 @@ subroutine InitHistory(this, bounds) ! ! !USES: use shr_infnan_mod, only : nan => shr_infnan_nan, assignment(=) - use clm_varctl , only : use_snicar_frc , use_SSRE + use clm_varctl , only : use_SSRE use clm_varpar , only : nlevsno use histFileMod , only : hist_addfld1d, hist_addfld2d use histFileMod , only : no_snow_normal @@ -375,7 +375,6 @@ subroutine Restart(this, bounds, ncid, flag) ! ! !USES: use shr_infnan_mod , only : shr_infnan_isnan - use clm_varctl , only : use_snicar_frc, iulog use spmdMod , only : masterproc use abortutils , only : endrun use ncdio_pio , only : file_desc_t, ncd_defvar, ncd_io, ncd_double, ncd_int, ncd_inqvdlen diff --git a/src/biogeophys/SurfaceAlbedoMod.F90 b/src/biogeophys/SurfaceAlbedoMod.F90 index ba025023db..d23320d5e7 100644 --- a/src/biogeophys/SurfaceAlbedoMod.F90 +++ b/src/biogeophys/SurfaceAlbedoMod.F90 @@ -12,11 +12,11 @@ module SurfaceAlbedoMod use decompMod , only : bounds_type, subgrid_level_patch use abortutils , only : endrun use landunit_varcon , only : istsoil, istcrop, istdlak - use clm_varcon , only : grlnd + use clm_varcon , only : grlnd, spval use clm_varpar , only : numrad, nlevcan, nlevsno, nlevcan - use clm_varctl , only : fsurdat, iulog, use_snicar_frc, use_SSRE + use clm_varctl , only : fsurdat, iulog, use_SSRE, do_sno_oc use pftconMod , only : pftcon - use SnowSnicarMod , only : sno_nbr_aer, SNICAR_RT, DO_SNO_AER, DO_SNO_OC + use SnowSnicarMod , only : sno_nbr_aer, SNICAR_RT, DO_SNO_AER use AerosolMod , only : aerosol_type use CanopyStateType , only : canopystate_type use LakeStateType , only : lakestate_type @@ -297,7 +297,6 @@ subroutine SurfaceAlbedo(bounds,nc, & real(r8) :: laisum ! sum of canopy layer lai for error check real(r8) :: saisum ! sum of canopy layer sai for error check integer :: flg_slr ! flag for SNICAR (=1 if direct, =2 if diffuse) - integer :: flg_snw_ice ! flag for SNICAR (=1 when called from CLM, =2 when called from sea-ice) integer :: num_vegsol ! number of vegetated patches where coszen>0 integer :: num_novegsol ! number of vegetated patches where coszen>0 integer :: filter_vegsol (bounds%endp-bounds%begp+1) ! patch filter where vegetated and coszen>0 @@ -349,7 +348,7 @@ subroutine SurfaceAlbedo(bounds,nc, & esai => canopystate_inst%esai_patch , & ! Input: [real(r8) (:) ] one-sided stem area index with burying by snow frac_sno => waterdiagnosticbulk_inst%frac_sno_col , & ! Input: [real(r8) (:) ] fraction of ground covered by snow (0 to 1) - fcansno => waterdiagnosticbulk_inst%fcansno_patch , & ! Input: [real(r8) (:) ] fraction of canopy that is snow-covered (0 to 1) + fcansno => waterdiagnosticbulk_inst%fcansno_patch , & ! Input: [real(r8) (:) ] fraction of canopy that is snow-covered (0 to 1) h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Input: [real(r8) (:,:) ] liquid water content (col,lyr) [kg/m2] h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice lens content (col,lyr) [kg/m2] snw_rds => waterdiagnosticbulk_inst%snw_rds_col , & ! Input: [real(r8) (:,:) ] snow grain radius (col,lyr) [microns] @@ -387,6 +386,22 @@ subroutine SurfaceAlbedo(bounds,nc, & albsni_hst => surfalb_inst%albsni_hst_col , & ! Output: [real(r8) (:,:) ] snow ground albedo, diffuse, for history files (col,bnd) [frc] albd => surfalb_inst%albd_patch , & ! Output: [real(r8) (:,:) ] surface albedo (direct) albi => surfalb_inst%albi_patch , & ! Output: [real(r8) (:,:) ] surface albedo (diffuse) +! add new snicar output albedo variables for history fields + albgrd_hst => surfalb_inst%albgrd_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo (direct) for history files + albgri_hst => surfalb_inst%albgri_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo (diffuse) for history files + albgrd_pur_hst => surfalb_inst%albgrd_pur_hst_col , & ! Output: [real(r8) (:,:) ] pure snow ground albedo (direct) for history files + albgri_pur_hst => surfalb_inst%albgri_pur_hst_col , & ! Output: [real(r8) (:,:) ] pure snow ground albedo (diffuse) for history files + albgrd_bc_hst => surfalb_inst%albgrd_bc_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo without BC (direct) for history files + albgri_bc_hst => surfalb_inst%albgri_bc_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo without BC (diffuse) for history files + albgrd_oc_hst => surfalb_inst%albgrd_oc_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo without OC (direct) for history files + albgri_oc_hst => surfalb_inst%albgri_oc_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo without OC (diffuse) for history files + albgrd_dst_hst => surfalb_inst%albgrd_dst_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo without dust (direct) for history files + albgri_dst_hst => surfalb_inst%albgri_dst_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo without dust (diffuse) for history files + albsnd_hst2 => surfalb_inst%albsnd_hst2_col , & ! Output: [real(r8) (:,:) ] snow albedo, direct, for history files (col,bnd) for history files + albsni_hst2 => surfalb_inst%albsni_hst2_col , & ! Output: [real(r8) (:,:) ] snow ground albedo, diffuse, for history files (col,bnd) for history files + albd_hst => surfalb_inst%albd_hst_patch , & ! Output: [real(r8) (:,:) ] surface albedo (direct) for history files + albi_hst => surfalb_inst%albi_hst_patch , & ! Output: [real(r8) (:,:) ] surface albedo (diffuse) for history files +! end add new snicar albdSF => surfalb_inst%albdSF_patch , & ! Output: [real(r8) (:,:) ] diagnostic snow-free surface albedo (direct) albiSF => surfalb_inst%albiSF_patch , & ! Output: [real(r8) (:,:) ] diagnostic snow-free surface albedo (diffuse) fabd => surfalb_inst%fabd_patch , & ! Output: [real(r8) (:,:) ] flux absorbed by canopy per unit direct flux @@ -420,7 +435,7 @@ subroutine SurfaceAlbedo(bounds,nc, & do fp = 1,num_nourbanp p = filter_nourbanp(fp) g = patch%gridcell(p) - coszen_patch(p) = coszen_gcell(g) + coszen_patch(p) = coszen_gcell(g) end do ! Initialize output because solar radiation only done if coszen > 0 @@ -440,6 +455,20 @@ subroutine SurfaceAlbedo(bounds,nc, & albgri_oc(c,ib) = 0._r8 albgrd_dst(c,ib) = 0._r8 albgri_dst(c,ib) = 0._r8 +! add new snicar output variables for history files + albgrd_hst(c,ib) = spval + albgri_hst(c,ib) = spval + albgrd_pur_hst(c,ib) = spval + albgri_pur_hst(c,ib) = spval + albgrd_bc_hst(c,ib) = spval + albgri_bc_hst(c,ib) = spval + albgrd_oc_hst(c,ib) = spval + albgri_oc_hst(c,ib) = spval + albgrd_dst_hst(c,ib) = spval + albgri_dst_hst(c,ib) = spval + albsnd_hst2(c,ib) = spval + albsni_hst2(c,ib) = spval +! end add new snicar do i=-nlevsno+1,1,1 flx_absdv(c,i) = 0._r8 flx_absdn(c,i) = 0._r8 @@ -452,6 +481,10 @@ subroutine SurfaceAlbedo(bounds,nc, & p = filter_nourbanp(fp) albd(p,ib) = 1._r8 albi(p,ib) = 1._r8 +! add new snicar output variables for history files + albd_hst(p,ib) = spval + albi_hst(p,ib) = spval +! end add new snicar if (use_SSRE) then albdSF(p,ib) = 1._r8 albiSF(p,ib) = 1._r8 @@ -490,7 +523,6 @@ subroutine SurfaceAlbedo(bounds,nc, & ! set variables to pass to SNICAR. - flg_snw_ice = 1 ! calling from CLM, not CSIM do c=bounds%begc,bounds%endc albsfc(c,:) = albsoi(c,:) h2osno_liq(c,:) = h2osoi_liq(c,-nlevsno+1:0) @@ -518,11 +550,11 @@ subroutine SurfaceAlbedo(bounds,nc, & mss_cnc_aer_in_fdb(bounds%begc:bounds%endc,:,1) = mss_cnc_bcphi(bounds%begc:bounds%endc,:) mss_cnc_aer_in_fdb(bounds%begc:bounds%endc,:,2) = mss_cnc_bcpho(bounds%begc:bounds%endc,:) - ! DO_SNO_OC is set in SNICAR_varpar. Default case is to ignore OC concentrations because: + ! do_sno_oc is set in SNICAR_varpar. Default case is to ignore OC concentrations because: ! 1) Knowledge of their optical properties is primitive ! 2) When 'water-soluble' OPAC optical properties are applied to OC in snow, ! it has a negligible darkening effect. - if (DO_SNO_OC) then + if (do_sno_oc) then mss_cnc_aer_in_fdb(bounds%begc:bounds%endc,:,3) = mss_cnc_ocphi(bounds%begc:bounds%endc,:) mss_cnc_aer_in_fdb(bounds%begc:bounds%endc,:,4) = mss_cnc_ocpho(bounds%begc:bounds%endc,:) endif @@ -546,14 +578,14 @@ subroutine SurfaceAlbedo(bounds,nc, & mss_cnc_aer_in_frc_bc(bounds%begc:bounds%endc,:,6) = mss_cnc_dst2(bounds%begc:bounds%endc,:) mss_cnc_aer_in_frc_bc(bounds%begc:bounds%endc,:,7) = mss_cnc_dst3(bounds%begc:bounds%endc,:) mss_cnc_aer_in_frc_bc(bounds%begc:bounds%endc,:,8) = mss_cnc_dst4(bounds%begc:bounds%endc,:) - if (DO_SNO_OC) then + if (do_sno_oc) then mss_cnc_aer_in_frc_bc(bounds%begc:bounds%endc,:,3) = mss_cnc_ocphi(bounds%begc:bounds%endc,:) mss_cnc_aer_in_frc_bc(bounds%begc:bounds%endc,:,4) = mss_cnc_ocpho(bounds%begc:bounds%endc,:) endif ! BC FORCING CALCULATIONS flg_slr = 1; ! direct-beam - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -567,7 +599,7 @@ subroutine SurfaceAlbedo(bounds,nc, & waterdiagnosticbulk_inst) flg_slr = 2; ! diffuse - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -582,7 +614,7 @@ subroutine SurfaceAlbedo(bounds,nc, & ! 2. OC input array: ! set BC and dust concentrations, so OC_FRC=[(BC+OC+dust)-(BC+dust)] - if (DO_SNO_OC) then + if (do_sno_oc) then mss_cnc_aer_in_frc_oc(bounds%begc:bounds%endc,:,1) = mss_cnc_bcphi(bounds%begc:bounds%endc,:) mss_cnc_aer_in_frc_oc(bounds%begc:bounds%endc,:,2) = mss_cnc_bcpho(bounds%begc:bounds%endc,:) mss_cnc_aer_in_frc_oc(bounds%begc:bounds%endc,:,5) = mss_cnc_dst1(bounds%begc:bounds%endc,:) @@ -592,7 +624,7 @@ subroutine SurfaceAlbedo(bounds,nc, & ! OC FORCING CALCULATIONS flg_slr = 1; ! direct-beam - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -606,7 +638,7 @@ subroutine SurfaceAlbedo(bounds,nc, & waterdiagnosticbulk_inst) flg_slr = 2; ! diffuse - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -624,14 +656,14 @@ subroutine SurfaceAlbedo(bounds,nc, & ! set BC and OC concentrations, so DST_FRC=[(BC+OC+dust)-(BC+OC)] mss_cnc_aer_in_frc_dst(bounds%begc:bounds%endc,:,1) = mss_cnc_bcphi(bounds%begc:bounds%endc,:) mss_cnc_aer_in_frc_dst(bounds%begc:bounds%endc,:,2) = mss_cnc_bcpho(bounds%begc:bounds%endc,:) - if (DO_SNO_OC) then + if (do_sno_oc) then mss_cnc_aer_in_frc_dst(bounds%begc:bounds%endc,:,3) = mss_cnc_ocphi(bounds%begc:bounds%endc,:) mss_cnc_aer_in_frc_dst(bounds%begc:bounds%endc,:,4) = mss_cnc_ocpho(bounds%begc:bounds%endc,:) endif ! DUST FORCING CALCULATIONS flg_slr = 1; ! direct-beam - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -645,7 +677,7 @@ subroutine SurfaceAlbedo(bounds,nc, & waterdiagnosticbulk_inst) flg_slr = 2; ! diffuse - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -661,7 +693,7 @@ subroutine SurfaceAlbedo(bounds,nc, & ! 4. ALL AEROSOL FORCING CALCULATION ! (pure snow albedo) flg_slr = 1; ! direct-beam - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -675,7 +707,7 @@ subroutine SurfaceAlbedo(bounds,nc, & waterdiagnosticbulk_inst) flg_slr = 2; ! diffuse - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -691,7 +723,7 @@ subroutine SurfaceAlbedo(bounds,nc, & ! CLIMATE FEEDBACK CALCULATIONS, ALL AEROSOLS: flg_slr = 1; ! direct-beam - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -705,7 +737,7 @@ subroutine SurfaceAlbedo(bounds,nc, & waterdiagnosticbulk_inst) flg_slr = 2; ! diffuse - call SNICAR_RT(flg_snw_ice, bounds, num_nourbanc, filter_nourbanc, & + call SNICAR_RT(bounds, num_nourbanc, filter_nourbanc, & coszen_col(bounds%begc:bounds%endc), & flg_slr, & h2osno_liq(bounds%begc:bounds%endc, :), & @@ -734,7 +766,7 @@ subroutine SurfaceAlbedo(bounds,nc, & albgrd_bc(c,ib) = albsod(c,ib)*(1.-frac_sno(c)) + albsnd_bc(c,ib)*frac_sno(c) albgri_bc(c,ib) = albsoi(c,ib)*(1.-frac_sno(c)) + albsni_bc(c,ib)*frac_sno(c) - if (DO_SNO_OC) then + if (do_sno_oc) then ! OC forcing albedo albgrd_oc(c,ib) = albsod(c,ib)*(1.-frac_sno(c)) + albsnd_oc(c,ib)*frac_sno(c) albgri_oc(c,ib) = albsoi(c,ib)*(1.-frac_sno(c)) + albsni_oc(c,ib)*frac_sno(c) @@ -1048,6 +1080,37 @@ subroutine SurfaceAlbedo(bounds,nc, & end do end do + ! add output variables for history files + do ib = 1, numrad + do fc = 1,num_nourbanc + c = filter_nourbanc(fc) + if (coszen_col(c) > 0._r8) then + albgrd_hst(c,ib) = albgrd(c,ib) + albgri_hst(c,ib) = albgri(c,ib) + albgrd_pur_hst(c,ib) = albgrd_pur(c,ib) + albgri_pur_hst(c,ib) = albgri_pur(c,ib) + albgrd_bc_hst(c,ib) = albgrd_bc(c,ib) + albgri_bc_hst(c,ib) = albgri_bc(c,ib) + albgrd_oc_hst(c,ib) = albgrd_oc(c,ib) + albgri_oc_hst(c,ib) = albgri_oc(c,ib) + albgrd_dst_hst(c,ib) = albgrd_dst(c,ib) + albgri_dst_hst(c,ib) = albgri_dst(c,ib) + if (h2osno_total(c) > 0._r8) then + albsnd_hst2(c,ib) = albsnd_hst(c,ib) + albsni_hst2(c,ib) = albsni_hst(c,ib) + end if + end if + end do + + do fp = 1,num_nourbanp + p = filter_nourbanp(fp) + if (coszen_patch(p) > 0._r8) then + albd_hst(p,ib) = albd(p,ib) + albi_hst(p,ib) = albi(p,ib) + end if + end do + end do + end associate end subroutine SurfaceAlbedo diff --git a/src/biogeophys/SurfaceAlbedoType.F90 b/src/biogeophys/SurfaceAlbedoType.F90 index cf6b0a518a..a8b645b84a 100644 --- a/src/biogeophys/SurfaceAlbedoType.F90 +++ b/src/biogeophys/SurfaceAlbedoType.F90 @@ -7,7 +7,7 @@ module SurfaceAlbedoType use decompMod , only : bounds_type use clm_varpar , only : numrad, nlevcan, nlevsno use abortutils , only : endrun - use clm_varctl , only : use_SSRE + use clm_varctl , only : use_SSRE, use_snicar_frc ! ! !PUBLIC TYPES: implicit none @@ -35,6 +35,22 @@ module SurfaceAlbedoType real(r8), pointer :: albsoi_col (:,:) ! col soil albedo: diffuse (col,bnd) [frc] real(r8), pointer :: albsnd_hst_col (:,:) ! col snow albedo, direct , for history files (col,bnd) [frc] real(r8), pointer :: albsni_hst_col (:,:) ! col snow albedo, diffuse, for history files (col,bnd) [frc] +! add new snicar output variables for albedo for history files only + real(r8), pointer :: albd_hst_patch (:,:) ! patch surface albedo (direct) for history files (numrad) + real(r8), pointer :: albi_hst_patch (:,:) ! patch surface albedo (diffuse) for history files (numrad) + real(r8), pointer :: albgrd_pur_hst_col (:,:) ! col pure snow ground direct albedo for history files (numrad) + real(r8), pointer :: albgri_pur_hst_col (:,:) ! col pure snow ground diffuse albedo for history files (numrad) + real(r8), pointer :: albgrd_bc_hst_col (:,:) ! col ground direct albedo without BC for history files (numrad) + real(r8), pointer :: albgri_bc_hst_col (:,:) ! col ground diffuse albedo without BC for history files (numrad) + real(r8), pointer :: albgrd_oc_hst_col (:,:) ! col ground direct albedo without OC for history files (numrad) + real(r8), pointer :: albgri_oc_hst_col (:,:) ! col ground diffuse albedo without OC for history files (numrad) + real(r8), pointer :: albgrd_dst_hst_col (:,:) ! col ground direct albedo without dust for history files (numrad) + real(r8), pointer :: albgri_dst_hst_col (:,:) ! col ground diffuse albedo without dust for history files (numrad) + real(r8), pointer :: albgrd_hst_col (:,:) ! col ground albedo (direct) for history files (numrad) + real(r8), pointer :: albgri_hst_col (:,:) ! col ground albedo (diffuse) for history files (numrad) + real(r8), pointer :: albsnd_hst2_col (:,:) ! col snow albedo, direct , for history files (col,bnd) [frc] + real(r8), pointer :: albsni_hst2_col (:,:) ! col snow albedo, diffuse, for history files (col,bnd) [frc] +! end add new snicar real(r8), pointer :: ftdd_patch (:,:) ! patch down direct flux below canopy per unit direct flx (numrad) real(r8), pointer :: ftid_patch (:,:) ! patch down diffuse flux below canopy per unit direct flx (numrad) @@ -157,6 +173,23 @@ subroutine InitAllocate(this, bounds) allocate(this%vcmaxcintsun_patch (begp:endp)) ; this%vcmaxcintsun_patch (:) = nan allocate(this%vcmaxcintsha_patch (begp:endp)) ; this%vcmaxcintsha_patch (:) = nan +! add new snicar output variables for albedo for history files only + allocate(this%albgrd_hst_col (begc:endc,numrad)) ; this%albgrd_hst_col (:,:) = spval + allocate(this%albgri_hst_col (begc:endc,numrad)) ; this%albgri_hst_col (:,:) = spval + allocate(this%albsnd_hst2_col (begc:endc,numrad)) ; this%albsnd_hst2_col (:,:) = spval + allocate(this%albsni_hst2_col (begc:endc,numrad)) ; this%albsni_hst2_col (:,:) = spval + allocate(this%albgrd_pur_hst_col (begc:endc,numrad)) ; this%albgrd_pur_hst_col (:,:) = spval + allocate(this%albgri_pur_hst_col (begc:endc,numrad)) ; this%albgri_pur_hst_col (:,:) = spval + allocate(this%albgrd_bc_hst_col (begc:endc,numrad)) ; this%albgrd_bc_hst_col (:,:) = spval + allocate(this%albgri_bc_hst_col (begc:endc,numrad)) ; this%albgri_bc_hst_col (:,:) = spval + allocate(this%albgrd_oc_hst_col (begc:endc,numrad)) ; this%albgrd_oc_hst_col (:,:) = spval + allocate(this%albgri_oc_hst_col (begc:endc,numrad)) ; this%albgri_oc_hst_col (:,:) = spval + allocate(this%albgrd_dst_hst_col (begc:endc,numrad)) ; this%albgrd_dst_hst_col (:,:) = spval + allocate(this%albgri_dst_hst_col (begc:endc,numrad)) ; this%albgri_dst_hst_col (:,:) = spval + allocate(this%albd_hst_patch (begp:endp,numrad)) ; this%albd_hst_patch (:,:) = spval + allocate(this%albi_hst_patch (begp:endp,numrad)) ; this%albi_hst_patch (:,:) = spval +! end and new snicar + end subroutine InitAllocate !----------------------------------------------------------------------- @@ -188,7 +221,7 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='cosine of solar zenith angle', & ptr_col=this%coszen_col, default='inactive') - this%albgri_col(begc:endc,:) = spval + this%albgrd_col(begc:endc,:) = spval call hist_addfld2d (fname='ALBGRD', units='proportion', type2d='numrad', & avgflag='A', long_name='ground albedo (direct)', & ptr_col=this%albgrd_col, default='inactive') @@ -221,6 +254,82 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='surface albedo (indirect)', & ptr_patch=this%albi_patch, default=defaultoutput, c2l_scale_type='urbanf') +! add new snicar output variables for albedo for history files only + use_snicar_frc_if: if (use_snicar_frc) then + + this%albd_hst_patch(begp:endp,:) = spval + call hist_addfld2d (fname='ALBD_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='surface albedo (direct)', & + ptr_patch=this%albd_hst_patch, default='inactive', c2l_scale_type='urbanf') + + this%albi_hst_patch(begp:endp,:) = spval + call hist_addfld2d (fname='ALBI_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='surface albedo (indirect)', & + ptr_patch=this%albi_hst_patch, default='inactive', c2l_scale_type='urbanf') + + this%albgrd_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRD_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo (direct)', & + ptr_col=this%albgrd_hst_col, default='inactive') + + this%albgri_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRI_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo (indirect)', & + ptr_col=this%albgri_hst_col, default='inactive') + + this%albgrd_pur_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRD_PUR_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo without aerosol in snow (direct)', & + ptr_col=this%albgrd_pur_hst_col, default='inactive') + + this%albgri_pur_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRI_PUR_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo without aerosol in snow (diffuse)', & + ptr_col=this%albgri_pur_hst_col, default='inactive') + + this%albgrd_bc_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRD_BC_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo without BC in snow (direct)', & + ptr_col=this%albgrd_bc_hst_col, default='inactive') + + this%albgri_bc_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRI_BC_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo without BC in snow (diffuse)', & + ptr_col=this%albgri_bc_hst_col, default='inactive') + + this%albgrd_oc_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRD_OC_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo without OC in snow (direct)', & + ptr_col=this%albgrd_oc_hst_col, default='inactive') + + this%albgri_oc_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRI_OC_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo without OC in snow (diffuse)', & + ptr_col=this%albgri_oc_hst_col, default='inactive') + + this%albgrd_dst_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRD_DST_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo without dust in snow (direct)', & + ptr_col=this%albgrd_dst_hst_col, default='inactive') + + this%albgri_dst_hst_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBGRI_DST_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='ground albedo without dust in snow (diffuse)', & + ptr_col=this%albgri_dst_hst_col, default='inactive') + + this%albsnd_hst2_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBSND_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='snow albedo (direct)', & + ptr_col=this%albsnd_hst2_col, default='inactive') + + this%albsni_hst2_col(begc:endc,:) = spval + call hist_addfld2d (fname='ALBSNI_HIST', units='proportion', type2d='numrad', & + avgflag='A', long_name='snow albedo (diffuse)', & + ptr_col=this%albsni_hst2_col, default='inactive') + + end if use_snicar_frc_if +! end add new snicar + end subroutine InitHistory !----------------------------------------------------------------------- @@ -270,7 +379,7 @@ subroutine InitCold(this, bounds) this%ftdd_patch (begp:endp, :) = 1.0_r8 this%ftid_patch (begp:endp, :) = 0.0_r8 this%ftii_patch (begp:endp, :) = 1.0_r8 - + end subroutine InitCold !--------------------------------------------------------------------- @@ -281,7 +390,7 @@ subroutine Restart(this, bounds, ncid, flag, & ! Read/Write module information to/from restart file. ! ! !USES: - use clm_varctl , only : use_snicar_frc, iulog + use clm_varctl , only : use_snicar_frc, iulog use spmdMod , only : masterproc use decompMod , only : bounds_type use abortutils , only : endrun @@ -453,7 +562,7 @@ subroutine Restart(this, bounds, ncid, flag, & this%vcmaxcintsha_patch(begp:endp) = 1._r8 end if - if (use_snicar_frc) then + use_snicar_frc_if: if (use_snicar_frc) then call restartvar(ncid=ncid, flag=flag, varname='albgrd_bc', xtype=ncd_double, & dim1name='column', dim2name='numrad', switchdim=.true., & @@ -543,7 +652,94 @@ subroutine Restart(this, bounds, ncid, flag, & this%albgri_dst_col(begc:endc,:) = this%albgri_col(begc:endc,:) end if - end if ! end of if-use_snicar_frc +! add new snicar output variables for albedo for history files only + + call restartvar(ncid=ncid, flag=flag, varname='albd_hist', xtype=ncd_double, & + dim1name='pft', dim2name='numrad', switchdim=.true., & + long_name='surface albedo (direct) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albd_hst_patch) + + call restartvar(ncid=ncid, flag=flag, varname='albi_hist', xtype=ncd_double, & + dim1name='pft', dim2name='numrad', switchdim=.true., & + long_name='surface albedo (diffuse) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albi_hst_patch) + + call restartvar(ncid=ncid, flag=flag, varname='albgrd_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='ground albedo (direct) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albgrd_hst_col) + + call restartvar(ncid=ncid, flag=flag, varname='albgri_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='ground albedo (indirect) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albgri_hst_col) + + call restartvar(ncid=ncid, flag=flag, varname='albsnd_hst2', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='snow albedo (direct) (0 to 1)', units='proportion', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albsnd_hst2_col) + + call restartvar(ncid=ncid, flag=flag, varname='albsni_hst2', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='snow albedo (diffuse) (0 to 1)', units='proportion', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albsni_hst2_col) + + call restartvar(ncid=ncid, flag=flag, varname='albgrd_bc_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='ground albedo without BC (direct) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp',readvar=readvar, data=this%albgrd_bc_hst_col) + + call restartvar(ncid=ncid, flag=flag, varname='albgri_bc_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='ground albedo without BC (diffuse) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albgri_bc_hst_col) + + call restartvar(ncid=ncid, flag=flag, varname='albgrd_pur_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='pure snow ground albedo (direct) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albgrd_pur_hst_col) + + call restartvar(ncid=ncid, flag=flag, varname='albgri_pur_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='pure snow ground albedo (diffuse) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albgri_pur_hst_col) + + call restartvar(ncid=ncid, flag=flag, varname='albgrd_oc_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='ground albedo without OC (direct) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albgrd_oc_hst_col) + + call restartvar(ncid=ncid, flag=flag, varname='albgri_oc_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='ground albedo without OC (diffuse) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albgri_oc_hst_col) + + call restartvar(ncid=ncid, flag=flag, varname='albgrd_dst_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='ground albedo without dust (direct) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albgrd_dst_hst_col) + + call restartvar(ncid=ncid, flag=flag, varname='albgri_dst_hist', xtype=ncd_double, & + dim1name='column', dim2name='numrad', switchdim=.true., & + long_name='ground albedo without dust (diffuse) (0 to 1)', units='', & + scale_by_thickness=.false., & + interpinic_flag='interp', readvar=readvar, data=this%albgri_dst_hst_col) + + end if use_snicar_frc_if +! end add new snicar ! patch type physical state variable - fabd call restartvar(ncid=ncid, flag=flag, varname='fabd', xtype=ncd_double, & diff --git a/src/biogeophys/SurfaceRadiationMod.F90 b/src/biogeophys/SurfaceRadiationMod.F90 index 5378e6315c..03557c6476 100644 --- a/src/biogeophys/SurfaceRadiationMod.F90 +++ b/src/biogeophys/SurfaceRadiationMod.F90 @@ -477,9 +477,8 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & use clm_varpar , only : numrad, nlevsno use clm_varcon , only : spval use landunit_varcon , only : istsoil, istcrop - use clm_varctl , only : use_subgrid_fluxes, use_snicar_frc, iulog, use_SSRE + use clm_varctl , only : use_subgrid_fluxes, use_snicar_frc, iulog, use_SSRE, do_sno_oc use clm_time_manager , only : get_step_size_real, is_near_local_noon - use SnowSnicarMod , only : DO_SNO_OC use abortutils , only : endrun ! ! !ARGUMENTS: @@ -856,7 +855,7 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & sfc_frc_bc(p) = sabg(p) - sabg_bc(p) ! OC aerosol forcing (patch-level): - if (DO_SNO_OC) then + if (do_sno_oc) then sfc_frc_oc(p) = sabg(p) - sabg_oc(p) else sfc_frc_oc(p) = 0._r8 diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index f2d9317f82..21445caaae 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -647,11 +647,11 @@ subroutine InitCold(this, bounds, & ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 use shr_const_mod , only : SHR_CONST_TKFRZ - use clm_varcon , only : denice, denh2o, sb - use landunit_varcon, only : istwet, istsoil, istdlak, istice + use clm_varcon , only : denice, denh2o + use landunit_varcon, only : istwet, istsoil, istdlak, istice, istcrop use column_varcon , only : icol_road_imperv, icol_roof, icol_sunwall use column_varcon , only : icol_shadewall, icol_road_perv - use clm_varctl , only : iulog, use_vancouver, use_mexicocity + use clm_varctl , only : iulog, use_vancouver, use_mexicocity, use_excess_ice ! ! !ARGUMENTS: class(temperature_type) :: this @@ -742,7 +742,9 @@ subroutine InitCold(this, bounds, & end if else this%t_soisno_col(c,1:nlevgrnd) = 274._r8 - + if (use_excess_ice .and. (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop)) then + this%t_soisno_col(c,1:nlevgrnd) = SHR_CONST_TKFRZ - 5.0_r8 !needs to be below freezing to properly initiate excess ice + end if endif endif end do @@ -1213,6 +1215,17 @@ subroutine InitAccBuffer (this, bounds) call init_accum_field (name='GDD10', units='K', & desc='growing degree-days base 10C from planting', accum_type='runaccum', accum_period=not_used, & subgrid_type='pft', numlev=1, init_value=0._r8) + + ! 20-year running means (20*365 days) + call init_accum_field (name='GDD020', units='K', & + desc='20-year running mean of growing degree days base 0C from planting', accum_type='runmean', accum_period=-20*365, & + subgrid_type='pft', numlev=1, init_value=0._r8) + call init_accum_field (name='GDD820', units='K', & + desc='20-year running mean of growing degree days base 8C from planting', accum_type='runmean', accum_period=-20*365, & + subgrid_type='pft', numlev=1, init_value=0._r8) + call init_accum_field (name='GDD1020', units='K', & + desc='20-year running mean of growing degree days base 10C from planting', accum_type='runmean', accum_period=-20*365, & + subgrid_type='pft', numlev=1, init_value=0._r8) end if @@ -1328,6 +1341,15 @@ subroutine InitAccVars(this, bounds) call extract_accum_field ('GDD10', rbufslp, nstep) this%gdd10_patch(begp:endp) = rbufslp(begp:endp) + call extract_accum_field ('GDD020', rbufslp, nstep) + this%gdd020_patch(begp:endp) = rbufslp(begp:endp) + + call extract_accum_field ('GDD820', rbufslp, nstep) + this%gdd820_patch(begp:endp) = rbufslp(begp:endp) + + call extract_accum_field ('GDD1020', rbufslp, nstep) + this%gdd1020_patch(begp:endp) = rbufslp(begp:endp) + end if deallocate(rbufslp) @@ -1340,7 +1362,7 @@ subroutine UpdateAccVars (this, bounds) ! ! USES use shr_const_mod , only : SHR_CONST_CDAY, SHR_CONST_TKFRZ - use clm_time_manager , only : get_step_size, get_nstep, is_end_curr_day, get_curr_date + use clm_time_manager , only : get_step_size, get_nstep, is_end_curr_day, get_curr_date, is_end_curr_year use accumulMod , only : update_accum_field, extract_accum_field, accumResetVal use CNSharedParamsMod, only : upper_soil_layer ! @@ -1574,6 +1596,16 @@ subroutine UpdateAccVars (this, bounds) call update_accum_field ('GDD10', rbufslp, nstep) call extract_accum_field ('GDD10', this%gdd10_patch, nstep) + ! Accumulate and extract running 20-year means + if (is_end_curr_year()) then + call update_accum_field ('GDD020', this%gdd0_patch, nstep) + call extract_accum_field ('GDD020', this%gdd020_patch, nstep) + call update_accum_field ('GDD820', this%gdd8_patch, nstep) + call extract_accum_field ('GDD820', this%gdd820_patch, nstep) + call update_accum_field ('GDD1020', this%gdd10_patch, nstep) + call extract_accum_field ('GDD1020', this%gdd1020_patch, nstep) + end if + end if deallocate(rbufslp) @@ -1581,4 +1613,25 @@ subroutine UpdateAccVars (this, bounds) end subroutine UpdateAccVars + subroutine Clean(this) + ! + ! !DESCRIPTION: + ! Finalize this instance + ! + ! !USES: + ! + ! !ARGUMENTS: + class(temperature_type), intent(inout) :: this + ! + ! !LOCAL VARIABLES: + + character(len=*), parameter :: subname = 'Clean' + !----------------------------------------------------------------------- + + deallocate(this%gdd020_patch) + deallocate(this%gdd820_patch) + deallocate(this%gdd1020_patch) + + end subroutine Clean + end module TemperatureType diff --git a/src/biogeophys/TotalWaterAndHeatMod.F90 b/src/biogeophys/TotalWaterAndHeatMod.F90 index bfeea81949..885222f33b 100644 --- a/src/biogeophys/TotalWaterAndHeatMod.F90 +++ b/src/biogeophys/TotalWaterAndHeatMod.F90 @@ -358,7 +358,8 @@ subroutine AccumulateSoilLiqIceMassNonLake(bounds, num_c, filter_c, & associate( & h2osoi_ice => waterstate_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice lens (kg/m2) - h2osoi_liq => waterstate_inst%h2osoi_liq_col & ! Input: [real(r8) (:,:) ] liquid water (kg/m2) + h2osoi_liq => waterstate_inst%h2osoi_liq_col , & ! Input: [real(r8) (:,:) ] liquid water (kg/m2) + excess_ice => waterstate_inst%excess_ice_col & ! Input [real(r8) (:,:) ] excess ice (kg/m2) ) do j = 1, nlevmaxurbgrnd @@ -382,7 +383,7 @@ subroutine AccumulateSoilLiqIceMassNonLake(bounds, num_c, filter_c, & if (has_h2o) then liquid_mass(c) = liquid_mass(c) + h2osoi_liq(c,j) - ice_mass(c) = ice_mass(c) + h2osoi_ice(c,j) + ice_mass(c) = ice_mass(c) + h2osoi_ice(c,j) + excess_ice(c,j) end if end do end do diff --git a/src/biogeophys/UrbBuildTempOleson2015Mod.F90 b/src/biogeophys/UrbBuildTempOleson2015Mod.F90 index bf8b68c7eb..4c985f0ab3 100644 --- a/src/biogeophys/UrbBuildTempOleson2015Mod.F90 +++ b/src/biogeophys/UrbBuildTempOleson2015Mod.F90 @@ -383,9 +383,11 @@ subroutine BuildingTemperature (bounds, num_urbanl, filter_urbanl, num_nolakec, ! Get terms from soil temperature equations to compute conduction flux ! Negative is toward surface - heat added - ! Note that the conduction flux here is in W m-2 wall area but for purposes of solving the set of - ! simultaneous equations this must be converted to W m-2 floor area. This is done below when - ! setting up the equation coefficients. + ! Note that the convection and conduction fluxes for the walls are in W m-2 wall area + ! but for purposes of solving the set of simultaneous equations this must be converted to W m-2 + ! floor or roof area. This is done below when setting up the equation coefficients by multiplying by building_hwr. + ! Note also that the longwave radiation terms for the walls are in terms of W m-2 floor area since the view + ! factors implicitly convert from per unit wall area to per unit floor or roof area. do fc = 1,num_nolakec c = filter_nolakec(fc) @@ -424,10 +426,8 @@ subroutine BuildingTemperature (bounds, num_urbanl, filter_urbanl, num_nolakec, ! This view factor implicitly converts from per unit wall area to per unit floor area vf_wf(l) = 0.5_r8*(1._r8 - vf_rf(l)) - ! This view factor implicitly converts from per unit floor area to per unit wall area - vf_fw(l) = vf_wf(l) / building_hwr(l) + vf_fw(l) = vf_wf(l) - ! This view factor implicitly converts from per unit roof area to per unit wall area vf_rw(l) = vf_fw(l) ! This view factor implicitly converts from per unit wall area to per unit roof area @@ -831,7 +831,7 @@ subroutine BuildingTemperature (bounds, num_urbanl, filter_urbanl, num_nolakec, + em_floori(l)*sb*t_floor_bef(l)**4._r8 & + 4._r8*em_floori(l)*sb*t_floor_bef(l)**3.*(t_floor(l) - t_floor_bef(l)) - qrd_building(l) = qrd_roof(l) + building_hwr(l)*(qrd_sunw(l) + qrd_shdw(l)) + qrd_floor(l) + qrd_building(l) = qrd_roof(l) + qrd_sunw(l) + qrd_shdw(l) + qrd_floor(l) if (abs(qrd_building(l)) > .10_r8 ) then write (iulog,*) 'urban inside building net longwave radiation balance error ',qrd_building(l) diff --git a/src/biogeophys/UrbanAlbedoMod.F90 b/src/biogeophys/UrbanAlbedoMod.F90 index 73fd3db08d..2e854a0497 100644 --- a/src/biogeophys/UrbanAlbedoMod.F90 +++ b/src/biogeophys/UrbanAlbedoMod.F90 @@ -156,7 +156,12 @@ subroutine UrbanAlbedo (bounds, num_urbanl, filter_urbanl, & albgri => surfalb_inst%albgri_col , & ! Output: [real(r8) (:,:) ] urban col ground albedo (diffuse) albd => surfalb_inst%albd_patch , & ! Output [real(r8) (:,:) ] urban pft surface albedo (direct) albi => surfalb_inst%albi_patch , & ! Output: [real(r8) (:,:) ] urban pft surface albedo (diffuse) - +! add new snicar albedo output for history files + albd_hst => surfalb_inst%albd_hst_patch , & ! Output: [real(r8) (:,:) ] surface albedo (direct) for history files + albi_hst => surfalb_inst%albi_hst_patch , & ! Output: [real(r8) (:,:) ] surface albedo (diffuse) for history files + albgrd_hst => surfalb_inst%albgrd_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo (direct) for history files + albgri_hst => surfalb_inst%albgri_hst_col , & ! Output: [real(r8) (:,:) ] ground albedo (diffuse) for history files +! end add new snicar begl => bounds%begl , & vf_sr => urbanparams_inst%vf_sr , & ! Input: [real(r8) (:) ] view factor of sky for road vf_sw => urbanparams_inst%vf_sw , & ! Input: [real(r8) (:) ] view factor of sky for one wall @@ -418,12 +423,25 @@ subroutine UrbanAlbedo (bounds, num_urbanl, filter_urbanl, & albgrd(c,ib) = sref_improad_dir(l,ib) albgri(c,ib) = sref_improad_dif(l,ib) endif +! add new snicar albedo variables for history fields + if (coszen(l) > 0._r8) then + albgrd_hst(c,ib) = albgrd(c,ib) + albgri_hst(c,ib) = albgri(c,ib) + end if +! end add new snicar end do do fp = 1,num_urbanp p = filter_urbanp(fp) c = patch%column(p) + l = patch%landunit(p) albd(p,ib) = albgrd(c,ib) albi(p,ib) = albgri(c,ib) +! add new snicar albedo variables for history fields + if (coszen(l) > 0._r8) then + albd_hst(p,ib) = albd(p,ib) + albi_hst(p,ib) = albi(p,ib) + end if +! end add new snicar end do end do end if diff --git a/src/biogeophys/UrbanFluxesMod.F90 b/src/biogeophys/UrbanFluxesMod.F90 index d02433a5bb..74f0d2612d 100644 --- a/src/biogeophys/UrbanFluxesMod.F90 +++ b/src/biogeophys/UrbanFluxesMod.F90 @@ -146,6 +146,7 @@ subroutine UrbanFluxes (bounds, num_nourbanl, filter_nourbanl, real(r8) :: dth(bounds%begl:bounds%endl) ! diff of virtual temp. between ref. height and surface real(r8) :: dqh(bounds%begl:bounds%endl) ! diff of humidity between ref. height and surface real(r8) :: zldis(bounds%begl:bounds%endl) ! reference height "minus" zero displacement height (m) + real(r8) :: zeta_lunit(bounds%begl:bounds%endl) ! landunit-level dimensionless stability parameter real(r8) :: um(bounds%begl:bounds%endl) ! wind speed including the stablity effect (m/s) real(r8) :: obu(bounds%begl:bounds%endl) ! Monin-Obukhov length (m) real(r8) :: taf_numer(bounds%begl:bounds%endl) ! numerator of taf equation (K m/s) @@ -187,7 +188,6 @@ subroutine UrbanFluxes (bounds, num_nourbanl, filter_nourbanl, real(r8) :: wtus_shadewall_unscl(bounds%begl:bounds%endl) ! sensible heat conductance for shadewall (not scaled) (m/s) real(r8) :: wtuq_shadewall_unscl(bounds%begl:bounds%endl) ! latent heat conductance for shadewall (not scaled) (m/s) real(r8) :: wc ! convective velocity (m/s) - real(r8) :: zeta ! dimensionless height used in Monin-Obukhov theory real(r8) :: eflx_sh_grnd_scale(bounds%begp:bounds%endp) ! scaled sensible heat flux from ground (W/m**2) [+ to atm] real(r8) :: qflx_evap_soi_scale(bounds%begp:bounds%endp) ! scaled soil evaporation (mm H2O/s) (+ = to atm) real(r8) :: eflx_wasteheat_roof(bounds%begl:bounds%endl) ! sensible heat flux from urban heating/cooling sources of waste heat for roof (W/m**2) @@ -293,6 +293,7 @@ subroutine UrbanFluxes (bounds, num_nourbanl, filter_nourbanl, forc_hgt_u_patch => frictionvel_inst%forc_hgt_u_patch , & ! Input: [real(r8) (:) ] observational height of wind at patch-level (m) forc_hgt_t_patch => frictionvel_inst%forc_hgt_t_patch , & ! Input: [real(r8) (:) ] observational height of temperature at patch-level (m) zetamax => frictionvel_inst%zetamaxstable , & ! Input: [real(r8) ] max zeta value under stable conditions + zeta => frictionvel_inst%zeta_patch , & ! Output: [real(r8) (:) ] dimensionless stability parameter ram1 => frictionvel_inst%ram1_patch , & ! Output: [real(r8) (:) ] aerodynamical resistance (s/m) u10_clm => frictionvel_inst%u10_clm_patch , & ! Input: [real(r8) (:) ] 10 m height winds (m/s) @@ -652,18 +653,18 @@ subroutine UrbanFluxes (bounds, num_nourbanl, filter_nourbanl, tstar = temp1(l)*dth(l) qstar = temp2(l)*dqh(l) thvstar = tstar*(1._r8+0.61_r8*forc_q(g)) + 0.61_r8*forc_th(g)*qstar - zeta = zldis(l)*vkc*grav*thvstar/(ustar(l)**2*thv_g(l)) + zeta_lunit(l) = zldis(l)*vkc*grav*thvstar/(ustar(l)**2*thv_g(l)) - if (zeta >= 0._r8) then !stable - zeta = min(zetamax,max(zeta,0.01_r8)) + if (zeta_lunit(l) >= 0._r8) then !stable + zeta_lunit(l) = min(zetamax,max(zeta_lunit(l),0.01_r8)) um(l) = max(ur(l),0.1_r8) else !unstable - zeta = max(-100._r8,min(zeta,-0.01_r8)) + zeta_lunit(l) = max(-100._r8,min(zeta_lunit(l),-0.01_r8)) wc = beta(l)*(-grav*ustar(l)*thvstar*zii(l)/thv_g(l))**0.333_r8 um(l) = sqrt(ur(l)*ur(l) + wc*wc) end if - obu(l) = zldis(l)/zeta + obu(l) = zldis(l)/zeta_lunit(l) end do end do ! end iteration @@ -682,7 +683,8 @@ subroutine UrbanFluxes (bounds, num_nourbanl, filter_nourbanl, g = patch%gridcell(p) l = patch%landunit(p) - ram1(p) = ramu(l) !pass value to global variable + ram1(p) = ramu(l) !pass value to global variable + zeta(p) = zeta_lunit(l) !pass value to global variable ! Upward and downward canopy longwave are zero diff --git a/src/biogeophys/WaterDiagnosticBulkType.F90 b/src/biogeophys/WaterDiagnosticBulkType.F90 index 7804fa3746..057062777f 100644 --- a/src/biogeophys/WaterDiagnosticBulkType.F90 +++ b/src/biogeophys/WaterDiagnosticBulkType.F90 @@ -17,7 +17,7 @@ module WaterDiagnosticBulkType use decompMod , only : bounds_type use abortutils , only : endrun use clm_varctl , only : use_cn, iulog, use_luna - use clm_varpar , only : nlevgrnd, nlevsno, nlevcan + use clm_varpar , only : nlevgrnd, nlevsno, nlevcan, nlevsoi use clm_varcon , only : spval use LandunitType , only : lun use ColumnType , only : col @@ -42,12 +42,16 @@ module WaterDiagnosticBulkType real(r8), pointer :: snowdp_col (:) ! col area-averaged snow height (m) real(r8), pointer :: snow_layer_unity_col (:,:) ! value 1 for each snow layer, used for history diagnostics real(r8), pointer :: bw_col (:,:) ! col partial density of water in the snow pack (ice + liquid) [kg/m3] - + real(r8), pointer :: snomelt_accum_col (:) ! accumulated col snow melt for z0m calculation (m H2O) real(r8), pointer :: h2osoi_liq_tot_col (:) ! vertically summed col liquid water (kg/m2) (new) (-nlevsno+1:nlevgrnd) real(r8), pointer :: h2osoi_ice_tot_col (:) ! vertically summed col ice lens (kg/m2) (new) (-nlevsno+1:nlevgrnd) real(r8), pointer :: air_vol_col (:,:) ! col air filled porosity real(r8), pointer :: h2osoi_liqvol_col (:,:) ! col volumetric liquid water content (v/v) real(r8), pointer :: swe_old_col (:,:) ! col initial snow water + real(r8), pointer :: exice_subs_tot_col (:) ! col total subsidence due to excess ice melt (m) + real(r8), pointer :: exice_subs_col (:,:) ! col per layer subsidence due to excess ice melt (m) + real(r8), pointer :: exice_vol_col (:,:) ! col per layer volumetric excess ice content (m3/m3) + real(r8), pointer :: exice_vol_tot_col (:) ! col averaged volumetric excess ice content (m3/m3) real(r8), pointer :: snw_rds_col (:,:) ! col snow grain radius (col,lyr) [m^-6, microns] real(r8), pointer :: snw_rds_top_col (:) ! col snow grain radius (top layer) [m^-6, microns] @@ -104,12 +108,10 @@ module WaterDiagnosticBulkType type, private :: params_type real(r8) :: zlnd ! Momentum roughness length for soil, glacier, wetland (m) + real(r8) :: snw_rds_min ! minimum allowed snow effective radius (also cold "fresh snow" value) [microns] end type params_type type(params_type), private :: params_inst - ! minimum allowed snow effective radius (also "fresh snow" value) [microns] - real(r8), public, parameter :: snw_rds_min = 54.526_r8 - character(len=*), parameter, private :: sourcefile = & __FILE__ !------------------------------------------------------------------------ @@ -132,6 +134,8 @@ subroutine readParams( ncid ) ! Momentum roughness length for soil, glacier, wetland (m) call readNcdioScalar(ncid, 'zlnd', subname, params_inst%zlnd) + ! minimum allowed snow effective radius (also cold "fresh snow" value) [microns] + call readNcdioScalar(ncid, 'snw_rds_min', subname, params_inst%snw_rds_min) end subroutine readParams @@ -166,6 +170,7 @@ subroutine InitBulkAllocate(this, bounds) ! ! !USES: use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) + use clm_varpar , only : nlevmaxurbgrnd ! ! !ARGUMENTS: class(waterdiagnosticbulk_type), intent(inout) :: this @@ -187,6 +192,7 @@ subroutine InitBulkAllocate(this, bounds) allocate(this%snow_depth_col (begc:endc)) ; this%snow_depth_col (:) = nan allocate(this%snow_5day_col (begc:endc)) ; this%snow_5day_col (:) = nan allocate(this%snowdp_col (begc:endc)) ; this%snowdp_col (:) = nan + allocate(this%snomelt_accum_col (begc:endc)) ; this%snomelt_accum_col (:) = nan allocate(this%snow_layer_unity_col (begc:endc,-nlevsno+1:0)) ; this%snow_layer_unity_col (:,:) = nan allocate(this%bw_col (begc:endc,-nlevsno+1:0)) ; this%bw_col (:,:) = nan allocate(this%air_vol_col (begc:endc, 1:nlevgrnd)) ; this%air_vol_col (:,:) = nan @@ -194,6 +200,10 @@ subroutine InitBulkAllocate(this, bounds) allocate(this%h2osoi_ice_tot_col (begc:endc)) ; this%h2osoi_ice_tot_col (:) = nan allocate(this%h2osoi_liq_tot_col (begc:endc)) ; this%h2osoi_liq_tot_col (:) = nan allocate(this%swe_old_col (begc:endc,-nlevsno+1:0)) ; this%swe_old_col (:,:) = nan + allocate(this%exice_subs_tot_col (begc:endc)) ; this%exice_subs_tot_col (:) = 0.0_r8 + allocate(this%exice_subs_col (begc:endc, 1:nlevmaxurbgrnd)) ; this%exice_subs_col (:,:) = 0.0_r8 + allocate(this%exice_vol_col (begc:endc, 1:nlevsoi)) ; this%exice_vol_col (:,:) = 0.0_r8 + allocate(this%exice_vol_tot_col (begc:endc)) ; this%exice_vol_tot_col (:) = 0.0_r8 allocate(this%snw_rds_col (begc:endc,-nlevsno+1:0)) ; this%snw_rds_col (:,:) = nan allocate(this%snw_rds_top_col (begc:endc)) ; this%snw_rds_top_col (:) = nan @@ -233,6 +243,7 @@ subroutine InitBulkHistory(this, bounds) ! !USES: use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) use histFileMod , only : hist_addfld1d, hist_addfld2d, no_snow_normal, no_snow_zero + use clm_varctl , only : use_excess_ice ! ! !ARGUMENTS: class(waterdiagnosticbulk_type), intent(in) :: this @@ -277,8 +288,28 @@ subroutine InitBulkHistory(this, bounds) fname=this%info%fname('TOTSOILICE'), & units='kg/m2', & avgflag='A', & - long_name=this%info%lname('vertically summed soil cie (veg landunits only)'), & + long_name=this%info%lname('vertically summed soil ice (veg landunits only)'), & ptr_col=this%h2osoi_ice_tot_col, l2g_scale_type='veg') + ! excess ice vars + if (use_excess_ice) then + this%exice_vol_tot_col(begc:endc) = 0.0_r8 + call hist_addfld1d ( & + fname=this%info%fname('TOTEXICE_VOL'), & + units='m3/m3', & + avgflag='A', & + l2g_scale_type='veg', & + long_name=this%info%lname('vertically averaged volumetric excess ice concentration (veg landunits only)'), & + ptr_col=this%exice_vol_tot_col) + + this%exice_subs_tot_col(begc:endc) = 0.0_r8 + call hist_addfld1d ( & + fname=this%info%fname('SUBSIDENCE'), & + units='m', & + avgflag='SUM', & + l2g_scale_type='veg', & + long_name=this%info%lname('subsidence due to excess ice melt (veg landunits only)'), & + ptr_col=this%exice_subs_tot_col) + end if this%iwue_ln_patch(begp:endp) = spval call hist_addfld1d ( & @@ -452,6 +483,14 @@ subroutine InitBulkHistory(this, bounds) long_name=this%info%lname('gridcell mean snow height'), & ptr_col=this%snowdp_col, c2l_scale_type='urbanf') + this%snomelt_accum_col(begc:endc) = 0._r8 + call hist_addfld1d ( & ! Have this as an output variable for now to check + fname=this%info%fname('SNOMELT_ACCUM'), & + units='m', & + avgflag='A', & + long_name=this%info%lname('accumulated snow melt for z0'), & + ptr_col=this%snomelt_accum_col, c2l_scale_type='urbanf') + if (use_cn) then this%wf_col(begc:endc) = spval call hist_addfld1d ( & @@ -711,11 +750,11 @@ subroutine InitBulkCold(this, bounds, & do c = bounds%begc,bounds%endc if (snl(c) < 0) then - this%snw_rds_col(c,snl(c)+1:0) = snw_rds_min + this%snw_rds_col(c,snl(c)+1:0) = params_inst%snw_rds_min this%snw_rds_col(c,-nlevsno+1:snl(c)) = 0._r8 - this%snw_rds_top_col(c) = snw_rds_min + this%snw_rds_top_col(c) = params_inst%snw_rds_min elseif (h2osno_input_col(c) > 0._r8) then - this%snw_rds_col(c,0) = snw_rds_min + this%snw_rds_col(c,0) = params_inst%snw_rds_min this%snw_rds_col(c,-nlevsno+1:-1) = 0._r8 this%snw_rds_top_col(c) = spval this%sno_liq_top_col(c) = spval @@ -741,9 +780,10 @@ subroutine RestartBulk(this, bounds, ncid, flag, writing_finidat_interp_dest_fil use spmdMod , only : masterproc use clm_varcon , only : pondmx, watmin, spval, nameg use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall - use clm_varctl , only : bound_h2osoi + use clm_varctl , only : bound_h2osoi, use_excess_ice, nsrest, nsrContinue use ncdio_pio , only : file_desc_t, ncd_io, ncd_double use restUtilMod + use ExcessIceStreamType, only : UseExcessIceStreams ! ! !ARGUMENTS: class(waterdiagnosticbulk_type), intent(inout) :: this @@ -755,6 +795,7 @@ subroutine RestartBulk(this, bounds, ncid, flag, writing_finidat_interp_dest_fil ! ! !LOCAL VARIABLES: logical :: readvar + logical :: excess_ice_on_restart !------------------------------------------------------------------------ @@ -786,6 +827,18 @@ subroutine RestartBulk(this, bounds, ncid, flag, writing_finidat_interp_dest_fil units='m', & interpinic_flag='interp', readvar=readvar, data=this%snow_depth_col) + call restartvar(ncid=ncid, flag=flag, & + varname=this%info%fname('SNOMELT_ACCUM'), & + xtype=ncd_double, & + dim1name='column', & + long_name=this%info%lname('accumulated snow melt for z0'), & + units='m', & + interpinic_flag='interp', readvar=readvar, data=this%snomelt_accum_col) + if (flag == 'read' .and. .not. readvar) then + ! initial run, not restart: initialize snomelt_accum_col to zero + this%snomelt_accum_col(bounds%begc:bounds%endc) = 0._r8 + endif + call restartvar(ncid=ncid, flag=flag, & varname=this%info%fname('frac_sno_eff'), & xtype=ncd_double, & @@ -875,7 +928,52 @@ subroutine RestartBulk(this, bounds, ncid, flag, writing_finidat_interp_dest_fil interpinic_flag='interp', readvar=readvar, data=this%wf_col) end if - + if (.not. use_excess_ice) then + ! no need to even define the restart vars + this%exice_subs_tot_col(bounds%begc:bounds%endc)=0.0_r8 + this%exice_vol_tot_col(bounds%begc:bounds%endc)=0.0_r8 + this%exice_subs_col(bounds%begc:bounds%endc,1:nlevgrnd)=0.0_r8 + this%exice_vol_col(bounds%begc:bounds%endc,1:nlevsoi)=0.0_r8 + else + ! initialization of these to zero is ok, since they might not be in the restart file + this%exice_subs_col(bounds%begc:bounds%endc,1:nlevgrnd)=0.0_r8 + this%exice_vol_col(bounds%begc:bounds%endc,1:nlevsoi)=0.0_r8 + call RestartExcessIceIssue( & + ncid = ncid, & + flag = flag, & + excess_ice_on_restart = excess_ice_on_restart) + ! have to at least define them + call restartvar(ncid=ncid, flag=flag, varname=this%info%fname('SUBSIDENCE'), & + dim1name='column', xtype=ncd_double, & + long_name=this%info%lname('vertically summed volumetric excess ice concentration (veg landunits only)'), & + units='m', & + interpinic_flag='interp', readvar=readvar, data=this%exice_subs_tot_col) + if (flag == 'read' .and. ((.not. readvar) .or. (.not. excess_ice_on_restart)) ) then ! when reading restart that does not have excess ice in it + if (nsrest == nsrContinue) then + call endrun(msg = "On a continue run, excess ice fields MUST be on the restart file "// & + errMsg(sourcefile, __LINE__)) + else if ( .not. UseExcessIceStreams() )then + call endrun(msg = "This input initial conditions file does NOT include excess ice fields" // & + ", and use_excess_ice_streams is off, one or the other needs to be changed "// & + errMsg(sourcefile, __LINE__)) + end if + this%exice_subs_tot_col(bounds%begc:bounds%endc)=0.0_r8 + this%exice_vol_tot_col(bounds%begc:bounds%endc)=0.0_r8 + this%exice_subs_col(bounds%begc:bounds%endc,1:nlevgrnd)=0.0_r8 + this%exice_vol_col(bounds%begc:bounds%endc,1:nlevsoi)=0.0_r8 + endif + call restartvar(ncid=ncid, flag=flag, varname=this%info%fname('TOTEXICE_VOL'), & + dim1name='column', xtype=ncd_double, & + long_name=this%info%lname('vertically averaged volumetric excess ice concentration (veg landunits only)'), & + units='m3/m3', & + interpinic_flag='interp', readvar=readvar, data=this%exice_vol_tot_col) + if (flag == 'read' .and. ((.not. readvar) .or. (.not. excess_ice_on_restart)) ) then ! when reading restart that does not have excess ice in it + if (nsrest == nsrContinue) then + call endrun(msg = "On a continue run, excess ice fields MUST be on the restart file "// & + errMsg(sourcefile, __LINE__)) + end if + end if + endif end subroutine RestartBulk @@ -981,7 +1079,8 @@ subroutine Summary(this, bounds, & ! Compute end-of-timestep summaries of water diagnostic terms ! ! !USES: - use clm_varpar , only : nlevsoi + use clm_varcon , only : denice + use landunit_varcon, only : istsoil, istcrop ! !ARGUMENTS: class(waterdiagnosticbulk_type) , intent(inout) :: this type(bounds_type) , intent(in) :: bounds @@ -997,6 +1096,8 @@ subroutine Summary(this, bounds, & ! !LOCAL VARIABLES: integer :: fp, p, j, l, fc, c ! Indices real(r8):: fracl ! fraction of soil layer contributing to 10cm total soil water + real(r8):: dz_ext ! extended layer thickness due to excess ice + real(r8):: dz_tot ! total depth with extended thicknesses character(len=*), parameter :: subname = 'Summary' !----------------------------------------------------------------------- @@ -1006,10 +1107,15 @@ subroutine Summary(this, bounds, & h2osoi_ice => waterstate_inst%h2osoi_ice_col, & ! Output: [real(r8) (:,:) ] ice lens (kg/m2) h2osoi_liq => waterstate_inst%h2osoi_liq_col, & ! Output: [real(r8) (:,:) ] liquid water (kg/m2) + excess_ice => waterstate_inst%excess_ice_col, & ! Input: [real(r8) (:,:) ] excess ice lenses (kg/m2) (new) (1:nlevgrnd) + exice_subs_col => this%exice_subs_col , & ! Output: [real(r8) (:,:) ] per layer subsidence due to excess ice melt (m) + exice_vol_col => this%exice_vol_col , & ! Output: [real(r8) (:,:) ] per layer volumetric excess ice content (m3/m3) h2osoi_ice_tot => this%h2osoi_ice_tot_col , & ! Output: [real(r8) (:) ] vertically summed ice lens (kg/m2) h2osoi_liq_tot => this%h2osoi_liq_tot_col , & ! Output: [real(r8) (:) ] vertically summed liquid water (kg/m2) - h2osoi_liqice_10cm => this%h2osoi_liqice_10cm_col & ! Output: [real(r8) (:) ] liquid water + ice lens in top 10cm of soil (kg/m2) + h2osoi_liqice_10cm => this%h2osoi_liqice_10cm_col , & ! Output: [real(r8) (:) ] liquid water + ice lens in top 10cm of soil (kg/m2) + exice_subs_tot_col => this%exice_subs_tot_col , & ! Output [real(r8) (:) ] vertically summed subsidence due to excess ice melt (m) + exice_vol_tot_col => this%exice_vol_tot_col & ! Output [real(r8) (:) ] vertically averaged volumetric excess ice content (m3/m3) ) call this%waterdiagnostic_type%Summary(bounds, & @@ -1042,6 +1148,7 @@ subroutine Summary(this, bounds, & h2osoi_liqice_10cm(c) = 0.0_r8 h2osoi_liq_tot(c) = 0._r8 h2osoi_ice_tot(c) = 0._r8 + exice_subs_tot_col(c) = 0._r8 end if end do do j = 1, nlevsoi @@ -1064,10 +1171,31 @@ subroutine Summary(this, bounds, & end if h2osoi_liq_tot(c) = h2osoi_liq_tot(c) + h2osoi_liq(c,j) h2osoi_ice_tot(c) = h2osoi_ice_tot(c) + h2osoi_ice(c,j) + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + exice_subs_tot_col(c) = exice_subs_tot_col(c) + exice_subs_col(c,j) + endif end if end do end do + do fc = 1, num_nolakec ! extra loop needed since the one above has outer loop with layers + c = filter_nolakec(fc) + l = col%landunit(c) + if (.not. lun%urbpoi(l)) then + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + dz_tot = 0.0_r8 + exice_vol_tot_col(c)=0.0_r8 + do j = 1, nlevsoi + dz_ext = dz(c,j)+excess_ice(c,j)/denice + exice_vol_col(c,j)=excess_ice(c,j)/(denice*dz_ext) + dz_tot=dz_tot+dz_ext + exice_vol_tot_col(c)=exice_vol_tot_col(c)+exice_vol_col(c,j)*dz_ext ! (m) + enddo + exice_vol_tot_col(c)=exice_vol_tot_col(c)/dz_tot ! (m3/m3) + end if + end if + end do + end associate end subroutine Summary @@ -1108,7 +1236,7 @@ subroutine ResetBulk(this, column) integer , intent(in) :: column ! column index !----------------------------------------------------------------------- - this%snw_rds_col(column,0) = snw_rds_min + this%snw_rds_col(column,0) = params_inst%snw_rds_min end subroutine ResetBulk diff --git a/src/biogeophys/WaterDiagnosticType.F90 b/src/biogeophys/WaterDiagnosticType.F90 index 7fa76b42b0..57be0e62af 100644 --- a/src/biogeophys/WaterDiagnosticType.F90 +++ b/src/biogeophys/WaterDiagnosticType.F90 @@ -164,13 +164,7 @@ subroutine InitHistory(this, bounds) begc = bounds%begc; endc= bounds%endc begg = bounds%begg; endg= bounds%endg - this%h2ocan_patch(begp:endp) = spval - call hist_addfld1d ( & - fname=this%info%fname('H2OCAN'), & - units='mm', & - avgflag='A', & - long_name=this%info%lname('intercepted water'), & - ptr_patch=this%h2ocan_patch) + this%h2osoi_liqice_10cm_col(begc:endc) = spval call hist_addfld1d ( & @@ -205,8 +199,15 @@ subroutine InitHistory(this, bounds) long_name=this%info%lname('2m specific humidity'), & ptr_patch=this%q_ref2m_patch) + this%h2ocan_patch(begp:endp) = spval + call hist_addfld1d ( & + fname=this%info%fname('H2OCAN'), & + units='mm', & + avgflag='A', & + long_name=this%info%lname('intercepted water'), & + ptr_patch=this%h2ocan_patch) - + ! Snow properties - these will be vertically averaged over the snow profile this%snowliq_col(begc:endc) = spval diff --git a/src/biogeophys/WaterStateBulkType.F90 b/src/biogeophys/WaterStateBulkType.F90 index 5c0298c8d5..ba3f0513c5 100644 --- a/src/biogeophys/WaterStateBulkType.F90 +++ b/src/biogeophys/WaterStateBulkType.F90 @@ -47,17 +47,17 @@ module WaterStateBulkType !------------------------------------------------------------------------ subroutine InitBulk(this, bounds, info, vars, & - h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer) + h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename) class(waterstatebulk_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds class(water_info_base_type), intent(in), target :: info type(water_tracer_container_type), intent(inout) :: vars real(r8) , intent(in) :: h2osno_input_col(bounds%begc:) - real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) + real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) - logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run - + logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run + character(len=*) , intent(in) :: NLFilename ! Namelist filename call this%Init(bounds = bounds, & info = info, & @@ -65,7 +65,8 @@ subroutine InitBulk(this, bounds, info, vars, & h2osno_input_col = h2osno_input_col, & watsat_col = watsat_col, & t_soisno_col = t_soisno_col, & - use_aquifer_layer = use_aquifer_layer) + use_aquifer_layer = use_aquifer_layer, & + NLFilename = NLFilename) call this%InitBulkAllocate(bounds) @@ -187,7 +188,7 @@ end subroutine InitBulkCold !------------------------------------------------------------------------ subroutine RestartBulk(this, bounds, ncid, flag, & - watsat_col) + watsat_col, t_soisno_col, altmax_lastyear_indx) ! ! !DESCRIPTION: ! Read/Write module information to/from restart file. @@ -199,9 +200,11 @@ subroutine RestartBulk(this, bounds, ncid, flag, & ! !ARGUMENTS: class(waterstatebulk_type), intent(in) :: this type(bounds_type), intent(in) :: bounds - type(file_desc_t), intent(inout) :: ncid ! netcdf id - character(len=*) , intent(in) :: flag ! 'read' or 'write' - real(r8) , intent(in) :: watsat_col (bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) + type(file_desc_t), intent(inout) :: ncid ! netcdf id + character(len=*) , intent(in) :: flag ! 'read' or 'write' + real(r8) , intent(in) :: watsat_col (bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) + real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) + integer , intent(in) :: altmax_lastyear_indx(bounds%begc:) !col active layer index last year ! ! !LOCAL VARIABLES: integer :: c,l,j @@ -211,7 +214,9 @@ subroutine RestartBulk(this, bounds, ncid, flag, & SHR_ASSERT_ALL_FL((ubound(watsat_col) == (/bounds%endc,nlevmaxurbgrnd/)) , sourcefile, __LINE__) call this%restart (bounds, ncid, flag=flag, & - watsat_col=watsat_col(bounds%begc:bounds%endc,:)) + watsat_col=watsat_col(bounds%begc:bounds%endc,:), & + t_soisno_col=t_soisno_col(bounds%begc:, -nlevsno+1:), & + altmax_lastyear_indx=altmax_lastyear_indx(bounds%begc:)) call restartvar(ncid=ncid, flag=flag, & diff --git a/src/biogeophys/WaterStateType.F90 b/src/biogeophys/WaterStateType.F90 index f61a7b943d..cdbefa2a04 100644 --- a/src/biogeophys/WaterStateType.F90 +++ b/src/biogeophys/WaterStateType.F90 @@ -13,8 +13,9 @@ module WaterStateType use abortutils , only : endrun use decompMod , only : bounds_type use decompMod , only : subgrid_level_patch, subgrid_level_column, subgrid_level_gridcell - use clm_varctl , only : use_bedrock, iulog - use clm_varctl , only : use_fates_planthydro + use clm_varctl , only : use_bedrock, use_excess_ice, iulog + use spmdMod , only : masterproc + use clm_varctl , only : use_fates use clm_varpar , only : nlevgrnd, nlevsoi, nlevurb, nlevmaxurbgrnd, nlevsno use clm_varcon , only : spval use LandunitType , only : lun @@ -22,6 +23,7 @@ module WaterStateType use WaterInfoBaseType, only : water_info_base_type use WaterTracerContainerType, only : water_tracer_container_type use WaterTracerUtils, only : AllocateVar1d, AllocateVar2d + use ExcessIceStreamType, only : excessicestream_type, UseExcessIceStreams ! implicit none save @@ -46,10 +48,15 @@ module WaterStateType ! For the following dynbal baseline variables: positive values are subtracted to ! avoid counting liquid water content of "virtual" states; negative values are added ! to account for missing states in the model. - real(r8), pointer :: dynbal_baseline_liq_col(:) ! baseline liquid water content subtracted from each column's total liquid water calculation (mm H2O) - real(r8), pointer :: dynbal_baseline_ice_col(:) ! baseline ice content subtracted from each column's total ice calculation (mm H2O) + real(r8), pointer :: dynbal_baseline_liq_col(:) ! baseline liquid water content subtracted from each column's total liquid water calculation (mm H2O) + real(r8), pointer :: dynbal_baseline_ice_col(:) ! baseline ice content subtracted from each column's total ice calculation (mm H2O) - real(r8) :: aquifer_water_baseline ! baseline value for water in the unconfined aquifer (wa_col) for this bulk / tracer (mm) + real(r8) :: aquifer_water_baseline ! baseline value for water in the unconfined aquifer (wa_col) for this bulk / tracer (mm) + + real(r8), pointer :: excess_ice_col (:,:) ! col excess ice (kg/m2) (new) (-nlevsno+1:nlevgrnd) + real(r8), pointer :: exice_bulk_init (:) ! inital value for excess ice (new) (unitless) + + type(excessicestream_type), private :: exicestream ! stream type for excess ice initialization NUOPC only contains @@ -72,7 +79,7 @@ module WaterStateType !------------------------------------------------------------------------ subroutine Init(this, bounds, info, tracer_vars, & - h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer) + h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename) class(waterstate_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds @@ -82,18 +89,19 @@ subroutine Init(this, bounds, info, tracer_vars, & real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run + character(len=*) , intent(in) :: NLFilename ! Namelist filename this%info => info call this%InitAllocate(bounds, tracer_vars) call this%InitHistory(bounds, use_aquifer_layer) - call this%InitCold(bounds = bounds, & - h2osno_input_col = h2osno_input_col, & - watsat_col = watsat_col, & - t_soisno_col = t_soisno_col, & - use_aquifer_layer = use_aquifer_layer) + h2osno_input_col = h2osno_input_col, & + watsat_col = watsat_col, & + t_soisno_col = t_soisno_col, & + use_aquifer_layer = use_aquifer_layer, & + NLFilename = NLFilename) end subroutine Init @@ -150,6 +158,14 @@ subroutine InitAllocate(this, bounds, tracer_vars) call AllocateVar1d(var = this%dynbal_baseline_ice_col, name = 'dynbal_baseline_ice_col', & container = tracer_vars, & bounds = bounds, subgrid_level = subgrid_level_column) + !excess ice vars + call AllocateVar2d(var = this%excess_ice_col, name = 'excess_ice_col', & + container = tracer_vars, & + bounds = bounds, subgrid_level = subgrid_level_column, & + dim2beg = -nlevsno+1, dim2end = nlevmaxurbgrnd) + call AllocateVar1d(var = this%exice_bulk_init, name = 'exice_bulk_init', & + container = tracer_vars, & + bounds = bounds, subgrid_level = subgrid_level_column) end subroutine InitAllocate @@ -268,6 +284,15 @@ subroutine InitHistory(this, bounds, use_aquifer_layer) ptr_col=this%wa_col, l2g_scale_type='veg') end if + ! Add excess ice fields to history + + if (use_excess_ice) then + data2dptr => this%excess_ice_col(begc:endc,1:nlevsoi) + call hist_addfld2d (fname='EXCESS_ICE', units='kg/m2', type2d='levsoi', & + avgflag='A', long_name='excess soil ice (vegetated landunits only)', & + ptr_col=this%excess_ice_col, l2g_scale_type='veg', default = 'inactive') + end if + ! (rgk 02-02-2017) There is intentionally no entry here for stored plant water ! I think that since the value is zero in all cases except ! for FATES plant hydraulics, it will be confusing for users @@ -281,7 +306,7 @@ end subroutine InitHistory !----------------------------------------------------------------------- subroutine InitCold(this, bounds, & - h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer) + h2osno_input_col, watsat_col, t_soisno_col, use_aquifer_layer, NLFilename) ! ! !DESCRIPTION: ! Initialize time constant variables and cold start conditions @@ -290,20 +315,22 @@ subroutine InitCold(this, bounds, & use shr_const_mod , only : SHR_CONST_TKFRZ use landunit_varcon , only : istwet, istsoil, istcrop, istice use column_varcon , only : icol_road_perv, icol_road_imperv - use clm_varcon , only : denice, denh2o, bdsno + use clm_varcon , only : denice, denh2o, bdsno , zisoi use clm_varcon , only : tfrz, aquifer_water_baseline + use initVerticalMod , only : find_soil_layer_containing_depth ! ! !ARGUMENTS: class(waterstate_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds real(r8) , intent(in) :: h2osno_input_col(bounds%begc:) - real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) + real(r8) , intent(in) :: watsat_col(bounds%begc:, 1:) ! volumetric soil water at saturation (porosity) real(r8) , intent(in) :: t_soisno_col(bounds%begc:, -nlevsno+1:) ! col soil temperature (Kelvin) - logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run + logical , intent(in) :: use_aquifer_layer ! whether an aquifer layer is used in this run + character(len=*) , intent(in) :: NLFilename ! Namelist filename ! ! !LOCAL VARIABLES: - integer :: c,j,l,nlevs - integer :: nbedrock + integer :: c,j,l,nlevs,g + integer :: nbedrock, n05m ! layer containing 0.5 m real(r8) :: ratio !----------------------------------------------------------------------- @@ -340,7 +367,7 @@ subroutine InitCold(this, bounds, & if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then nlevs = nlevgrnd do j = 1, nlevs - if (use_bedrock) then + if (use_bedrock .and. col%nbedrock(c) <=nlevsoi) then nbedrock = col%nbedrock(c) else nbedrock = nlevsoi @@ -348,7 +375,7 @@ subroutine InitCold(this, bounds, & if (j > nbedrock) then this%h2osoi_vol_col(c,j) = 0.0_r8 else - if(use_fates_planthydro) then + if(use_fates) then this%h2osoi_vol_col(c,j) = 0.75_r8*watsat_col(c,j)*ratio else this%h2osoi_vol_col(c,j) = 0.15_r8*ratio @@ -505,39 +532,91 @@ subroutine InitCold(this, bounds, & this%dynbal_baseline_liq_col(bounds%begc:bounds%endc) = 0._r8 this%dynbal_baseline_ice_col(bounds%begc:bounds%endc) = 0._r8 + !Initialize excess ice + if (use_excess_ice .and. NLFilename /= '') then + ! enforce initialization with 0 for everything + this%excess_ice_col(bounds%begc:bounds%endc,-nlevsno+1:nlevmaxurbgrnd)=0.0_r8 + this%exice_bulk_init(bounds%begc:bounds%endc)=0.0_r8 + call this%exicestream%Init(bounds, NLFilename) ! get initial fraction of excess ice per column + ! + ! If excess ice is being read from streams, use the streams to + ! initialize + ! + if ( UseExcessIceStreams() )then + call this%exicestream%CalcExcessIce(bounds, this%exice_bulk_init) + do c = bounds%begc,bounds%endc + g = col%gridcell(c) + l = col%landunit(c) + if (.not. lun%lakpoi(l)) then !not lake + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + if (zisoi(nlevsoi) >= 0.5_r8) then + call find_soil_layer_containing_depth(0.5_r8,n05m) + else + n05m=nlevsoi-1 + endif + if (use_bedrock .and. col%nbedrock(c) <=nlevsoi) then + nbedrock = col%nbedrock(c) + else + nbedrock = nlevsoi + endif + do j = 2, nlevmaxurbgrnd ! ignore first layer + if (n05m= n05m .and. jnlevsoi) then + nbedrock = col%nbedrock(c) + else + nbedrock = nlevsoi + end if + do j = 2, nlevmaxurbgrnd ! ignore first layer + if(altmax_lastyear_indx(c) < nbedrock) then + if (j>altmax_lastyear_indx(c) .and. j shr_kind_r8 use unittestTimeManagerMod, only : unittest_timemgr_setup, unittest_timemgr_teardown diff --git a/src/biogeophys/test/Daylength_test/CMakeLists.txt b/src/biogeophys/test/Daylength_test/CMakeLists.txt index 6182551580..2e5cb58cf9 100644 --- a/src/biogeophys/test/Daylength_test/CMakeLists.txt +++ b/src/biogeophys/test/Daylength_test/CMakeLists.txt @@ -2,7 +2,6 @@ set (pfunit_sources test_daylength.pf test_compute_max_daylength.pf) -create_pFUnit_test(Daylength test_Daylength_exe - "${pfunit_sources}" "") - -target_link_libraries(test_Daylength_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(Daylength + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/Daylength_test/test_compute_max_daylength.pf b/src/biogeophys/test/Daylength_test/test_compute_max_daylength.pf index 3c8d080128..ac06b8f0c7 100644 --- a/src/biogeophys/test/Daylength_test/test_compute_max_daylength.pf +++ b/src/biogeophys/test/Daylength_test/test_compute_max_daylength.pf @@ -2,7 +2,7 @@ module test_compute_max_daylength ! Tests of DaylengthMod: ComputeMaxDaylength - use pfunit_mod + use funit use DaylengthMod, only: ComputeMaxDaylength, daylength use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod, only : unittest_subgrid_teardown, bounds diff --git a/src/biogeophys/test/Daylength_test/test_daylength.pf b/src/biogeophys/test/Daylength_test/test_daylength.pf index b326a1190a..0445832b40 100644 --- a/src/biogeophys/test/Daylength_test/test_daylength.pf +++ b/src/biogeophys/test/Daylength_test/test_daylength.pf @@ -2,7 +2,7 @@ module test_daylength ! Tests of the daylength function in DaylengthMod - use pfunit_mod + use funit use shr_kind_mod , only : r8 => shr_kind_r8 use shr_const_mod, only : SHR_CONST_PI diff --git a/src/biogeophys/test/HumanStress_test/CMakeLists.txt b/src/biogeophys/test/HumanStress_test/CMakeLists.txt index d2583a3a47..d23fcb8f71 100644 --- a/src/biogeophys/test/HumanStress_test/CMakeLists.txt +++ b/src/biogeophys/test/HumanStress_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(humanstress test_humanstress_exe - "test_humanstress.pf" "") - -target_link_libraries(test_humanstress_exe clm csm_share) +add_pfunit_ctest(humanstress + TEST_SOURCES "test_humanstress.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/biogeophys/test/HumanStress_test/test_humanstress.pf b/src/biogeophys/test/HumanStress_test/test_humanstress.pf index a6cf85bbdd..5a51cb4689 100644 --- a/src/biogeophys/test/HumanStress_test/test_humanstress.pf +++ b/src/biogeophys/test/HumanStress_test/test_humanstress.pf @@ -2,7 +2,7 @@ module test_humanstress ! Tests of the humanstress functions in HumanIndexMod - use pfunit_mod + use funit use shr_kind_mod , only : r8 => shr_kind_r8 use HumanIndexMod diff --git a/src/biogeophys/test/Irrigation_test/CMakeLists.txt b/src/biogeophys/test/Irrigation_test/CMakeLists.txt index 7a1b7cc5ee..8cb531ba1a 100644 --- a/src/biogeophys/test/Irrigation_test/CMakeLists.txt +++ b/src/biogeophys/test/Irrigation_test/CMakeLists.txt @@ -1,12 +1,6 @@ set (pfunit_sources test_irrigation.pf) -# extra sources used for this test, which are not .pf files -# (currently none) -set (extra_sources - ) - -create_pFUnit_test(irrigation test_irrigation_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_irrigation_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(irrigation + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/Irrigation_test/test_irrigation.pf b/src/biogeophys/test/Irrigation_test/test_irrigation.pf index e810d39ddb..d83fc94329 100644 --- a/src/biogeophys/test/Irrigation_test/test_irrigation.pf +++ b/src/biogeophys/test/Irrigation_test/test_irrigation.pf @@ -2,7 +2,7 @@ module test_irrigation ! Tests of IrrigationMod - use pfunit_mod + use funit use unittestSubgridMod use unittestTimeManagerMod, only : unittest_timemgr_setup, unittest_timemgr_teardown use unittestTimeManagerMod, only : unittest_timemgr_set_curr_date diff --git a/src/biogeophys/test/Photosynthesis_test/CMakeLists.txt b/src/biogeophys/test/Photosynthesis_test/CMakeLists.txt index 149ec47c1d..29810cbd9d 100644 --- a/src/biogeophys/test/Photosynthesis_test/CMakeLists.txt +++ b/src/biogeophys/test/Photosynthesis_test/CMakeLists.txt @@ -1,7 +1,6 @@ set (pfunit_sources test_Photosynthesis.pf) -create_pFUnit_test(Photosynthesis test_Photosynthesis_exe - "${pfunit_sources}" "") - -target_link_libraries(test_Photosynthesis_exe clm csm_share esmf_wrf_timemgr) +add_pfunit_ctest(Photosynthesis + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/Photosynthesis_test/test_Photosynthesis.pf b/src/biogeophys/test/Photosynthesis_test/test_Photosynthesis.pf index 74ca9c9c4c..faa506a99f 100644 --- a/src/biogeophys/test/Photosynthesis_test/test_Photosynthesis.pf +++ b/src/biogeophys/test/Photosynthesis_test/test_Photosynthesis.pf @@ -2,7 +2,7 @@ module test_Photosynthesis ! Tests of PhotosynthesisMod.F90 - use pfunit_mod + use funit use PhotosynthesisMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod, only : unittest_subgrid_teardown, bounds diff --git a/src/biogeophys/test/SnowHydrology_test/CMakeLists.txt b/src/biogeophys/test/SnowHydrology_test/CMakeLists.txt index cfd1e3a4bb..600356b2ff 100644 --- a/src/biogeophys/test/SnowHydrology_test/CMakeLists.txt +++ b/src/biogeophys/test/SnowHydrology_test/CMakeLists.txt @@ -3,7 +3,6 @@ set (pfunit_sources test_SnowHydrology_newSnowBulkDensity.pf test_SnowHydrology_SnowCappingExcess.pf) -create_pFUnit_test(SnowHydrology test_SnowHydrology_exe - "${pfunit_sources}" "") - -target_link_libraries(test_SnowHydrology_exe clm csm_share esmf_wrf_timemgr) +add_pfunit_ctest(SnowHydrology + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_SnowCappingExcess.pf b/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_SnowCappingExcess.pf index 5f42d37226..65448a9207 100644 --- a/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_SnowCappingExcess.pf +++ b/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_SnowCappingExcess.pf @@ -2,7 +2,7 @@ module test_SnowHydrology_SnowCappingExcess ! Tests of SnowHydrologyMod: SnowCappingExcess - use pfunit_mod + use funit use SnowHydrologyMod use TopoMod, only : topo_type use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_initSnowLayers.pf b/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_initSnowLayers.pf index ab1ab810fb..dc5afcab78 100644 --- a/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_initSnowLayers.pf +++ b/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_initSnowLayers.pf @@ -2,7 +2,7 @@ module test_SnowHydrology_initSnowLayers ! Tests of SnowHydrologyMod: initSnowLayers - use pfunit_mod + use funit use SnowHydrologyMod use shr_kind_mod, only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_newSnowBulkDensity.pf b/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_newSnowBulkDensity.pf index db8c7ba980..e3df17b9a0 100644 --- a/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_newSnowBulkDensity.pf +++ b/src/biogeophys/test/SnowHydrology_test/test_SnowHydrology_newSnowBulkDensity.pf @@ -2,7 +2,7 @@ module test_SnowHydrology_newSnowBulkDensity ! Tests of SnowHydrologyMod: newSnowBulkDensity - use pfunit_mod + use funit use unittestSubgridMod use SnowHydrologyMod use atm2lndType , only : atm2lnd_type diff --git a/src/biogeophys/test/TotalWaterAndHeat_test/CMakeLists.txt b/src/biogeophys/test/TotalWaterAndHeat_test/CMakeLists.txt index 67f2a4812e..a82532f69b 100644 --- a/src/biogeophys/test/TotalWaterAndHeat_test/CMakeLists.txt +++ b/src/biogeophys/test/TotalWaterAndHeat_test/CMakeLists.txt @@ -1,12 +1,6 @@ set (pfunit_sources test_total_water_and_heat.pf) -# extra sources used for this test, which are not .pf files -# (currently none) -set (extra_sources - ) - -create_pFUnit_test(total_water_and_heat test_total_water_and_heat_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_total_water_and_heat_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(total_water_and_heat + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/TotalWaterAndHeat_test/test_total_water_and_heat.pf b/src/biogeophys/test/TotalWaterAndHeat_test/test_total_water_and_heat.pf index 05cb5eed57..88f9dae5b2 100644 --- a/src/biogeophys/test/TotalWaterAndHeat_test/test_total_water_and_heat.pf +++ b/src/biogeophys/test/TotalWaterAndHeat_test/test_total_water_and_heat.pf @@ -2,7 +2,7 @@ module test_total_water_and_heat ! Tests of TotalWaterAndHeatMod - use pfunit_mod + use funit use TotalWaterAndHeatMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/biogeophys/test/WaterTracerContainerType_test/CMakeLists.txt b/src/biogeophys/test/WaterTracerContainerType_test/CMakeLists.txt index 10bb931346..283906a442 100644 --- a/src/biogeophys/test/WaterTracerContainerType_test/CMakeLists.txt +++ b/src/biogeophys/test/WaterTracerContainerType_test/CMakeLists.txt @@ -1,12 +1,6 @@ set (pfunit_sources test_water_tracer_container.pf) -# extra sources used for this test, which are not .pf files -# (currently none) -set (extra_sources - ) - -create_pFUnit_test(water_tracer_container test_water_tracer_container_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_water_tracer_container_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(water_tracer_container + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/WaterTracerContainerType_test/test_water_tracer_container.pf b/src/biogeophys/test/WaterTracerContainerType_test/test_water_tracer_container.pf index 58bee7792b..ce37c896a1 100644 --- a/src/biogeophys/test/WaterTracerContainerType_test/test_water_tracer_container.pf +++ b/src/biogeophys/test/WaterTracerContainerType_test/test_water_tracer_container.pf @@ -2,7 +2,7 @@ module test_water_tracer_container ! Tests of WaterTracerContainerType - use pfunit_mod + use funit use WaterTracerContainerType use shr_kind_mod , only : r8 => shr_kind_r8 use decompMod, only : subgrid_level_gridcell diff --git a/src/biogeophys/test/WaterTracerUtils_test/CMakeLists.txt b/src/biogeophys/test/WaterTracerUtils_test/CMakeLists.txt index ed16d6200f..321e06883a 100644 --- a/src/biogeophys/test/WaterTracerUtils_test/CMakeLists.txt +++ b/src/biogeophys/test/WaterTracerUtils_test/CMakeLists.txt @@ -3,12 +3,6 @@ set (pfunit_sources test_calc_tracer_from_bulk.pf test_compare_bulk_to_tracer.pf) -# extra sources used for this test, which are not .pf files -# (currently none) -set (extra_sources - ) - -create_pFUnit_test(water_tracer_utils test_water_tracer_utils_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_water_tracer_utils_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(water_tracer_utils + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/WaterTracerUtils_test/test_calc_tracer_from_bulk.pf b/src/biogeophys/test/WaterTracerUtils_test/test_calc_tracer_from_bulk.pf index df3afd8d0f..e6e7aac1f1 100644 --- a/src/biogeophys/test/WaterTracerUtils_test/test_calc_tracer_from_bulk.pf +++ b/src/biogeophys/test/WaterTracerUtils_test/test_calc_tracer_from_bulk.pf @@ -2,7 +2,7 @@ module test_calc_tracer_from_bulk ! Tests of WaterTracerUtils: CalcTracerFromBulk - use pfunit_mod + use funit use WaterTracerUtils use shr_kind_mod , only : r8 => shr_kind_r8 use decompMod, only : subgrid_level_unspecified diff --git a/src/biogeophys/test/WaterTracerUtils_test/test_calc_tracer_from_bulk_fixed_ratio.pf b/src/biogeophys/test/WaterTracerUtils_test/test_calc_tracer_from_bulk_fixed_ratio.pf index 5bde146ac3..1871fc281d 100644 --- a/src/biogeophys/test/WaterTracerUtils_test/test_calc_tracer_from_bulk_fixed_ratio.pf +++ b/src/biogeophys/test/WaterTracerUtils_test/test_calc_tracer_from_bulk_fixed_ratio.pf @@ -2,7 +2,7 @@ module test_calc_tracer_from_bulk_fixed_ratio ! Tests of WaterTracerUtils: CalcTracerFromBulkFixedRatio - use pfunit_mod + use funit use WaterTracerUtils, only : CalcTracerFromBulkFixedRatio use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/biogeophys/test/WaterTracerUtils_test/test_compare_bulk_to_tracer.pf b/src/biogeophys/test/WaterTracerUtils_test/test_compare_bulk_to_tracer.pf index 01292402ff..3e185b9e85 100644 --- a/src/biogeophys/test/WaterTracerUtils_test/test_compare_bulk_to_tracer.pf +++ b/src/biogeophys/test/WaterTracerUtils_test/test_compare_bulk_to_tracer.pf @@ -2,7 +2,7 @@ module test_compare_bulk_to_tracer ! Tests of WaterTracerUtils: CompareBulkToTracer - use pfunit_mod + use funit use WaterTracerUtils, only : CompareBulkToTracer use shr_kind_mod , only : r8 => shr_kind_r8 use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) diff --git a/src/biogeophys/test/WaterType_test/CMakeLists.txt b/src/biogeophys/test/WaterType_test/CMakeLists.txt index 5ca5d23d72..506179aabd 100644 --- a/src/biogeophys/test/WaterType_test/CMakeLists.txt +++ b/src/biogeophys/test/WaterType_test/CMakeLists.txt @@ -1,12 +1,6 @@ set (pfunit_sources test_water_type.pf) -# extra sources used for this test, which are not .pf files -# (currently none) -set (extra_sources - ) - -create_pFUnit_test(water_type test_water_type_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_water_type_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(water_type + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/WaterType_test/test_water_type.pf b/src/biogeophys/test/WaterType_test/test_water_type.pf index c709f6e991..f3edc731a3 100644 --- a/src/biogeophys/test/WaterType_test/test_water_type.pf +++ b/src/biogeophys/test/WaterType_test/test_water_type.pf @@ -2,7 +2,7 @@ module test_water_type ! Tests of WaterType - use pfunit_mod + use funit use WaterType use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod, only : bounds, unittest_subgrid_teardown diff --git a/src/biogeophys/test/Wateratm2lnd_test/CMakeLists.txt b/src/biogeophys/test/Wateratm2lnd_test/CMakeLists.txt index 35e12aea3e..c2157952e0 100644 --- a/src/biogeophys/test/Wateratm2lnd_test/CMakeLists.txt +++ b/src/biogeophys/test/Wateratm2lnd_test/CMakeLists.txt @@ -1,12 +1,6 @@ set (pfunit_sources test_set_tracers.pf) -# extra sources used for this test, which are not .pf files -# (currently none) -set (extra_sources - ) - -create_pFUnit_test(water_atm2lnd test_water_atm2lnd_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_water_atm2lnd_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(water_atm2lnd + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/Wateratm2lnd_test/test_set_tracers.pf b/src/biogeophys/test/Wateratm2lnd_test/test_set_tracers.pf index e16eeea73a..13928b566b 100644 --- a/src/biogeophys/test/Wateratm2lnd_test/test_set_tracers.pf +++ b/src/biogeophys/test/Wateratm2lnd_test/test_set_tracers.pf @@ -2,7 +2,7 @@ module test_set_tracers ! Tests of Wateratm2lndType: routines that set tracers - use pfunit_mod + use funit use Wateratm2lndType use WaterInfoBulkType, only : water_info_bulk_type use WaterInfoTracerType, only : water_info_tracer_type diff --git a/src/cpl/lilac/lnd_comp_esmf.F90 b/src/cpl/lilac/lnd_comp_esmf.F90 index c172ec8c06..298aa730c0 100644 --- a/src/cpl/lilac/lnd_comp_esmf.F90 +++ b/src/cpl/lilac/lnd_comp_esmf.F90 @@ -6,7 +6,14 @@ module lnd_comp_esmf !---------------------------------------------------------------------------- ! external libraries - use ESMF + use ESMF , only : ESMF_GridComp, ESMF_SUCCESS, ESMF_LogSet, ESMF_State + use ESMF , only : ESMF_Clock, ESMF_FieldBundle, ESMF_MAXSTR, ESMF_Field + use ESMF , only : ESMF_FieldCreate, ESMF_AttributeGet + use ESMF , only : ESMF_Time, ESMF_LogWrite, ESMF_LogFoundError, ESMF_Finalize + use ESMF , only : ESMF_FieldBundleAdd, ESMF_FieldBundleCreate + use ESMF , only : ESMF_ClockGet, ESMF_ClockGetAlarm, ESMF_LOGMSG_INFO + use ESMF , only : ESMF_TYPEKIND_R8, ESMF_MESHLOC_ELEMENT, ESMF_LOGERR_PASSTHRU + use ESMF , only : ESMF_END_ABORT, ESMF_TimeGet, ESMF_LOGMSG_ERROR use shr_mpi_mod , only : shr_mpi_bcast use perf_mod , only : t_startf, t_stopf, t_barrierf @@ -68,6 +75,8 @@ module lnd_comp_esmf subroutine lnd_register(comp, rc) ! Register the clm initial, run, and final phase methods with ESMF. + use ESMF , only : ESMF_GridCompSetEntryPoint + use ESMF , only : ESMF_METHOD_INITIALIZE, ESMF_METHOD_RUN, ESMF_METHOD_FINALIZE ! input/output argumenents type(ESMF_GridComp) :: comp ! CLM grid component @@ -97,6 +106,13 @@ subroutine lnd_init(comp, import_state, export_state, clock, rc) ! Initialize land surface model and obtain relevant atmospheric model arrays ! back from (i.e. albedos, surface temperature and snow cover over land). + ! Uses: + use ESMF , only : ESMF_VM, ESMF_VMGet, ESMF_VMGetCurrent + use ESMF , only : ESMF_DistGrid, ESMF_AttributeSet + use ESMF , only : ESMF_CalKind_Flag, ESMF_CALKIND_NOLEAP, ESMF_CALKIND_GREGORIAN + use ESMF , only : ESMF_TimeInterval, ESMF_TimeIntervalGet + use ESMF , only : ESMF_StateAdd + use ESMF , only : operator(==) ! input/output variables type(ESMF_GridComp) :: comp ! CLM gridded component @@ -509,6 +525,8 @@ subroutine lnd_run(gcomp, import_state, export_state, clock, rc) !------------------------ ! Run CTSM !------------------------ + use ESMF , only : ESMF_Alarm, ESMF_AlarmIsRinging, ESMF_AlarmRingerOff + use ESMF , only : ESMF_FAILURE, ESMF_ClockGetNextTime ! input/output variables type(ESMF_GridComp) :: gcomp ! CLM gridded component @@ -834,6 +852,8 @@ end subroutine lnd_final subroutine log_clock_advance(clock, logunit, rc) + !----------------------------------------------------------------------- + use ESMF , only : ESMF_ClockPrint ! input/output variables type(ESMF_Clock) :: clock integer , intent(in) :: logunit diff --git a/src/cpl/lilac/lnd_comp_shr.F90 b/src/cpl/lilac/lnd_comp_shr.F90 index dd619c7648..8dc9738f7a 100644 --- a/src/cpl/lilac/lnd_comp_shr.F90 +++ b/src/cpl/lilac/lnd_comp_shr.F90 @@ -2,7 +2,7 @@ module lnd_comp_shr ! Model mesh info is here in order to be leveraged by CDEPS in line calls - use ESMF + use ESMF , only : ESMF_Clock, ESMF_Mesh use shr_kind_mod, only : r8 => shr_kind_r8, cl=>shr_kind_cl implicit none diff --git a/src/cpl/lilac/lnd_import_export.F90 b/src/cpl/lilac/lnd_import_export.F90 index 32d1bace46..281666c3e7 100644 --- a/src/cpl/lilac/lnd_import_export.F90 +++ b/src/cpl/lilac/lnd_import_export.F90 @@ -1,6 +1,7 @@ module lnd_import_export - use ESMF + use ESMF , only : ESMF_State, ESMF_SUCCESS, ESMF_StatePrint, ESMF_LogWrite, ESMF_LOGMSG_INFO, ESMF_LOGMSG_INFO + use ESMF , only : ESMF_FAILURE use shr_kind_mod , only : r8 => shr_kind_r8, cx=>shr_kind_cx, cxx=>shr_kind_cxx, cs=>shr_kind_cs use shr_sys_mod , only : shr_sys_abort use shr_const_mod , only : fillvalue=>SHR_CONST_SPVAL @@ -570,7 +571,7 @@ subroutine state_getimport(state, fb, fldname, bounds, output, ungridded_index, end if ! Check for nans - call check_for_nans(output, trim(fldname), bounds%begg) + call check_for_nans(output, trim(fldname), bounds%begg, "output") end subroutine state_getimport @@ -656,7 +657,7 @@ subroutine state_setexport(state, fb, fldname, bounds, input, minus, ungridded_i end if ! check for nans - call check_for_nans(input, trim(fldname), bounds%begg) + call check_for_nans(input, trim(fldname), bounds%begg, "input") end subroutine state_setexport @@ -667,6 +668,10 @@ subroutine state_getfldptr(State, fb, fldname, fldptr1d, fldptr2d, rc) ! ---------------------------------------------- ! Get pointer to a state field ! ---------------------------------------------- + use ESMF , only : ESMF_FieldStatus_Flag, ESMF_Field, ESMF_FieldBundle + use ESMF , only : ESMF_FIELDSTATUS_COMPLETE, ESMF_StateGet + use ESMF , only : ESMF_FieldBundleGet, ESMF_FieldGet + use ESMF , only : operator(/=) ! input/output variables type(ESMF_State), intent(in) :: State diff --git a/src/cpl/lilac/lnd_shr_methods.F90 b/src/cpl/lilac/lnd_shr_methods.F90 index 078aef08d9..f8ca9f4f3e 100644 --- a/src/cpl/lilac/lnd_shr_methods.F90 +++ b/src/cpl/lilac/lnd_shr_methods.F90 @@ -1,6 +1,9 @@ module lnd_shr_methods - use ESMF + use ESMF , only : ESMF_State, ESMF_MAXSTR, ESMF_StateGet + use ESMF , only : ESMF_LogWrite, ESMF_LOGMSG_ERROR, ESMF_FAILURE + use ESMF , only : ESMF_LOGERR_PASSTHRU, ESMF_LogFoundError + use ESMF , only : ESMF_SUCCESS, ESMF_Field, ESMF_LOGMSG_INFO use shr_kind_mod , only : r8 => shr_kind_r8, cl=>shr_kind_cl, cs=>shr_kind_cs use shr_sys_mod , only : shr_sys_abort @@ -93,6 +96,11 @@ subroutine field_getfldptr(field, rc, fldptr1, fldptr2, rank, abort) ! abort is true by default and will abort if fldptr is not yet allocated in field ! rank returns 0, 1, or 2. 0 means fldptr not allocated and abort=false ! ---------------------------------------------- + use ESMF , only : ESMF_FieldStatus_Flag, ESMF_Mesh + use ESMF , only : ESMF_FieldGet, ESMF_GEOMTYPE_GRID, ESMF_GEOMTYPE_MESH + use ESMF , only : ESMF_MeshGet, ESMF_GeomType_Flag + use ESMF , only : ESMF_FIELDSTATUS_COMPLETE + use ESMF , only : operator(/=), operator(==) ! input/output variables type(ESMF_Field) , intent(in) :: field diff --git a/src/cpl/mct/ExcessIceStreamType.F90 b/src/cpl/mct/ExcessIceStreamType.F90 new file mode 100644 index 0000000000..5c5394233c --- /dev/null +++ b/src/cpl/mct/ExcessIceStreamType.F90 @@ -0,0 +1,144 @@ +module ExcessIceStreamType + + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! Stub for ExcessIceStreams for the MCT driver. So that MCT can be used + ! without excess ice streams. + ! + ! !USES + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL + use shr_log_mod , only : errMsg => shr_log_errMsg + use spmdMod , only : mpicom, masterproc + use clm_varctl , only : iulog + use abortutils , only : endrun + use decompMod , only : bounds_type + + ! !PUBLIC TYPES: + implicit none + private + + public :: UseExcessIceStreams ! If streams will be used + + type, public :: excessicestream_type + contains + + ! !PUBLIC MEMBER FUNCTIONS: + procedure, public :: Init ! Initialize and read data in + procedure, public :: CalcExcessIce ! Calculate excess ice ammount + + ! !PRIVATE MEMBER FUNCTIONS: + procedure, private :: ReadNML ! Read in namelist + + end type excessicestream_type + ! ! PRIVATE DATA: + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + +!============================================================================== +contains +!============================================================================== + + subroutine Init(this, bounds, NLFilename) + ! + ! + ! arguments + implicit none + class(excessicestream_type) :: this + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! Namelist filename + + ! + ! local variables + + call this%ReadNML( bounds, NLFileName ) + end subroutine Init + + subroutine CalcExcessIce(this,bounds,exice_bulk_init) + + ! only transfers grid values to columns + implicit none + class(excessicestream_type) :: this + type(bounds_type), intent(in) :: bounds + real(r8) , intent(inout) :: exice_bulk_init(bounds%begc:bounds%endc) + ! + ! !LOCAL VARIABLES: + + end subroutine CalcExcessIce + + logical function UseExcessIceStreams() + ! + ! !DESCRIPTION: + ! Return true if + ! + ! !USES: + ! + ! !ARGUMENTS: + implicit none + ! + ! !LOCAL VARIABLES: + UseExcessIceStreams = .false. +end function UseExcessIceStreams + +subroutine ReadNML(this, bounds, NLFilename) + ! + ! Read the namelist data stream information. + ! + ! Uses: + use shr_nl_mod , only : shr_nl_find_group_name + use shr_log_mod , only : errMsg => shr_log_errMsg + use shr_mpi_mod , only : shr_mpi_bcast + ! + ! arguments + implicit none + class(excessicestream_type) :: this + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! Namelist filename + ! + ! local variables + integer :: nu_nml ! unit for namelist file + integer :: nml_error ! namelist i/o error flag + logical :: use_excess_ice_streams = .false. ! logical to turn on use of excess ice streams + character(len=CL) :: stream_fldFileName_exice = ' ' + character(len=CL) :: stream_mapalgo_exice = 'none' + character(len=*), parameter :: namelist_name = 'exice_streams' ! MUST agree with name in namelist and read + character(len=*), parameter :: subName = "('exice_streams::ReadNML')" + !----------------------------------------------------------------------- + + namelist /exice_streams/ & ! MUST agree with namelist_name above + stream_mapalgo_exice, stream_fldFileName_exice, use_excess_ice_streams + !----------------------------------------------------------------------- + ! Default values for namelist + + ! Read excess ice namelist + if (masterproc) then + open( newunit=nu_nml, file=trim(NLFilename), status='old', iostat=nml_error ) + call shr_nl_find_group_name(nu_nml, namelist_name, status=nml_error) + if (nml_error == 0) then + read(nu_nml, nml=exice_streams,iostat=nml_error) ! MUST agree with namelist_name above + if (nml_error /= 0) then + call endrun(msg=' ERROR reading '//namelist_name//' namelist'//errMsg(sourcefile, __LINE__)) + end if + else + call endrun(msg=' ERROR finding '//namelist_name//' namelist'//errMsg(sourcefile, __LINE__)) + end if + close(nu_nml) + endif + + call shr_mpi_bcast(use_excess_ice_streams , mpicom) + + if (masterproc) then + if ( use_excess_ice_streams ) then + call endrun(msg=' ERROR excess ice streams can NOT be on for the MCT driver'//errMsg(sourcefile, __LINE__)) + end if + if ( trim(stream_fldFileName_exice) /= '' ) then + call endrun(msg=' ERROR stream_fldFileName_exice can NOT be set for the MCT driver'//errMsg(sourcefile, __LINE__)) + end if + if ( trim(stream_mapalgo_exice) /= 'none' ) then + call endrun(msg=' ERROR stream_mapalgo_exice can only be none for the MCT driver'//errMsg(sourcefile, __LINE__)) + end if + endif + +end subroutine ReadNML + +end module ExcessIceStreamType diff --git a/src/cpl/mct/lnd_import_export.F90 b/src/cpl/mct/lnd_import_export.F90 index 2c84d2e471..3f7e67af68 100644 --- a/src/cpl/mct/lnd_import_export.F90 +++ b/src/cpl/mct/lnd_import_export.F90 @@ -136,7 +136,7 @@ subroutine lnd_import( bounds, x2l, glc_present, atm2lnd_inst, glc2lnd_inst, wat ! Check for nans from coupler !-------------------------- - call check_for_nans(x2l(:,i), fname, begg) + call check_for_nans(x2l(:,i), fname, begg, "x2l") end do @@ -344,7 +344,7 @@ subroutine lnd_export( bounds, waterlnd2atmbulk_inst, lnd2atm_inst, lnd2glc_inst ! Check for nans to coupler !-------------------------- - call check_for_nans(l2x(:,i), fname, begg) + call check_for_nans(l2x(:,i), fname, begg, "l2x") end do diff --git a/src/cpl/nuopc/lnd_comp_nuopc.F90 b/src/cpl/nuopc/lnd_comp_nuopc.F90 index f7c3cb9a13..3852a1bf1b 100644 --- a/src/cpl/nuopc/lnd_comp_nuopc.F90 +++ b/src/cpl/nuopc/lnd_comp_nuopc.F90 @@ -4,9 +4,20 @@ module lnd_comp_nuopc ! This is the NUOPC cap for CTSM !---------------------------------------------------------------------------- - use ESMF + use ESMF , only : ESMF_SUCCESS, ESMF_GridComp, ESMF_LogWrite, ESMF_LOGMSG_INFO, ESMF_GridCompSetEntryPoint + use ESMF , only : ESMF_METHOD_INITIALIZE, ESMF_FAILURE, ESMF_Time, ESMF_LogSetError, ESMF_RC_NOT_VALID + use ESMF , only : ESMF_Clock, ESMF_State, ESMF_Field, ESMF_MAXSTR, ESMF_LOGMSG_WARNING,ESMF_RC_ARG_BAD + use ESMF , only : ESMF_LOGFOUNDERROR, ESMF_LOGERR_PASSTHRU, ESMF_CALKIND_FLAG, ESMF_TIMEINTERVAL + use ESMF , only : ESMF_LogSetError, ESMF_FieldGet, ESMF_ClockGet, ESMF_GridCompGet, ESMF_ClockGetNextTime + use ESMF , only : ESMF_AlarmRingerOff, ESMF_TimeIntervalGet, ESMF_TimeGet, ESMF_StateGet + use ESMF , only : ESMF_MethodRemove, ESMF_VM, ESMF_VMGet, ESMF_CALKIND_NOLEAP, ESMF_CALKIND_GREGORIAN + use ESMF , only : ESMF_ALARMLIST_ALL, ESMF_ALARM, ESMF_ALARMISRINGING, ESMF_ClockGetAlarm, ESMF_ClockGetAlarmList + use ESMF , only : ESMF_AlarmSet, ESMF_ClockAdvance + use ESMF , only : operator(==), operator(+) + use ESMF , only : ESMF_AlarmIsCreated, ESMF_LOGMSG_ERROR, ESMF_ClockSet use NUOPC , only : NUOPC_CompDerive, NUOPC_CompSetEntryPoint, NUOPC_CompSpecialize use NUOPC , only : NUOPC_CompFilterPhaseMap, NUOPC_CompAttributeGet, NUOPC_CompAttributeSet + use NUOPC , only : NUOPC_CompGet, Nuopc_IsAtTime, Nuopc_GetAttribute use NUOPC_Model , only : model_routine_SS => SetServices use NUOPC_Model , only : SetVM use NUOPC_Model , only : model_label_Advance => label_Advance @@ -1275,8 +1286,6 @@ subroutine clm_orbital_update(clock, logunit, mastertask, eccen, obliqr, lambm0 end subroutine clm_orbital_update subroutine CheckImport(gcomp, rc) - use NUOPC - use ESMF type(ESMF_GridComp) :: gcomp integer, intent(out) :: rc character(len=*) , parameter :: subname = "("//__FILE__//":CheckImport)" diff --git a/src/cpl/nuopc/lnd_comp_shr.F90 b/src/cpl/nuopc/lnd_comp_shr.F90 index dd619c7648..8dc9738f7a 100644 --- a/src/cpl/nuopc/lnd_comp_shr.F90 +++ b/src/cpl/nuopc/lnd_comp_shr.F90 @@ -2,7 +2,7 @@ module lnd_comp_shr ! Model mesh info is here in order to be leveraged by CDEPS in line calls - use ESMF + use ESMF , only : ESMF_Clock, ESMF_Mesh use shr_kind_mod, only : r8 => shr_kind_r8, cl=>shr_kind_cl implicit none diff --git a/src/cpl/nuopc/lnd_import_export.F90 b/src/cpl/nuopc/lnd_import_export.F90 index 0e7a5e2eef..5ed5ff76d1 100644 --- a/src/cpl/nuopc/lnd_import_export.F90 +++ b/src/cpl/nuopc/lnd_import_export.F90 @@ -65,7 +65,7 @@ module lnd_import_export logical :: flds_co2a ! use case logical :: flds_co2b ! use case logical :: flds_co2c ! use case - logical :: force_send_to_atm = .true. ! Force sending export data to atmosphere even if ATM is not prognostic + logical :: force_send_to_atm ! Force sending export data to atmosphere even if ATM is not prognostic integer :: glc_nec ! number of glc elevation classes integer, parameter :: debug = 0 ! internal debug level @@ -201,7 +201,8 @@ subroutine advertise_fields(gcomp, flds_scalar_name, glc_present, cism_evolve, r ! Advertise export fields !-------------------------------- - call ReadCapNamelist( NLFilename ) + call ReadCapNamelist( NLFilename, rc ) + if (ChkErr(rc,__LINE__,u_FILE_u)) return ! Need to determine if there is no land for single column before the advertise call is done @@ -1080,7 +1081,7 @@ subroutine state_getimport_1d(state, fldname, ctsmdata, rc) do g = 1,size(ctsmdata) ctsmdata(g) = fldptr1d(g) end do - call check_for_nans(ctsmdata, trim(fldname), 1) + call check_for_nans(ctsmdata, trim(fldname), 1, "import_1D") end subroutine state_getimport_1d @@ -1114,7 +1115,7 @@ subroutine state_getimport_2d(state, fldname, ctsmdata, rc) do g = 1,size(ctsmdata,dim=1) ctsmdata(g,n) = fldptr2d(n,g) end do - call check_for_nans(ctsmdata(:,n), trim(fldname)//trim(cnum), 1) + call check_for_nans(ctsmdata(:,n), trim(fldname)//trim(cnum), 1, "import_2D") end do end subroutine state_getimport_2d @@ -1167,7 +1168,7 @@ subroutine state_setexport_1d(state, fldname, ctsmdata, init_spval, minus, rc) fldptr1d(g) = ctsmdata(g) end do end if - call check_for_nans(ctsmdata, trim(fldname), 1) + call check_for_nans(ctsmdata, trim(fldname), 1, "export_1D") end subroutine state_setexport_1d @@ -1222,7 +1223,7 @@ subroutine state_setexport_2d(state, fldname, ctsmdata, init_spval, minus, rc) fldptr2d(n,g) = ctsmdata(g,n) end do end if - call check_for_nans(ctsmdata(:,n), trim(fldname)//trim(cnum), 1) + call check_for_nans(ctsmdata(:,n), trim(fldname)//trim(cnum), 1, "export_2D") end do end subroutine state_setexport_2d @@ -1289,25 +1290,32 @@ logical function fldchk(state, fldname) end function fldchk !=============================================================================== - subroutine ReadCapNamelist( NLFilename ) + subroutine ReadCapNamelist( NLFilename, rc ) ! ---------------------------------------------------- ! Read in tne namelist for CTSM nuopc cap level items ! ---------------------------------------------------- + use ESMF , only : ESMF_VMGetCurrent, ESMF_VMBroadcast, ESMF_VM use clm_nlUtilsMod , only : find_nlgroup_name - use shr_mpi_mod , only : shr_mpi_bcast - use spmdMod , only : mpicom use abortutils , only : endrun use shr_log_mod , only : errMsg => shr_log_errMsg ! !ARGUMENTS: character(len=*), intent(IN) :: NLFilename ! Namelist filename + integer, intent(out) :: rc ! ESMF return code ! !LOCAL VARIABLES: integer :: nu_nml ! unit for namelist file integer :: nml_error ! namelist i/o error flag + integer, target :: tmp(1) + type(ESMF_VM) :: vm character(*), parameter :: nml_name = "ctsm_nuopc_cap" ! MUST match with namelist name below + + namelist /ctsm_nuopc_cap/ force_send_to_atm + tmp = 0 + rc = ESMF_SUCCESS ! Read namelist + force_send_to_atm = .true. if (masterproc) then open( newunit=nu_nml, file=trim(NLFilename), status='old', iostat=nml_error ) call find_nlgroup_name(nu_nml, nml_name, status=nml_error) @@ -1318,10 +1326,16 @@ subroutine ReadCapNamelist( NLFilename ) end if end if close(nu_nml) + if (force_send_to_atm) tmp(1) = 1 endif + call ESMF_VMGetCurrent(vm, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return ! Broadcast namelist to all processors - call shr_mpi_bcast(force_send_to_atm , mpicom) + call ESMF_VMBroadcast(vm, tmp, 1, 0, rc=rc) + + force_send_to_atm = (tmp(1) == 1) + if (ChkErr(rc,__LINE__,u_FILE_u)) return end subroutine ReadCapNamelist diff --git a/src/cpl/share_esmf/ExcessIceStreamType.F90 b/src/cpl/share_esmf/ExcessIceStreamType.F90 new file mode 100644 index 0000000000..92d5632aff --- /dev/null +++ b/src/cpl/share_esmf/ExcessIceStreamType.F90 @@ -0,0 +1,325 @@ +module ExcessIceStreamType + +#include "shr_assert.h" + + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! Contains methods for reading in excess ice initial bulk values from data stream. + ! Needed in parameterization for excess ice in soil (Lee et al., 2014). + ! Used when use_excess_ice is true for initialization: + ! startup type runs starting from coldstart and initial datasets + ! that do not have required variables + ! or hybrid runs from cases with use_excess_ice was false. + ! Dataset is interpolated to 0.125x0.125 degrees grid from Brown et al., 1997 + ! with values derived from permafrost types. + ! Values represent fraction of excess ice within soil column + ! and are distributed within it later in initialization + ! + ! !USES + use ESMF + use dshr_strdata_mod , only : shr_strdata_type + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_cl + use shr_log_mod , only : errMsg => shr_log_errMsg + use spmdMod , only : mpicom, masterproc + use clm_varctl , only : iulog + use abortutils , only : endrun + use decompMod , only : bounds_type + + ! !PUBLIC TYPES: + implicit none + private + + public :: UseExcessIceStreams ! If streams will be used + + type, public :: excessicestream_type + real(r8), pointer, private :: exice_bulk (:) ! excess ice bulk value (-) + contains + + ! !PUBLIC MEMBER FUNCTIONS: + procedure, public :: Init ! Initialize and read data in + procedure, public :: CalcExcessIce ! Calculate excess ice ammount + + ! !PRIVATE MEMBER FUNCTIONS: + procedure, private :: InitAllocate ! Allocate data + + end type excessicestream_type + ! ! PRIVATE DATA: + type, private :: streamcontrol_type + character(len=CL) :: stream_fldFileName_exice ! data Filename + character(len=CL) :: stream_meshfile_exice ! mesh Filename + character(len=CL) :: stream_mapalgo_exice ! map algo + contains + procedure, private :: ReadNML ! Read in namelist + end type streamcontrol_type + + logical :: namelist_read = .false. + type(streamcontrol_type), private :: control ! Stream control data + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + +!============================================================================== +contains +!============================================================================== + + subroutine Init(this, bounds, NLFilename) + ! + use spmdMod , only : iam + use lnd_comp_shr , only : mesh, model_clock + use dshr_strdata_mod , only : shr_strdata_init_from_inline, shr_strdata_print + use dshr_strdata_mod , only : shr_strdata_advance + use dshr_methods_mod , only : dshr_fldbun_getfldptr + ! + ! arguments + implicit none + class(excessicestream_type) :: this + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! Namelist filename + + ! + ! local variables + integer :: ig, g, n ! Indices + integer :: year ! year (0, ...) for nstep+1 + integer :: mon ! month (1, ..., 12) for nstep+1 + integer :: day ! day of month (1, ..., 31) for nstep+1 + integer :: sec ! seconds into current date for nstep+1 + integer :: mcdate ! Current model date (yyyymmdd) + type(shr_strdata_type) :: sdat_exice ! input data stream + character(len=16), allocatable :: stream_varnames(:) ! array of stream field names + integer :: rc ! error code + real(r8), pointer :: dataptr1d(:) ! temporary pointer + character(len=*), parameter :: stream_name = 'excess ice' + + call this%InitAllocate( bounds ) + call control%ReadNML( bounds, NLFileName ) + if ( UseExcessIceStreams() )then + allocate(stream_varnames(1)) + stream_varnames = (/"EXICE"/) + + if (masterproc) then + write(iulog,*) ' stream_varnames = ',stream_varnames + write(iulog,*) ' Values will be used if the variable is not on the initial conditions dataset' + end if + + call shr_strdata_init_from_inline(sdat_exice, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = control%stream_meshfile_exice, & + stream_lev_dimname = 'null', & + stream_mapalgo = control%stream_mapalgo_exice, & + stream_filenames = (/trim(control%stream_fldFileName_exice)/), & + stream_fldlistFile = stream_varnames, & + stream_fldListModel = stream_varnames, & + stream_yearFirst = 1996, & + stream_yearLast = 1996, & + stream_yearAlign = 1, & + stream_offset = 0, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + ! in ch4FinundatedStreamType it is set to linear but we have a single date dataset + stream_tintalgo = 'nearest', & + stream_name = 'excess ice ', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + !TODO + ! Explicitly set current date to a hardcoded constant value. Otherwise + ! using the real date can cause roundoff differences that are + ! detrected as issues with exact restart. EBK M05/20/2017 + ! call get_curr_date(year, mon, day, sec) + year = 1996 + mon = 12 + day = 31 + sec = 0 + mcdate = year*10000 + mon*100 + day + + call shr_strdata_advance(sdat_exice, ymd=mcdate, tod=sec, logunit=iulog, istr='exice', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! Get pointer for stream data that is time and spatially interpolate to model time and grid + do n = 1,size(stream_varnames) + call dshr_fldbun_getFldPtr(sdat_exice%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + if (trim(stream_varnames(n)) == 'EXICE') then + ig = 0 + do g = bounds%begg,bounds%endg + ig = ig+1 + this%exice_bulk(g) = dataptr1d(ig) + end do + end if + end do + end if + end subroutine Init + + subroutine InitAllocate(this, bounds) + ! + ! !DESCRIPTION: + ! Allocate module variables and data structures + ! + ! !USES: + use shr_infnan_mod, only: nan => shr_infnan_nan, assignment(=) + ! + ! !ARGUMENTS: + implicit none + class(excessicestream_type) :: this + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + integer :: begc, endc + integer :: begg, endg + !--------------------------------------------------------------------- + + begc = bounds%begc; endc = bounds%endc + begg = bounds%begg; endg = bounds%endg + + + allocate(this%exice_bulk(begg:endg)) ; this%exice_bulk(:) = nan + + end subroutine InitAllocate + + subroutine CalcExcessIce(this,bounds,exice_bulk_init) + + ! only transfers grid values to columns + use shr_const_mod , only : SHR_CONST_TKFRZ + use landunit_varcon , only : istwet, istsoil, istcrop, istice + use column_varcon , only : icol_road_perv, icol_road_imperv + use clm_varcon , only : denice + use clm_varcon , only : tfrz + use ColumnType , only : col + use LandunitType , only : lun + implicit none + class(excessicestream_type) :: this + type(bounds_type), intent(in) :: bounds + real(r8) , intent(inout) :: exice_bulk_init(bounds%begc:bounds%endc) + ! + ! !LOCAL VARIABLES: + integer :: begc, endc + integer :: begg, endg + integer :: c, l, g !counters + + exice_bulk_init(bounds%begc:bounds%endc)=0.0_r8 + + do c = bounds%begc,bounds%endc + g = col%gridcell(c) + l = col%landunit(c) + if ((.not. lun%lakpoi(l)) .and. (.not. lun%urbpoi(l)) .and. (.not. lun%itype(l) == istwet) .and. (.not. lun%itype(l) == istice)) then !not lake + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + exice_bulk_init(c)=this%exice_bulk(g) + else + exice_bulk_init(c) = 0.0_r8 + endif + else + exice_bulk_init(c)=0.0_r8 + endif + enddo + + end subroutine CalcExcessIce + + logical function UseExcessIceStreams() + ! + ! !DESCRIPTION: + ! Return true if + ! + ! !USES: + ! + ! !ARGUMENTS: + implicit none + ! + ! !LOCAL VARIABLES: + if ( .not. namelist_read ) then + call endrun(msg=' ERROR UseExcessIceStreams being called, but namelist has not been read yet'//errMsg(sourcefile, __LINE__)) + end if + if ( trim(control%stream_fldFileName_exice) == '' )then + UseExcessIceStreams = .false. + else + UseExcessIceStreams = .true. + end if +end function UseExcessIceStreams + +subroutine ReadNML(this, bounds, NLFilename) + ! + ! Read the namelist data stream information. + ! + ! Uses: + use shr_nl_mod , only : shr_nl_find_group_name + use shr_log_mod , only : errMsg => shr_log_errMsg + use shr_mpi_mod , only : shr_mpi_bcast + ! + ! arguments + implicit none + class(streamcontrol_type) :: this + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! Namelist filename + ! + ! local variables + integer :: nu_nml ! unit for namelist file + integer :: nml_error ! namelist i/o error flag + logical :: use_excess_ice_streams = .false. ! logical to turn on use of excess ice streams + character(len=CL) :: stream_fldFileName_exice = ' ' + character(len=CL) :: stream_meshfile_exice = ' ' + character(len=CL) :: stream_mapalgo_exice = 'bilinear' + character(len=*), parameter :: namelist_name = 'exice_streams' ! MUST agree with name in namelist and read + character(len=*), parameter :: subName = "('exice_streams::ReadNML')" + !----------------------------------------------------------------------- + + namelist /exice_streams/ & ! MUST agree with namelist_name above + stream_mapalgo_exice, stream_fldFileName_exice, stream_meshfile_exice, use_excess_ice_streams + + ! Default values for namelist + + ! Read excess ice namelist + if (masterproc) then + open( newunit=nu_nml, file=trim(NLFilename), status='old', iostat=nml_error ) + call shr_nl_find_group_name(nu_nml, namelist_name, status=nml_error) + if (nml_error == 0) then + read(nu_nml, nml=exice_streams,iostat=nml_error) ! MUST agree with namelist_name above + if (nml_error /= 0) then + call endrun(msg=' ERROR reading '//namelist_name//' namelist'//errMsg(sourcefile, __LINE__)) + end if + else + call endrun(msg=' ERROR finding '//namelist_name//' namelist'//errMsg(sourcefile, __LINE__)) + end if + close(nu_nml) + endif + + call shr_mpi_bcast(use_excess_ice_streams , mpicom) + call shr_mpi_bcast(stream_mapalgo_exice , mpicom) + call shr_mpi_bcast(stream_fldFileName_exice , mpicom) + call shr_mpi_bcast(stream_meshfile_exice , mpicom) + + if (masterproc) then + write(iulog,*) ' ' + if ( use_excess_ice_streams ) then + write(iulog,*) 'excess ice streams are enabled: ' + write(iulog,*) namelist_name, ' stream settings:' + write(iulog,*) ' stream_fldFileName_exice = ',stream_fldFileName_exice + write(iulog,*) ' stream_meshfile_exice = ',stream_meshfile_exice + write(iulog,*) ' stream_mapalgo_exice = ',stream_mapalgo_exice + if ( trim(stream_fldFileName_exice) == '' )then + call endrun(msg=' ERROR excess ice streams are on, but stream_fldFileName_exice is NOT set'//errMsg(sourcefile, __LINE__)) + end if + else + write(iulog,*) 'excess ice streams are off' + if ( trim(stream_fldFileName_exice) /= '' )then + call endrun(msg=' ERROR excess ice streams are off, but stream_fldFileName_exice is set'//errMsg(sourcefile, __LINE__)) + end if + end if + endif + this%stream_fldFileName_exice = stream_fldFileName_exice + this%stream_meshfile_exice = stream_meshfile_exice + this%stream_mapalgo_exice = stream_mapalgo_exice + namelist_read = .true. + +end subroutine ReadNML + + +end module ExcessIceStreamType diff --git a/src/cpl/share_esmf/FireDataBaseType.F90 b/src/cpl/share_esmf/FireDataBaseType.F90 index 40d5f3c260..42bb874812 100644 --- a/src/cpl/share_esmf/FireDataBaseType.F90 +++ b/src/cpl/share_esmf/FireDataBaseType.F90 @@ -7,7 +7,7 @@ module FireDataBaseType ! module for handling of fire data ! ! !USES: - use ESMF + use ESMF , only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU, ESMF_Finalize, ESMF_END_ABORT use dshr_strdata_mod , only : shr_strdata_type use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL use shr_log_mod , only : errMsg => shr_log_errMsg diff --git a/src/cpl/share_esmf/SoilMoistureStreamMod.F90 b/src/cpl/share_esmf/SoilMoistureStreamMod.F90 index d5ef28f924..a93f413e7a 100644 --- a/src/cpl/share_esmf/SoilMoistureStreamMod.F90 +++ b/src/cpl/share_esmf/SoilMoistureStreamMod.F90 @@ -7,7 +7,7 @@ module SoilMoistureStreamMod ! Read in soil moisture from data stream ! ! !USES: - use ESMF + use ESMF , only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU, ESMF_Finalize, ESMF_END_ABORT, ESMF_SUCCESS use dshr_strdata_mod , only : shr_strdata_type, shr_strdata_print use dshr_strdata_mod , only : shr_strdata_init_from_inline, shr_strdata_advance use dshr_methods_mod , only : dshr_fldbun_getfldptr diff --git a/src/cpl/share_esmf/UrbanTimeVarType.F90 b/src/cpl/share_esmf/UrbanTimeVarType.F90 index cc48bf4833..088ec9eeae 100644 --- a/src/cpl/share_esmf/UrbanTimeVarType.F90 +++ b/src/cpl/share_esmf/UrbanTimeVarType.F90 @@ -5,7 +5,7 @@ module UrbanTimeVarType ! Urban Time Varying Data ! ! !USES: - use ESMF + use ESMF , only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU, ESMF_Finalize, ESMF_END_ABORT use dshr_strdata_mod, only : shr_strdata_type use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL use shr_log_mod , only : errMsg => shr_log_errMsg @@ -252,16 +252,13 @@ subroutine urbantv_interp(this, bounds) ! Determine this%tbuilding_max for all landunits do l = bounds%begl,bounds%endl if (lun%urbpoi(l)) then - ig = 0 - do g = bounds%begg,bounds%endg - ig = ig+1 - if (g == lun%gridcell(l)) exit - end do - do n = isturb_MIN,isturb_MAX - if (stream_varnames(lun%itype(l)) == stream_varnames(n)) then - this%t_building_max(l) = dataptr2d(ig,n) - end if - end do + ! Note that since l is within [begl, endl] bounds, we can assume + ! lun%gricell(l) is within [begg, endg] + ig = lun%gridcell(l) - bounds%begg + 1 + + ! Since we are within an urban land unit, we know that + ! lun%itype is within [pisturb_MIN, isturb_MAX] + this%t_building_max(l) = dataptr2d(ig, lun%itype(l)) else this%t_building_max(l) = spval end if @@ -272,9 +269,7 @@ subroutine urbantv_interp(this, bounds) found = .false. do l = bounds%begl,bounds%endl if (lun%urbpoi(l)) then - ig = 0 do g = bounds%begg,bounds%endg - ig = ig+1 if (g == lun%gridcell(l)) exit end do if ( .not. urban_valid(g) .or. (this%t_building_max(l) <= 0._r8)) then diff --git a/src/cpl/share_esmf/ch4FInundatedStreamType.F90 b/src/cpl/share_esmf/ch4FInundatedStreamType.F90 index 0d78bd6469..2bebf30062 100644 --- a/src/cpl/share_esmf/ch4FInundatedStreamType.F90 +++ b/src/cpl/share_esmf/ch4FInundatedStreamType.F90 @@ -7,7 +7,7 @@ module ch4FInundatedStreamType ! Contains methods for reading in finundated streams file for methane code. ! ! !USES - use ESMF + use ESMF , only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU, ESMF_Finalize, ESMF_END_ABORT use dshr_strdata_mod , only : shr_strdata_type use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_cl use shr_log_mod , only : errMsg => shr_log_errMsg diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 new file mode 100644 index 0000000000..0ea63f2c6d --- /dev/null +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -0,0 +1,502 @@ +module cropcalStreamMod + +#include "shr_assert.h" + + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! Read crop calendars from streams + ! + ! !USES: + use ESMF , only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU, ESMF_Finalize + use ESMF , only : ESMF_END_ABORT + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL, CS => shr_kind_CS + use dshr_strdata_mod , only : shr_strdata_type + use decompMod , only : bounds_type + use abortutils , only : endrun + use clm_varctl , only : iulog + use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams + use clm_varpar , only : mxpft + use clm_varpar , only : mxsowings + use perf_mod , only : t_startf, t_stopf + use spmdMod , only : masterproc, mpicom, iam + use pftconMod , only : npcropmin + use CNPhenologyMod , only : generate_crop_gdds + ! + ! !PUBLIC TYPES: + implicit none + private + + ! !PUBLIC MEMBER FUNCTIONS: + public :: cropcal_init ! position datasets for crop calendars + public :: cropcal_advance ! Advance the crop calendar streams (outside of a Open-MP threading loop) + public :: cropcal_interp ! interpolates between two years of crop calendar data + + ! !PRIVATE MEMBER DATA: + integer, allocatable :: g_to_ig(:) ! Array matching gridcell index to data index + type(shr_strdata_type) :: sdat_cropcal_swindow_start ! sowing window start input data stream + type(shr_strdata_type) :: sdat_cropcal_swindow_end ! sowing window end input data stream + type(shr_strdata_type) :: sdat_cropcal_cultivar_gdds ! sdate input data stream + character(len=CS), allocatable :: stream_varnames_sdate(:) ! used for both start and end dates + character(len=CS), allocatable :: stream_varnames_cultivar_gdds(:) + integer :: ncft ! Number of crop functional types (excl. generic crops) + logical :: allow_invalid_swindow_inputs ! Fall back on paramfile sowing windows in cases of invalid values in stream_fldFileName_swindow_start and _end? + character(len=CL) :: stream_fldFileName_swindow_start ! sowing window start stream filename to read + character(len=CL) :: stream_fldFileName_swindow_end ! sowing window end stream filename to read + character(len=CL) :: stream_fldFileName_cultivar_gdds ! cultivar growing degree-days stream filename to read + + character(len=*), parameter :: sourcefile = & + __FILE__ + +!============================================================================== +contains +!============================================================================== + + subroutine cropcal_init(bounds) + ! + ! Initialize data stream information for crop calendars. + ! + ! !USES: + use shr_mpi_mod , only : shr_mpi_bcast + use clm_nlUtilsMod , only : find_nlgroup_name + use lnd_comp_shr , only : mesh, model_clock + use dshr_strdata_mod , only : shr_strdata_init_from_inline + use controlMod , only : NLFilename + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds ! bounds + ! + ! !LOCAL VARIABLES: + integer :: i,n,ivt ! index + integer :: stream_year_first_cropcal ! first year in crop calendar streams to use + integer :: stream_year_last_cropcal ! last year in crop calendar streams to use + integer :: model_year_align_cropcal ! align stream_year_first_cropcal with + integer :: nu_nml ! unit for namelist file + integer :: nml_error ! namelist i/o error flag + character(len=CL) :: stream_meshfile_cropcal ! crop calendar stream meshfile + character(len=CL) :: cropcal_mapalgo = 'nn' ! Mapping alogrithm + character(len=CL) :: cropcal_tintalgo = 'nearest' ! Time interpolation alogrithm + integer :: cropcal_offset = 0 ! Offset in time for dataset (sec) + integer :: rc + character(*), parameter :: subName = "('cropcaldyn_init')" + !----------------------------------------------------------------------- + ! + ! deal with namelist variables here in init + ! + namelist /cropcal_streams/ & + stream_year_first_cropcal, & + stream_year_last_cropcal, & + model_year_align_cropcal, & + allow_invalid_swindow_inputs, & + stream_fldFileName_swindow_start, & + stream_fldFileName_swindow_end, & + stream_fldFileName_cultivar_gdds, & + stream_meshfile_cropcal + + ! Default values for namelist + stream_year_first_cropcal = 1 ! first year in stream to use + stream_year_last_cropcal = 1 ! last year in stream to use + model_year_align_cropcal = 1 ! align stream_year_first_cropcal with this model year + allow_invalid_swindow_inputs = .false. + stream_meshfile_cropcal = '' + stream_fldFileName_swindow_start = '' + stream_fldFileName_swindow_end = '' + stream_fldFileName_cultivar_gdds = '' + ! Will need modification to work with mxsowings > 1 + ncft = mxpft - npcropmin + 1 ! Ignores generic crops + allocate(stream_varnames_sdate(ncft)) + allocate(stream_varnames_cultivar_gdds(ncft)) + do n = 1,ncft + ivt = npcropmin + n - 1 + write(stream_varnames_sdate(n),'(a,i0)') "sdate1_",ivt + write(stream_varnames_cultivar_gdds(n),'(a,i0)') "gdd1_",ivt + end do + + ! Read cropcal_streams namelist + if (masterproc) then + open( newunit=nu_nml, file=trim(NLFilename), status='old', iostat=nml_error ) + call find_nlgroup_name(nu_nml, 'cropcal_streams', status=nml_error) + if (nml_error == 0) then + read(nu_nml, nml=cropcal_streams,iostat=nml_error) + if (nml_error /= 0) then + call endrun(subname // ':: ERROR reading cropcal_streams namelist') + end if + else + call endrun(subname // ':: ERROR finding cropcal_streams namelist') + end if + close(nu_nml) + endif + call shr_mpi_bcast(stream_year_first_cropcal , mpicom) + call shr_mpi_bcast(stream_year_last_cropcal , mpicom) + call shr_mpi_bcast(model_year_align_cropcal , mpicom) + call shr_mpi_bcast(allow_invalid_swindow_inputs, mpicom) + call shr_mpi_bcast(stream_fldFileName_swindow_start, mpicom) + call shr_mpi_bcast(stream_fldFileName_swindow_end , mpicom) + call shr_mpi_bcast(stream_fldFileName_cultivar_gdds, mpicom) + call shr_mpi_bcast(stream_meshfile_cropcal , mpicom) + + if (masterproc) then + write(iulog,*) + write(iulog,*) 'cropcal_stream settings:' + write(iulog,'(a,i8)') ' stream_year_first_cropcal = ',stream_year_first_cropcal + write(iulog,'(a,i8)') ' stream_year_last_cropcal = ',stream_year_last_cropcal + write(iulog,'(a,i8)') ' model_year_align_cropcal = ',model_year_align_cropcal + write(iulog,'(a,l1)') ' allow_invalid_swindow_inputs = ',allow_invalid_swindow_inputs + write(iulog,'(a,a)' ) ' stream_fldFileName_swindow_start = ',trim(stream_fldFileName_swindow_start) + write(iulog,'(a,a)' ) ' stream_fldFileName_swindow_end = ',trim(stream_fldFileName_swindow_end) + write(iulog,'(a,a)' ) ' stream_fldFileName_cultivar_gdds = ',trim(stream_fldFileName_cultivar_gdds) + write(iulog,'(a,a)' ) ' stream_meshfile_cropcal = ',trim(stream_meshfile_cropcal) + do n = 1,ncft + write(iulog,'(a,a)' ) ' stream_varnames_sdate = ',trim(stream_varnames_sdate(n)) + write(iulog,'(a,a)' ) ' stream_varnames_cultivar_gdds = ',trim(stream_varnames_cultivar_gdds(n)) + end do + write(iulog,*) + endif + + ! CLMBuildNamelist checks that both start and end files are provided if either is + use_cropcal_rx_swindows = stream_fldFileName_swindow_start /= '' + use_cropcal_rx_cultivar_gdds = stream_fldFileName_cultivar_gdds /= '' + use_cropcal_streams = use_cropcal_rx_swindows .or. use_cropcal_rx_cultivar_gdds + + if (use_cropcal_rx_swindows) then + ! Initialize the cdeps data type sdat_cropcal_swindow_start + ! NOTE: stream_dtlimit 1.5 didn't work for some reason + call shr_strdata_init_from_inline(sdat_cropcal_swindow_start, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = trim(stream_meshfile_cropcal), & + stream_lev_dimname = 'null', & + stream_mapalgo = trim(cropcal_mapalgo), & + stream_filenames = (/trim(stream_fldFileName_swindow_start)/), & + stream_fldlistFile = stream_varnames_sdate, & + stream_fldListModel = stream_varnames_sdate, & + stream_yearFirst = stream_year_first_cropcal, & + stream_yearLast = stream_year_last_cropcal, & + stream_yearAlign = model_year_align_cropcal, & + stream_offset = cropcal_offset, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = cropcal_tintalgo, & + stream_name = 'sowing window start data', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! Initialize the cdeps data type sdat_cropcal_swindow_end + ! NOTE: stream_dtlimit 1.5 didn't work for some reason + call shr_strdata_init_from_inline(sdat_cropcal_swindow_end, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = trim(stream_meshfile_cropcal), & + stream_lev_dimname = 'null', & + stream_mapalgo = trim(cropcal_mapalgo), & + stream_filenames = (/trim(stream_fldFileName_swindow_end)/), & + stream_fldlistFile = stream_varnames_sdate, & + stream_fldListModel = stream_varnames_sdate, & + stream_yearFirst = stream_year_first_cropcal, & + stream_yearLast = stream_year_last_cropcal, & + stream_yearAlign = model_year_align_cropcal, & + stream_offset = cropcal_offset, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = cropcal_tintalgo, & + stream_name = 'sowing window end data', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + + ! Initialize the cdeps data type sdat_cropcal_cultivar_gdds + ! NOTE: stream_dtlimit 1.5 didn't work for some reason + if (use_cropcal_rx_cultivar_gdds) then + call shr_strdata_init_from_inline(sdat_cropcal_cultivar_gdds, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = trim(stream_meshfile_cropcal), & + stream_lev_dimname = 'null', & + stream_mapalgo = trim(cropcal_mapalgo), & + stream_filenames = (/trim(stream_fldFileName_cultivar_gdds)/), & + stream_fldlistFile = stream_varnames_cultivar_gdds, & + stream_fldListModel = stream_varnames_cultivar_gdds, & + stream_yearFirst = stream_year_first_cropcal, & + stream_yearLast = stream_year_last_cropcal, & + stream_yearAlign = model_year_align_cropcal, & + stream_offset = cropcal_offset, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = cropcal_tintalgo, & + stream_name = 'cultivar gdd data', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + + end subroutine cropcal_init + + !================================================================ + subroutine cropcal_advance( bounds ) + ! + ! Advance crop calendar streams + ! + ! !USES: + use clm_time_manager , only : get_curr_date + use dshr_strdata_mod , only : shr_strdata_advance + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + integer :: g, ig ! Indices + integer :: year ! year (0, ...) for nstep+1 + integer :: mon ! month (1, ..., 12) for nstep+1 + integer :: day ! day of month (1, ..., 31) for nstep+1 + integer :: sec ! seconds into current date for nstep+1 + integer :: mcdate ! Current model date (yyyymmdd) + integer :: rc + !----------------------------------------------------------------------- + + call get_curr_date(year, mon, day, sec) + mcdate = year*10000 + mon*100 + day + if (use_cropcal_rx_swindows) then + call shr_strdata_advance(sdat_cropcal_swindow_start, ymd=mcdate, tod=sec, logunit=iulog, istr='cropcaldyn', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + call shr_strdata_advance(sdat_cropcal_swindow_end, ymd=mcdate, tod=sec, logunit=iulog, istr='cropcaldyn', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + if (use_cropcal_rx_cultivar_gdds) then + call shr_strdata_advance(sdat_cropcal_cultivar_gdds, ymd=mcdate, tod=sec, logunit=iulog, istr='cropcaldyn', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + + if ( .not. allocated(g_to_ig) )then + allocate (g_to_ig(bounds%begg:bounds%endg) ) + ig = 0 + do g = bounds%begg,bounds%endg + ig = ig+1 + g_to_ig(g) = ig + end do + end if + + end subroutine cropcal_advance + + !================================================================ + + subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) + ! + ! Interpolate data stream information for crop calendars. + ! + ! !USES: + use CropType , only : crop_type + use PatchType , only : patch + use clm_time_manager, only : get_curr_days_per_year + use pftconMod , only : pftname + use dshr_methods_mod , only : dshr_fldbun_getfldptr + ! + ! !ARGUMENTS: + implicit none + type(bounds_type) , intent(in) :: bounds + integer , intent(in) :: num_pcropp ! number of prog. crop patches in filter + integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + type(crop_type) , intent(inout) :: crop_inst + ! + ! !LOCAL VARIABLES: + integer :: ivt, p, ip, ig + integer :: nc, fp + integer :: dayspyr + integer :: n, g + integer :: lsize + integer :: rc + integer :: begp, endp + real(r8), pointer :: dataptr1d_swindow_start(:) + real(r8), pointer :: dataptr1d_swindow_end (:) + real(r8), pointer :: dataptr1d_cultivar_gdds(:) + real(r8), pointer :: dataptr2d_swindow_start(:,:) + real(r8), pointer :: dataptr2d_swindow_end (:,:) + real(r8), pointer :: dataptr2d_cultivar_gdds(:,:) + !----------------------------------------------------------------------- + + associate( & + starts => crop_inst%rx_swindow_starts_thisyr_patch, & + ends => crop_inst%rx_swindow_ends_thisyr_patch & + ) + + SHR_ASSERT_FL( (lbound(g_to_ig,1) <= bounds%begg ), sourcefile, __LINE__) + SHR_ASSERT_FL( (ubound(g_to_ig,1) >= bounds%endg ), sourcefile, __LINE__) + + ! Get pointer for stream data that is time and spatially interpolate to model time and grid + ! Place all data from each type into a temporary 2d array + lsize = bounds%endg - bounds%begg + 1 + + begp = bounds%begp + endp= bounds%endp + + dayspyr = get_curr_days_per_year() + + ! Read prescribed sowing window start dates from input files + allocate(dataptr2d_swindow_start(lsize, ncft)) + dataptr2d_swindow_start(:,:) = -1._r8 + allocate(dataptr2d_swindow_end (lsize, ncft)) + dataptr2d_swindow_end(:,:) = -1._r8 + if (use_cropcal_rx_swindows) then + ! Starting with npcropmin will skip generic crops + do n = 1, ncft + call dshr_fldbun_getFldPtr(sdat_cropcal_swindow_start%pstrm(1)%fldbun_model, trim(stream_varnames_sdate(n)), & + fldptr1=dataptr1d_swindow_start, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + call dshr_fldbun_getFldPtr(sdat_cropcal_swindow_end%pstrm(1)%fldbun_model, trim(stream_varnames_sdate(n)), & + fldptr1=dataptr1d_swindow_end, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize + ! So an explicit loop is required here + do g = 1,lsize + + ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. + if (dataptr1d_swindow_start(g) <= 0 .or. dataptr1d_swindow_start(g) > dayspyr & + .or. dataptr1d_swindow_end(g) <= 0 .or. dataptr1d_swindow_end(g) > dayspyr) then + dataptr1d_swindow_start(g) = -1 + dataptr1d_swindow_end (g) = -1 + end if + + dataptr2d_swindow_start(g,n) = dataptr1d_swindow_start(g) + dataptr2d_swindow_end (g,n) = dataptr1d_swindow_end (g) + end do + end do + + ! Set sowing window for each gridcell/patch combination + do fp = 1, num_pcropp + p = filter_pcropp(fp) + ivt = patch%itype(p) + ! Will skip generic crops + if (ivt >= npcropmin) then + n = ivt - npcropmin + 1 + ! vegetated pft + ig = g_to_ig(patch%gridcell(p)) + starts(p,1) = dataptr2d_swindow_start(ig,n) + ends(p,1) = dataptr2d_swindow_end (ig,n) + else + write(iulog,'(a,i0)') 'cropcal_interp(), prescribed sowing windows: Crop patch has ivt ',ivt + call ESMF_Finalize(endflag=ESMF_END_ABORT) + endif + end do + + ! Ensure that, if mxsowings > 1, sowing windows are ordered such that ENDS are monotonically increasing. This is necessary because of how get_swindow() works. + if (mxsowings > 1) then + if (any(ends(begp:endp,2:mxsowings) <= ends(begp:endp,1:mxsowings-1) .and. & + ends(begp:endp,2:mxsowings) >= 1)) then + write(iulog, *) 'Sowing window inputs must be ordered such that end dates are monotonically increasing.' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + + ! Handle invalid sowing window values + if (any(starts(begp:endp,:) < 1 .or. ends(begp:endp,:) < 1)) then + ! Fail if not allowing fallback to paramfile sowing windows + if ((.not. allow_invalid_swindow_inputs) .and. any(all(starts(begp:endp,:) < 1, dim=2) .and. patch%wtgcell > 0._r8 .and. patch%itype >= npcropmin)) then + write(iulog, *) 'At least one crop in one gridcell has invalid prescribed sowing window start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_swindow_inputs to .true.' + write(iulog, *) 'Affected crops:' + do ivt = npcropmin, mxpft + do fp = 1, num_pcropp + p = filter_pcropp(fp) + if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. all(starts(p,:) < 1)) then + write(iulog, *) ' ',pftname(ivt),' (',ivt,')' + exit ! Stop looking for patches of this type + end if + end do + end do + call ESMF_Finalize(endflag=ESMF_END_ABORT) + + ! Fail if a sowing window start date is prescribed without an end date (or vice versa) + else if (any((starts(begp:endp,:) >= 1 .and. ends(begp:endp,:) < 1) .or. (starts(begp:endp,:) < 1 .and. ends(begp:endp,:) >= 1))) then + write(iulog, *) 'Every prescribed sowing window start date must have a corresponding end date.' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + + end if ! use_cropcal_rx_swindows + deallocate(dataptr2d_swindow_start) + deallocate(dataptr2d_swindow_end) + + allocate(dataptr2d_cultivar_gdds(lsize, ncft)) + if (use_cropcal_rx_cultivar_gdds) then + ! Read prescribed cultivar GDDs from input files + ! Starting with npcropmin will skip generic crops + do n = 1, ncft + call dshr_fldbun_getFldPtr(sdat_cropcal_cultivar_gdds%pstrm(1)%fldbun_model, trim(stream_varnames_cultivar_gdds(n)), & + fldptr1=dataptr1d_cultivar_gdds, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize + ! So an explicit loop is required here + do g = 1,lsize + + ! If read-in value is invalid, have PlantCrop() set gddmaturity to PFT-default value. + if (dataptr1d_cultivar_gdds(g) < 0 .or. dataptr1d_cultivar_gdds(g) > 1000000._r8) then + dataptr1d_cultivar_gdds(g) = -1 + end if + + dataptr2d_cultivar_gdds(g,n) = dataptr1d_cultivar_gdds(g) + end do + end do + + ! Set rx_cultivar_gdd for each gridcell/patch combination + do fp = 1, num_pcropp + p = filter_pcropp(fp) + + ivt = patch%itype(p) + ! Will skip generic crops + if (ivt >= npcropmin) then + n = ivt - npcropmin + 1 + + if (n > ncft) then + write(iulog,'(a,i0,a,i0,a)') 'n (',n,') > ncft (',ncft,')' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! vegetated pft + ig = g_to_ig(patch%gridcell(p)) + + if (ig > lsize) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') > lsize (',lsize,')' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + crop_inst%rx_cultivar_gdds_thisyr_patch(p,1) = dataptr2d_cultivar_gdds(ig,n) + + else + write(iulog,'(a,i0)') 'cropcal_interp(), rx_cultivar_gdds: Crop patch has ivt ',ivt + call ESMF_Finalize(endflag=ESMF_END_ABORT) + endif + end do + write(iulog,*) 'cropcal_interp(): Reading cultivar_gdds file DONE' + end if ! use_cropcal_rx_cultivar_gdds + + deallocate(dataptr2d_cultivar_gdds) + + end associate + + end subroutine cropcal_interp + +end module cropcalStreamMod diff --git a/src/cpl/share_esmf/laiStreamMod.F90 b/src/cpl/share_esmf/laiStreamMod.F90 index a44f0b7198..3e4074fbde 100644 --- a/src/cpl/share_esmf/laiStreamMod.F90 +++ b/src/cpl/share_esmf/laiStreamMod.F90 @@ -7,7 +7,7 @@ module laiStreamMod ! Read LAI from stream ! ! !USES: - use ESMF + use ESMF , only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU, ESMF_Finalize, ESMF_END_ABORT use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_CL, CS => shr_kind_CS use dshr_strdata_mod , only : shr_strdata_type use decompMod , only : bounds_type @@ -54,7 +54,7 @@ subroutine lai_init(bounds) type(bounds_type), intent(in) :: bounds ! bounds ! ! !LOCAL VARIABLES: - integer :: i,n ! index + integer :: i,n, ig, g ! index integer :: stream_year_first_lai ! first year in Lai stream to use integer :: stream_year_last_lai ! last year in Lai stream to use integer :: model_year_align_lai ! align stream_year_first_lai with @@ -151,6 +151,15 @@ subroutine lai_init(bounds) if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then call ESMF_Finalize(endflag=ESMF_END_ABORT) end if + + if ( .not. allocated(g_to_ig) )then + allocate (g_to_ig(bounds%begg:bounds%endg) ) + ig = 0 + do g = bounds%begg,bounds%endg + ig = ig+1 + g_to_ig(g) = ig + end do + end if end subroutine lai_init diff --git a/src/cpl/share_esmf/lnd_set_decomp_and_domain.F90 b/src/cpl/share_esmf/lnd_set_decomp_and_domain.F90 index c3556193a7..592d5f441c 100644 --- a/src/cpl/share_esmf/lnd_set_decomp_and_domain.F90 +++ b/src/cpl/share_esmf/lnd_set_decomp_and_domain.F90 @@ -1,6 +1,19 @@ module lnd_set_decomp_and_domain - use ESMF + use ESMF , only : ESMF_VM, ESMF_MESH, ESMF_DistGrid, ESMF_Field + use ESMF , only : ESMF_RouteHandle, ESMF_SUCCESS, ESMF_MeshCreate + use ESMF , only : ESMF_FileFormat_ESMFMESH, ESMF_VMLogMemInfo + use ESMF , only : ESMF_FieldCreate, ESMF_FieldRedistStore, ESMF_FieldRedist + use ESMF , only : ESMF_FieldGet, ESMF_GridCreateNoPeriDimUfrm + use ESMF , only : ESMF_FieldRegrid, ESMF_FieldRegridStore + use ESMF , only : ESMF_Grid, ESMF_ARRAY, ESMF_DistGridCreate, ESMF_TYPEKIND_R8 + use ESMF , only : ESMF_MESHLOC_ELEMENT, ESMF_FieldCreate, ESMF_STAGGERLOC_CORNER, ESMF_STAGGERLOC_CENTER + use ESMF , only : ESMF_REGRIDMETHOD_CONSERVE, ESMF_NORMTYPE_DSTAREA + use ESMF , only : ESMF_UNMAPPEDACTION_IGNORE, ESMF_TERMORDER_SRCSEQ + use ESMF , only : ESMF_REGION_TOTAL, ESMF_REDUCE_SUM, ESMF_ARRAYCREATE + use ESMF , only : ESMF_MeshGet, ESMF_DistGridGet, ESMF_LOGFOUNDERROR + use ESMF , only : ESMF_LOGERR_PASSTHRU, ESMF_VMAllReduce, ESMF_FieldRegridGetArea + use ESMF , only : ESMF_FieldDestroy use shr_kind_mod , only : r8 => shr_kind_r8, cl=>shr_kind_cl use shr_sys_mod , only : shr_sys_abort use shr_log_mod , only : errMsg => shr_log_errMsg @@ -53,20 +66,25 @@ subroutine lnd_set_decomp_and_domain_from_readmesh(driver, vm, meshfile_lnd, mes integer , intent(out) :: rc ! local variables - type(ESMF_Mesh) :: mesh_maskinput - type(ESMF_Mesh) :: mesh_lndinput - type(ESMF_DistGrid) :: distgrid_ctsm - integer :: g,n ! indices - integer :: nlnd, nocn ! local size of arrays - integer :: gsize ! global size of grid - logical :: isgrid2d ! true => grid is 2d - type(bounds_type) :: bounds ! bounds - integer :: begg,endg ! local bounds - integer , pointer :: gindex_lnd(:) ! global index space for just land points - integer , pointer :: gindex_ocn(:) ! global index space for just ocean points - integer , pointer :: gindex_ctsm(:) ! global index space for land and ocean points - integer , pointer :: lndmask_glob(:) - real(r8) , pointer :: lndfrac_glob(:) + type(ESMF_Mesh) :: mesh_maskinput + type(ESMF_Mesh) :: mesh_lndinput + type(ESMF_DistGrid) :: distgrid_ctsm + type(ESMF_Field) :: field_lnd + type(ESMF_Field) :: field_ctsm + type(ESMF_RouteHandle) :: rhandle_lnd2ctsm + integer :: g,n ! indices + integer :: nlnd, nocn ! local size of arrays + integer :: gsize ! global size of grid + logical :: isgrid2d ! true => grid is 2d + type(bounds_type) :: bounds ! bounds + integer :: begg,endg ! local bounds + integer , pointer :: gindex_lnd(:) ! global index space for just land points + integer , pointer :: gindex_ocn(:) ! global index space for just ocean points + integer , pointer :: gindex_ctsm(:) ! global index space for land and ocean points + integer , pointer :: lndmask_glob(:) + real(r8) , pointer :: lndfrac_glob(:) + real(r8) , pointer :: lndfrac_loc_input(:) + real(r8) , pointer :: dataptr1d(:) !------------------------------------------------------------------------------- rc = ESMF_SUCCESS @@ -86,8 +104,6 @@ subroutine lnd_set_decomp_and_domain_from_readmesh(driver, vm, meshfile_lnd, mes ! Determine global 2d sizes from read of dimensions of surface dataset and allocate global memory call lnd_get_global_dims(ni, nj, gsize, isgrid2d) - allocate(lndmask_glob(gsize)); lndmask_glob(:) = 0 - allocate(lndfrac_glob(gsize)); lndfrac_glob(:) = 0._r8 ! Read in the land mesh from the file mesh_lndinput = ESMF_MeshCreate(filename=trim(meshfile_lnd), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) @@ -96,18 +112,29 @@ subroutine lnd_set_decomp_and_domain_from_readmesh(driver, vm, meshfile_lnd, mes if (trim(driver) == 'cmeps') then ! Read in mask meshfile if needed if (trim(meshfile_mask) /= trim(meshfile_lnd)) then +#ifdef DEBUG + ! This will get added to the ESMF PET files if DEBUG=TRUE and CREATE_ESMF_PET_FILES=TRUE + call ESMF_VMLogMemInfo("clm: Before lnd mesh create in ") +#endif mesh_maskinput = ESMF_MeshCreate(filename=trim(meshfile_mask), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return - end if - - ! Determine lndmask_glob and lndfrac_glob - if (trim(meshfile_mask) /= trim(meshfile_lnd)) then +#ifdef DEBUG + ! This will get added to the ESMF PET files if DEBUG=TRUE and CREATE_ESMF_PET_FILES=TRUE + call ESMF_VMLogMemInfo("clm: After lnd mesh create in ") +#endif + ! Determine lndmask_glob and lndfrac_glob ! obain land mask and land fraction by mapping ocean mesh conservatively to land mesh - call lnd_set_lndmask_from_maskmesh(mesh_lndinput, mesh_maskinput, vm, gsize, lndmask_glob, lndfrac_glob, rc) + ! Note that lndmask_glob and lndfrac_loc_input are allocated in lnd_set_lndmask_from_maskmesh + call lnd_set_lndmask_from_maskmesh(mesh_lndinput, mesh_maskinput, vm, gsize, lndmask_glob, & + lndfrac_loc_input, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return +#ifdef DEBUG + ! This will get added to the ESMF PET files if DEBUG=TRUE and CREATE_ESMF_PET_FILES=TRUE + call ESMF_VMLogMemInfo("clm: After lnd_set_lndmask_from_maskmesh ") +#endif else ! obtain land mask from land mesh file - assume that land frac is identical to land mask - call lnd_set_lndmask_from_lndmesh(mesh_lndinput, vm, gsize, lndmask_glob, lndfrac_glob, rc) + call lnd_set_lndmask_from_lndmesh(mesh_lndinput, vm, gsize, lndmask_glob, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return end if else if (trim(driver) == 'lilac') then @@ -142,16 +169,14 @@ subroutine lnd_set_decomp_and_domain_from_readmesh(driver, vm, meshfile_lnd, mes ! Initialize domain data structure call domain_init(domain=ldomain, isgrid2d=isgrid2d, ni=ni, nj=nj, nbeg=begg, nend=endg) - ! Determine ldomain%mask and ldomain%frac using ctsm decomposition + ! Determine ldomain%mask do g = begg, endg n = 1 + (g - begg) ldomain%mask(g) = lndmask_glob(gindex_lnd(n)) - ldomain%frac(g) = lndfrac_glob(gindex_lnd(n)) end do ! Deallocate global pointer memory deallocate(lndmask_glob) - deallocate(lndfrac_glob) ! Generate a ctsm global index that includes both land and ocean points nocn = size(gindex_ocn) @@ -174,6 +199,53 @@ subroutine lnd_set_decomp_and_domain_from_readmesh(driver, vm, meshfile_lnd, mes call lnd_set_ldomain_gridinfo_from_mesh(mesh_ctsm, vm, gindex_ctsm, begg, endg, isgrid2d, ni, nj, ldomain, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return + ! Set ldomain%lfrac + ! Create fields on the input decomp and ctsm decomp + ! Determine route handle to do a redist from input mesh read decomp to ctsm decomp + ! Fill in the field on the input mesh read decomp with lndfrac_loc_input values + ! Redistribute field_lnd to field_ctsm + + ! Determine ldomain%frac using ctsm decomposition + if (trim(driver) == 'cmeps') then + + if (trim(meshfile_mask) /= trim(meshfile_lnd)) then + field_lnd = ESMF_FieldCreate(mesh_lndinput, ESMF_TYPEKIND_R8, meshloc=ESMF_MESHLOC_ELEMENT, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + field_ctsm = ESMF_FieldCreate(mesh_ctsm, ESMF_TYPEKIND_R8, meshloc=ESMF_MESHLOC_ELEMENT, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_FieldRedistStore(field_lnd, field_ctsm, routehandle=rhandle_lnd2ctsm, & + ignoreUnmatchedIndices=.true., rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + call ESMF_FieldGet(field_lnd, farrayptr=dataptr1d, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + do n = 1,size(dataptr1d) + dataptr1d(n) = lndfrac_loc_input(n) + end do + call ESMF_FieldRedist(field_lnd, field_ctsm, routehandle=rhandle_lnd2ctsm, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + call ESMF_FieldGet(field_ctsm, farrayptr=dataptr1d, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + do g = begg, endg + n = 1 + (g - begg) + ldomain%frac(g) = dataptr1d(n) + end do + else + ! ASSUME that land fraction is identical to land mask in this case + do g = begg, endg + ldomain%frac(g) = ldomain%mask(g) + end do + end if + + else + + do g = begg, endg + n = 1 + (g - begg) + ldomain%frac(g) = lndfrac_glob(gindex_lnd(n)) + end do + deallocate(lndfrac_glob) + + end if + ! Deallocate local pointer memory deallocate(gindex_lnd) deallocate(gindex_ocn) @@ -185,7 +257,6 @@ end subroutine lnd_set_decomp_and_domain_from_readmesh subroutine lnd_set_mesh_for_single_column(scol_lon, scol_lat, mesh, rc) ! Generate a mesh for single column - use netcdf use clm_varcon, only : spval ! input/output variables @@ -339,7 +410,7 @@ subroutine lnd_get_global_dims(ni, nj, gsize, isgrid2d) end subroutine lnd_get_global_dims !=============================================================================== - subroutine lnd_set_lndmask_from_maskmesh(mesh_lnd, mesh_mask, vm, gsize, lndmask_glob, lndfrac_glob, rc) + subroutine lnd_set_lndmask_from_maskmesh(mesh_lnd, mesh_mask, vm, gsize, lndmask_glob, lndfrac_loc, rc) ! If the landfrac/landmask file does not exists then determine the ! land fraction and land mask on the land grid by mapping the mask @@ -354,7 +425,7 @@ subroutine lnd_set_lndmask_from_maskmesh(mesh_lnd, mesh_mask, vm, gsize, lndmask type(ESMF_VM) , intent(in) :: vm integer , intent(in) :: gsize integer , pointer :: lndmask_glob(:) - real(r8) , pointer :: lndfrac_glob(:) + real(r8) , pointer :: lndfrac_loc(:) integer , intent(out) :: rc ! local variables: @@ -366,8 +437,6 @@ subroutine lnd_set_lndmask_from_maskmesh(mesh_lnd, mesh_mask, vm, gsize, lndmask integer , pointer :: gindex_input(:) ! global index space for land and ocean points integer , pointer :: lndmask_loc(:) integer , pointer :: itemp_glob(:) - real(r8) , pointer :: rtemp_glob(:) - real(r8) , pointer :: lndfrac_loc(:) real(r8) , pointer :: maskmask_loc(:) ! on ocean mesh real(r8) , pointer :: maskfrac_loc(:) ! on land mesh real(r8) , pointer :: dataptr1d(:) @@ -393,6 +462,8 @@ subroutine lnd_set_lndmask_from_maskmesh(mesh_lnd, mesh_mask, vm, gsize, lndmask klen = len_trim(flandfrac) - 3 ! remove the .nc flandfrac_status = flandfrac(1:klen)//'.status' + allocate(lndmask_glob(gsize)); lndmask_glob(:) = 0 + ! Determine if lndfrac/lndmask file exists inquire(file=trim(flandfrac), exist=lexist) @@ -403,9 +474,6 @@ subroutine lnd_set_lndmask_from_maskmesh(mesh_lnd, mesh_mask, vm, gsize, lndmask write(iulog,*) write(iulog,'(a)')' Reading in land fraction and land mask from '//trim(flandfrac) end if - call lnd_set_read_write_landmask(trim(flandfrac), trim(flandfrac_status), .false., .true., & - lndmask_glob, lndfrac_glob, size(lndmask_glob)) - else ! If file does not exist - compute lndmask and lndfrac and write to output file @@ -483,39 +551,22 @@ subroutine lnd_set_lndmask_from_maskmesh(mesh_lnd, mesh_mask, vm, gsize, lndmask lndmask_glob(:) = int(itemp_glob(:)) deallocate(itemp_glob) - ! Determine ldomain%frac using both input and ctsm decompositions - ! lndfrac_glob is filled using the input decomposition and - ! ldomin%frac is set using the ctsm decomposition - allocate(rtemp_glob(gsize)) - do n = 1,lsize_lnd - lndfrac_glob(gindex_input(n)) = lndfrac_loc(n) - end do - call ESMF_VMAllReduce(vm, sendData=lndfrac_glob, recvData=rtemp_glob, count=gsize, & - reduceflag=ESMF_REDUCE_SUM, rc=rc) - lndfrac_glob(:) = rtemp_glob(:) - deallocate(rtemp_glob) - ! deallocate memory deallocate(maskmask_loc) deallocate(lndmask_loc) - deallocate(lndfrac_loc) - - call lnd_set_read_write_landmask(trim(flandfrac), trim(flandfrac_status), .true., .false., & - lndmask_glob, lndfrac_glob, size(lndmask_glob)) end if end subroutine lnd_set_lndmask_from_maskmesh !=============================================================================== - subroutine lnd_set_lndmask_from_lndmesh(mesh_lnd, vm, gsize, lndmask_glob, lndfrac_glob, rc) + subroutine lnd_set_lndmask_from_lndmesh(mesh_lnd, vm, gsize, lndmask_glob, rc) ! input/out variables type(ESMF_Mesh) , intent(in) :: mesh_lnd type(ESMF_VM) , intent(in) :: vm integer , intent(in) :: gsize integer , pointer :: lndmask_glob(:) - real(r8) , pointer :: lndfrac_glob(:) integer , intent(out) :: rc ! local variables: @@ -550,6 +601,9 @@ subroutine lnd_set_lndmask_from_lndmesh(mesh_lnd, vm, gsize, lndmask_glob, lndfr allocate(itemp_glob(gsize)) call ESMF_DistGridGet(distgrid, 0, seqIndexList=gindex, rc=rc) if (chkerr(rc,__LINE__,u_FILE_u)) return + + allocate(lndmask_glob(gsize)); lndmask_glob(:) = 0 + do n = 1,lsize lndmask_glob(gindex(n)) = lndmask_loc(n) end do @@ -560,9 +614,6 @@ subroutine lnd_set_lndmask_from_lndmesh(mesh_lnd, vm, gsize, lndmask_glob, lndfr deallocate(gindex) deallocate(lndmask_loc) - ! ASSUME that land fraction is identical to land mask in this case - lndfrac_glob(:) = lndmask_glob(:) - end subroutine lnd_set_lndmask_from_lndmesh !=============================================================================== @@ -592,7 +643,7 @@ subroutine lnd_set_lndmask_from_fatmlndfrc(mask, frac, ni, nj) logical :: readvar ! read variable in or not integer , allocatable :: idata2d(:,:) real(r8), allocatable :: rdata2d(:,:) - integer :: unitn + integer :: unitn character(len=32) :: subname = 'lnd_set_mask_from_fatmlndfrc' ! subroutine name !----------------------------------------------------------------------- @@ -605,6 +656,7 @@ subroutine lnd_set_lndmask_from_fatmlndfrc(mask, frac, ni, nj) if (masterproc) then write(iulog,*)'lat/lon grid flag (isgrid2d) is ',isgrid2d end if + allocate(frac(ni*nj), mask(ni*nj)) if (isgrid2d) then ! Grid is 2d @@ -804,7 +856,7 @@ subroutine lnd_set_read_write_landmask(flandfrac, flandfrac_status, write_file, integer :: dimid integer :: iun integer :: ioe - integer :: ier + integer :: ier logical :: lexists !------------------------------------------------------------------------------- @@ -845,7 +897,7 @@ subroutine lnd_set_read_write_landmask(flandfrac, flandfrac_status, write_file, close(iun) write(iulog,'(a)')' Successfully wrote land fraction/mask status file '//trim(flandfrac_status) end if - + else if (read_file) then if (masterproc) then diff --git a/src/cpl/share_esmf/ndepStreamMod.F90 b/src/cpl/share_esmf/ndepStreamMod.F90 index 1a51382862..b1c9d1a0e5 100644 --- a/src/cpl/share_esmf/ndepStreamMod.F90 +++ b/src/cpl/share_esmf/ndepStreamMod.F90 @@ -7,7 +7,7 @@ module ndepStreamMod ! interpolation. ! ! !USES - use ESMF + use ESMF , only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU, ESMF_Finalize, ESMF_END_ABORT use dshr_strdata_mod , only : shr_strdata_type use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_cl, CS => shr_kind_cs use spmdMod , only : mpicom, masterproc, iam diff --git a/src/cpl/utils/lnd_import_export_utils.F90 b/src/cpl/utils/lnd_import_export_utils.F90 index 032cb19b6f..4b7941da5b 100644 --- a/src/cpl/utils/lnd_import_export_utils.F90 +++ b/src/cpl/utils/lnd_import_export_utils.F90 @@ -140,12 +140,13 @@ end subroutine check_for_errors !============================================================================= - subroutine check_for_nans(array, fname, begg) + subroutine check_for_nans(array, fname, begg, direction) ! input/output variables real(r8) , intent(in) :: array(:) character(len=*) , intent(in) :: fname integer , intent(in) :: begg + character(len=*) , intent(in) :: direction ! local variables integer :: i @@ -161,7 +162,7 @@ subroutine check_for_nans(array, fname, begg) write(iulog,*) "NaN found in field ", trim(fname), ' at gridcell index ',begg+i-1 end if end do - call shr_sys_abort(' ERROR: One or more of the output from CLM to the coupler are NaN ' ) + call shr_sys_abort(' ERROR: One or more of the CTSM cap '//direction//' fields are NaN ' ) end if end subroutine check_for_nans diff --git a/src/dyn_subgrid/dynFATESLandUseChangeMod.F90 b/src/dyn_subgrid/dynFATESLandUseChangeMod.F90 new file mode 100644 index 0000000000..45f4340d6a --- /dev/null +++ b/src/dyn_subgrid/dynFATESLandUseChangeMod.F90 @@ -0,0 +1,208 @@ +module dynFATESLandUseChangeMod + +#include "shr_assert.h" + + !--------------------------------------------------------------------------- + ! !DESCRIPTION: + ! Handle reading of the land use harmonization (LUH2) dataset + + ! !USES: + use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_log_mod , only : errMsg => shr_log_errMsg + use decompMod , only : bounds_type, BOUNDS_LEVEL_PROC + use abortutils , only : endrun + use dynFileMod , only : dyn_file_type + use dynVarTimeUninterpMod , only : dyn_var_time_uninterp_type + use clm_varcon , only : grlnd + use clm_varctl , only : iulog + + implicit none + + private + + ! Yearly landuse transition rate (fraction of gridcell), landuse transition name x gridcell + real(r8), allocatable, public :: landuse_transitions(:,:) + + ! Landuse state at beginning of year (fraction of gridcell), landuse state name x gridcell + real(r8), allocatable, public :: landuse_states(:,:) + + ! Number of landuse transition and state names + integer, public, parameter :: num_landuse_transition_vars = 108 + integer, public, parameter :: num_landuse_state_vars = 12 + + ! landuse filename + type(dyn_file_type), target :: dynFatesLandUse_file + + ! Land use name arrays + character(len=5), public, parameter :: landuse_state_varnames(num_landuse_state_vars) = & + [character(len=5) :: 'primf','primn','secdf','secdn','pastr','range', & + 'urban','c3ann','c4ann','c3per','c4per','c3nfx'] + + character(len=14), public, parameter :: landuse_transition_varnames(num_landuse_transition_vars) = & + [character(len=14) :: 'primf_to_secdn','primf_to_pastr','primf_to_range','primf_to_urban', & + 'primf_to_c3ann','primf_to_c4ann','primf_to_c3per','primf_to_c4per','primf_to_c3nfx', & + 'primn_to_secdf','primn_to_pastr','primn_to_range','primn_to_urban', & + 'primn_to_c3ann','primn_to_c4ann','primn_to_c3per','primn_to_c4per','primn_to_c3nfx', & + 'secdf_to_secdn','secdf_to_pastr','secdf_to_range','secdf_to_urban', & + 'secdf_to_c3ann','secdf_to_c4ann','secdf_to_c3per','secdf_to_c4per','secdf_to_c3nfx', & + 'secdn_to_secdf','secdn_to_pastr','secdn_to_range','secdn_to_urban', & + 'secdn_to_c3ann','secdn_to_c4ann','secdn_to_c3per','secdn_to_c4per','secdn_to_c3nfx', & + 'pastr_to_secdf','pastr_to_secdn','pastr_to_range','pastr_to_urban', & + 'pastr_to_c3ann','pastr_to_c4ann','pastr_to_c3per','pastr_to_c4per','pastr_to_c3nfx', & + 'range_to_secdf','range_to_secdn','range_to_pastr','range_to_urban', & + 'range_to_c3ann','range_to_c4ann','range_to_c3per','range_to_c4per','range_to_c3nfx', & + 'urban_to_secdf','urban_to_secdn','urban_to_pastr','urban_to_range', & + 'urban_to_c3ann','urban_to_c4ann','urban_to_c3per','urban_to_c4per','urban_to_c3nfx', & + 'c3ann_to_c4ann','c3ann_to_c3per','c3ann_to_c4per','c3ann_to_c3nfx', & + 'c3ann_to_secdf','c3ann_to_secdn','c3ann_to_pastr','c3ann_to_range','c3ann_to_urban', & + 'c4ann_to_c3ann','c4ann_to_c3per','c4ann_to_c4per','c4ann_to_c3nfx', & + 'c4ann_to_secdf','c4ann_to_secdn','c4ann_to_pastr','c4ann_to_range','c4ann_to_urban', & + 'c3per_to_c3ann','c3per_to_c4ann','c3per_to_c4per','c3per_to_c3nfx', & + 'c3per_to_secdf','c3per_to_secdn','c3per_to_pastr','c3per_to_range','c3per_to_urban', & + 'c4per_to_c3ann','c4per_to_c4ann','c4per_to_c3per','c4per_to_c3nfx', & + 'c4per_to_secdf','c4per_to_secdn','c4per_to_pastr','c4per_to_range','c4per_to_urban', & + 'c3nfx_to_c3ann','c3nfx_to_c4ann','c3nfx_to_c3per','c3nfx_to_c4per', & + 'c3nfx_to_secdf','c3nfx_to_secdn','c3nfx_to_pastr','c3nfx_to_range','c3nfx_to_urban'] + + type(dyn_var_time_uninterp_type) :: landuse_transition_vars(num_landuse_transition_vars) ! value of each landuse variable + type(dyn_var_time_uninterp_type) :: landuse_state_vars(num_landuse_state_vars) ! value of each landuse variable + + public :: dynFatesLandUseInit + public :: dynFatesLandUseInterp + +contains + + !----------------------------------------------------------------------- + subroutine dynFatesLandUseInit(bounds, landuse_filename) + + ! !DESCRIPTION: + ! Initialize data structures for land use information. + + ! !USES: + use clm_varctl , only : use_cn, use_fates_luh + use dynVarTimeUninterpMod , only : dyn_var_time_uninterp_type + use dynTimeInfoMod , only : YEAR_POSITION_START_OF_TIMESTEP + use dynTimeInfoMod , only : YEAR_POSITION_END_OF_TIMESTEP + + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds ! proc-level bounds + character(len=*) , intent(in) :: landuse_filename ! name of file containing land use information + + ! !LOCAL VARIABLES + integer :: varnum, i ! counter for harvest variables + integer :: landuse_shape(1) ! land use shape + integer :: num_points ! number of spatial points + integer :: ier ! error code + real(r8), allocatable :: this_data(:) ! data for a single harvest variable + ! + character(len=*), parameter :: subname = 'dynFatesLandUseInit' + !----------------------------------------------------------------------- + + SHR_ASSERT_ALL(bounds%level == BOUNDS_LEVEL_PROC, subname // ': argument must be PROC-level bounds') + + if (use_cn) return ! Use this as a protection in lieu of build namelist check? + + ! Allocate and initialize the land use arrays + allocate(landuse_states(num_landuse_state_vars,bounds%begg:bounds%endg),stat=ier) + if (ier /= 0) then + call endrun(msg=' allocation error for landuse_states'//errMsg(__FILE__, __LINE__)) + end if + allocate(landuse_transitions(num_landuse_transition_vars,bounds%begg:bounds%endg),stat=ier) + if (ier /= 0) then + call endrun(msg=' allocation error for landuse_transitions'//errMsg(__FILE__, __LINE__)) + end if + + landuse_states = 0._r8 + landuse_transitions = 0._r8 + + if (use_fates_luh) then + + ! Generate the dyn_file_type object. Note that the land use data being read in is for the + ! transitions occuring within the current year + dynFatesLandUse_file = dyn_file_type(landuse_filename, YEAR_POSITION_END_OF_TIMESTEP) + + ! Get initial land use data + num_points = (bounds%endg - bounds%begg + 1) + landuse_shape(1) = num_points ! Does this need an explicit array shape to be passed to the constructor? + do varnum = 1, num_landuse_transition_vars + landuse_transition_vars(varnum) = dyn_var_time_uninterp_type( & + dyn_file=dynFatesLandUse_file, varname=landuse_transition_varnames(varnum), & + dim1name=grlnd, conversion_factor=1.0_r8, & + do_check_sums_equal_1=.false., data_shape=landuse_shape) + end do + do varnum = 1, num_landuse_state_vars + landuse_state_vars(varnum) = dyn_var_time_uninterp_type( & + dyn_file=dynFatesLandUse_file, varname=landuse_state_varnames(varnum), & + dim1name=grlnd, conversion_factor=1.0_r8, & + do_check_sums_equal_1=.false., data_shape=landuse_shape) + end do + end if + + ! Since fates needs state data during initialization, make sure to call + ! the interpolation routine at the start + call dynFatesLandUseInterp(bounds,init_state=.true.) + + end subroutine dynFatesLandUseInit + + + !----------------------------------------------------------------------- + subroutine dynFatesLandUseInterp(bounds, init_state) + + + ! !DESCRIPTION: + ! Get landuse state and transition rate data + ! + ! Note that the landuse state and transition rates are stored as fractions + ! of a gridcell for a given year, which is constant throughout the year. + ! This routine does not interpolate the rate at this time; any interpolation + ! is handled by FATES. + + ! !USES: + use dynTimeInfoMod , only : time_info_type + use clm_varctl , only : use_cn + + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds ! proc-level bounds + logical, optional, intent(in) :: init_state ! fates needs state for initialization + + ! !LOCAL VARIABLES: + integer :: varnum ! variable number index + logical :: init_flag ! local variable to take optional argument value + real(r8), allocatable :: this_data(:) ! temporary array to take get_current_date results + + character(len=*), parameter :: subname = 'dynFatesLandUseInterp' + !----------------------------------------------------------------------- + SHR_ASSERT_ALL(bounds%level == BOUNDS_LEVEL_PROC, subname // ': argument must be PROC-level bounds') + + ! Determine if the optional initialization state flag is present and if so + ! set the init_flag to that optional input value + init_flag = .false. + if (present(init_state)) then + init_flag = init_state + end if + + ! Get the current year + call dynFatesLandUse_file%time_info%set_current_year() + + if (dynFatesLandUse_file%time_info%is_before_time_series() .and. .not.(init_flag)) then + ! Reset the land use transitions to zero for safety + landuse_transitions(1:num_landuse_transition_vars,bounds%begg:bounds%endg) = 0._r8 + landuse_states(1:num_landuse_state_vars,bounds%begg:bounds%endg) = 0._r8 + else + ! Loop through all variables on the data file and put data into the temporary array + ! then update the global state and transitions array. + allocate(this_data(bounds%begg:bounds%endg)) + do varnum = 1, num_landuse_transition_vars + call landuse_transition_vars(varnum)%get_current_data(this_data) + landuse_transitions(varnum,bounds%begg:bounds%endg) = this_data(bounds%begg:bounds%endg) + end do + do varnum = 1, num_landuse_state_vars + call landuse_state_vars(varnum)%get_current_data(this_data) + landuse_states(varnum,bounds%begg:bounds%endg) = this_data(bounds%begg:bounds%endg) + end do + deallocate(this_data) + end if + + end subroutine dynFatesLandUseInterp + +end module dynFATESLandUseChangeMod diff --git a/src/dyn_subgrid/dynGrossUnrepMod.F90 b/src/dyn_subgrid/dynGrossUnrepMod.F90 new file mode 100644 index 0000000000..8d0e7ee004 --- /dev/null +++ b/src/dyn_subgrid/dynGrossUnrepMod.F90 @@ -0,0 +1,483 @@ +module dynGrossUnrepMod + +#include "shr_assert.h" + + !--------------------------------------------------------------------------- + ! !DESCRIPTION: + ! Handle reading of the gross unrepresented landcover change data, as well as the + ! state updates that happen as a result. + ! + ! !USES: + use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_log_mod , only : errMsg => shr_log_errMsg + use decompMod , only : bounds_type, BOUNDS_LEVEL_PROC + use abortutils , only : endrun + use dynFileMod , only : dyn_file_type + use dynVarTimeUninterpMod , only : dyn_var_time_uninterp_type + use CNVegCarbonStateType , only : cnveg_carbonstate_type + use CNVegCarbonFluxType , only : cnveg_carbonflux_type + use CNVegNitrogenStateType , only : cnveg_nitrogenstate_type + use CNVegNitrogenFluxType , only : cnveg_nitrogenflux_type + use SoilBiogeochemStateType , only : soilbiogeochem_state_type + use pftconMod , only : pftcon + use clm_varcon , only : grlnd + use clm_varpar , only : natpft_size, i_litr_min, i_litr_max, i_met_lit + use ColumnType , only : col + use PatchType , only : patch + ! + ! !PUBLIC MEMBER FUNCTIONS: + implicit none + private + ! + public :: dynGrossUnrep_init ! initialize data structures for grossunrep information + public :: dynGrossUnrep_interp ! get grossunrep data for current time step, if needed + public :: CNGrossUnrep ! grossunrep mortality routine for CN code + ! + ! !PRIVATE MEMBER FUNCTIONS: + private :: CNGrossUnrepPftToColumn ! gather patch-level grossunrep fluxes to the column level + ! + ! !PRIVATE TYPES: + + ! Note that, since we have our own dyngrossunrep_file object (distinct from dynpft_file), + ! we could theoretically have a separate file providing grossunrep data from that providing + ! the pftdyn data + type(dyn_file_type), target :: dyngrossunrep_file ! information for the file containing grossunrep data + + ! Define the underlying grossunrep variables + character(len=64), parameter :: grossunrep_varname = 'UNREPRESENTED_PFT_LULCC' + + type(dyn_var_time_uninterp_type) :: gru_inst ! value of gross unrepresented variable + + real(r8) , allocatable :: grossunrepfrac(:,:) ! gross unrepresented landcover change fraction + logical :: do_grossunrep ! whether we're in a period when we should do gross unrepresented landcover change + character(len=*), parameter, private :: sourcefile = & + __FILE__ + !--------------------------------------------------------------------------- + +contains + + !----------------------------------------------------------------------- + subroutine dynGrossUnrep_init(bounds, grossunrep_filename) + ! + ! !DESCRIPTION: + ! Initialize data structures for grossunrep information. + ! This should be called once, during model initialization. + ! + ! !USES: + use dynVarTimeUninterpMod , only : dyn_var_time_uninterp_type + use dynTimeInfoMod , only : YEAR_POSITION_START_OF_TIMESTEP + ! + ! !ARGUMENTS: + type(bounds_type) , intent(in) :: bounds ! proc-level bounds + character(len=*) , intent(in) :: grossunrep_filename ! name of file containing grossunrep information + ! + ! !LOCAL VARIABLES: + integer :: num_points ! number of spatial points + integer :: ier ! error code + + character(len=*), parameter :: subname = 'dynGrossUnrep_init' + !----------------------------------------------------------------------- + + SHR_ASSERT_ALL(bounds%level == BOUNDS_LEVEL_PROC, subname // ': argument must be PROC-level bounds') + + allocate(grossunrepfrac(bounds%begg:bounds%endg,0:natpft_size-1),stat=ier) + if (ier /= 0) then + call endrun(msg=' allocation error for grossunrep'//errMsg(sourcefile, __LINE__)) + end if + + ! Get the year from the START of the timestep for consistency with other dyn file + ! stuff (though it shouldn't actually matter for grossunrep, given the way the + ! once-per-year grossunrep is applied). + dyngrossunrep_file = dyn_file_type(grossunrep_filename, YEAR_POSITION_START_OF_TIMESTEP) + + ! Get initial grossunrep data + num_points = (bounds%endg - bounds%begg + 1) + gru_inst = dyn_var_time_uninterp_type( & + dyn_file=dyngrossunrep_file, varname=grossunrep_varname, & + dim1name=grlnd, conversion_factor=1.0_r8, & + do_check_sums_equal_1=.false., data_shape=[num_points,natpft_size], & + allow_nodata=.true.) + + end subroutine dynGrossUnrep_init + + + !----------------------------------------------------------------------- + subroutine dynGrossUnrep_interp(bounds) + ! + ! !DESCRIPTION: + ! Get grossunrep data for model time, when needed. + ! + ! Note that grossunrep data are stored as rates (not weights) and so time interpolation + ! is not necessary - the grossunrep rate is held constant through the year. This is + ! consistent with the treatment of changing PFT weights, where interpolation of the + ! annual endpoint weights leads to a constant rate of change in PFT weight through the + ! year, with abrupt changes in the rate at annual boundaries. + ! + ! !USES: + use dynTimeInfoMod , only : time_info_type + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds ! proc-level bounds + ! + ! !LOCAL VARIABLES: + + character(len=*), parameter :: subname = 'dyngru_interp' + !----------------------------------------------------------------------- + + SHR_ASSERT_ALL(bounds%level == BOUNDS_LEVEL_PROC, subname // ': argument must be PROC-level bounds') + + call dyngrossunrep_file%time_info%set_current_year() + + ! Get total grossunrep for this time step + grossunrepfrac(bounds%begg:bounds%endg,0:natpft_size-1) = 0._r8 + + if (dyngrossunrep_file%time_info%is_before_time_series()) then + ! Turn off grossunrep before the start of the grossunrep time series + do_grossunrep = .false. + else + ! Note that do_grossunrep stays true even past the end of the time series. This + ! means that grossunrep rates will be maintained at the rate given in the last + ! year of the file for all years past the end of this specified time series. + do_grossunrep = .true. + call gru_inst%get_current_data(grossunrepfrac) + end if + + end subroutine dynGrossUnrep_interp + + + !----------------------------------------------------------------------- + subroutine CNGrossUnrep (num_soilp, filter_soilp, & + soilbiogeochem_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & + cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) + ! + ! !DESCRIPTION: + ! GrossUnrep mortality routine for coupled carbon-nitrogen code (CN) + ! + ! !USES: + use pftconMod , only : noveg, nbrdlf_evr_shrub, nc4_grass + use clm_varcon , only : secspday + use clm_time_manager, only : get_step_size_real, is_beg_curr_year + ! + ! !ARGUMENTS: + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! patch filter for soil points + type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst + type(cnveg_carbonstate_type) , intent(in) :: cnveg_carbonstate_inst + type(cnveg_nitrogenstate_type) , intent(in) :: cnveg_nitrogenstate_inst + type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst + type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst + ! + ! !LOCAL VARIABLES: + integer :: p ! patch index + integer :: g ! gridcell index + integer :: fp ! patch filter index + real(r8):: thistreec ! carbon in this tree for calculating grossunrep fraction (gC/m2) + real(r8):: cm ! rate for carbon grossunrep mortality (gC/m2/yr) + real(r8):: am ! rate for fractional grossunrep mortality (1/yr) + real(r8):: m ! rate for fractional grossunrep mortality (1/s) + real(r8):: dtime ! model time step (s) + !----------------------------------------------------------------------- + + associate(& + ivt => patch%itype , & ! Input: [integer (:)] pft vegetation type + convfrac => pftcon%pconv , & ! Input: [real (:)] pft conversion to atmosphere fraction + + leafc => cnveg_carbonstate_inst%leafc_patch , & ! Input: [real(r8) (:)] (gC/m2) leaf C + frootc => cnveg_carbonstate_inst%frootc_patch , & ! Input: [real(r8) (:)] (gC/m2) fine root C + livestemc => cnveg_carbonstate_inst%livestemc_patch , & ! Input: [real(r8) (:)] (gC/m2) live stem C + deadstemc => cnveg_carbonstate_inst%deadstemc_patch , & ! Input: [real(r8) (:)] (gC/m2) dead stem C + livecrootc => cnveg_carbonstate_inst%livecrootc_patch , & ! Input: [real(r8) (:)] (gC/m2) live coarse root C + deadcrootc => cnveg_carbonstate_inst%deadcrootc_patch , & ! Input: [real(r8) (:)] (gC/m2) dead coarse root C + xsmrpool => cnveg_carbonstate_inst%xsmrpool_patch , & ! Input: [real(r8) (:)] (gC/m2) abstract C pool to meet excess MR demand + leafc_storage => cnveg_carbonstate_inst%leafc_storage_patch , & ! Input: [real(r8) (:)] (gC/m2) leaf C storage + frootc_storage => cnveg_carbonstate_inst%frootc_storage_patch , & ! Input: [real(r8) (:)] (gC/m2) fine root C storage + livestemc_storage => cnveg_carbonstate_inst%livestemc_storage_patch , & ! Input: [real(r8) (:)] (gC/m2) live stem C storage + deadstemc_storage => cnveg_carbonstate_inst%deadstemc_storage_patch , & ! Input: [real(r8) (:)] (gC/m2) dead stem C storage + livecrootc_storage => cnveg_carbonstate_inst%livecrootc_storage_patch , & ! Input: [real(r8) (:)] (gC/m2) live coarse root C storage + deadcrootc_storage => cnveg_carbonstate_inst%deadcrootc_storage_patch , & ! Input: [real(r8) (:)] (gC/m2) dead coarse root C storage + gresp_storage => cnveg_carbonstate_inst%gresp_storage_patch , & ! Input: [real(r8) (:)] (gC/m2) growth respiration storage + leafc_xfer => cnveg_carbonstate_inst%leafc_xfer_patch , & ! Input: [real(r8) (:)] (gC/m2) leaf C transfer + frootc_xfer => cnveg_carbonstate_inst%frootc_xfer_patch , & ! Input: [real(r8) (:)] (gC/m2) fine root C transfer + livestemc_xfer => cnveg_carbonstate_inst%livestemc_xfer_patch , & ! Input: [real(r8) (:)] (gC/m2) live stem C transfer + deadstemc_xfer => cnveg_carbonstate_inst%deadstemc_xfer_patch , & ! Input: [real(r8) (:)] (gC/m2) dead stem C transfer + livecrootc_xfer => cnveg_carbonstate_inst%livecrootc_xfer_patch , & ! Input: [real(r8) (:)] (gC/m2) live coarse root C transfer + deadcrootc_xfer => cnveg_carbonstate_inst%deadcrootc_xfer_patch , & ! Input: [real(r8) (:)] (gC/m2) dead coarse root C transfer + gresp_xfer => cnveg_carbonstate_inst%gresp_xfer_patch , & ! Input: [real(r8) (:)] (gC/m2) growth respiration transfer + + leafn => cnveg_nitrogenstate_inst%leafn_patch , & ! Input: [real(r8) (:)] (gN/m2) leaf N + frootn => cnveg_nitrogenstate_inst%frootn_patch , & ! Input: [real(r8) (:)] (gN/m2) fine root N + livestemn => cnveg_nitrogenstate_inst%livestemn_patch , & ! Input: [real(r8) (:)] (gN/m2) live stem N + deadstemn => cnveg_nitrogenstate_inst%deadstemn_patch , & ! Input: [real(r8) (:)] (gN/m2) dead stem N + livecrootn => cnveg_nitrogenstate_inst%livecrootn_patch , & ! Input: [real(r8) (:)] (gN/m2) live coarse root N + deadcrootn => cnveg_nitrogenstate_inst%deadcrootn_patch , & ! Input: [real(r8) (:)] (gN/m2) dead coarse root N + retransn => cnveg_nitrogenstate_inst%retransn_patch , & ! Input: [real(r8) (:)] (gN/m2) plant pool of retranslocated N + leafn_storage => cnveg_nitrogenstate_inst%leafn_storage_patch , & ! Input: [real(r8) (:)] (gN/m2) leaf N storage + frootn_storage => cnveg_nitrogenstate_inst%frootn_storage_patch , & ! Input: [real(r8) (:)] (gN/m2) fine root N storage + livestemn_storage => cnveg_nitrogenstate_inst%livestemn_storage_patch , & ! Input: [real(r8) (:)] (gN/m2) live stem N storage + deadstemn_storage => cnveg_nitrogenstate_inst%deadstemn_storage_patch , & ! Input: [real(r8) (:)] (gN/m2) dead stem N storage + livecrootn_storage => cnveg_nitrogenstate_inst%livecrootn_storage_patch , & ! Input: [real(r8) (:)] (gN/m2) live coarse root N storage + deadcrootn_storage => cnveg_nitrogenstate_inst%deadcrootn_storage_patch , & ! Input: [real(r8) (:)] (gN/m2) dead coarse root N storage + leafn_xfer => cnveg_nitrogenstate_inst%leafn_xfer_patch , & ! Input: [real(r8) (:)] (gN/m2) leaf N transfer + frootn_xfer => cnveg_nitrogenstate_inst%frootn_xfer_patch , & ! Input: [real(r8) (:)] (gN/m2) fine root N transfer + livestemn_xfer => cnveg_nitrogenstate_inst%livestemn_xfer_patch , & ! Input: [real(r8) (:)] (gN/m2) live stem N transfer + deadstemn_xfer => cnveg_nitrogenstate_inst%deadstemn_xfer_patch , & ! Input: [real(r8) (:)] (gN/m2) dead stem N transfer + livecrootn_xfer => cnveg_nitrogenstate_inst%livecrootn_xfer_patch , & ! Input: [real(r8) (:)] (gN/m2) live coarse root N transfer + deadcrootn_xfer => cnveg_nitrogenstate_inst%deadcrootn_xfer_patch , & ! Input: [real(r8) (:)] (gN/m2) dead coarse root N transfer + + gru_leafc_to_litter => cnveg_carbonflux_inst%gru_leafc_to_litter_patch , & ! Output: [real(r8) (:)] + gru_frootc_to_litter => cnveg_carbonflux_inst%gru_frootc_to_litter_patch , & ! Output: [real(r8) (:)] + gru_livestemc_to_atm => cnveg_carbonflux_inst%gru_livestemc_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadstemc_to_atm => cnveg_carbonflux_inst%gru_deadstemc_to_atm_patch , & ! Output: [real(r8) (:)] + gru_wood_productc_gain => cnveg_carbonflux_inst%gru_wood_productc_gain_patch , & ! Output: [real(r8) (:)] + gru_livecrootc_to_litter => cnveg_carbonflux_inst%gru_livecrootc_to_litter_patch , & ! Output: [real(r8) (:)] + gru_deadcrootc_to_litter => cnveg_carbonflux_inst%gru_deadcrootc_to_litter_patch , & ! Output: [real(r8) (:)] + gru_xsmrpool_to_atm => cnveg_carbonflux_inst%gru_xsmrpool_to_atm_patch , & ! Output: [real(r8) (:)] + gru_leafc_storage_to_atm => cnveg_carbonflux_inst%gru_leafc_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_frootc_storage_to_atm => cnveg_carbonflux_inst%gru_frootc_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_livestemc_storage_to_atm => cnveg_carbonflux_inst%gru_livestemc_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadstemc_storage_to_atm => cnveg_carbonflux_inst%gru_deadstemc_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_livecrootc_storage_to_atm => cnveg_carbonflux_inst%gru_livecrootc_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadcrootc_storage_to_atm => cnveg_carbonflux_inst%gru_deadcrootc_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_gresp_storage_to_atm => cnveg_carbonflux_inst%gru_gresp_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_leafc_xfer_to_atm => cnveg_carbonflux_inst%gru_leafc_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_frootc_xfer_to_atm => cnveg_carbonflux_inst%gru_frootc_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_livestemc_xfer_to_atm => cnveg_carbonflux_inst%gru_livestemc_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadstemc_xfer_to_atm => cnveg_carbonflux_inst%gru_deadstemc_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_livecrootc_xfer_to_atm => cnveg_carbonflux_inst%gru_livecrootc_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadcrootc_xfer_to_atm => cnveg_carbonflux_inst%gru_deadcrootc_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_gresp_xfer_to_atm => cnveg_carbonflux_inst%gru_gresp_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + + gru_leafn_to_litter => cnveg_nitrogenflux_inst%gru_leafn_to_litter_patch , & ! Output: [real(r8) (:)] + gru_frootn_to_litter => cnveg_nitrogenflux_inst%gru_frootn_to_litter_patch , & ! Output: [real(r8) (:)] + gru_livestemn_to_atm => cnveg_nitrogenflux_inst%gru_livestemn_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadstemn_to_atm => cnveg_nitrogenflux_inst%gru_deadstemn_to_atm_patch , & ! Output: [real(r8) (:)] + gru_wood_productn_gain => cnveg_nitrogenflux_inst%gru_wood_productn_gain_patch , & ! Output: [real(r8) (:)] + gru_livecrootn_to_litter => cnveg_nitrogenflux_inst%gru_livecrootn_to_litter_patch , & ! Output: [real(r8) (:)] + gru_deadcrootn_to_litter => cnveg_nitrogenflux_inst%gru_deadcrootn_to_litter_patch , & ! Output: [real(r8) (:)] + gru_retransn_to_litter => cnveg_nitrogenflux_inst%gru_retransn_to_litter_patch , & ! Output: [real(r8) (:)] + gru_leafn_storage_to_atm => cnveg_nitrogenflux_inst%gru_leafn_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_frootn_storage_to_atm => cnveg_nitrogenflux_inst%gru_frootn_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_livestemn_storage_to_atm => cnveg_nitrogenflux_inst%gru_livestemn_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadstemn_storage_to_atm => cnveg_nitrogenflux_inst%gru_deadstemn_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_livecrootn_storage_to_atm => cnveg_nitrogenflux_inst%gru_livecrootn_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadcrootn_storage_to_atm => cnveg_nitrogenflux_inst%gru_deadcrootn_storage_to_atm_patch , & ! Output: [real(r8) (:)] + gru_leafn_xfer_to_atm => cnveg_nitrogenflux_inst%gru_leafn_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_frootn_xfer_to_atm => cnveg_nitrogenflux_inst%gru_frootn_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_livestemn_xfer_to_atm => cnveg_nitrogenflux_inst%gru_livestemn_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadstemn_xfer_to_atm => cnveg_nitrogenflux_inst%gru_deadstemn_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_livecrootn_xfer_to_atm => cnveg_nitrogenflux_inst%gru_livecrootn_xfer_to_atm_patch , & ! Output: [real(r8) (:)] + gru_deadcrootn_xfer_to_atm => cnveg_nitrogenflux_inst%gru_deadcrootn_xfer_to_atm_patch & ! Output: [real(r8) (:)] + ) + + dtime = get_step_size_real() + + ! patch loop + do fp = 1,num_soilp + p = filter_soilp(fp) + g = patch%gridcell(p) + + ! If this is a natural pft, then + ! get the annual grossunrep "mortality" rate (am) from grossunrep array + ! and convert to rate per second + if (ivt(p) > noveg .and. ivt(p) <= nc4_grass) then + + if (do_grossunrep) then + am = grossunrepfrac(g,ivt(p)) + + ! Apply all grossunrep at the start of the year + if (is_beg_curr_year()) then + m = am/dtime + else + m = 0._r8 + end if + else + m = 0._r8 + end if + + ! patch-level gross unrepresented landcover change carbon fluxes + ! displayed pools + gru_leafc_to_litter(p) = leafc(p) * m + gru_frootc_to_litter(p) = frootc(p) * m + gru_livestemc_to_atm(p) = livestemc(p) * m + gru_deadstemc_to_atm(p) = deadstemc(p) * m * convfrac(ivt(p)) + gru_wood_productc_gain(p) = deadstemc(p) * m * (1._r8 - convfrac(ivt(p))) + gru_livecrootc_to_litter(p) = livecrootc(p) * m + gru_deadcrootc_to_litter(p) = deadcrootc(p) * m + gru_xsmrpool_to_atm(p) = xsmrpool(p) * m + + ! storage pools + gru_leafc_storage_to_atm(p) = leafc_storage(p) * m + gru_frootc_storage_to_atm(p) = frootc_storage(p) * m + gru_livestemc_storage_to_atm(p) = livestemc_storage(p) * m + gru_deadstemc_storage_to_atm(p) = deadstemc_storage(p) * m + gru_livecrootc_storage_to_atm(p) = livecrootc_storage(p) * m + gru_deadcrootc_storage_to_atm(p) = deadcrootc_storage(p) * m + gru_gresp_storage_to_atm(p) = gresp_storage(p) * m + + ! transfer pools + gru_leafc_xfer_to_atm(p) = leafc_xfer(p) * m + gru_frootc_xfer_to_atm(p) = frootc_xfer(p) * m + gru_livestemc_xfer_to_atm(p) = livestemc_xfer(p) * m + gru_deadstemc_xfer_to_atm(p) = deadstemc_xfer(p) * m + gru_livecrootc_xfer_to_atm(p) = livecrootc_xfer(p) * m + gru_deadcrootc_xfer_to_atm(p) = deadcrootc_xfer(p) * m + gru_gresp_xfer_to_atm(p) = gresp_xfer(p) * m + + ! patch-level gross unrepresented landcover change mortality nitrogen fluxes + ! displayed pools + gru_leafn_to_litter(p) = leafn(p) * m + gru_frootn_to_litter(p) = frootn(p) * m + gru_livestemn_to_atm(p) = livestemn(p) * m + gru_deadstemn_to_atm(p) = deadstemn(p) * m * convfrac(ivt(p)) + gru_wood_productn_gain(p) = deadstemn(p) * m * (1._r8 - convfrac(ivt(p))) + gru_livecrootn_to_litter(p) = livecrootn(p) * m + gru_deadcrootn_to_litter(p) = deadcrootn(p) * m + gru_retransn_to_litter(p) = retransn(p) * m + + ! storage pools + gru_leafn_storage_to_atm(p) = leafn_storage(p) * m + gru_frootn_storage_to_atm(p) = frootn_storage(p) * m + gru_livestemn_storage_to_atm(p) = livestemn_storage(p) * m + gru_deadstemn_storage_to_atm(p) = deadstemn_storage(p) * m + gru_livecrootn_storage_to_atm(p) = livecrootn_storage(p) * m + gru_deadcrootn_storage_to_atm(p) = deadcrootn_storage(p) * m + + ! transfer pools + gru_leafn_xfer_to_atm(p) = leafn_xfer(p) * m + gru_frootn_xfer_to_atm(p) = frootn_xfer(p) * m + gru_livestemn_xfer_to_atm(p) = livestemn_xfer(p) * m + gru_deadstemn_xfer_to_atm(p) = deadstemn_xfer(p) * m + gru_livecrootn_xfer_to_atm(p) = livecrootn_xfer(p) * m + gru_deadcrootn_xfer_to_atm(p) = deadcrootn_xfer(p) * m + + end if ! end tree block + + end do ! end of pft loop + + ! gather all patch-level litterfall fluxes from grossunrep to the column + ! for litter C and N inputs + + call CNGrossUnrepPftToColumn(num_soilp, filter_soilp, & + soilbiogeochem_state_inst, cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) + + end associate + + end subroutine CNGrossUnrep + + !----------------------------------------------------------------------- + subroutine CNGrossUnrepPftToColumn (num_soilp, filter_soilp, & + soilbiogeochem_state_inst, CNVeg_carbonflux_inst, cnveg_nitrogenflux_inst) + ! + ! !DESCRIPTION: + ! called at the end of CNGrossUnrep to gather all patch-level grossunrep litterfall fluxes + ! to the column level and assign them to the three litter pools + ! + ! !USES: + use clm_varpar , only : maxsoil_patches, nlevdecomp + ! + ! !ARGUMENTS: + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! soil patch filter + type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst + type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst + type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst + ! + ! !LOCAL VARIABLES: + integer :: fp,c,p,j,i ! indices + !----------------------------------------------------------------------- + + associate( & + ivt => patch%itype , & ! Input: [integer (:) ] pft vegetation type + wtcol => patch%wtcol , & ! Input: [real(r8) (:) ] pft weight relative to column (0-1) + + lf_f => pftcon%lf_f , & ! Input: leaf litter fractions + fr_f => pftcon%fr_f , & ! Input: fine root litter fractions + + leaf_prof => soilbiogeochem_state_inst%leaf_prof_patch , & ! Input: [real(r8) (:,:) ] (1/m) profile of leaves + froot_prof => soilbiogeochem_state_inst%froot_prof_patch , & ! Input: [real(r8) (:,:) ] (1/m) profile of fine roots + croot_prof => soilbiogeochem_state_inst%croot_prof_patch , & ! Input: [real(r8) (:,:) ] (1/m) profile of coarse roots + stem_prof => soilbiogeochem_state_inst%stem_prof_patch , & ! Input: [real(r8) (:,:) ] (1/m) profile of stems + + gru_leafc_to_litter => cnveg_carbonflux_inst%gru_leafc_to_litter_patch , & ! Input: [real(r8) (:)] + gru_frootc_to_litter => cnveg_carbonflux_inst%gru_frootc_to_litter_patch , & ! Input: [real(r8) (:)] + gru_wood_productc_gain => cnveg_carbonflux_inst%gru_wood_productc_gain_patch , & ! Input: [real(r8) (:)] + gru_livecrootc_to_litter => cnveg_carbonflux_inst%gru_livecrootc_to_litter_patch , & ! Input: [real(r8) (:)] + gru_deadcrootc_to_litter => cnveg_carbonflux_inst%gru_deadcrootc_to_litter_patch , & ! Input: [real(r8) (:)] + gru_conv_cflux => cnveg_carbonflux_inst%gru_conv_cflux_patch , & ! Input: [real(r8) (:) ] C fluxes associated with conversion (immediate loss to atm) + + gru_c_to_litr_c => cnveg_carbonflux_inst%gru_c_to_litr_c_col , & ! Output: [real(r8) (:,:,:) ] C fluxes associated with gross unrepresented landcover change to litter pools (gC/m3/s) + gru_c_to_cwdc_c => cnveg_carbonflux_inst%gru_c_to_cwdc_col , & ! Output: [real(r8) (:,:) ] C fluxes associated with grossunrep to CWD pool (gC/m3/s) + + gru_wood_productc_gain_c => cnveg_carbonflux_inst%gru_wood_productc_gain_col , & ! Input: [real(r8) (:)] + + gru_leafn_to_litter => cnveg_nitrogenflux_inst%gru_leafn_to_litter_patch , & ! Input: [real(r8) (:)] + gru_frootn_to_litter => cnveg_nitrogenflux_inst%gru_frootn_to_litter_patch , & ! Input: [real(r8) (:)] + gru_wood_productn_gain => cnveg_nitrogenflux_inst%gru_wood_productn_gain_patch , & ! Input: [real(r8) (:)] + gru_livecrootn_to_litter => cnveg_nitrogenflux_inst%gru_livecrootn_to_litter_patch , & ! Input: [real(r8) (:)] + gru_deadcrootn_to_litter => cnveg_nitrogenflux_inst%gru_deadcrootn_to_litter_patch , & ! Input: [real(r8) (:)] + gru_retransn_to_litter => cnveg_nitrogenflux_inst%gru_retransn_to_litter_patch , & ! Input: [real(r8) (:)] + gru_n_to_litr_c => cnveg_nitrogenflux_inst%gru_n_to_litr_n_col , & ! Output: [real(r8) (:,:,:) ] N fluxes associated with grossunrep to litter pools (gN/m3/s) + gru_n_to_cwdn_c => cnveg_nitrogenflux_inst%gru_n_to_cwdn_col , & ! Output: [real(r8) (:,:) ] N fluxes associated with grossunrep to CWD pool (gN/m3/s) + + gru_wood_productn_gain_c => cnveg_nitrogenflux_inst%gru_wood_productn_gain_col & ! Input: [real(r8) (:)] + ) + + do j = 1, nlevdecomp + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) + + do i = i_litr_min, i_litr_max + gru_c_to_litr_c(c,j,i) = gru_c_to_litr_c(c,j,i) + & + ! leaf gross unrepresented landcover change mortality carbon fluxes + gru_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & + ! fine root gross unrepresented landcover change mortality carbon fluxes + gru_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + gru_n_to_litr_c(c,j,i) = gru_n_to_litr_c(c,j,i) + & + ! leaf gross unrepresented landcover change mortality nitrogen fluxes + gru_leafn_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & + ! fine root gross unrepresented landcover change mortality nitrogen fluxes + gru_frootn_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + end do + + ! coarse root gross unrepresented landcover change mortality carbon fluxes + gru_c_to_cwdc_c(c,j) = gru_c_to_cwdc_c(c,j) + & + gru_livecrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + gru_c_to_cwdc_c(c,j) = gru_c_to_cwdc_c(c,j) + & + gru_deadcrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + + ! coarse root gross unrepresented landcover change mortality nitrogen fluxes + gru_n_to_cwdn_c(c,j) = gru_n_to_cwdn_c(c,j) + & + gru_livecrootn_to_litter(p) * wtcol(p) * croot_prof(p,j) + gru_n_to_cwdn_c(c,j) = gru_n_to_cwdn_c(c,j) + & + gru_deadcrootn_to_litter(p) * wtcol(p) * croot_prof(p,j) + + ! retranslocated N pool gross unrepresented landcover change mortality fluxes + ! process specific to i_met_lit, so we keep it outside + ! the i_litr_min to i_litr_max loop above + gru_n_to_litr_c(c,j,i_met_lit) = & + gru_n_to_litr_c(c,j,i_met_lit) + & + gru_retransn_to_litter(p) * wtcol(p) * leaf_prof(p,j) + + end do + end do + + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) + + ! wood gross unrepresented landcover change mortality carbon fluxes to product pools + gru_wood_productc_gain_c(c) = gru_wood_productc_gain_c(c) + & + gru_wood_productc_gain(p) * wtcol(p) + + ! wood gross unrepresented landcover change mortality nitrogen fluxes to product pools + gru_wood_productn_gain_c(c) = gru_wood_productn_gain_c(c) + & + gru_wood_productn_gain(p) * wtcol(p) + + end do + + end associate + + end subroutine CNGrossUnrepPftToColumn + +end module dynGrossUnrepMod diff --git a/src/dyn_subgrid/dynHarvestMod.F90 b/src/dyn_subgrid/dynHarvestMod.F90 index a55da036f3..d5a72aa547 100644 --- a/src/dyn_subgrid/dynHarvestMod.F90 +++ b/src/dyn_subgrid/dynHarvestMod.F90 @@ -234,7 +234,7 @@ subroutine dynHarvest_interp_resolve_harvesttypes(bounds, harvest_rates, after_s end subroutine dynHarvest_interp_resolve_harvesttypes !----------------------------------------------------------------------- - subroutine CNHarvest (num_soilc, filter_soilc, num_soilp, filter_soilp, & + subroutine CNHarvest (num_soilp, filter_soilp, & soilbiogeochem_state_inst, cnveg_carbonstate_inst, cnveg_nitrogenstate_inst, & cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) ! @@ -247,8 +247,6 @@ subroutine CNHarvest (num_soilc, filter_soilc, num_soilp, filter_soilp, & use clm_time_manager, only : get_step_size_real, is_beg_curr_year ! ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! column filter for soil points integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! patch filter for soil points type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst @@ -457,7 +455,7 @@ subroutine CNHarvest (num_soilc, filter_soilc, num_soilp, filter_soilp, & ! gather all patch-level litterfall fluxes from harvest to the column ! for litter C and N inputs - call CNHarvestPftToColumn(num_soilc, filter_soilc, & + call CNHarvestPftToColumn(num_soilp, filter_soilp, & soilbiogeochem_state_inst, cnveg_carbonflux_inst, cnveg_nitrogenflux_inst) end associate @@ -465,7 +463,7 @@ subroutine CNHarvest (num_soilc, filter_soilc, num_soilp, filter_soilp, & end subroutine CNHarvest !----------------------------------------------------------------------- - subroutine CNHarvestPftToColumn (num_soilc, filter_soilc, & + subroutine CNHarvestPftToColumn (num_soilp, filter_soilp, & soilbiogeochem_state_inst, CNVeg_carbonflux_inst, cnveg_nitrogenflux_inst) ! ! !DESCRIPTION: @@ -476,14 +474,14 @@ subroutine CNHarvestPftToColumn (num_soilc, filter_soilc, & use clm_varpar , only : nlevdecomp, maxsoil_patches, i_litr_min, i_litr_max, i_met_lit ! ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! soil column filter + integer , intent(in) :: num_soilp ! number of soil patches in filter + integer , intent(in) :: filter_soilp(:) ! patch filter for soil points type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst type(cnveg_carbonflux_type) , intent(inout) :: cnveg_carbonflux_inst type(cnveg_nitrogenflux_type) , intent(inout) :: cnveg_nitrogenflux_inst ! ! !LOCAL VARIABLES: - integer :: fc,c,pi,p,j,i ! indices + integer :: fp,c,p,j,i ! indices !----------------------------------------------------------------------- associate( & @@ -547,124 +545,106 @@ subroutine CNHarvestPftToColumn (num_soilc, filter_soilc, & ) do j = 1, nlevdecomp - do pi = 1,maxsoil_patches - do fc = 1,num_soilc - c = filter_soilc(fc) - - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 - - if (patch%active(p)) then - - do i = i_litr_min, i_litr_max - ! leaf harvest mortality carbon fluxes - harvest_c_to_litr_c(c,j,i) = & - harvest_c_to_litr_c(c,j,i) + & - hrv_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) - - ! fine root harvest mortality carbon fluxes - harvest_c_to_litr_c(c,j,i) = & - harvest_c_to_litr_c(c,j,i) + & - hrv_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) - end do - - ! wood harvest mortality carbon fluxes - harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & - hrv_livestemc_to_litter(p) * wtcol(p) * stem_prof(p,j) - harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & - hrv_livecrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) - harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & - hrv_deadcrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) - - ! storage harvest mortality carbon fluxes - ! Metabolic litter is treated differently than other types - ! of litter, so it gets this additional line after the - ! most recent loop over all litter types - harvest_c_to_litr_c(c,j,i_met_lit) = & - harvest_c_to_litr_c(c,j,i_met_lit) + & - hrv_leafc_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - hrv_frootc_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - hrv_livestemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_deadstemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_livecrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_deadcrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_gresp_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - - ! transfer harvest mortality carbon fluxes - hrv_leafc_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - hrv_frootc_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - hrv_livestemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_deadstemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_livecrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_deadcrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_gresp_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) - - do i = i_litr_min, i_litr_max - harvest_n_to_litr_n(c,j,i) = & - harvest_n_to_litr_n(c,j,i) + & - ! leaf harvest mortality nitrogen fluxes - hrv_leafn_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & - ! fine root litter nitrogen fluxes - hrv_frootn_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) - end do - - ! wood harvest mortality nitrogen fluxes - harvest_n_to_cwdn(c,j) = harvest_n_to_cwdn(c,j) + & - hrv_livestemn_to_litter(p) * wtcol(p) * stem_prof(p,j) - harvest_n_to_cwdn(c,j) = harvest_n_to_cwdn(c,j) + & - hrv_livecrootn_to_litter(p) * wtcol(p) * croot_prof(p,j) - harvest_n_to_cwdn(c,j) = harvest_n_to_cwdn(c,j) + & - hrv_deadcrootn_to_litter(p) * wtcol(p) * croot_prof(p,j) - - ! Metabolic litter is treated differently than other types - ! of litter, so it gets this additional line after the - ! most recent loop over all litter types - harvest_n_to_litr_n(c,j,i_met_lit) = & - harvest_n_to_litr_n(c,j,i_met_lit) + & - ! retranslocated N pool harvest mortality fluxes - hrv_retransn_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - ! storage harvest mortality nitrogen fluxes - hrv_leafn_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - hrv_frootn_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - hrv_livestemn_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_deadstemn_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_livecrootn_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_deadcrootn_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - ! transfer harvest mortality nitrogen fluxes - hrv_leafn_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & - hrv_frootn_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & - hrv_livestemn_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_deadstemn_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & - hrv_livecrootn_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & - hrv_deadcrootn_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) - - end if - end if + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) + + do i = i_litr_min, i_litr_max + ! leaf harvest mortality carbon fluxes + harvest_c_to_litr_c(c,j,i) = & + harvest_c_to_litr_c(c,j,i) + & + hrv_leafc_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + + ! fine root harvest mortality carbon fluxes + harvest_c_to_litr_c(c,j,i) = & + harvest_c_to_litr_c(c,j,i) + & + hrv_frootc_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) + end do + ! wood harvest mortality carbon fluxes + harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & + hrv_livestemc_to_litter(p) * wtcol(p) * stem_prof(p,j) + harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & + hrv_livecrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + harvest_c_to_cwdc(c,j) = harvest_c_to_cwdc(c,j) + & + hrv_deadcrootc_to_litter(p) * wtcol(p) * croot_prof(p,j) + + ! storage harvest mortality carbon fluxes + ! Metabolic litter is treated differently than other types + ! of litter, so it gets this additional line after the + ! most recent loop over all litter types + harvest_c_to_litr_c(c,j,i_met_lit) = & + harvest_c_to_litr_c(c,j,i_met_lit) + & + hrv_leafc_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + hrv_frootc_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + hrv_livestemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_deadstemc_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_livecrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_deadcrootc_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_gresp_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + + ! transfer harvest mortality carbon fluxes + hrv_leafc_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + hrv_frootc_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + hrv_livestemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_deadstemc_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_livecrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_deadcrootc_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_gresp_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + + do i = i_litr_min, i_litr_max + harvest_n_to_litr_n(c,j,i) = & + harvest_n_to_litr_n(c,j,i) + & + ! leaf harvest mortality nitrogen fluxes + hrv_leafn_to_litter(p) * lf_f(ivt(p),i) * wtcol(p) * leaf_prof(p,j) + & + ! fine root litter nitrogen fluxes + hrv_frootn_to_litter(p) * fr_f(ivt(p),i) * wtcol(p) * froot_prof(p,j) end do + ! wood harvest mortality nitrogen fluxes + harvest_n_to_cwdn(c,j) = harvest_n_to_cwdn(c,j) + & + hrv_livestemn_to_litter(p) * wtcol(p) * stem_prof(p,j) + harvest_n_to_cwdn(c,j) = harvest_n_to_cwdn(c,j) + & + hrv_livecrootn_to_litter(p) * wtcol(p) * croot_prof(p,j) + harvest_n_to_cwdn(c,j) = harvest_n_to_cwdn(c,j) + & + hrv_deadcrootn_to_litter(p) * wtcol(p) * croot_prof(p,j) + + ! Metabolic litter is treated differently than other types + ! of litter, so it gets this additional line after the + ! most recent loop over all litter types + harvest_n_to_litr_n(c,j,i_met_lit) = & + harvest_n_to_litr_n(c,j,i_met_lit) + & + ! retranslocated N pool harvest mortality fluxes + hrv_retransn_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + ! storage harvest mortality nitrogen fluxes + hrv_leafn_storage_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + hrv_frootn_storage_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + hrv_livestemn_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_deadstemn_storage_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_livecrootn_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_deadcrootn_storage_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + ! transfer harvest mortality nitrogen fluxes + hrv_leafn_xfer_to_litter(p) * wtcol(p) * leaf_prof(p,j) + & + hrv_frootn_xfer_to_litter(p) * wtcol(p) * froot_prof(p,j) + & + hrv_livestemn_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_deadstemn_xfer_to_litter(p) * wtcol(p) * stem_prof(p,j) + & + hrv_livecrootn_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + & + hrv_deadcrootn_xfer_to_litter(p) * wtcol(p) * croot_prof(p,j) + end do end do - do pi = 1,maxsoil_patches - do fc = 1,num_soilc - c = filter_soilc(fc) - - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 + do fp = 1,num_soilp + p = filter_soilp(fp) + c = patch%column(p) - if (patch%active(p)) then - ! wood harvest mortality carbon fluxes to product pools - cwood_harvestc(c) = cwood_harvestc(c) + & - pwood_harvestc(p) * wtcol(p) + ! wood harvest mortality carbon fluxes to product pools + cwood_harvestc(c) = cwood_harvestc(c) + & + pwood_harvestc(p) * wtcol(p) - ! wood harvest mortality nitrogen fluxes to product pools - cwood_harvestn(c) = cwood_harvestn(c) + & - pwood_harvestn(p) * wtcol(p) - end if - end if - - end do + ! wood harvest mortality nitrogen fluxes to product pools + cwood_harvestn(c) = cwood_harvestn(c) + & + pwood_harvestn(p) * wtcol(p) end do diff --git a/src/dyn_subgrid/dynSubgridControlMod.F90 b/src/dyn_subgrid/dynSubgridControlMod.F90 index c408f1a038..72e7229d0b 100644 --- a/src/dyn_subgrid/dynSubgridControlMod.F90 +++ b/src/dyn_subgrid/dynSubgridControlMod.F90 @@ -28,6 +28,7 @@ module dynSubgridControlMod public :: get_do_transient_urban ! return the value of the do_transient_urban control flag public :: run_has_transient_landcover ! returns true if any aspects of prescribed transient landcover are enabled public :: get_do_harvest ! return the value of the do_harvest control flag + public :: get_do_grossunrep ! return the value of the do_grossunrep control flag public :: get_reset_dynbal_baselines ! return the value of the reset_dynbal_baselines control flag public :: get_for_testing_allow_non_annual_changes ! return true if user has requested to allow area changes at times other than the year boundary, for testing purposes public :: get_for_testing_zero_dynbal_fluxes ! return true if user has requested to set the dynbal water and energy fluxes to zero, for testing purposes @@ -45,6 +46,7 @@ module dynSubgridControlMod logical :: do_transient_lakes = .false. ! whether to apply transient lakes from dataset logical :: do_transient_urban = .false. ! whether to apply transient urban from dataset logical :: do_harvest = .false. ! whether to apply harvest from dataset + logical :: do_grossunrep = .false. ! whether to apply gross unrepresented landcover change from dataset logical :: reset_dynbal_baselines = .false. ! whether to reset baseline values of total column water and energy in the first step of the run @@ -123,6 +125,7 @@ subroutine read_namelist( NLFilename ) logical :: do_transient_lakes logical :: do_transient_urban logical :: do_harvest + logical :: do_grossunrep logical :: reset_dynbal_baselines logical :: for_testing_allow_non_annual_changes logical :: for_testing_zero_dynbal_fluxes @@ -140,6 +143,7 @@ subroutine read_namelist( NLFilename ) do_transient_lakes, & do_transient_urban, & do_harvest, & + do_grossunrep, & reset_dynbal_baselines, & for_testing_allow_non_annual_changes, & for_testing_zero_dynbal_fluxes @@ -151,6 +155,7 @@ subroutine read_namelist( NLFilename ) do_transient_lakes = .false. do_transient_urban = .false. do_harvest = .false. + do_grossunrep = .false. reset_dynbal_baselines = .false. for_testing_allow_non_annual_changes = .false. for_testing_zero_dynbal_fluxes = .false. @@ -177,6 +182,7 @@ subroutine read_namelist( NLFilename ) call shr_mpi_bcast (do_transient_lakes, mpicom) call shr_mpi_bcast (do_transient_urban, mpicom) call shr_mpi_bcast (do_harvest, mpicom) + call shr_mpi_bcast (do_grossunrep, mpicom) call shr_mpi_bcast (reset_dynbal_baselines, mpicom) call shr_mpi_bcast (for_testing_allow_non_annual_changes, mpicom) call shr_mpi_bcast (for_testing_zero_dynbal_fluxes, mpicom) @@ -188,6 +194,7 @@ subroutine read_namelist( NLFilename ) do_transient_lakes = do_transient_lakes, & do_transient_urban = do_transient_urban, & do_harvest = do_harvest, & + do_grossunrep = do_grossunrep, & reset_dynbal_baselines = reset_dynbal_baselines, & for_testing_allow_non_annual_changes = for_testing_allow_non_annual_changes, & for_testing_zero_dynbal_fluxes = for_testing_zero_dynbal_fluxes) @@ -247,6 +254,11 @@ subroutine check_namelist_consistency write(iulog,*) 'a flanduse_timeseries file (currently flanduse_timeseries is blank)' call endrun(msg=errMsg(sourcefile, __LINE__)) end if + if (dyn_subgrid_control_inst%do_grossunrep) then + write(iulog,*) 'ERROR: do_grossunrep can only be true if you are running with' + write(iulog,*) 'a flanduse_timeseries file (currently flanduse_timeseries is blank)' + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if end if if (dyn_subgrid_control_inst%do_transient_pfts) then @@ -305,8 +317,22 @@ subroutine check_namelist_consistency write(iulog,*) 'ERROR: do_harvest can only be true if either use_cn or use_fates are true' call endrun(msg=errMsg(sourcefile, __LINE__)) end if - end if - + end if + + if (dyn_subgrid_control_inst%do_grossunrep) then + ! First check if use_fates. In this case the .not. use_cn error will not + ! appear. The .not. use_cn error will appea + ! if .not. use_fates and .not. use_cn. + if (use_fates) then + write(iulog,*) 'ERROR: do_grossunrep currently does not work with use_fates' + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + if (.not. use_cn) then + write(iulog,*) 'ERROR: do_grossunrep can only be true if use_cn is true' + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + end if + end subroutine check_namelist_consistency !----------------------------------------------------------------------- @@ -395,6 +421,18 @@ logical function get_do_harvest() end function get_do_harvest + !----------------------------------------------------------------------- + logical function get_do_grossunrep() + ! !DESCRIPTION: + ! Return the value of the do_grossunrep control flag + !----------------------------------------------------------------------- + + SHR_ASSERT(dyn_subgrid_control_inst%initialized, errMsg(sourcefile, __LINE__)) + + get_do_grossunrep = dyn_subgrid_control_inst%do_grossunrep + + end function get_do_grossunrep + !----------------------------------------------------------------------- logical function get_reset_dynbal_baselines() ! !DESCRIPTION: diff --git a/src/dyn_subgrid/dynSubgridDriverMod.F90 b/src/dyn_subgrid/dynSubgridDriverMod.F90 index 9446f52e3b..e5ca3f002e 100644 --- a/src/dyn_subgrid/dynSubgridDriverMod.F90 +++ b/src/dyn_subgrid/dynSubgridDriverMod.F90 @@ -15,12 +15,14 @@ module dynSubgridDriverMod use dynSubgridControlMod , only : get_do_transient_pfts, get_do_transient_crops, get_do_transient_lakes, & get_do_transient_urban use dynSubgridControlMod , only : get_do_harvest + use dynSubgridControlMod , only : get_do_grossunrep use dynPriorWeightsMod , only : prior_weights_type use dynPatchStateUpdaterMod , only : patch_state_updater_type use dynColumnStateUpdaterMod , only : column_state_updater_type use dynpftFileMod , only : dynpft_init, dynpft_interp use dyncropFileMod , only : dyncrop_init, dyncrop_interp use dynHarvestMod , only : dynHarvest_init, dynHarvest_interp + use dynGrossUnrepMod , only : dynGrossUnrep_init, dynGrossUnrep_interp use dynlakeFileMod , only : dynlake_init, dynlake_interp use dynurbanFileMod , only : dynurban_init, dynurban_interp use dynLandunitAreaMod , only : update_landunit_weights @@ -125,7 +127,11 @@ subroutine dynSubgrid_init(bounds_proc, glc_behavior, crop_inst) call dynHarvest_init(bounds_proc, harvest_filename=get_flanduse_timeseries()) end if - + ! Initialize stuff for gross unrepresented landuse data. + if (get_do_grossunrep()) then + call dynGrossUnrep_init(bounds_proc, grossunrep_filename=get_flanduse_timeseries()) + end if + ! Initialize stuff for prescribed transient lakes if (get_do_transient_lakes()) then call dynlake_init(bounds_proc, dynlake_filename=get_flanduse_timeseries()) @@ -195,10 +201,11 @@ subroutine dynSubgrid_driver(bounds_proc, ! OUTSIDE any loops over clumps in the driver. ! ! !USES: - use clm_varctl , only : use_cn, use_fates - use dynInitColumnsMod , only : initialize_new_columns - use dynConsBiogeophysMod , only : dyn_hwcontent_init, dyn_hwcontent_final - use dynEDMod , only : dyn_ED + use clm_varctl , only : use_cn, use_fates, use_fates_luh + use dynInitColumnsMod , only : initialize_new_columns + use dynConsBiogeophysMod , only : dyn_hwcontent_init, dyn_hwcontent_final + use dynEDMod , only : dyn_ED + use dynFATESLandUseChangeMod, only : dynFatesLandUseInterp ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds_proc ! processor-level bounds @@ -270,7 +277,11 @@ subroutine dynSubgrid_driver(bounds_proc, if (get_do_harvest() .and. .not. use_fates) then call dynHarvest_interp(bounds_proc) end if - + + if (get_do_grossunrep()) then + call dynGrossUnrep_interp(bounds_proc) + end if + if (get_do_transient_lakes()) then call dynlake_interp(bounds_proc) end if @@ -278,6 +289,11 @@ subroutine dynSubgrid_driver(bounds_proc, if (get_do_transient_urban()) then call dynurban_interp(bounds_proc) end if + + if (use_fates_luh) then + call dynFatesLandUseInterp(bounds_proc) + end if + ! ========================================================================== ! Do land cover change that does not require I/O ! ========================================================================== diff --git a/src/dyn_subgrid/test/dynColumnStateUpdater_test/CMakeLists.txt b/src/dyn_subgrid/test/dynColumnStateUpdater_test/CMakeLists.txt index 8384506601..5329042a59 100644 --- a/src/dyn_subgrid/test/dynColumnStateUpdater_test/CMakeLists.txt +++ b/src/dyn_subgrid/test/dynColumnStateUpdater_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(ColumnStateUpdater test_ColumnStateUpdater_exe - "test_column_state_updater.pf" "") - -target_link_libraries(test_ColumnStateUpdater_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(ColumnStateUpdater + TEST_SOURCES "test_column_state_updater.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/dyn_subgrid/test/dynColumnStateUpdater_test/test_column_state_updater.pf b/src/dyn_subgrid/test/dynColumnStateUpdater_test/test_column_state_updater.pf index a9c456ab04..0758b84ed2 100644 --- a/src/dyn_subgrid/test/dynColumnStateUpdater_test/test_column_state_updater.pf +++ b/src/dyn_subgrid/test/dynColumnStateUpdater_test/test_column_state_updater.pf @@ -2,7 +2,7 @@ module test_column_state_updater ! Tests of dynColumnStateUpdaterMod - use pfunit_mod + use funit use dynColumnStateUpdaterMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/dyn_subgrid/test/dynColumnTemplate_test/CMakeLists.txt b/src/dyn_subgrid/test/dynColumnTemplate_test/CMakeLists.txt index 0f6ad1c753..e809bed311 100644 --- a/src/dyn_subgrid/test/dynColumnTemplate_test/CMakeLists.txt +++ b/src/dyn_subgrid/test/dynColumnTemplate_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(ColumnTemplate test_ColumnTemplate_exe - "test_column_template.pf" "") - -target_link_libraries(test_ColumnTemplate_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(ColumnTemplate + TEST_SOURCES "test_column_template.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/dyn_subgrid/test/dynColumnTemplate_test/test_column_template.pf b/src/dyn_subgrid/test/dynColumnTemplate_test/test_column_template.pf index e5ab68fe29..ba4e692601 100644 --- a/src/dyn_subgrid/test/dynColumnTemplate_test/test_column_template.pf +++ b/src/dyn_subgrid/test/dynColumnTemplate_test/test_column_template.pf @@ -2,7 +2,7 @@ module test_column_template ! Tests of dynColumnTemplateMod - use pfunit_mod + use funit use dynColumnTemplateMod use unittestSubgridMod use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/dyn_subgrid/test/dynConsBiogeophys_test/CMakeLists.txt b/src/dyn_subgrid/test/dynConsBiogeophys_test/CMakeLists.txt index 2ef1ab38b9..5e981270a4 100644 --- a/src/dyn_subgrid/test/dynConsBiogeophys_test/CMakeLists.txt +++ b/src/dyn_subgrid/test/dynConsBiogeophys_test/CMakeLists.txt @@ -1,8 +1,6 @@ set(pfunit_sources test_dyn_cons_biogeophys.pf) -create_pFUnit_test(dynConsBiogeophys test_dynConsBiogeophys_exe - "${pfunit_sources}" "") - -target_link_libraries(test_dynConsBiogeophys_exe clm csm_share esmf_wrf_timemgr) - +add_pfunit_ctest(dynConsBiogeophys + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/dyn_subgrid/test/dynConsBiogeophys_test/test_dyn_cons_biogeophys.pf b/src/dyn_subgrid/test/dynConsBiogeophys_test/test_dyn_cons_biogeophys.pf index 7a847c24c4..d991f0cc54 100644 --- a/src/dyn_subgrid/test/dynConsBiogeophys_test/test_dyn_cons_biogeophys.pf +++ b/src/dyn_subgrid/test/dynConsBiogeophys_test/test_dyn_cons_biogeophys.pf @@ -2,7 +2,7 @@ module test_dyn_cons_biogeophys ! Tests of dynConsBiogeophysMod - use pfunit_mod + use funit use dynConsBiogeophysMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/dyn_subgrid/test/dynInitColumns_test/CMakeLists.txt b/src/dyn_subgrid/test/dynInitColumns_test/CMakeLists.txt index 15647e0517..7952f66756 100644 --- a/src/dyn_subgrid/test/dynInitColumns_test/CMakeLists.txt +++ b/src/dyn_subgrid/test/dynInitColumns_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(dynInitColumns test_dynInitColumns_exe - "test_init_columns.pf" "") - -target_link_libraries(test_dynInitColumns_exe clm csm_share esmf_wrf_timemgr) +add_pfunit_ctest(dynInitColumns + TEST_SOURCES "test_init_columns.pf" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/dyn_subgrid/test/dynInitColumns_test/test_init_columns.pf b/src/dyn_subgrid/test/dynInitColumns_test/test_init_columns.pf index 829d9e8260..ade2e6d955 100644 --- a/src/dyn_subgrid/test/dynInitColumns_test/test_init_columns.pf +++ b/src/dyn_subgrid/test/dynInitColumns_test/test_init_columns.pf @@ -2,7 +2,7 @@ module test_init_columns ! Tests of the dynInitColumns module - use pfunit_mod + use funit use unittestSubgridMod use dynInitColumnsMod use ColumnType , only : col diff --git a/src/dyn_subgrid/test/dynLandunitArea_test/CMakeLists.txt b/src/dyn_subgrid/test/dynLandunitArea_test/CMakeLists.txt index 2547dc4a17..cd0100d72a 100644 --- a/src/dyn_subgrid/test/dynLandunitArea_test/CMakeLists.txt +++ b/src/dyn_subgrid/test/dynLandunitArea_test/CMakeLists.txt @@ -2,8 +2,6 @@ set(pfunit_sources test_update_landunit_weights_one_gcell.pf test_update_landunit_weights.pf) -create_pFUnit_test(dynLandunitArea test_dynLandunitArea_exe - "${pfunit_sources}" "") - -target_link_libraries(test_dynLandunitArea_exe clm csm_share) - +add_pfunit_ctest(dynLandunitArea + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share) diff --git a/src/dyn_subgrid/test/dynLandunitArea_test/test_update_landunit_weights.pf b/src/dyn_subgrid/test/dynLandunitArea_test/test_update_landunit_weights.pf index 7bfc76429b..d4e7c8c7ed 100644 --- a/src/dyn_subgrid/test/dynLandunitArea_test/test_update_landunit_weights.pf +++ b/src/dyn_subgrid/test/dynLandunitArea_test/test_update_landunit_weights.pf @@ -2,7 +2,7 @@ module test_update_landunit_weights ! Tests of the update_landunit_weights routine in the dynLandunitArea module - use pfunit_mod + use funit use unittestSubgridMod use dynLandunitAreaMod use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/dyn_subgrid/test/dynLandunitArea_test/test_update_landunit_weights_one_gcell.pf b/src/dyn_subgrid/test/dynLandunitArea_test/test_update_landunit_weights_one_gcell.pf index 03ee916a06..1f255dfd9b 100644 --- a/src/dyn_subgrid/test/dynLandunitArea_test/test_update_landunit_weights_one_gcell.pf +++ b/src/dyn_subgrid/test/dynLandunitArea_test/test_update_landunit_weights_one_gcell.pf @@ -2,7 +2,7 @@ module test_update_landunit_weights_one_gcell ! Tests of the update_landunit_weights_one_gcell routine in the dynLandunitArea module - use pfunit_mod + use funit use dynLandunitAreaMod use landunit_varcon, only : istsoil, istcrop, isturb_md, istice, istdlak, max_lunit use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/dyn_subgrid/test/dynPatchStateUpdater_test/CMakeLists.txt b/src/dyn_subgrid/test/dynPatchStateUpdater_test/CMakeLists.txt index 932fa98ea2..10c05b8c14 100644 --- a/src/dyn_subgrid/test/dynPatchStateUpdater_test/CMakeLists.txt +++ b/src/dyn_subgrid/test/dynPatchStateUpdater_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(PatchStateUpdater test_PatchStateUpdater_exe - "test_patch_state_updater.pf" "") - -target_link_libraries(test_PatchStateUpdater_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(PatchStateUpdater + TEST_SOURCES "test_patch_state_updater.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/dyn_subgrid/test/dynPatchStateUpdater_test/test_patch_state_updater.pf b/src/dyn_subgrid/test/dynPatchStateUpdater_test/test_patch_state_updater.pf index 6e0cd9fc10..af0d1992d5 100644 --- a/src/dyn_subgrid/test/dynPatchStateUpdater_test/test_patch_state_updater.pf +++ b/src/dyn_subgrid/test/dynPatchStateUpdater_test/test_patch_state_updater.pf @@ -2,7 +2,7 @@ module test_patch_state_updater ! Tests of dynPatchStateUpdaterMod - use pfunit_mod + use funit use dynPatchStateUpdaterMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/dyn_subgrid/test/dynTimeInfo_test/CMakeLists.txt b/src/dyn_subgrid/test/dynTimeInfo_test/CMakeLists.txt index 3e2e20e756..66f2027c36 100644 --- a/src/dyn_subgrid/test/dynTimeInfo_test/CMakeLists.txt +++ b/src/dyn_subgrid/test/dynTimeInfo_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(dynTimeInfo test_dynTimeInfo_exe - "test_dynTimeInfo.pf" "") - -target_link_libraries(test_dynTimeInfo_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(dynTimeInfo + TEST_SOURCES "test_dynTimeInfo.pf" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/dyn_subgrid/test/dynTimeInfo_test/test_dynTimeInfo.pf b/src/dyn_subgrid/test/dynTimeInfo_test/test_dynTimeInfo.pf index cdb1e394ec..3c6092381e 100644 --- a/src/dyn_subgrid/test/dynTimeInfo_test/test_dynTimeInfo.pf +++ b/src/dyn_subgrid/test/dynTimeInfo_test/test_dynTimeInfo.pf @@ -2,7 +2,7 @@ module test_dynTimeInfo ! Tests of the dynTimeInfo class - use pfunit_mod + use funit use dynTimeInfoMod use shr_kind_mod, only: r8 => shr_kind_r8 use unittestTimeManagerMod, only : unittest_timemgr_setup, unittest_timemgr_teardown diff --git a/src/dyn_subgrid/test/dynVar_test/CMakeLists.txt b/src/dyn_subgrid/test/dynVar_test/CMakeLists.txt index 2fadf5d844..fc4cf07b30 100644 --- a/src/dyn_subgrid/test/dynVar_test/CMakeLists.txt +++ b/src/dyn_subgrid/test/dynVar_test/CMakeLists.txt @@ -6,7 +6,7 @@ set (pfunit_sources set (extra_sources test_dynVarShared.F90) -create_pfUnit_test(dynVar test_dynVar_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_dynVar_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(dynVar + TEST_SOURCES "${pfunit_sources}" + OTHER_SOURCES "${extra_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/dyn_subgrid/test/dynVar_test/test_dynVarTimeInterp.pf b/src/dyn_subgrid/test/dynVar_test/test_dynVarTimeInterp.pf index e50acf09a4..da2168f2ce 100644 --- a/src/dyn_subgrid/test/dynVar_test/test_dynVarTimeInterp.pf +++ b/src/dyn_subgrid/test/dynVar_test/test_dynVarTimeInterp.pf @@ -2,7 +2,7 @@ module test_dynVarTimeInterp ! Tests of dyn_var_time_interp - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use dynVarTimeInterpMod, only : dyn_var_time_interp_type use test_dynVarShared diff --git a/src/dyn_subgrid/test/dynVar_test/test_dynVarTimeUninterp.pf b/src/dyn_subgrid/test/dynVar_test/test_dynVarTimeUninterp.pf index 9f41a8c1ac..1e2fe31836 100644 --- a/src/dyn_subgrid/test/dynVar_test/test_dynVarTimeUninterp.pf +++ b/src/dyn_subgrid/test/dynVar_test/test_dynVarTimeUninterp.pf @@ -2,7 +2,7 @@ module test_dynVarTimeUninterp ! Tests of dyn_var_time_uninterp - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use dynVarTimeUninterpMod, only : dyn_var_time_uninterp_type use test_dynVarShared diff --git a/src/init_interp/test/initInterpMindist_test/CMakeLists.txt b/src/init_interp/test/initInterpMindist_test/CMakeLists.txt index 03d669ef34..f349f0e021 100644 --- a/src/init_interp/test/initInterpMindist_test/CMakeLists.txt +++ b/src/init_interp/test/initInterpMindist_test/CMakeLists.txt @@ -3,7 +3,6 @@ set (pfunit_sources test_set_single_match.pf initInterpMindistTestUtils.pf) -create_pFUnit_test(initInterpMindist test_initInterpMindist_exe - "${pfunit_sources}" "") - -target_link_libraries(test_initInterpMindist_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(initInterpMindist + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share) diff --git a/src/init_interp/test/initInterpMindist_test/initInterpMindistTestUtils.pf b/src/init_interp/test/initInterpMindist_test/initInterpMindistTestUtils.pf index 83f524c5d8..04f09cb55d 100644 --- a/src/init_interp/test/initInterpMindist_test/initInterpMindistTestUtils.pf +++ b/src/init_interp/test/initInterpMindist_test/initInterpMindistTestUtils.pf @@ -2,7 +2,7 @@ module initInterpMindistTestUtils ! Utilities to aid the testing of initInterpMindist - use pfunit_mod + use funit use shr_kind_mod , only : r8 => shr_kind_r8 use initInterpMindist, only : subgrid_type, subgrid_special_indices_type use glcBehaviorMod, only: glc_behavior_type diff --git a/src/init_interp/test/initInterpMindist_test/test_set_mindist.pf b/src/init_interp/test/initInterpMindist_test/test_set_mindist.pf index 67509c35af..06ce20d7de 100644 --- a/src/init_interp/test/initInterpMindist_test/test_set_mindist.pf +++ b/src/init_interp/test/initInterpMindist_test/test_set_mindist.pf @@ -2,7 +2,7 @@ module test_set_mindist ! Tests of initInterpMindist: set_mindist - use pfunit_mod + use funit use initInterpMindist use initInterpMindistTestUtils, only : create_subgrid_info, create_glc_behavior use initInterpMindistTestUtils, only : subgrid_special_indices, ilun_special diff --git a/src/init_interp/test/initInterpMindist_test/test_set_single_match.pf b/src/init_interp/test/initInterpMindist_test/test_set_single_match.pf index 64e66a8d3c..e8ac6bf672 100644 --- a/src/init_interp/test/initInterpMindist_test/test_set_single_match.pf +++ b/src/init_interp/test/initInterpMindist_test/test_set_single_match.pf @@ -2,7 +2,7 @@ module test_set_single_match ! Tests of initInterpMindist: set_single_match - use pfunit_mod + use funit use initInterpMindist use initInterpMindistTestUtils, only : create_subgrid_info, create_glc_behavior use initInterpMindistTestUtils, only : subgrid_special_indices, ilun_special diff --git a/src/init_interp/test/initInterpMultilevel_test/CMakeLists.txt b/src/init_interp/test/initInterpMultilevel_test/CMakeLists.txt index d87d37529c..92ab54e54b 100644 --- a/src/init_interp/test/initInterpMultilevel_test/CMakeLists.txt +++ b/src/init_interp/test/initInterpMultilevel_test/CMakeLists.txt @@ -8,7 +8,7 @@ set (pfunit_sources set (extra_sources multilevel_interp_factory.F90) -create_pFUnit_test(initInterpMultilevel test_initInterpMultilevel_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_initInterpMultilevel_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(initInterpMultilevel + TEST_SOURCES "${pfunit_sources}" + OTHER_SOURCES "${extra_sources}" + LINK_LIBRARIES clm csm_share) diff --git a/src/init_interp/test/initInterpMultilevel_test/initInterpMultilevelMock.pf b/src/init_interp/test/initInterpMultilevel_test/initInterpMultilevelMock.pf index 04911c0278..988f1e5e19 100644 --- a/src/init_interp/test/initInterpMultilevel_test/initInterpMultilevelMock.pf +++ b/src/init_interp/test/initInterpMultilevel_test/initInterpMultilevelMock.pf @@ -6,7 +6,7 @@ module initInterpMultilevelMock ! interp_multilevel routine is called correctly. ! ! !USES: - use pfunit_mod + use funit use shr_kind_mod , only : r8 => shr_kind_r8 use initInterpMultilevelBase , only : interp_multilevel_type diff --git a/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_interp.pf b/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_interp.pf index cf5a2279a8..1635ab2528 100644 --- a/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_interp.pf +++ b/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_interp.pf @@ -2,7 +2,7 @@ module test_init_interp_multilevel_interp ! Tests of initInterpMultilevelInterp - use pfunit_mod + use funit use initInterpMultilevelInterp use multilevel_interp_factory use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_snow.pf b/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_snow.pf index 0e31df0c68..4a4d3be0d1 100644 --- a/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_snow.pf +++ b/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_snow.pf @@ -2,7 +2,7 @@ module test_init_interp_multilevel_snow ! Tests of initInterpMultilevelSnow - use pfunit_mod + use funit use initInterpMultilevelSnow use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_split.pf b/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_split.pf index de9256aa45..c86696ba5e 100644 --- a/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_split.pf +++ b/src/init_interp/test/initInterpMultilevel_test/test_init_interp_multilevel_split.pf @@ -2,7 +2,7 @@ module test_init_interp_multilevel_split ! Tests of initInterpMultilevelSplit - use pfunit_mod + use funit use initInterpMultilevelSplit use initInterpMultilevelInterp, only : interp_multilevel_interp_type use initInterpMultilevelCopy, only : interp_multilevel_copy_type diff --git a/src/init_interp/test/initInterpUtils_test/CMakeLists.txt b/src/init_interp/test/initInterpUtils_test/CMakeLists.txt index 099120472c..874900e21a 100644 --- a/src/init_interp/test/initInterpUtils_test/CMakeLists.txt +++ b/src/init_interp/test/initInterpUtils_test/CMakeLists.txt @@ -1,7 +1,6 @@ set (pfunit_sources test_glc_elevclasses_are_same.pf) -create_pFUnit_test(initInterpUtils test_initInterpUtils_exe - "${pfunit_sources}" "") - -target_link_libraries(test_initInterpUtils_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(initInterpUtils + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share) diff --git a/src/init_interp/test/initInterpUtils_test/test_glc_elevclasses_are_same.pf b/src/init_interp/test/initInterpUtils_test/test_glc_elevclasses_are_same.pf index 987429cf5d..0835dd6534 100644 --- a/src/init_interp/test/initInterpUtils_test/test_glc_elevclasses_are_same.pf +++ b/src/init_interp/test/initInterpUtils_test/test_glc_elevclasses_are_same.pf @@ -2,7 +2,7 @@ module test_glc_elevclasses_are_same ! Tests of initInterpUtils: glc_elevclasses_are_same - use pfunit_mod + use funit use initInterpUtils use shr_kind_mod , only : r8 => shr_kind_r8 use ncdio_pio, only : file_desc_t, ncd_set_dim, ncd_set_var diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index ae178b226c..33e9412ba9 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -9,12 +9,13 @@ module clm_driver ! ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 - use clm_varctl , only : iulog, use_fates, use_fates_sp + use clm_varctl , only : iulog, use_fates, use_fates_sp, use_fates_bgc use clm_varctl , only : use_cn, use_lch4, use_noio, use_c13, use_c14 use CNSharedParamsMod , only : use_matrixcn use clm_varctl , only : use_crop, irrigate, ndep_from_cpl use clm_varctl , only : use_soil_moisture_streams - use clm_time_manager , only : get_nstep, is_beg_curr_day + use clm_varctl , only : use_cropcal_streams + use clm_time_manager , only : get_nstep, is_beg_curr_day, is_beg_curr_year use clm_time_manager , only : get_prev_date, is_first_step use clm_varpar , only : nlevsno, nlevgrnd use clm_varorb , only : obliqr @@ -59,6 +60,7 @@ module clm_driver use SoilBiogeochemVerticalProfileMod , only : SoilBiogeochemVerticalProfile use SatellitePhenologyMod , only : SatellitePhenology, interpMonthlyVeg use ndepStreamMod , only : ndep_interp + use cropcalStreamMod , only : cropcal_advance, cropcal_interp use ch4Mod , only : ch4, ch4_init_gridcell_balance_check, ch4_init_column_balance_check use DUSTMod , only : DustDryDep, DustEmission use VOCEmissionMod , only : VOCEmission @@ -80,7 +82,6 @@ module clm_driver use ColumnType , only : col use PatchType , only : patch use clm_instMod - use EDBGCDynMod , only : EDBGCDyn, EDBGCDynSummary use SoilMoistureStreamMod , only : PrescribedSoilMoistureInterp, PrescribedSoilMoistureAdvance use SoilBiogeochemDecompCascadeConType , only : no_soil_decomp, decomp_method ! @@ -110,10 +111,12 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! the calling tree is given in the description of this module. ! ! !USES: - use clm_time_manager , only : get_curr_date - use clm_varctl , only : use_lai_streams, fates_spitfire_mode - use laiStreamMod , only : lai_advance - use FATESFireFactoryMod , only : scalar_lightning + use clm_time_manager , only : get_curr_date + use clm_varctl , only : use_lai_streams, fates_spitfire_mode + use clm_varctl , only : fates_seeddisp_cadence + use laiStreamMod , only : lai_advance + use FATESFireFactoryMod , only : scalar_lightning + use FatesInterfaceTypesMod, only : fates_dispersal_cadence_none ! ! !ARGUMENTS: implicit none @@ -205,7 +208,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! ======================================================================== need_glacier_initialization = is_first_step() - + if (need_glacier_initialization) then !$OMP PARALLEL DO PRIVATE (nc, bounds_clump) do nc = 1, nclumps @@ -224,7 +227,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! Specified phenology ! Done in SP mode, FATES-SP mode and also when dry-deposition is active ! ============================================================================ - + if (use_cn) then ! For dry-deposition need to call CLMSP so that mlaidiff is obtained ! NOTE: This is also true of FATES below @@ -263,7 +266,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro end if end if - + ! ================================================================================== ! Determine decomp vertical profiles ! @@ -289,10 +292,12 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro call active_layer_inst%alt_calc(filter_inactive_and_active(nc)%num_soilc, filter_inactive_and_active(nc)%soilc, & temperature_inst) - if (use_cn .and. decomp_method /= no_soil_decomp) then + ! Filter bgc_soilc operates on all non-sp soil columns + ! Filter bgc_vegp operates on all non-fates, non-sp patches (use_cn) on soil + if ((use_cn .or. use_fates_bgc) .and. decomp_method /= no_soil_decomp) then call SoilBiogeochemVerticalProfile(bounds_clump , & - filter_inactive_and_active(nc)%num_soilc, filter_inactive_and_active(nc)%soilc , & - filter_inactive_and_active(nc)%num_soilp, filter_inactive_and_active(nc)%soilp , & + filter_inactive_and_active(nc)%num_bgc_soilc, filter_inactive_and_active(nc)%bgc_soilc , & + filter_inactive_and_active(nc)%num_bgc_vegp, filter_inactive_and_active(nc)%bgc_vegp , & active_layer_inst, soilstate_inst, soilbiogeochem_state_inst) end if @@ -325,12 +330,12 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro call get_clump_bounds(nc, bounds_clump) call t_startf('begcnbal_grc') - if (use_cn) then + if (use_cn .or. use_fates_bgc) then ! Initialize gridcell-level balance check call bgc_vegetation_inst%InitGridcellBalance(bounds_clump, & filter(nc)%num_allc, filter(nc)%allc, & - filter(nc)%num_soilc, filter(nc)%soilc, & - filter(nc)%num_soilp, filter(nc)%soilp, & + filter(nc)%num_bgc_soilc, filter(nc)%bgc_soilc, & + filter(nc)%num_bgc_vegp, filter(nc)%bgc_vegp, & soilbiogeochem_carbonstate_inst, & c13_soilbiogeochem_carbonstate_inst, & c14_soilbiogeochem_carbonstate_inst, & @@ -415,12 +420,12 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro call t_stopf('begwbal') call t_startf('begcnbal_col') - if (use_cn) then + if (use_cn .or. use_fates_bgc) then ! Initialize column-level balance check call bgc_vegetation_inst%InitColumnBalance(bounds_clump, & filter(nc)%num_allc, filter(nc)%allc, & - filter(nc)%num_soilc, filter(nc)%soilc, & - filter(nc)%num_soilp, filter(nc)%soilp, & + filter(nc)%num_bgc_soilc, filter(nc)%bgc_soilc, & + filter(nc)%num_bgc_vegp, filter(nc)%bgc_vegp, & soilbiogeochem_carbonstate_inst, & c13_soilbiogeochem_carbonstate_inst, & c14_soilbiogeochem_carbonstate_inst, & @@ -444,15 +449,18 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! re-written to go inside. ! ============================================================================ - if (use_cn) then - call t_startf('bgc_interp') + if (use_cn) then ! .or. use_fates_bgc) then (ndep with fates will be added soon) if (.not. ndep_from_cpl) then call ndep_interp(bounds_proc, atm2lnd_inst) end if + end if + + if(use_cn) then + call t_startf('bgc_interp') call bgc_vegetation_inst%InterpFileInputs(bounds_proc) call t_stopf('bgc_interp') - ! fates_spitfire_mode is assigned an integer value in the namelist - ! see bld/namelist_files/namelist_definition_clm4_5.xml for details + ! fates_spitfire_mode is assigned an integer value in the namelist + ! see bld/namelist_files/namelist_definition_clm4_5.xml for details else if (fates_spitfire_mode > scalar_lightning) then call clm_fates%InterpFileInputs(bounds_proc) end if @@ -462,10 +470,16 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! When LAI streams are being used ! NOTE: This call needs to happen outside loops over nclumps (as streams are not threadsafe) - if ((.not. use_cn) .and. (.not. use_fates) .and. (doalb) .and. use_lai_streams) then - call lai_advance( bounds_proc ) + if (doalb .and. use_lai_streams) then + call lai_advance(bounds_proc) endif + ! When crop calendar streams are being used + ! NOTE: This call needs to happen outside loops over nclumps (as streams are not threadsafe) + if (use_cropcal_streams .and. is_beg_curr_year()) then + call cropcal_advance( bounds_proc ) + end if + ! ============================================================================ ! Initialize variables from previous time step, downscale atm forcings, and ! Determine canopy interception and precipitation onto ground surface. @@ -637,7 +651,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro atm2lnd_inst, canopystate_inst, energyflux_inst, frictionvel_inst, & soilstate_inst, temperature_inst, & water_inst%wateratm2lndbulk_inst, water_inst%waterdiagnosticbulk_inst, & - water_inst%waterstatebulk_inst) + water_inst%waterstatebulk_inst, water_inst%waterfluxbulk_inst) call ozone_inst%CalcOzoneStress(bounds_clump, & filter(nc)%num_exposedvegp, filter(nc)%exposedvegp, & @@ -831,6 +845,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro call SoilTemperature(bounds_clump, & filter(nc)%num_urbanl , filter(nc)%urbanl, & filter(nc)%num_urbanc , filter(nc)%urbanc, & + filter(nc)%num_nolakep , filter(nc)%nolakep, & filter(nc)%num_nolakec , filter(nc)%nolakec, & atm2lnd_inst, urbanparams_inst, canopystate_inst, water_inst%waterstatebulk_inst, & water_inst%waterdiagnosticbulk_inst, water_inst%waterfluxbulk_inst, & @@ -997,17 +1012,20 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! - CNDV defined: prognostic biogeography; else prescribed ! - crop model: crop algorithms called from within CNDriver - if (use_cn) then + ! Filter bgc_soilc operates on all non-sp soil columns + ! Filter bgc_vegp operates on all non-fates, non-sp patches (use_cn) on soil + + if(use_cn .or. use_fates_bgc)then call t_startf('ecosysdyn') call bgc_vegetation_inst%EcosystemDynamicsPreDrainage(bounds_clump, & - filter(nc)%num_soilc, filter(nc)%soilc, & - filter(nc)%num_soilp, filter(nc)%soilp, & - filter(nc)%num_actfirec, filter(nc)%actfirec, & - filter(nc)%num_actfirep, filter(nc)%actfirep, & - filter(nc)%num_pcropp, filter(nc)%pcropp, & - filter(nc)%num_soilnopcropp, filter(nc)%soilnopcropp, & - filter(nc)%num_exposedvegp, filter(nc)%exposedvegp, & - filter(nc)%num_noexposedvegp, filter(nc)%noexposedvegp, & + filter(nc)%num_bgc_soilc, filter(nc)%bgc_soilc, & + filter(nc)%num_bgc_vegp, filter(nc)%bgc_vegp, & + filter(nc)%num_actfirec, filter(nc)%actfirec, & + filter(nc)%num_actfirep, filter(nc)%actfirep, & + filter(nc)%num_pcropp, filter(nc)%pcropp, & + filter(nc)%num_soilnopcropp, filter(nc)%soilnopcropp, & + filter(nc)%num_exposedvegp, filter(nc)%exposedvegp, & + filter(nc)%num_noexposedvegp, filter(nc)%noexposedvegp, & soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & c13_soilbiogeochem_carbonflux_inst, c13_soilbiogeochem_carbonstate_inst, & c14_soilbiogeochem_carbonflux_inst, c14_soilbiogeochem_carbonstate_inst, & @@ -1020,9 +1038,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro soil_water_retention_curve, crop_inst, ch4_inst, & photosyns_inst, saturated_excess_runoff_inst, energyflux_inst, & nutrient_competition_method, fireemis_inst) - call t_stopf('ecosysdyn') - end if ! Prescribed biogeography - prescribed canopy structure, some prognostic carbon fluxes @@ -1058,6 +1074,13 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro frictionvel_inst, photosyns_inst, drydepvel_inst) call t_stopf('depvel') + if (use_cropcal_streams .and. is_beg_curr_year()) then + ! ============================================================================ + ! Update crop calendars + ! ============================================================================ + call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, filter_inactive_and_active(nc)%pcropp, crop_inst) + end if + ! ============================================================================ ! Calculate soil/snow hydrology with drainage (subsurface runoff) ! ============================================================================ @@ -1077,13 +1100,13 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro call t_stopf('hydro2_drainage') - if (use_cn) then - + + if (use_cn .or. use_fates_bgc) then call t_startf('EcosysDynPostDrainage') call bgc_vegetation_inst%EcosystemDynamicsPostDrainage(bounds_clump, & filter(nc)%num_allc, filter(nc)%allc, & - filter(nc)%num_soilc, filter(nc)%soilc, & - filter(nc)%num_soilp, filter(nc)%soilp, & + filter(nc)%num_bgc_soilc, filter(nc)%bgc_soilc, & + filter(nc)%num_bgc_vegp, filter(nc)%bgc_vegp, & filter(nc)%num_actfirec, filter(nc)%actfirec, & filter(nc)%num_actfirep, filter(nc)%actfirep, & doalb, crop_inst, & @@ -1095,11 +1118,8 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro c14_soilbiogeochem_carbonflux_inst, c14_soilbiogeochem_carbonstate_inst, & soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst) call t_stopf('EcosysDynPostDrainage') - end if - - if ( use_fates) then ! FATES has its own running mean functions, such as 24hr @@ -1108,30 +1128,6 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! moving averages are updated here call clm_fates%WrapUpdateFatesRmean(nc,temperature_inst) - call EDBGCDyn(bounds_clump, & - filter(nc)%num_soilc, filter(nc)%soilc, & - filter(nc)%num_soilp, filter(nc)%soilp, & - filter(nc)%num_pcropp, filter(nc)%pcropp, doalb, & - bgc_vegetation_inst%cnveg_carbonflux_inst, & - bgc_vegetation_inst%cnveg_carbonstate_inst, & - soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & - soilbiogeochem_state_inst, clm_fates, & - soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst, & - c13_soilbiogeochem_carbonstate_inst, c13_soilbiogeochem_carbonflux_inst, & - c14_soilbiogeochem_carbonstate_inst, c14_soilbiogeochem_carbonflux_inst, & - active_layer_inst, atm2lnd_inst, water_inst%waterfluxbulk_inst, & - canopystate_inst, soilstate_inst, temperature_inst, crop_inst, ch4_inst) - - if ( decomp_method /= no_soil_decomp )then - call EDBGCDynSummary(bounds_clump, & - filter(nc)%num_soilc, filter(nc)%soilc, & - filter(nc)%num_soilp, filter(nc)%soilp, & - soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & - c13_soilbiogeochem_carbonflux_inst, c13_soilbiogeochem_carbonstate_inst, & - c14_soilbiogeochem_carbonflux_inst, c14_soilbiogeochem_carbonstate_inst, & - soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst, & - nc) - end if call clm_fates%wrap_update_hifrq_hist(bounds_clump, & soilbiogeochem_carbonflux_inst, & @@ -1144,14 +1140,11 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! This is the main call to FATES dynamics ! -------------------------------------------------------------------------- - if ( masterproc ) then - write(iulog,*) 'clm: calling FATES model ', get_nstep() - end if call clm_fates%dynamics_driv( nc, bounds_clump, & atm2lnd_inst, soilstate_inst, temperature_inst, active_layer_inst, & water_inst%waterstatebulk_inst, water_inst%waterdiagnosticbulk_inst, & water_inst%wateratm2lndbulk_inst, canopystate_inst, soilbiogeochem_carbonflux_inst, & - frictionvel_inst) + frictionvel_inst, soil_water_retention_curve) ! TODO(wjs, 2016-04-01) I think this setFilters call should be replaced by a ! call to reweight_wrapup, if it's needed at all. @@ -1176,15 +1169,18 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! Check the carbon and nitrogen balance ! ============================================================================ - if (use_cn) then + if(use_cn .or. use_fates_bgc)then call t_startf('cnbalchk') call bgc_vegetation_inst%BalanceCheck( & - bounds_clump, filter(nc)%num_soilc, filter(nc)%soilc, & - soilbiogeochem_carbonflux_inst, & - soilbiogeochem_nitrogenflux_inst, atm2lnd_inst ) + bounds_clump, filter(nc)%num_bgc_soilc, filter(nc)%bgc_soilc, & + soilbiogeochem_carbonflux_inst, & + soilbiogeochem_nitrogenflux_inst, & + soilbiogeochem_carbonstate_inst, & + soilbiogeochem_nitrogenstate_inst, & + atm2lnd_inst, clm_fates ) call t_stopf('cnbalchk') end if - + ! Calculation of methane fluxes if (use_lch4) then @@ -1273,6 +1269,14 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro end do !$OMP END PARALLEL DO + + ! Pass fates seed dispersal information to neighboring gridcells across + ! all MPI tasks. Note that WrapGlobalSeedDispersal calls an MPI collective routine + ! and as such WrapGlobalSeedDispersal should be called outside of OMP threaded loop regions + if (use_fates) then + if (fates_seeddisp_cadence /= fates_dispersal_cadence_none) call clm_fates%WrapGlobalSeedDispersal() + end if + ! ============================================================================ ! Determine gridcell averaged properties to send to atm ! ============================================================================ @@ -1403,7 +1407,6 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! Update history buffer ! ============================================================================ - call t_startf('hbuf') call hist_update_hbuf(bounds_proc) call t_stopf('hbuf') diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index 7988fbfc7b..a53f6f2bdc 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -10,10 +10,11 @@ module clm_initializeMod use spmdMod , only : masterproc, mpicom use decompMod , only : bounds_type, get_proc_bounds, get_proc_clumps, get_clump_bounds use abortutils , only : endrun - use clm_varctl , only : nsrest, nsrStartup, nsrContinue, nsrBranch, use_fates_sp + use clm_varctl , only : nsrest, nsrStartup, nsrContinue, nsrBranch + use clm_varctl , only : use_fates_sp, use_fates_bgc, use_fates use clm_varctl , only : is_cold_start use clm_varctl , only : iulog - use clm_varctl , only : use_lch4, use_cn, use_cndv, use_c13, use_c14, use_fates + use clm_varctl , only : use_lch4, use_cn, use_cndv, use_c13, use_c14, use_fates, use_fates_nocomp use clm_varctl , only : use_soil_moisture_streams use clm_instur , only : wt_lunit, urban_valid, wt_nat_patch, wt_cft, fert_cft use clm_instur , only : irrig_method, wt_glc_mec, topo_glc_mec, haslake, pct_urban_max @@ -132,15 +133,16 @@ subroutine initialize2(ni,nj) use clm_varpar , only : natpft_size,cft_size use clm_varctl , only : fsurdat use clm_varctl , only : finidat, finidat_interp_source, finidat_interp_dest, fsurdat - use clm_varctl , only : use_cn, use_fates + use clm_varctl , only : use_cn, use_fates, use_fates_luh use clm_varctl , only : use_crop, ndep_from_cpl, fates_spitfire_mode use clm_varorb , only : eccen, mvelpp, lambm0, obliqr + use clm_varctl , only : use_cropcal_streams use landunit_varcon , only : landunit_varcon_init, max_lunit, numurbl use pftconMod , only : pftcon use decompInitMod , only : decompInit_clumps, decompInit_glcp use domainMod , only : domain_check, ldomain, domain_init use surfrdMod , only : surfrd_get_data - use controlMod , only : NLFilename + use controlMod , only : NLFilename, fluh_timeseries use initGridCellsMod , only : initGridCells use ch4varcon , only : ch4conrd use UrbanParamsType , only : UrbanInput, IsSimpleBuildTemp @@ -162,6 +164,7 @@ subroutine initialize2(ni,nj) use restFileMod , only : restFile_getfile, restFile_open, restFile_close use restFileMod , only : restFile_read, restFile_write use ndepStreamMod , only : ndep_init, ndep_interp + use cropcalStreamMod , only : cropcal_init, cropcal_interp, cropcal_advance use LakeCon , only : LakeConInit use SatellitePhenologyMod , only : SatellitePhenologyInit, readAnnualVegetation, interpMonthlyVeg, SatellitePhenology use SnowSnicarMod , only : SnowAge_init, SnowOptics_init @@ -172,6 +175,7 @@ subroutine initialize2(ni,nj) use CNSharedParamsMod , only : CNParamsSetSoilDepth use NutrientCompetitionFactoryMod , only : create_nutrient_competition_method use FATESFireFactoryMod , only : scalar_lightning + use dynFATESLandUseChangeMod , only : dynFatesLandUseInit ! ! !ARGUMENTS integer, intent(in) :: ni, nj ! global grid sizes @@ -337,7 +341,7 @@ subroutine initialize2(ni,nj) end if ! Pass model timestep info to FATES - call CLMFatesTimesteps() + if (use_fates) call CLMFatesTimesteps() ! Initialize daylength from the previous time step (needed so prev_dayl can be set correctly) call t_startf('init_orbd') @@ -403,6 +407,11 @@ subroutine initialize2(ni,nj) call dynSubgrid_init(bounds_proc, glc_behavior, crop_inst) call t_stopf('init_dyn_subgrid') + ! Initialize fates LUH2 usage + if (use_fates_luh) then + call dynFatesLandUseInit(bounds_proc, fluh_timeseries) + end if + ! Initialize baseline water and energy states needed for dynamic subgrid operation ! This will be overwritten by the restart file, but needs to be done for a cold start ! case. @@ -429,8 +438,11 @@ subroutine initialize2(ni,nj) !$OMP END PARALLEL DO ! Initialize modules (after time-manager initialization in most cases) - if (use_cn) then + if (use_cn .or. use_fates) then call bgc_vegetation_inst%Init2(bounds_proc, NLFilename) + end if + + if (use_cn) then ! NOTE(wjs, 2016-02-23) Maybe the rest of the body of this conditional should also ! be moved into bgc_vegetation_inst%Init2 @@ -449,11 +461,14 @@ subroutine initialize2(ni,nj) else ! FATES OR Satellite phenology - ! For SP FATES-SP Initialize SP + ! For FATES-SP or FATES-NOCOMP Initialize SP ! Also for FATES with Dry-Deposition on as well (see above) - !if(use_fates_sp .or. (.not.use_cn) .or. (n_drydep > 0) )then ! Replace with this when we have dry-deposition working - ! For now don't allow for dry-deposition because of issues in #1044 EBK Jun/17/2022 - if( use_fates_sp .or. .not. use_fates )then + ! For now don't allow for dry-deposition with full fates + ! because of issues in #1044 EBK Jun/17/2022 + if( use_fates_nocomp .or. (.not. use_fates )) then + if (masterproc) then + write(iulog,'(a)')'Initializing Satellite Phenology' + end if call SatellitePhenologyInit(bounds_proc) end if @@ -622,7 +637,7 @@ subroutine initialize2(ni,nj) !$OMP END PARALLEL DO ! Initialize nitrogen deposition - if (use_cn) then + if (use_cn ) then !.or. use_fates_bgc) then (ndep with fates will be added soon RGK) call t_startf('init_ndep') if (.not. ndep_from_cpl) then call ndep_init(bounds_proc, NLFilename) @@ -631,6 +646,21 @@ subroutine initialize2(ni,nj) call t_stopf('init_ndep') end if + ! Initialize crop calendars + call t_startf('init_cropcal') + call cropcal_init(bounds_proc) + if (use_cropcal_streams) then + call cropcal_advance( bounds_proc ) + !$OMP PARALLEL DO PRIVATE (nc, bounds_clump) + do nc = 1,nclumps + call get_clump_bounds(nc, bounds_clump) + call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & + filter_inactive_and_active(nc)%pcropp, crop_inst) + end do + !$OMP END PARALLEL DO + end if + call t_stopf('init_cropcal') + ! Initialize active history fields. ! This is only done if not a restart run. If a restart run, then this ! information has already been obtained from the restart data read above. @@ -665,12 +695,12 @@ subroutine initialize2(ni,nj) ! Call interpMonthlyVeg for dry-deposition so that mlaidiff will be calculated ! This needs to be done even if FATES, CN or CNDV is on! call interpMonthlyVeg(bounds_proc, canopystate_inst) - ! If fates has satellite phenology enabled, get the monthly veg values - ! prior to the first call to SatellitePhenology() elseif ( use_fates_sp ) then + ! If fates has satellite phenology enabled, get the monthly veg values + ! prior to the first call to SatellitePhenology() call interpMonthlyVeg(bounds_proc, canopystate_inst) end if - + ! Determine gridcell averaged properties to send to atm if (nsrest == nsrStartup) then call t_startf('init_map2gc') @@ -706,7 +736,6 @@ subroutine initialize2(ni,nj) !$OMP PARALLEL DO PRIVATE (nc, bounds_clump) do nc = 1,nclumps call get_clump_bounds(nc, bounds_clump) - ! FATES satellite phenology mode needs to include all active and inactive patch-level soil ! filters due to the translation between the hlm pfts and the fates pfts. ! E.g. in FATES, an active PFT vector of 1, 0, 0, 0, 1, 0, 1, 0 would be mapped into @@ -719,11 +748,12 @@ subroutine initialize2(ni,nj) end do !$OMP END PARALLEL DO end if + call clm_fates%init_coldstart(water_inst%waterstatebulk_inst, & water_inst%waterdiagnosticbulk_inst, canopystate_inst, & soilstate_inst, soilbiogeochem_carbonflux_inst) end if - + ! topo_glc_mec was allocated in initialize1, but needed to be kept around through ! initialize2 because it is used to initialize other variables; now it can be deallocated deallocate(topo_glc_mec, fert_cft, irrig_method) diff --git a/src/main/clm_instMod.F90 b/src/main/clm_instMod.F90 index 7924c2111e..1ca450b48d 100644 --- a/src/main/clm_instMod.F90 +++ b/src/main/clm_instMod.F90 @@ -8,7 +8,7 @@ module clm_instMod use shr_kind_mod , only : r8 => shr_kind_r8 use decompMod , only : bounds_type use clm_varpar , only : ndecomp_pools, nlevdecomp_full - use clm_varctl , only : use_cn, use_c13, use_c14, use_lch4, use_cndv, use_fates + use clm_varctl , only : use_cn, use_c13, use_c14, use_lch4, use_cndv, use_fates, use_fates_bgc use clm_varctl , only : iulog use clm_varctl , only : use_crop, snow_cover_fraction_method, paramfile use SoilBiogeochemDecompCascadeConType , only : mimics_decomp, no_soil_decomp, century_decomp, decomp_method @@ -231,7 +231,6 @@ subroutine clm_instInit(bounds) allocate (h2osno_col(begc:endc)) allocate (snow_depth_col(begc:endc)) - ! snow water do c = begc,endc l = col%landunit(c) @@ -371,7 +370,7 @@ subroutine clm_instInit(bounds) call drydepvel_inst%Init(bounds) - if (decomp_method /= no_soil_decomp) then + if_decomp: if (decomp_method /= no_soil_decomp) then ! Initialize soilbiogeochem_state_inst @@ -423,9 +422,10 @@ subroutine clm_instInit(bounds) call SoilBiogeochemPrecisionControlInit( soilbiogeochem_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst, & c14_soilbiogeochem_carbonstate_inst, soilbiogeochem_nitrogenstate_inst) - end if ! end of if use_cn + end if if_decomp ! Note - always call Init for bgc_vegetation_inst: some pieces need to be initialized always + ! Even for a FATES simulation, we call this to initialize product pools call bgc_vegetation_inst%Init(bounds, nlfilename, GetBalanceCheckSkipSteps(), params_ncid ) if (use_cn .or. use_fates) then @@ -486,6 +486,7 @@ subroutine clm_instRest(bounds, ncid, flag, writing_finidat_interp_dest_file) use ncdio_pio , only : file_desc_t use UrbanParamsType , only : IsSimpleBuildTemp, IsProgBuildTemp use decompMod , only : get_proc_bounds, get_proc_clumps, get_clump_bounds + use clm_varpar , only : nlevsno ! ! !DESCRIPTION: @@ -532,7 +533,9 @@ subroutine clm_instRest(bounds, ncid, flag, writing_finidat_interp_dest_file) call water_inst%restart(bounds, ncid, flag=flag, & writing_finidat_interp_dest_file = writing_finidat_interp_dest_file, & - watsat_col = soilstate_inst%watsat_col(bounds%begc:bounds%endc,:)) + watsat_col = soilstate_inst%watsat_col(bounds%begc:bounds%endc,:), & + t_soisno_col=temperature_inst%t_soisno_col(bounds%begc:bounds%endc, -nlevsno+1:), & + altmax_lastyear_indx=active_layer_inst%altmax_lastyear_indx_col(bounds%begc:bounds%endc)) call irrigation_inst%restart (bounds, ncid, flag=flag) @@ -550,7 +553,7 @@ subroutine clm_instRest(bounds, ncid, flag, writing_finidat_interp_dest_file) call ch4_inst%restart(bounds, ncid, flag=flag) end if - if ( use_cn ) then + if ( use_cn .or. use_fates_bgc) then ! Need to do vegetation restart before soil bgc restart to get totvegc_col for purpose ! of resetting soil carbon at exit spinup when no vegetation is growing. call bgc_vegetation_inst%restart(bounds, ncid, flag=flag) @@ -558,7 +561,7 @@ subroutine clm_instRest(bounds, ncid, flag, writing_finidat_interp_dest_file) call soilbiogeochem_nitrogenstate_inst%restart(bounds, ncid, flag=flag, & totvegc_col=bgc_vegetation_inst%get_totvegc_col(bounds)) - call crop_inst%restart(bounds, ncid, flag=flag) + call crop_inst%restart(bounds, ncid, bgc_vegetation_inst%cnveg_state_inst, flag=flag) end if if (decomp_method /= no_soil_decomp) then @@ -588,7 +591,8 @@ subroutine clm_instRest(bounds, ncid, flag, writing_finidat_interp_dest_file) canopystate_inst=canopystate_inst, & soilstate_inst=soilstate_inst, & active_layer_inst=active_layer_inst, & - soilbiogeochem_carbonflux_inst=soilbiogeochem_carbonflux_inst) + soilbiogeochem_carbonflux_inst=soilbiogeochem_carbonflux_inst, & + soilbiogeochem_nitrogenflux_inst=soilbiogeochem_nitrogenflux_inst) end if diff --git a/src/main/clm_varcon.F90 b/src/main/clm_varcon.F90 index 340115fe90..e45f5c440e 100644 --- a/src/main/clm_varcon.F90 +++ b/src/main/clm_varcon.F90 @@ -90,6 +90,7 @@ module clm_varcon integer, public, parameter :: fun_period = 1 ! A FUN parameter, and probably needs to be changed for testing real(r8),public, parameter :: smallValue = 1.e-12_r8 ! A small values used by FUN + real(r8),public, parameter :: sum_to_1_tol = 1.e-13_r8 ! error tolerance ! ------------------------------------------------------------------------ ! Special value flags @@ -154,6 +155,18 @@ module clm_varcon real(r8), public :: c14ratio = 1.e-12_r8 ! real(r8) :: c14ratio = 1._r8 ! debug lets set to 1 to try to avoid numerical errors + !------------------------------------------------------------------ + ! Surface roughness constants + !------------------------------------------------------------------ + real(r8), public, parameter :: beta_param = 7.2_r8 ! Meier et al. (2022) https://doi.org/10.5194/gmd-15-2365-2022 + real(r8), public, parameter :: nu_param = 1.5e-5_r8 ! Meier et al. (2022) kinematic viscosity of air + real(r8), public, parameter :: b1_param = 1.4_r8 ! Meier et al. (2022) empirical constant + real(r8), public, parameter :: b4_param = -0.31_r8 ! Meier et al. (2022) empirical constant + real(r8), public, parameter :: cd1_param = 7.5_r8 ! Meier et al. (2022) originally from Raupach (1994) + real(r8), public, parameter :: meier_param1 = 0.23_r8 ! slevis did not find it documented + real(r8), public, parameter :: meier_param2 = 0.08_r8 ! slevis did not find it documented + real(r8), public, parameter :: meier_param3 = 70.0_r8 ! slevis did not find it documented, but to the question "What is the 70 in the formula for roughness length" bard.google.com responds "[...] a dimensionless constant [...] originally introduced by von Karman. It is based on experimental data and is thought to represent the ratio of the average height of the surface roughness elements to the distance that the wind travels before it is slowed down by the roughness." + !------------------------------------------------------------------ ! Urban building temperature constants !------------------------------------------------------------------ diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 9be9af2f73..615f3b2606 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -157,6 +157,9 @@ module clm_varctl ! set saturated excess runoff to zero for crops logical, public :: crop_fsat_equals_zero = .false. + + ! remove this fraction of crop residues to a 1-year product pool (instead of going to litter) + real(r8), public :: crop_residue_removal_frac = 0.0 !---------------------------------------------------------- ! Other subgrid logic @@ -221,6 +224,8 @@ module clm_varctl ! which snow cover fraction parameterization to use character(len=64), public :: snow_cover_fraction_method + ! which snow thermal conductivity parameterization to use + character(len=25), public :: snow_thermal_cond_method ! atmospheric CO2 molar ratio (by volume) (umol/mol) real(r8), public :: co2_ppmv = 355._r8 ! @@ -230,6 +235,36 @@ module clm_varctl real(r8), public :: o3_ppbv = 100._r8 + ! number of wavelength bands used in SNICAR snow albedo calculation + integer, public :: snicar_numrad_snw = 5 + + ! type of downward solar radiation spectrum for SNICAR snow albedo calculation + ! options: + ! mid_latitude_winter, mid_latitude_summer, sub_arctic_winter, + ! sub_arctic_summer, summit_greenland_summer, high_mountain_summer; + character(len=25), public :: snicar_solarspec = 'mid_latitude_winter' + + ! dust optics type for SNICAR snow albedo calculation + ! options: + ! sahara: Saharan dust (Balkanski et al., 2007, central hematite) + ! san_juan_mtns_colorado: San Juan Mountains dust, CO (Skiles et al, 2017) + ! greenland: Greenland dust (Polashenski et al., 2015, central absorptivity) + character(len=25), public :: snicar_dust_optics = 'sahara' + ! option to turn off aerosol effect in snow in SNICAR + logical, public :: snicar_use_aerosol = .true. ! if .false., turn off aerosol deposition flux + + ! option for snow grain shape in SNICAR (He et al. 2017 JC) + character(len=25), public :: snicar_snw_shape = 'hexagonal_plate' ! sphere, spheroid, hexagonal_plate, koch_snowflake + + ! option to activate BC-snow internal mixing in SNICAR (He et al. 2017 JC), ceniln + logical, public :: snicar_snobc_intmix = .false. ! false->external mixing for all BC; true->internal mixing for hydrophilic BC + + ! option to activate dust-snow internal mixing in SNICAR (He et al. 2017 JC), ceniln + logical, public :: snicar_snodst_intmix = .false. ! false->external mixing for all dust; true->internal mixing for all dust + + ! option to activate OC in snow in SNICAR + logical, public :: do_sno_oc = .false. ! control to include organic carbon (OC) in snow + !---------------------------------------------------------- ! C isotopes !---------------------------------------------------------- @@ -245,31 +280,48 @@ module clm_varctl ! this error-check. logical, public :: for_testing_allow_interp_non_ciso_to_ciso = .false. + !---------------------------------------------------------- + ! Surface roughness parameterization + !---------------------------------------------------------- + + character(len=64), public :: z0param_method ! ZengWang2007 or Meier2022 + logical, public :: use_z0m_snowmelt = .false. ! true => use snow z0m parameterization of Brock2006 + !---------------------------------------------------------- ! FATES switches !---------------------------------------------------------- - logical, public :: use_fates = .false. ! true => use fates + logical, public :: use_fates = .false. ! true => use fates ! These are INTERNAL to the FATES module - integer, public :: fates_parteh_mode = -9 ! 1 => carbon only - ! 2 => C+N+P (not enabled yet) - ! no others enabled + + integer, public :: fates_seeddisp_cadence = iundef ! 0 => no seed dispersal + ! 1, 2, 3 => daily, monthly, or yearly dispersal + integer, public :: fates_parteh_mode = -9 ! 1 => carbon only + ! 2 => C+N+P (not enabled yet) + ! no others enabled integer, public :: fates_spitfire_mode = 0 - ! 0 for no fire; 1 for constant ignitions; > 1 for external data (lightning and/or anthropogenic ignitions) - ! see bld/namelist_files/namelist_definition_clm4_5.xml for details - logical, public :: use_fates_tree_damage = .false. ! true => turn on tree damage module - logical, public :: use_fates_logging = .false. ! true => turn on logging module - logical, public :: use_fates_planthydro = .false. ! true => turn on fates hydro + ! 0 for no fire; 1 for constant ignitions; + ! > 1 for external data (lightning and/or anthropogenic ignitions) + ! see bld/namelist_files/namelist_definition_clm4_5.xml for details + logical, public :: use_fates_tree_damage = .false. ! true => turn on tree damage module + logical, public :: use_fates_logging = .false. ! true => turn on logging module + logical, public :: use_fates_planthydro = .false. ! true => turn on fates hydro logical, public :: use_fates_cohort_age_tracking = .false. ! true => turn on cohort age tracking - logical, public :: use_fates_ed_st3 = .false. ! true => static stand structure - logical, public :: use_fates_ed_prescribed_phys = .false. ! true => prescribed physiology - logical, public :: use_fates_inventory_init = .false. ! true => initialize fates from inventory - logical, public :: use_fates_fixed_biogeog = .false. ! true => use fixed biogeography mode - logical, public :: use_fates_nocomp = .false. ! true => use no comopetition mode - logical, public :: use_fates_sp = .false. ! true => use FATES satellite phenology mode - character(len=256), public :: fates_inventory_ctrl_filename = '' ! filename for inventory control - + logical, public :: use_fates_ed_st3 = .false. ! true => static stand structure + logical, public :: use_fates_ed_prescribed_phys = .false. ! true => prescribed physiology + logical, public :: use_fates_inventory_init = .false. ! true => initialize fates from inventory + logical, public :: use_fates_fixed_biogeog = .false. ! true => use fixed biogeography mode + logical, public :: use_fates_nocomp = .false. ! true => use no comopetition mode + logical, public :: use_fates_luh = .false. ! true => use FATES landuse data mode + character(len=256), public :: fluh_timeseries = '' ! filename for fates landuse timeseries data + character(len=256), public :: fates_inventory_ctrl_filename = '' ! filename for inventory control + + ! FATES SP AND FATES BGC are MUTUTALLY EXCLUSIVE, THEY CAN'T BOTH BE ON + ! BUT... THEY CAN BOTH BE OFF (IF FATES IS OFF) + logical, public :: use_fates_sp = .false. ! true => use FATES satellite phenology mode + logical, public :: use_fates_bgc = .false. ! true => use FATES along with CLM soil biogeochemistry + !---------------------------------------------------------- ! LUNA switches !---------------------------------------------------------- @@ -302,6 +354,14 @@ module clm_varctl logical, public :: use_lai_streams = .false. ! true => use lai streams in SatellitePhenologyMod.F90 + !---------------------------------------------------------- + ! crop calendar streams switch for CropPhenology + !---------------------------------------------------------- + + logical, public :: use_cropcal_streams = .false. + logical, public :: use_cropcal_rx_swindows = .false. + logical, public :: use_cropcal_rx_cultivar_gdds = .false. + !---------------------------------------------------------- ! biomass heat storage switch !---------------------------------------------------------- @@ -317,6 +377,11 @@ module clm_varctl real(r8), public :: soil_layerstruct_userdefined(99) = rundef integer, public :: soil_layerstruct_userdefined_nlevsoi = iundef + !---------------------------------------------------------- + !excess ice physics switch + !---------------------------------------------------------- + logical, public :: use_excess_ice = .false. ! true. => use excess ice physics + !---------------------------------------------------------- ! plant hydraulic stress switch !---------------------------------------------------------- @@ -377,8 +442,8 @@ module clm_varctl ! namelist: write CH4 extra diagnostic output logical, public :: hist_wrtch4diag = .false. - ! namelist: write history master list to a file for use in documentation - logical, public :: hist_master_list_file = .false. + ! namelist: write list of all history fields to a file for use in documentation + logical, public :: hist_fields_list_file = .false. !---------------------------------------------------------- ! FATES diff --git a/src/main/clm_varpar.F90 b/src/main/clm_varpar.F90 index f54b750181..ffa851482a 100644 --- a/src/main/clm_varpar.F90 +++ b/src/main/clm_varpar.F90 @@ -113,7 +113,6 @@ module clm_varpar integer, public :: cft_size ! Number of PFTs on crop landunit in arrays of PFTs integer, public :: maxpatch_glc ! max number of elevation classes - integer, public :: max_patch_per_col ! ! !PUBLIC MEMBER FUNCTIONS: public clm_varpar_init ! set parameters @@ -195,13 +194,6 @@ subroutine clm_varpar_init(actual_maxsoil_patches, surf_numpft, surf_numcft) mxharvests = mxsowings + 1 - ! TODO(wjs, 2015-10-04, bugz 2227) Using surf_numcft in this 'max' gives a significant - ! overestimate of max_patch_per_col when use_crop is true. This should be reworked - - ! or, better, removed from the code entirely (because it is a maintenance problem, and - ! I can't imagine that looping idioms that use it help performance that much, and - ! likely they hurt performance.) - max_patch_per_col= max(maxsoil_patches, surf_numcft, maxpatch_urb) - nlevsoifl = 10 nlevurb = 5 diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index a07228aa0d..d95c0e28e0 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -162,7 +162,7 @@ subroutine control_init(dtime) hist_fexcl4, hist_fexcl5, hist_fexcl6, & hist_fexcl7, hist_fexcl8, & hist_fexcl9, hist_fexcl10 - namelist /clm_inparm/ hist_wrtch4diag, hist_master_list_file + namelist /clm_inparm/ hist_wrtch4diag, hist_fields_list_file ! BGC info @@ -199,11 +199,13 @@ subroutine control_init(dtime) clump_pproc, & create_crop_landunit, nsegspc, co2_ppmv, & albice, soil_layerstruct_predefined, soil_layerstruct_userdefined, & - soil_layerstruct_userdefined_nlevsoi, use_subgrid_fluxes, snow_cover_fraction_method, & + soil_layerstruct_userdefined_nlevsoi, use_subgrid_fluxes, & + snow_thermal_cond_method, snow_cover_fraction_method, & irrigate, run_zero_weight_urban, all_active, & crop_fsat_equals_zero, for_testing_run_ncdiopio_tests, & for_testing_use_second_grain_pool, for_testing_use_repr_structure_pool, & - for_testing_no_crop_seed_replenishment + for_testing_no_crop_seed_replenishment, & + z0param_method, use_z0m_snowmelt ! vertical soil mixing variables namelist /clm_inparm/ & @@ -228,12 +230,15 @@ subroutine control_init(dtime) use_fates_fixed_biogeog, & use_fates_nocomp, & use_fates_sp, & + use_fates_luh, & + fluh_timeseries, & fates_inventory_ctrl_filename, & fates_parteh_mode, & + fates_seeddisp_cadence, & use_fates_tree_damage - ! Ozone vegetation stress method - namelist / clm_inparam / o3_veg_stress_method + ! Ozone vegetation stress method + namelist / clm_inparm / o3_veg_stress_method ! CLM 5.0 nitrogen flags namelist /clm_inparm/ use_flexibleCN, use_luna @@ -244,12 +249,15 @@ subroutine control_init(dtime) namelist /clm_inparm/ use_soil_moisture_streams + namelist /clm_inparm/ use_excess_ice + namelist /clm_inparm/ use_lai_streams namelist /clm_inparm/ use_bedrock namelist /clm_inparm/ use_biomass_heat_storage + namelist /clm_inparm/ use_hydrstress namelist /clm_inparm/ use_dynroot @@ -278,10 +286,15 @@ subroutine control_init(dtime) namelist /clm_inparm/ & use_lch4, use_nitrif_denitrif, use_extralakelayers, & - use_vichydro, use_cn, use_cndv, use_crop, use_fertilizer, o3_veg_stress_method, & + use_vichydro, use_cn, use_cndv, use_crop, use_fertilizer, & use_grainproduct, use_snicar_frc, use_vancouver, use_mexicocity, use_noio, & - use_nguardrail + use_nguardrail, crop_residue_removal_frac + ! SNICAR + namelist /clm_inparm/ & + snicar_numrad_snw, snicar_solarspec, snicar_dust_optics, & + snicar_use_aerosol, snicar_snw_shape, snicar_snobc_intmix, & + snicar_snodst_intmix, do_sno_oc ! ---------------------------------------------------------------------- ! Default values @@ -433,6 +446,21 @@ subroutine control_init(dtime) ! Check compatibility with the FATES model if ( use_fates ) then + if(use_fates_sp) then + use_fates_bgc = .false. + else + use_fates_bgc = .true. + end if + + if (fates_parteh_mode == 1 .and. suplnitro == suplnNon .and. use_fates_bgc )then + write(iulog,*) ' When FATES with fates_parteh_mode == 1 (ie carbon only mode),' + write(iulog,*) ' you must have supplemental nitrogen turned on, there will be' + write(iulog,*) ' no nitrogen dynamics with the plants, and therefore no' + write(iulog,*) ' meaningful limitations to nitrogen.' + call endrun(msg=' ERROR: fates_parteh_mode=1 must have suplnitro set to suplnAll.'//& + errMsg(sourcefile, __LINE__)) + end if + if ( use_cn) then call endrun(msg=' ERROR: use_cn and use_fates cannot both be set to true.'//& errMsg(sourcefile, __LINE__)) @@ -458,8 +486,28 @@ subroutine control_init(dtime) errMsg(sourcefile, __LINE__)) end if + if (use_c13 .or. use_c14) then + call endrun(msg=' ERROR: C13 and C14 dynamics are not compatible with FATES.'//& + errMsg(sourcefile, __LINE__)) + end if + + else + + ! These do default to false anyway, but this emphasizes they + ! are false when fates is false + use_fates_sp = .false. + use_fates_bgc = .false. + end if + ! Check compatibility with use_lai_streams + if (use_lai_streams) then + if ((use_fates .and. .not. use_fates_sp) .or. use_cn) then + call endrun(msg=' ERROR: cannot use LAI streams unless in SP mode (use_cn = .false. or use_fates_sp=.true.).'//& + errMsg(sourcefile, __LINE__)) + end if + end if + ! If nfix_timeconst is equal to the junk default value, then it was not specified ! by the user namelist and we need to assign it the correct default value. If the ! user specified it in the namelist, we leave it alone. @@ -526,7 +574,8 @@ subroutine control_init(dtime) end if call soilHydReadNML( NLFilename ) - if ( use_cn ) then + + if( use_cn ) then call CNFireReadNML( NLFilename ) call CNPrecisionControlReadNML( NLFilename ) call CNNDynamicsReadNML ( NLFilename ) @@ -569,6 +618,24 @@ subroutine control_init(dtime) errMsg(sourcefile, __LINE__)) end if + ! check on SNICAR BC-snow and dust-snow internal mixing + if ( snicar_snobc_intmix .and. snicar_snodst_intmix ) then + call endrun(msg=' ERROR: currently dust-snow and BC-snow internal mixing cannot be activated together'//& + errMsg(sourcefile, __LINE__)) + end if + + ! other SNICAR warnings + if ((snicar_snw_shape /= 'sphere' .and. snicar_snw_shape /= 'hexagonal_plate') .or. & + snicar_solarspec /= 'mid_latitude_winter' .or. & + snicar_dust_optics /= 'sahara' .or. & + snicar_numrad_snw /= 5 .or. & + snicar_snobc_intmix .or. snicar_snodst_intmix .or. & + .not. snicar_use_aerosol .or. & + do_sno_oc) then + call endrun(msg=' ERROR: You have selected an option that is EXPERIMENTAL, UNSUPPORTED, and UNTESTED. For guidance see namelist_defaults_ctsm.xml'//& + errMsg(sourcefile, __LINE__)) + end if + ! Consistency settings for nrevsn if (nsrest == nsrStartup ) nrevsn = ' ' @@ -630,6 +697,7 @@ subroutine control_spmd() call mpi_bcast (use_crop, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_fertilizer, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_grainproduct, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (crop_residue_removal_frac, 1, MPI_REAL8, 0, mpicom, ier) call mpi_bcast (o3_veg_stress_method, len(o3_veg_stress_method), MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (use_snicar_frc, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_vancouver, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -689,7 +757,10 @@ subroutine control_spmd() ! BGC call mpi_bcast (co2_type, len(co2_type), MPI_CHARACTER, 0, mpicom, ier) - if (use_cn) then + + call mpi_bcast (use_fates, 1, MPI_LOGICAL, 0, mpicom, ier) + + if (use_cn .or. use_fates) then call mpi_bcast (suplnitro, len(suplnitro), MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (nfix_timeconst, 1, MPI_REAL8, 0, mpicom, ier) call mpi_bcast (spinup_state, 1, MPI_INTEGER, 0, mpicom, ier) @@ -701,8 +772,6 @@ subroutine control_spmd() call mpi_bcast (use_c14, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (for_testing_allow_interp_non_ciso_to_ciso, 1, MPI_LOGICAL, 0, mpicom, ier) - call mpi_bcast (use_fates, 1, MPI_LOGICAL, 0, mpicom, ier) - call mpi_bcast (fates_spitfire_mode, 1, MPI_INTEGER, 0, mpicom, ier) call mpi_bcast (use_fates_logging, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_fates_planthydro, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -714,9 +783,13 @@ subroutine control_spmd() call mpi_bcast (use_fates_fixed_biogeog, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_fates_nocomp, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_fates_sp, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (use_fates_luh, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (use_fates_bgc, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (fates_inventory_ctrl_filename, len(fates_inventory_ctrl_filename), MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (fates_paramfile, len(fates_paramfile) , MPI_CHARACTER, 0, mpicom, ier) + call mpi_bcast (fluh_timeseries, len(fluh_timeseries) , MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (fates_parteh_mode, 1, MPI_INTEGER, 0, mpicom, ier) + call mpi_bcast (fates_seeddisp_cadence, 1, MPI_INTEGER, 0, mpicom, ier) ! flexibleCN nitrogen model call mpi_bcast (use_flexibleCN, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -733,17 +806,22 @@ subroutine control_spmd() call mpi_bcast (use_soil_moisture_streams, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (use_excess_ice, 1, MPI_LOGICAL, 0, mpicom,ier) + call mpi_bcast (use_lai_streams, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (use_cropcal_streams, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (use_bedrock, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_biomass_heat_storage, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (use_hydrstress, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_dynroot, 1, MPI_LOGICAL, 0, mpicom, ier) - if (use_cn ) then + if (use_cn .or. use_fates) then ! vertical soil mixing variables call mpi_bcast (som_adv_flux, 1, MPI_REAL8, 0, mpicom, ier) call mpi_bcast (max_depth_cryoturb, 1, MPI_REAL8, 0, mpicom, ier) @@ -752,7 +830,7 @@ subroutine control_spmd() call mpi_bcast (surfprof_exp, 1, MPI_REAL8, 0, mpicom, ier) end if - if (use_cn .and. use_nitrif_denitrif) then + if ((use_cn.or.use_fates) .and. use_nitrif_denitrif) then call mpi_bcast (no_frozen_nitrif_denitrif, 1, MPI_LOGICAL, 0, mpicom, ier) end if @@ -778,7 +856,10 @@ subroutine control_spmd() ! physics variables call mpi_bcast (nsegspc, 1, MPI_INTEGER, 0, mpicom, ier) call mpi_bcast (use_subgrid_fluxes , 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (snow_thermal_cond_method, len(snow_thermal_cond_method), MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (snow_cover_fraction_method , len(snow_cover_fraction_method), MPI_CHARACTER, 0, mpicom, ier) + call mpi_bcast (z0param_method , len(z0param_method), MPI_CHARACTER, 0, mpicom, ier) + call mpi_bcast (use_z0m_snowmelt, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (single_column,1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (scmlat, 1, MPI_REAL8,0, mpicom, ier) call mpi_bcast (scmlon, 1, MPI_REAL8,0, mpicom, ier) @@ -787,6 +868,14 @@ subroutine control_spmd() call mpi_bcast (soil_layerstruct_predefined,len(soil_layerstruct_predefined), MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (soil_layerstruct_userdefined,size(soil_layerstruct_userdefined), MPI_REAL8, 0, mpicom, ier) call mpi_bcast (soil_layerstruct_userdefined_nlevsoi, 1, MPI_INTEGER, 0, mpicom, ier) + call mpi_bcast (snicar_numrad_snw, 1, MPI_INTEGER, 0, mpicom, ier) + call mpi_bcast (snicar_solarspec, len(snicar_solarspec), MPI_CHARACTER, 0, mpicom, ier) + call mpi_bcast (snicar_dust_optics, len(snicar_dust_optics), MPI_CHARACTER, 0, mpicom, ier) + call mpi_bcast (snicar_snw_shape, len(snicar_snw_shape), MPI_CHARACTER, 0, mpicom, ier) + call mpi_bcast (snicar_use_aerosol, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (snicar_snobc_intmix, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (snicar_snodst_intmix, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (do_sno_oc, 1, MPI_LOGICAL, 0, mpicom, ier) ! snow pack variables call mpi_bcast (nlevsno, 1, MPI_INTEGER, 0, mpicom, ier) @@ -808,7 +897,7 @@ subroutine control_spmd() if (use_lch4) then call mpi_bcast (hist_wrtch4diag, 1, MPI_LOGICAL, 0, mpicom, ier) end if - call mpi_bcast (hist_master_list_file, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (hist_fields_list_file, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (hist_fexcl1, max_namlen*size(hist_fexcl1), MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (hist_fexcl2, max_namlen*size(hist_fexcl2), MPI_CHARACTER, 0, mpicom, ier) call mpi_bcast (hist_fexcl3, max_namlen*size(hist_fexcl3), MPI_CHARACTER, 0, mpicom, ier) @@ -867,13 +956,16 @@ subroutine control_print () write(iulog,*) ' use_nitrif_denitrif = ', use_nitrif_denitrif write(iulog,*) ' use_extralakelayers = ', use_extralakelayers write(iulog,*) ' use_vichydro = ', use_vichydro + write(iulog,*) ' use_excess_ice = ', use_excess_ice write(iulog,*) ' use_cn = ', use_cn write(iulog,*) ' use_cndv = ', use_cndv write(iulog,*) ' use_crop = ', use_crop write(iulog,*) ' use_fertilizer = ', use_fertilizer write(iulog,*) ' use_grainproduct = ', use_grainproduct + write(iulog,*) ' crop_residue_removal_frac = ', crop_residue_removal_frac write(iulog,*) ' o3_veg_stress_method = ', o3_veg_stress_method write(iulog,*) ' use_snicar_frc = ', use_snicar_frc + write(iulog,*) ' snicar_use_aerosol = ',snicar_use_aerosol write(iulog,*) ' use_vancouver = ', use_vancouver write(iulog,*) ' use_mexicocity = ', use_mexicocity write(iulog,*) ' use_noio = ', use_noio @@ -899,7 +991,8 @@ subroutine control_print () write(iulog,*) ' Threshold above which the model keeps the lake landunit =', toosmall_lake write(iulog,*) ' Threshold above which the model keeps the wetland landunit =', toosmall_wetland write(iulog,*) ' Threshold above which the model keeps the urban landunits =', toosmall_urban - if (use_cn) then + + if (use_cn .or. use_fates) then if (suplnitro /= suplnNon)then write(iulog,*) ' Supplemental Nitrogen mode is set to run over Patches: ', & trim(suplnitro) @@ -930,13 +1023,13 @@ subroutine control_print () write(iulog,*) ' override_bgc_restart_mismatch_dump : ', override_bgc_restart_mismatch_dump end if - if (use_cn ) then + if (use_cn .or. use_fates) then write(iulog, *) ' som_adv_flux, the advection term in soil mixing (m/s) : ', som_adv_flux write(iulog, *) ' max_depth_cryoturb (m) : ', max_depth_cryoturb write(iulog, *) ' surfprof_exp : ', surfprof_exp end if - if (use_cn .and. .not. use_nitrif_denitrif) then + if ((use_cn .or. use_fates) .and. .not. use_nitrif_denitrif) then write(iulog, *) ' no_frozen_nitrif_denitrif : ', no_frozen_nitrif_denitrif end if @@ -961,6 +1054,14 @@ subroutine control_print () write(iulog,'(a)') ' snow aging parameters file = '//trim(fsnowaging) endif + write(iulog,*) ' SNICAR: downward solar radiation spectrum type =', snicar_solarspec + write(iulog,*) ' SNICAR: dust optics type = ', snicar_dust_optics + write(iulog,*) ' SNICAR: number of bands in snow albedo calculation =', snicar_numrad_snw + write(iulog,*) ' SNICAR: snow grain shape type = ', snicar_snw_shape + write(iulog,*) ' SNICAR: BC-snow internal mixing = ', snicar_snobc_intmix + write(iulog,*) ' SNICAR: dust-snow internal mixing = ', snicar_snodst_intmix + write(iulog,*) ' SNICAR: OC in snow = ', do_sno_oc + write(iulog,'(a,i8)') ' Number of snow layers =', nlevsno write(iulog,'(a,d20.10)') ' Max snow depth (mm) =', h2osno_max @@ -1040,7 +1141,6 @@ subroutine control_print () write(iulog, *) ' carbon_resp_opt = ', carbon_resp_opt end if write(iulog, *) ' use_luna = ', use_luna - write(iulog, *) ' ozone vegetation stress method = ', o3_veg_stress_method write(iulog, *) ' ED/FATES: ' write(iulog, *) ' use_fates = ', use_fates @@ -1058,7 +1158,11 @@ subroutine control_print () write(iulog, *) ' use_fates_fixed_biogeog = ', use_fates_fixed_biogeog write(iulog, *) ' use_fates_nocomp = ', use_fates_nocomp write(iulog, *) ' use_fates_sp = ', use_fates_sp - write(iulog, *) ' fates_inventory_ctrl_filename = ',fates_inventory_ctrl_filename + write(iulog, *) ' use_fates_luh= ', use_fates_luh + write(iulog, *) ' fluh_timeseries = ', trim(fluh_timeseries) + write(iulog, *) ' fates_seeddisp_cadence = ', fates_seeddisp_cadence + write(iulog, *) ' fates_seeddisp_cadence: 0, 1, 2, 3 => off, daily, monthly, or yearly dispersal' + write(iulog, *) ' fates_inventory_ctrl_filename = ', trim(fates_inventory_ctrl_filename) end if end subroutine control_print diff --git a/src/main/filterMod.F90 b/src/main/filterMod.F90 index 526cb7c8f3..2fb7d23079 100644 --- a/src/main/filterMod.F90 +++ b/src/main/filterMod.F90 @@ -19,6 +19,7 @@ module filterMod use ColumnType , only : col use PatchType , only : patch use glcBehaviorMod , only : glc_behavior_type + use clm_varctl , only : use_cn, use_fates, use_fates_bgc ! ! !PUBLIC TYPES: implicit none @@ -32,9 +33,10 @@ module filterMod integer, pointer :: natvegp(:) ! CNDV nat-vegetated (present) filter (patches) integer :: num_natvegp ! number of patches in nat-vegetated filter - integer, pointer :: pcropp(:) ! prognostic crop filter (patches) + integer, pointer :: pcropp(:) ! prognostic crop filter (patches) integer :: num_pcropp ! number of patches in prognostic crop filter - integer, pointer :: soilnopcropp(:) ! soil w/o prog. crops (patches) + + integer, pointer :: soilnopcropp(:) ! soil w/o prog. crops (patches) integer :: num_soilnopcropp ! number of patches in soil w/o prog crops integer, pointer :: all_soil_patches(:) ! all soil or crop patches. Used for updating FATES SP drivers @@ -49,6 +51,14 @@ module filterMod integer, pointer :: nolakec(:) ! non-lake filter (columns) integer :: num_nolakec ! number of columns in non-lake filter + integer, pointer :: bgc_soilc(:) ! soil with biogeochemistry active, negates + ! SP type runs, could be CN, FATES or CROP + integer :: num_bgc_soilc + + integer, pointer :: bgc_vegp(:) ! patches with vegetation biochemistry active, negates + ! SP type runs, could be CN or Crop (NOT FATES) + integer :: num_bgc_vegp + integer, pointer :: soilc(:) ! soil filter (columns) integer :: num_soilc ! number of columns in soil filter integer, pointer :: soilp(:) ! soil filter (patches) @@ -211,6 +221,9 @@ subroutine allocFiltersOneGroup(this_filter) allocate(this_filter(nc)%soilc(bounds%endc-bounds%begc+1)) allocate(this_filter(nc)%soilp(bounds%endp-bounds%begp+1)) + allocate(this_filter(nc)%bgc_soilc(bounds%endc-bounds%begc+1)) + allocate(this_filter(nc)%bgc_vegp(bounds%endp-bounds%begp+1)) + allocate(this_filter(nc)%snowc(bounds%endc-bounds%begc+1)) allocate(this_filter(nc)%nosnowc(bounds%endc-bounds%begc+1)) @@ -380,6 +393,39 @@ subroutine setFiltersOneGroup(bounds, this_filter, include_inactive, glc_behavio this_filter(nc)%num_nolakep = fnl this_filter(nc)%num_nolakeurbanp = fnlu + + ! Create the soil bgc filter, all non-sp columns for vegetation + fs = 0 + if( use_cn .or. use_fates_bgc )then + do c = bounds%begc,bounds%endc + if (col%active(c) .or. include_inactive) then + l =col%landunit(c) + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + fs = fs + 1 + this_filter(nc)%bgc_soilc(fs) = c + end if + end if + end do + end if + this_filter(nc)%num_bgc_soilc = fs + + ! Create a filter at patch-level for vegetation biochemistry + ! all non-SP and non-fates patches on soil + fs = 0 + if(use_cn)then + do p = bounds%begp,bounds%endp + if (patch%active(p) .or. include_inactive) then + l =patch%landunit(p) + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + fs = fs + 1 + this_filter(nc)%bgc_vegp(fs) = p + end if + end if + end do + end if + this_filter(nc)%num_bgc_vegp = fs + + ! Create soil filter at column-level fs = 0 @@ -393,8 +439,12 @@ subroutine setFiltersOneGroup(bounds, this_filter, include_inactive, glc_behavio end if end do this_filter(nc)%num_soilc = fs - ! Create soil filter at patch-level + + + + + ! Create soil filter at patch-level fs = 0 do p = bounds%begp,bounds%endp if (patch%active(p) .or. include_inactive) then @@ -405,6 +455,7 @@ subroutine setFiltersOneGroup(bounds, this_filter, include_inactive, glc_behavio end if end if end do + this_filter(nc)%num_soilp = fs ! Create column-level hydrology filter (soil and Urban pervious road cols) @@ -426,15 +477,17 @@ subroutine setFiltersOneGroup(bounds, this_filter, include_inactive, glc_behavio fl = 0 fnl = 0 do p = bounds%begp,bounds%endp - if (patch%active(p) .or. include_inactive) then - if (patch%itype(p) >= npcropmin) then !skips 2 generic crop types - fl = fl + 1 - this_filter(nc)%pcropp(fl) = p - else - l =patch%landunit(p) - if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then - fnl = fnl + 1 - this_filter(nc)%soilnopcropp(fnl) = p + if(.not.use_fates)then + if (patch%active(p) .or. include_inactive) then + if (patch%itype(p) >= npcropmin) then !skips 2 generic crop types + fl = fl + 1 + this_filter(nc)%pcropp(fl) = p + else + l =patch%landunit(p) + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + fnl = fnl + 1 + this_filter(nc)%soilnopcropp(fnl) = p + end if end if end if end if diff --git a/src/main/histFileMod.F90 b/src/main/histFileMod.F90 index a8c1c639b6..fb1a25db37 100644 --- a/src/main/histFileMod.F90 +++ b/src/main/histFileMod.F90 @@ -5,6 +5,7 @@ module histFileMod !----------------------------------------------------------------------- ! !DESCRIPTION: ! Module containing methods to for CLM history file handling. + ! See 'history_tape' type for more details. ! ! !USES: use shr_kind_mod , only : r8 => shr_kind_r8 @@ -21,14 +22,15 @@ module histFileMod use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch - use EDTypesMod , only : nclmax - use EDTypesMod , only : nlevleaf + use EDParamsMod , only : nclmax + use EDParamsMod , only : nlevleaf use FatesInterfaceTypesMod , only : nlevsclass, nlevage, nlevcoage use FatesInterfaceTypesMod , only : nlevheight use FatesInterfaceTypesMod , only : nlevdamage - use EDTypesMod , only : nfsc - use FatesLitterMod , only : ncwd - use PRTGenericMod , only : num_elements_fates => num_elements + use FatesConstantsMod , only : n_landuse_cats + use FatesLitterMod , only : nfsc + use FatesLitterMod , only : ncwd + use PRTGenericMod , only : num_elements_fates => num_elements use FatesInterfaceTypesMod , only : numpft_fates => numpft use ncdio_pio @@ -69,8 +71,6 @@ module histFileMod ! Namelist ! integer :: ni ! implicit index below - logical, public :: & - hist_empty_htapes = .false. ! namelist: flag indicates no default history fields integer, public :: & hist_ndens(max_tapes) = 2 ! namelist: output density of netcdf history files integer, public :: & @@ -84,75 +84,86 @@ module histFileMod character(len=max_namlen), public :: & hist_type1d_pertape(max_tapes) = (/(' ',ni=1,max_tapes)/) ! namelist: per tape type1d - character(len=max_namlen+2), public :: & - fincl(max_flds,max_tapes) ! namelist-equivalence list of fields to add + logical, public :: & + hist_empty_htapes = .false. ! namelist: disable default-active history fields (which + ! only exist on history tape 1). Use hist_fincl1 to enable + ! select fields on top of this. character(len=max_namlen+2), public :: & - hist_fincl1(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl1(max_flds) = ' ' ! namelist: list of fields to include in history tape 1 character(len=max_namlen+2), public :: & - hist_fincl2(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl2(max_flds) = ' ' ! namelist: list of fields to include in history tape 2 character(len=max_namlen+2), public :: & - hist_fincl3(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl3(max_flds) = ' ' ! namelist: list of fields to include in history tape 3 character(len=max_namlen+2), public :: & - hist_fincl4(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl4(max_flds) = ' ' ! namelist: list of fields to include in history tape 4 character(len=max_namlen+2), public :: & - hist_fincl5(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl5(max_flds) = ' ' ! namelist: list of fields to include in history tape 5 character(len=max_namlen+2), public :: & - hist_fincl6(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl6(max_flds) = ' ' ! namelist: list of fields to include in history tape 6 character(len=max_namlen+2), public :: & - hist_fincl7(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl7(max_flds) = ' ' ! namelist: list of fields to include in history tape 7 character(len=max_namlen+2), public :: & - hist_fincl8(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl8(max_flds) = ' ' ! namelist: list of fields to include in history tape 8 character(len=max_namlen+2), public :: & - hist_fincl9(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl9(max_flds) = ' ' ! namelist: list of fields to include in history tape 9 character(len=max_namlen+2), public :: & - hist_fincl10(max_flds) = ' ' ! namelist: list of fields to add + hist_fincl10(max_flds) = ' ' ! namelist: list of fields to include in history tape 10 character(len=max_namlen+2), public :: & - fexcl(max_flds,max_tapes) ! namelist-equivalence list of fields to remove + fincl(max_flds,max_tapes) ! copy of hist_fincl* fields in 2-D format. Note Fortran + ! used to have a bug in 2-D namelists, thus this workaround. character(len=max_namlen+2), public :: & - hist_fexcl1(max_flds) = ' ' ! namelist: list of fields to remove + hist_fexcl1(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 1 + character(len=max_namlen+2), public :: & + hist_fexcl2(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 2 character(len=max_namlen+2), public :: & - hist_fexcl2(max_flds) = ' ' ! namelist: list of fields to remove + hist_fexcl3(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 3 character(len=max_namlen+2), public :: & - hist_fexcl3(max_flds) = ' ' ! namelist: list of fields to remove + hist_fexcl4(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 4 character(len=max_namlen+2), public :: & - hist_fexcl4(max_flds) = ' ' ! namelist: list of fields to remove + hist_fexcl5(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 5 character(len=max_namlen+2), public :: & - hist_fexcl5(max_flds) = ' ' ! namelist: list of fields to remove + hist_fexcl6(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 6 character(len=max_namlen+2), public :: & - hist_fexcl6(max_flds) = ' ' ! namelist: list of fields to remove + hist_fexcl7(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 7 character(len=max_namlen+2), public :: & - hist_fexcl7(max_flds) = ' ' ! namelist: list of fields to remove + hist_fexcl8(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 8 character(len=max_namlen+2), public :: & - hist_fexcl8(max_flds) = ' ' ! namelist: list of fields to remove + hist_fexcl9(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 9 character(len=max_namlen+2), public :: & - hist_fexcl9(max_flds) = ' ' ! namelist: list of fields to remove + hist_fexcl10(max_flds) = ' ' ! namelist: list of fields to exclude from history tape 10 + character(len=max_namlen+2), public :: & - hist_fexcl10(max_flds) = ' ' ! namelist: list of fields to remove + fexcl(max_flds,max_tapes) ! copy of hist_fexcl* fields in 2-D format. Note Fortran + ! used to have a bug in 2-D namelists, thus this workaround. logical, private :: if_disphist(max_tapes) ! restart, true => save history file ! - ! !PUBLIC MEMBER FUNCTIONS: - public :: hist_addfld1d ! Add a 1d single-level field to the master field list - public :: hist_addfld2d ! Add a 2d multi-level field to the master field list - public :: hist_addfld_decomp ! Add a 2d multi-level field to the master field list + ! !PUBLIC MEMBER FUNCTIONS: (in rough call order) + public :: hist_addfld1d ! Add a 1d single-level field to the list of all history fields + public :: hist_addfld2d ! Add a 2d multi-level field to the list of all history fields + public :: hist_addfld_decomp ! Add a 1d/2d field based on patch or column data public :: hist_add_subscript ! Add a 2d subscript dimension - public :: hist_printflds ! Print summary of master field list - public :: hist_htapes_build ! Initialize history file handler for initial or continue run - public :: hist_update_hbuf ! Updates history buffer for all fields and tapes + + public :: hist_printflds ! Print summary of list of all history fields + public :: htapes_fieldlist ! Finalize history file field lists, intersecting allhistfldlist with + ! namelist params. + + public :: hist_htapes_build ! Initialize history file handler (for initial or continued run) + public :: hist_update_hbuf ! Accumulate into history buffer (all fields and tapes) public :: hist_htapes_wrapup ! Write history tape(s) + public :: hist_restart_ncd ! Read/write history file restart data - public :: htapes_fieldlist ! Define the contents of each history file based on namelist ! ! !PRIVATE MEMBER FUNCTIONS: private :: is_mapping_upto_subgrid ! Is this field being mapped up to a higher subgrid level? - private :: masterlist_make_active ! Add a field to a history file default "on" list - private :: masterlist_addfld ! Add a field to the master field list - private :: masterlist_change_timeavg ! Override default history tape contents for specific tape - private :: htape_addfld ! Add a field to the active list for a history tape - private :: htape_create ! Define contents of history file t + private :: allhistfldlist_make_active ! Declare a single field active for a single tape + private :: allhistfldlist_addfld ! Add a field to the list of all history fields + private :: allhistfldlist_change_timeavg ! Override default history tape contents for specific tape + private :: htape_addfld ! Transfer field metadata from allhistfldlist to a history tape. + private :: htape_create ! Define netcdf metadata of history file t private :: htape_add_ltype_metadata ! Add global metadata defining landunit types private :: htape_add_ctype_metadata ! Add global metadata defining column types private :: htape_add_natpft_metadata ! Add global metadata defining natpft types @@ -171,7 +182,7 @@ module histFileMod private :: set_hist_filename ! Determine history dataset filenames private :: getname ! Retrieve name portion of input "inname" private :: getflag ! Retrieve flag - private :: pointer_index ! Track data pointer indices + private :: next_history_pointer_index ! Latest index into raw history data (clmptr_r*) arrays private :: max_nFields ! The max number of fields on any tape private :: avgflag_valid ! Whether a given avgflag is a valid option private :: add_landunit_mask_metadata ! Add landunit_mask metadata for the given history field @@ -187,6 +198,20 @@ module histFileMod integer :: num_subs = 0 ! actual number of subscripts character(len=32) :: subs_name(max_subs) ! name of subscript integer :: subs_dim(max_subs) ! dimension of subscript + + ! type2d value for a field without a level dimension. This value is important for the + ! following reasons (as of 2023-08-21): + ! - type2d is used to determine the sort order of history fields both within the history + ! file (e.g., what you see from 'ncdump -h') and in the documentation that lists all + ! history fields. For these purposes, it is important that variables with + ! type2d_unset appear before variables with a real type2d, so type2d_unset should + ! appear early in alphabetical sort order. (If type2d_unset were changed to something + ! that appeared later in alphabetical sort order, then sort_hist_list should be + ! changed to have some special handling of fields with type2d_unset, forcing them to + ! appear first.) + ! - This will soon be added to the history field documentation, so should be a sensible + ! value for the type2d column in that output. + character(len=*), parameter :: type2d_unset = '-' ! type field_info character(len=max_namlen) :: name ! field name @@ -205,13 +230,14 @@ module histFileMod ! for 2D arrays, where the second dimension is allowed ! to be 1 integer :: num2d ! size of hbuf second dimension (e.g. number of vertical levels) - integer :: hpindex ! history pointer index + integer :: hpindex ! index into raw history data (clmptr_r*) arrays character(len=scale_type_strlen) :: p2c_scale_type ! scale factor when averaging patch to column character(len=scale_type_strlen) :: c2l_scale_type ! scale factor when averaging column to landunit character(len=scale_type_strlen) :: l2g_scale_type ! scale factor when averaging landunit to gridcell integer :: no_snow_behavior ! for multi-layer snow fields, flag saying how to treat times when a given snow layer is absent end type field_info + ! Metadata about a single history field. type, abstract :: entry_base type (field_info) :: field ! field information contains @@ -227,13 +253,17 @@ subroutine copy_entry_interface(this, other) end subroutine copy_entry_interface end interface - type, extends(entry_base) :: master_entry - logical :: actflag(max_tapes) ! active/inactive flag - character(len=avgflag_strlen) :: avgflag(max_tapes) ! time averaging flag + ! Additional per-field metadata. See also history_entry. + ! These values are specified in hist_addfld* calls but then can be + ! overridden by namelist params like hist_fincl1. + type, extends(entry_base) :: allhistfldlist_entry + logical :: actflag(max_tapes) ! which history tapes to write to. + character(len=avgflag_strlen) :: avgflag(max_tapes) ! type of time averaging contains - procedure :: copy => copy_master_entry - end type master_entry + procedure :: copy => copy_allhistfldlist_entry + end type allhistfldlist_entry + ! Actual per-field history data, accumulated from clmptr_r* vars. See also allhistfldlist_entry. type, extends(entry_base) :: history_entry character(len=avgflag_strlen) :: avgflag ! time averaging flag ("X","A","M","I","SUM") real(r8), pointer :: hbuf(:,:) ! history buffer (dimensions: dim1d x num2d) @@ -242,6 +272,12 @@ end subroutine copy_entry_interface procedure :: copy => copy_history_entry end type history_entry + ! Each 'history tape' accumulates output values for a set of fields marked 'active' for this run, + ! at a given time frequency and precision. The first ('primary') tape defaults to a non-empty set + ! of active fields (see hist_addfld* methods), overridable by namelist flags, while the other + ! tapes are entirely manually configured via namelist flags. The set of active fields across all + ! tapes is assembled in the 'allhistfldlist' variable. Note that the first history tape is index 1 in + ! the code but contains 'h0' in its output filenames (see set_hist_filename method). type history_tape integer :: nflds ! number of active fields on tape integer :: ntimes ! current number of time samples on tape @@ -251,7 +287,8 @@ end subroutine copy_entry_interface logical :: dov2xy ! true => do xy average for all fields logical :: is_endhist ! true => current time step is end of history interval real(r8) :: begtime ! time at beginning of history averaging interval - type (history_entry) :: hlist(max_flds) ! array of active history tape entries + type (history_entry) :: hlist(max_flds) ! array of active history tape entries. + ! The ordering matches the allhistfldlist's. end type history_tape type clmpoint_rs ! Pointer to real scalar data (1D) @@ -261,35 +298,41 @@ end subroutine copy_entry_interface real(r8), pointer :: ptr(:,:) end type clmpoint_ra - ! Pointers into datatype arrays + ! Raw history field data (not accumulated). One entry per history field, indexed by 'hpindex' + ! aka the history pointer index. For accumulated values see 'tape'. integer, parameter :: max_mapflds = 2500 ! Maximum number of fields to track type (clmpoint_rs) :: clmptr_rs(max_mapflds) ! Real scalar data (1D) type (clmpoint_ra) :: clmptr_ra(max_mapflds) ! Real array data (2D) ! - ! Master list: an array of master_entry entities + ! History field metadata including which history tapes (if any) it should be output to, and + ! type of accumulation to perform. The field ordering is arbitrary, depending on the order of + ! hist_addfld* calls in the code. + ! For the field data itself, see 'tape'. ! - type (master_entry) :: masterlist(max_flds) ! master field list + type (allhistfldlist_entry) :: allhistfldlist(max_flds) ! list of all history fields ! ! Whether each history tape is in use in this run. If history_tape_in_use(i) is false, ! then data in tape(i) is undefined and should not be referenced. ! logical :: history_tape_in_use(max_tapes) ! whether each history tape is in use in this run ! - ! History tape: an array of history_tape entities (only active fields) - ! - type (history_tape) :: tape(max_tapes) ! array history tapes + ! The actual (accumulated) history data for all active fields in each in-use tape. See + ! 'history_tape_in_use' for in-use tapes, and 'allhistfldlist' for active fields. See also + ! clmptr_r* variables for raw history data. + ! + type (history_tape) :: tape(max_tapes) ! array of history tapes ! ! Namelist input ! ! Counters ! - integer :: nfmaster = 0 ! number of fields in master field list + integer :: nallhistflds = 0 ! number of fields in list of all history fields ! ! Other variables ! character(len=max_length_filename) :: locfnh(max_tapes) ! local history file names character(len=max_length_filename) :: locfnhr(max_tapes) ! local history restart file names - logical :: htapes_defined = .false. ! flag indicates history contents have been defined + logical :: htapes_defined = .false. ! flag indicates history output fields have been defined ! ! NetCDF Id's ! @@ -319,10 +362,10 @@ end subroutine copy_entry_interface subroutine hist_printflds() ! ! !DESCRIPTION: - ! Print summary of master field list. + ! Print summary of list of all history fields. ! ! !USES: - use clm_varctl, only: hist_master_list_file + use clm_varctl, only: hist_fields_list_file use fileutils, only: getavu, relavu ! ! !ARGUMENTS: @@ -330,43 +373,43 @@ subroutine hist_printflds() ! !LOCAL VARIABLES: integer, parameter :: ncol = 5 ! number of table columns integer nf, i, j ! do-loop counters - integer master_list_file ! file unit number + integer hist_fields_file ! file unit number integer width_col(ncol) ! widths of table columns integer width_col_sum ! widths of columns summed, including spaces character(len=3) str_width_col(ncol) ! string version of width_col character(len=3) str_w_col_sum ! string version of width_col_sum character(len=7) file_identifier ! fates identifier used in file_name - character(len=23) file_name ! master_list_file.rst with or without fates + character(len=26) file_name ! hist_fields_file.rst with or without fates character(len=99) fmt_txt ! format statement character(len=*),parameter :: subname = 'CLM_hist_printflds' !----------------------------------------------------------------------- if (masterproc) then - write(iulog,*) trim(subname),' : number of master fields = ',nfmaster - write(iulog,*)' ******* MASTER FIELD LIST *******' - do nf = 1,nfmaster - write(iulog,9000)nf, masterlist(nf)%field%name, masterlist(nf)%field%units + write(iulog,*) trim(subname),' : number of history fields = ',nallhistflds + write(iulog,*)' ******* LIST OF ALL HISTORY FIELDS *******' + do nf = 1,nallhistflds + write(iulog,9000)nf, allhistfldlist(nf)%field%name, allhistfldlist(nf)%field%units 9000 format (i5,1x,a32,1x,a16) end do call shr_sys_flush(iulog) end if - ! Print master field list in separate text file when namelist + ! Print list of all history fields in separate text file when namelist ! variable requests it. Text file is formatted in the .rst ! (reStructuredText) format for easy introduction of the file to ! the CTSM's web-based documentation. ! First sort the list to be in alphabetical order - call sort_hist_list(1, nfmaster, masterlist) + call sort_hist_list(1, nallhistflds, allhistfldlist) - if (masterproc .and. hist_master_list_file) then + if (masterproc .and. hist_fields_list_file) then ! Hardwired table column widths to fit the table on a computer ! screen. Some strings will be truncated as a result of the - ! current choices (4, 35, 94, 65, 7). In sphinx (ie the web-based + ! current choices (35, 16, 94, 65, 7). In sphinx (ie the web-based ! documentation), text that has not been truncated will wrap ! around in the available space. - width_col(1) = 4 ! column that shows the variable number, nf - width_col(2) = 35 ! variable name column + width_col(1) = 35 ! variable name column + width_col(2) = hist_dim_name_length ! level dimension column width_col(3) = 94 ! long description column width_col(4) = 65 ! units column width_col(5) = 7 ! active (T or F) column @@ -379,97 +422,98 @@ subroutine hist_printflds() end do write(str_w_col_sum,'(i0)') width_col_sum - ! Open master_list_file - master_list_file = getavu() ! get next available file unit number + ! Open hist_fields_file + hist_fields_file = getavu() ! get next available file unit number if (use_fates) then file_identifier = 'fates' else file_identifier = 'nofates' end if - file_name = 'master_list_' // trim(file_identifier) // '.rst' - open(unit = master_list_file, file = file_name, & + file_name = 'history_fields_' // trim(file_identifier) // '.rst' + open(unit = hist_fields_file, file = file_name, & status = 'replace', action = 'write', form = 'formatted') ! File title fmt_txt = '(a)' - write(master_list_file,fmt_txt) '=============================' - write(master_list_file,fmt_txt) 'CTSM History Fields (' // trim(file_identifier) // ')' - write(master_list_file,fmt_txt) '=============================' - write(master_list_file,*) + write(hist_fields_file,fmt_txt) '=============================' + write(hist_fields_file,fmt_txt) 'CTSM History Fields (' // trim(file_identifier) // ')' + write(hist_fields_file,fmt_txt) '=============================' + write(hist_fields_file,*) ! A warning message and flags from the current CTSM case - write(master_list_file,fmt_txt) 'CAUTION: Not all variables are relevant / present for all CTSM cases.' - write(master_list_file,fmt_txt) 'Key flags used in this CTSM case:' + write(hist_fields_file,fmt_txt) 'CAUTION: Not all variables are relevant / present for all CTSM cases.' + write(hist_fields_file,fmt_txt) 'Key flags used in this CTSM case:' fmt_txt = '(a,l)' - write(master_list_file,fmt_txt) 'use_cn = ', use_cn - write(master_list_file,fmt_txt) 'use_crop = ', use_crop - write(master_list_file,fmt_txt) 'use_fates = ', use_fates - write(master_list_file,*) + write(hist_fields_file,fmt_txt) 'use_cn = ', use_cn + write(hist_fields_file,fmt_txt) 'use_crop = ', use_crop + write(hist_fields_file,fmt_txt) 'use_fates = ', use_fates + write(hist_fields_file,*) ! Table header ! Concatenate strings needed in format statement do i = 1, ncol fmt_txt = '('//str_width_col(i)//'a,x)' - write(master_list_file,fmt_txt,advance='no') ('=', j=1,width_col(i)) + write(hist_fields_file,fmt_txt,advance='no') ('=', j=1,width_col(i)) end do - write(master_list_file,*) ! next write statement will now appear in new line + write(hist_fields_file,*) ! next write statement will now appear in new line ! Table title fmt_txt = '(a)' - write(master_list_file,fmt_txt) 'CTSM History Fields' + write(hist_fields_file,fmt_txt) 'CTSM History Fields' ! Sub-header ! Concatenate strings needed in format statement fmt_txt = '('//str_w_col_sum//'a)' - write(master_list_file,fmt_txt) ('-', i=1, width_col_sum) + write(hist_fields_file,fmt_txt) ('-', i=1, width_col_sum) ! Concatenate strings needed in format statement fmt_txt = '(a'//str_width_col(1)//',x,a'//str_width_col(2)//',x,a'//str_width_col(3)//',x,a'//str_width_col(4)//',x,a'//str_width_col(5)//')' - write(master_list_file,fmt_txt) '#', 'Variable Name', & - 'Long Description', 'Units', 'Active?' + write(hist_fields_file,fmt_txt) 'Variable Name', & + 'Level Dim.', 'Long Description', 'Units', 'Active?' ! End header, same as header ! Concatenate strings needed in format statement do i = 1, ncol fmt_txt = '('//str_width_col(i)//'a,x)' - write(master_list_file,fmt_txt,advance='no') ('=', j=1,width_col(i)) + write(hist_fields_file,fmt_txt,advance='no') ('=', j=1,width_col(i)) end do - write(master_list_file,*) ! next write statement will now appear in new line + write(hist_fields_file,*) ! next write statement will now appear in new line ! Main table ! Concatenate strings needed in format statement - fmt_txt = '(i'//str_width_col(1)//',x,a'//str_width_col(2)//',x,a'//str_width_col(3)//',x,a'//str_width_col(4)//',l'//str_width_col(5)//')' - do nf = 1,nfmaster - write(master_list_file,fmt_txt) nf, & - masterlist(nf)%field%name, & - masterlist(nf)%field%long_name, & - masterlist(nf)%field%units, & - masterlist(nf)%actflag(1) + fmt_txt = '(a'//str_width_col(1)//',x,a'//str_width_col(2)//',x,a'//str_width_col(3)//',x,a'//str_width_col(4)//',l'//str_width_col(5)//')' + do nf = 1,nallhistflds + write(hist_fields_file,fmt_txt) & + allhistfldlist(nf)%field%name, & + allhistfldlist(nf)%field%type2d, & + allhistfldlist(nf)%field%long_name, & + allhistfldlist(nf)%field%units, & + allhistfldlist(nf)%actflag(1) end do ! Table footer, same as header ! Concatenate strings needed in format statement do i = 1, ncol fmt_txt = '('//str_width_col(i)//'a,x)' - write(master_list_file,fmt_txt,advance='no') ('=', j=1,width_col(i)) + write(hist_fields_file,fmt_txt,advance='no') ('=', j=1,width_col(i)) end do - call shr_sys_flush(master_list_file) - close(unit = master_list_file) - call relavu(master_list_file) ! close and release file unit number + call shr_sys_flush(hist_fields_file) + close(unit = hist_fields_file) + call relavu(hist_fields_file) ! close and release file unit number end if end subroutine hist_printflds !----------------------------------------------------------------------- - subroutine masterlist_addfld (fname, numdims, type1d, type1d_out, & + subroutine allhistfldlist_addfld (fname, numdims, type1d, type1d_out, & type2d, num2d, units, avgflag, long_name, hpindex, & p2c_scale_type, c2l_scale_type, l2g_scale_type, & no_snow_behavior) ! ! !DESCRIPTION: - ! Add a field to the master field list. Put input arguments of + ! Add a field to the list of all history fields. Put input arguments of ! field name, units, number of levels, averaging flag, and long name - ! into a type entry in the global master field list (masterlist). + ! into a type entry in the global list of all history fields (allhistfldlist). ! ! The optional argument no_snow_behavior should be given when this is a multi-layer ! snow field, and should be absent otherwise. It should take on one of the no_snow_* @@ -485,7 +529,7 @@ subroutine masterlist_addfld (fname, numdims, type1d, type1d_out, & character(len=*), intent(in) :: units ! units of field character(len=*), intent(in) :: avgflag ! time averaging flag character(len=*), intent(in) :: long_name ! long name of field - integer , intent(in) :: hpindex ! data type index for history buffer output + integer , intent(in) :: hpindex ! index into raw history data (clmptr_r*) arrays character(len=*), intent(in) :: p2c_scale_type ! scale type for subgrid averaging of pfts to column character(len=*), intent(in) :: c2l_scale_type ! scale type for subgrid averaging of columns to landunits character(len=*), intent(in) :: l2g_scale_type ! scale type for subgrid averaging of landunits to gridcells @@ -493,14 +537,14 @@ subroutine masterlist_addfld (fname, numdims, type1d, type1d_out, & ! ! !LOCAL VARIABLES: integer :: n ! loop index - integer :: f ! masterlist index + integer :: f ! allhistfldlist index integer :: numa ! total number of atm cells across all processors integer :: numg ! total number of gridcells across all processors integer :: numl ! total number of landunits across all processors integer :: numc ! total number of columns across all processors integer :: nump ! total number of pfts across all processors type(bounds_type) :: bounds - character(len=*),parameter :: subname = 'masterlist_addfld' + character(len=*),parameter :: subname = 'allhistfldlist_addfld' !------------------------------------------------------------------------ if (.not. avgflag_valid(avgflag, blank_valid=.true.)) then @@ -528,82 +572,82 @@ subroutine masterlist_addfld (fname, numdims, type1d, type1d_out, & end if ! Ensure that new field doesn't already exist - do n = 1,nfmaster - if (masterlist(n)%field%name == fname) then + do n = 1,nallhistflds + if (allhistfldlist(n)%field%name == fname) then write(iulog,*) trim(subname),' ERROR:', fname, ' already on list' call endrun(msg=errMsg(sourcefile, __LINE__)) end if end do - ! Increase number of fields on master field list + ! Increase number of fields on list of all history fields - nfmaster = nfmaster + 1 - f = nfmaster + nallhistflds = nallhistflds + 1 + f = nallhistflds - ! Check number of fields in master list against maximum number for master list + ! Check number of fields in list against maximum number - if (nfmaster > max_flds) then + if (nallhistflds > max_flds) then write(iulog,*) trim(subname),' ERROR: too many fields for primary history file ', & - '-- max_flds,nfmaster=', max_flds, nfmaster + '-- max_flds,nallhistflds=', max_flds, nallhistflds call endrun(msg=errMsg(sourcefile, __LINE__)) end if - ! Add field to master list - - masterlist(f)%field%name = fname - masterlist(f)%field%long_name = long_name - masterlist(f)%field%units = units - masterlist(f)%field%type1d = type1d - masterlist(f)%field%type1d_out = type1d_out - masterlist(f)%field%type2d = type2d - masterlist(f)%field%numdims = numdims - masterlist(f)%field%num2d = num2d - masterlist(f)%field%hpindex = hpindex - masterlist(f)%field%p2c_scale_type = p2c_scale_type - masterlist(f)%field%c2l_scale_type = c2l_scale_type - masterlist(f)%field%l2g_scale_type = l2g_scale_type + ! Add field to list of all history fields + + allhistfldlist(f)%field%name = fname + allhistfldlist(f)%field%long_name = long_name + allhistfldlist(f)%field%units = units + allhistfldlist(f)%field%type1d = type1d + allhistfldlist(f)%field%type1d_out = type1d_out + allhistfldlist(f)%field%type2d = type2d + allhistfldlist(f)%field%numdims = numdims + allhistfldlist(f)%field%num2d = num2d + allhistfldlist(f)%field%hpindex = hpindex + allhistfldlist(f)%field%p2c_scale_type = p2c_scale_type + allhistfldlist(f)%field%c2l_scale_type = c2l_scale_type + allhistfldlist(f)%field%l2g_scale_type = l2g_scale_type select case (type1d) case (grlnd) - masterlist(f)%field%beg1d = bounds%begg - masterlist(f)%field%end1d = bounds%endg - masterlist(f)%field%num1d = numg + allhistfldlist(f)%field%beg1d = bounds%begg + allhistfldlist(f)%field%end1d = bounds%endg + allhistfldlist(f)%field%num1d = numg case (nameg) - masterlist(f)%field%beg1d = bounds%begg - masterlist(f)%field%end1d = bounds%endg - masterlist(f)%field%num1d = numg + allhistfldlist(f)%field%beg1d = bounds%begg + allhistfldlist(f)%field%end1d = bounds%endg + allhistfldlist(f)%field%num1d = numg case (namel) - masterlist(f)%field%beg1d = bounds%begl - masterlist(f)%field%end1d = bounds%endl - masterlist(f)%field%num1d = numl + allhistfldlist(f)%field%beg1d = bounds%begl + allhistfldlist(f)%field%end1d = bounds%endl + allhistfldlist(f)%field%num1d = numl case (namec) - masterlist(f)%field%beg1d = bounds%begc - masterlist(f)%field%end1d = bounds%endc - masterlist(f)%field%num1d = numc + allhistfldlist(f)%field%beg1d = bounds%begc + allhistfldlist(f)%field%end1d = bounds%endc + allhistfldlist(f)%field%num1d = numc case (namep) - masterlist(f)%field%beg1d = bounds%begp - masterlist(f)%field%end1d = bounds%endp - masterlist(f)%field%num1d = nump + allhistfldlist(f)%field%beg1d = bounds%begp + allhistfldlist(f)%field%end1d = bounds%endp + allhistfldlist(f)%field%num1d = nump case default write(iulog,*) trim(subname),' ERROR: unknown 1d output type= ',type1d call endrun(msg=errMsg(sourcefile, __LINE__)) end select if (present(no_snow_behavior)) then - masterlist(f)%field%no_snow_behavior = no_snow_behavior + allhistfldlist(f)%field%no_snow_behavior = no_snow_behavior else - masterlist(f)%field%no_snow_behavior = no_snow_unset + allhistfldlist(f)%field%no_snow_behavior = no_snow_unset end if - ! The following two fields are used only in master field list, + ! The following two fields are used only in list of all history fields, ! NOT in the runtime active field list - ! ALL FIELDS IN THE MASTER LIST ARE INITIALIZED WITH THE ACTIVE + ! ALL FIELDS IN THE FORMER ARE INITIALIZED WITH THE ACTIVE ! FLAG SET TO FALSE - masterlist(f)%avgflag(:) = avgflag - masterlist(f)%actflag(:) = .false. + allhistfldlist(f)%avgflag(:) = avgflag + allhistfldlist(f)%actflag(:) = .false. - end subroutine masterlist_addfld + end subroutine allhistfldlist_addfld !----------------------------------------------------------------------- subroutine hist_htapes_build () @@ -687,7 +731,7 @@ subroutine hist_htapes_build () end subroutine hist_htapes_build !----------------------------------------------------------------------- - subroutine masterlist_make_active (name, tape_index, avgflag) + subroutine allhistfldlist_make_active (name, tape_index, avgflag) ! ! !DESCRIPTION: ! Add a field to the default ``on'' list for a given history file. @@ -700,8 +744,8 @@ subroutine masterlist_make_active (name, tape_index, avgflag) ! ! !LOCAL VARIABLES: integer :: f ! field index - logical :: found ! flag indicates field found in masterlist - character(len=*),parameter :: subname = 'masterlist_make_active' + logical :: found ! flag indicates field found in allhistfldlist + character(len=*),parameter :: subname = 'allhistfldlist_make_active' !----------------------------------------------------------------------- ! Check validity of input arguments @@ -718,16 +762,16 @@ subroutine masterlist_make_active (name, tape_index, avgflag) endif end if - ! Look through master list for input field name. + ! Look through list of all history fields for input field name. ! When found, set active flag for that tape to true. ! Also reset averaging flag if told to use other than default. found = .false. - do f = 1,nfmaster - if (trim(name) == trim(masterlist(f)%field%name)) then - masterlist(f)%actflag(tape_index) = .true. + do f = 1,nallhistflds + if (trim(name) == trim(allhistfldlist(f)%field%name)) then + allhistfldlist(f)%actflag(tape_index) = .true. if (present(avgflag)) then - if (avgflag/= ' ') masterlist(f)%avgflag(tape_index) = avgflag + if (avgflag/= ' ') allhistfldlist(f)%avgflag(tape_index) = avgflag end if found = .true. exit @@ -738,14 +782,14 @@ subroutine masterlist_make_active (name, tape_index, avgflag) call endrun(msg=errMsg(sourcefile, __LINE__)) end if - end subroutine masterlist_make_active + end subroutine allhistfldlist_make_active !----------------------------------------------------------------------- - subroutine masterlist_change_timeavg (t) + subroutine allhistfldlist_change_timeavg (t) ! ! !DESCRIPTION: ! Override default history tape contents for a specific tape. - ! Copy the flag into the master field list. + ! Copy the flag into the list of all history fields. ! ! !ARGUMENTS: integer, intent(in) :: t ! history tape index @@ -753,7 +797,7 @@ subroutine masterlist_change_timeavg (t) ! !LOCAL VARIABLES: integer :: f ! field index character(len=avgflag_strlen) :: avgflag ! local equiv of hist_avgflag_pertape(t) - character(len=*),parameter :: subname = 'masterlist_change_timeavg' + character(len=*),parameter :: subname = 'allhistfldlist_change_timeavg' !----------------------------------------------------------------------- avgflag = hist_avgflag_pertape(t) @@ -762,11 +806,11 @@ subroutine masterlist_change_timeavg (t) call endrun(msg=errMsg(sourcefile, __LINE__)) end if - do f = 1,nfmaster - masterlist(f)%avgflag(t) = avgflag + do f = 1,nallhistflds + allhistfldlist(f)%avgflag(t) = avgflag end do - end subroutine masterlist_change_timeavg + end subroutine allhistfldlist_change_timeavg !----------------------------------------------------------------------- subroutine htapes_fieldlist() @@ -774,16 +818,19 @@ subroutine htapes_fieldlist() ! !DESCRIPTION: ! Define the contents of each history file based on namelist ! input for initial or branch run, and restart data if a restart run. - ! Use arrays fincl and fexcl to modify default history tape contents. + ! Fill and use arrays fincl and fexcl to modify default history tape contents. ! Then sort the result alphanumerically. ! + ! Sets history_tape_in_use and htapes_defined. Fills fields in 'tape' array. + ! Optionally updates allhistfldlist avgflag. + ! ! !ARGUMENTS: ! ! !LOCAL VARIABLES: integer :: t, f ! tape, field indices integer :: ff ! index into include, exclude and fprec list character(len=max_namlen) :: name ! field name portion of fincl (i.e. no avgflag separator) - character(len=max_namlen) :: mastername ! name from masterlist field + character(len=max_namlen) :: allhistfldname ! name from allhistfldlist field character(len=avgflag_strlen) :: avgflag ! averaging flag character(len=1) :: prec_acc ! history buffer precision flag character(len=1) :: prec_wrt ! history buffer write precision flag @@ -795,7 +842,7 @@ subroutine htapes_fieldlist() do t=1,max_tapes if (hist_avgflag_pertape(t) /= ' ') then - call masterlist_change_timeavg (t) + call allhistfldlist_change_timeavg (t) end if end do @@ -828,11 +875,11 @@ subroutine htapes_fieldlist() f = 1 do while (f < max_flds .and. fincl(f,t) /= ' ') name = getname (fincl(f,t)) - do ff = 1,nfmaster - mastername = masterlist(ff)%field%name - if (name == mastername) exit + do ff = 1,nallhistflds + allhistfldname = allhistfldlist(ff)%field%name + if (name == allhistfldname) exit end do - if (name /= mastername) then + if (name /= allhistfldname) then write(iulog,*) trim(subname),' ERROR: ', trim(name), ' in fincl(', f, ') ',& 'for history tape ',t,' not found' call endrun(msg=errMsg(sourcefile, __LINE__)) @@ -842,11 +889,11 @@ subroutine htapes_fieldlist() f = 1 do while (f < max_flds .and. fexcl(f,t) /= ' ') - do ff = 1,nfmaster - mastername = masterlist(ff)%field%name - if (fexcl(f,t) == mastername) exit + do ff = 1,nallhistflds + allhistfldname = allhistfldlist(ff)%field%name + if (fexcl(f,t) == allhistfldname) exit end do - if (fexcl(f,t) /= mastername) then + if (fexcl(f,t) /= allhistfldname) then write(iulog,*) trim(subname),' ERROR: ', fexcl(f,t), ' in fexcl(', f, ') ', & 'for history tape ',t,' not found' call endrun(msg=errMsg(sourcefile, __LINE__)) @@ -859,21 +906,21 @@ subroutine htapes_fieldlist() tape(:)%nflds = 0 do t = 1,max_tapes - ! Loop through the masterlist set of field names and determine if any of those + ! Loop through the allhistfldlist set of field names and determine if any of those ! are in the FINCL or FEXCL arrays ! The call to list_index determines the index in the FINCL or FEXCL arrays - ! that the masterlist field corresponds to + ! that the allhistfldlist field corresponds to ! Add the field to the tape if specified via namelist (FINCL[1-max_tapes]), ! or if it is on by default and was not excluded via namelist (FEXCL[1-max_tapes]). - do f = 1,nfmaster - mastername = masterlist(f)%field%name - call list_index (fincl(1,t), mastername, ff) + do f = 1,nallhistflds + allhistfldname = allhistfldlist(f)%field%name + call list_index (fincl(1,t), allhistfldname, ff) if (ff > 0) then ! if field is in include list, ff > 0 and htape_addfld - ! will not be called for field + ! will be called for field avgflag = getflag (fincl(ff,t)) call htape_addfld (t, f, avgflag) @@ -882,7 +929,7 @@ subroutine htapes_fieldlist() ! find index of field in exclude list - call list_index (fexcl(1,t), mastername, ff) + call list_index (fexcl(1,t), allhistfldname, ff) ! if field is in exclude list, ff > 0 and htape_addfld ! will not be called for field @@ -891,7 +938,7 @@ subroutine htapes_fieldlist() ! called below only if field is not in exclude list OR in ! include list - if (ff == 0 .and. masterlist(f)%actflag(t)) then + if (ff == 0 .and. allhistfldlist(f)%actflag(t)) then call htape_addfld (t, f, ' ') end if @@ -971,7 +1018,7 @@ subroutine htapes_fieldlist() call shr_sys_flush(iulog) end if - ! Set flag indicating h-tape contents are now defined (needed by masterlist_addfld) + ! Set flag indicating h-tape contents are now defined (needed by allhistfldlist_addfld) htapes_defined = .true. @@ -979,23 +1026,23 @@ subroutine htapes_fieldlist() end subroutine htapes_fieldlist !----------------------------------------------------------------------- - subroutine copy_master_entry(this, other) + subroutine copy_allhistfldlist_entry(this, other) ! set this = other - class(master_entry), intent(out) :: this + class(allhistfldlist_entry), intent(out) :: this class(entry_base), intent(in) :: other select type(this) - type is (master_entry) + type is (allhistfldlist_entry) select type(other) - type is (master_entry) + type is (allhistfldlist_entry) this = other class default - call endrun('Unexpected type of "other" in copy_master_entry') + call endrun('Unexpected type of "other" in copy_allhistfldlist_entry') end select class default - call endrun('Unexpected type of "this" in copy_master_entry') + call endrun('Unexpected type of "this" in copy_allhistfldlist_entry') end select - end subroutine copy_master_entry + end subroutine copy_allhistfldlist_entry !----------------------------------------------------------------------- subroutine copy_history_entry(this, other) @@ -1045,18 +1092,18 @@ subroutine sort_hist_list(t, n_fields, hist_list) do f = n_fields-1, 1, -1 do ff = 1, f - if (hist_list(ff)%field%name > hist_list(ff+1)%field%name) then + ! First sort by the name of the level dimension; then, within the list of + ! fields with the same level dimension, sort by field name. Sorting first by + ! the level dimension gives a significant performance improvement especially + ! notable on lustre file systems such as on derecho. + if (hist_list(ff)%field%type2d > hist_list(ff+1)%field%type2d .or. & + (hist_list(ff)%field%type2d == hist_list(ff+1)%field%type2d .and. & + hist_list(ff)%field%name > hist_list(ff+1)%field%name)) then call tmp%copy(hist_list(ff)) call hist_list(ff )%copy(hist_list(ff+1)) call hist_list(ff+1)%copy(tmp) - else if (hist_list(ff)%field%name == hist_list(ff+1)%field%name) then - - write(iulog,*) trim(subname),' ERROR: Duplicate field ', & - hist_list(ff)%field%name, & - 't,ff,name=',t,ff,hist_list(ff+1)%field%name - call endrun(msg=errMsg(sourcefile, __LINE__)) end if end do end do @@ -1102,12 +1149,11 @@ end function is_mapping_upto_subgrid subroutine htape_addfld (t, f, avgflag) ! ! !DESCRIPTION: - ! Add a field to the active list for a history tape. Copy the data from - ! the master field list to the active list for the tape. + ! Add a field to a history tape, copying metadata from the list of all history fields ! ! !ARGUMENTS: integer, intent(in) :: t ! history tape index - integer, intent(in) :: f ! field index from master field list + integer, intent(in) :: f ! field index from list of all history fields character(len=*), intent(in) :: avgflag ! time averaging flag ! ! !LOCAL VARIABLES: @@ -1127,11 +1173,11 @@ subroutine htape_addfld (t, f, avgflag) character(len=*),parameter :: subname = 'htape_addfld' !----------------------------------------------------------------------- - ! Ensure that it is not to late to add a field to the history tape + ! Ensure that it is not too late to add a field to the history tape if (htapes_defined) then write(iulog,*) trim(subname),' ERROR: attempt to add field ', & - masterlist(f)%field%name, ' after history files are set' + allhistfldlist(f)%field%name, ' after history files are set' call endrun(msg=errMsg(sourcefile, __LINE__)) end if @@ -1140,7 +1186,7 @@ subroutine htape_addfld (t, f, avgflag) ! Copy field information - tape(t)%hlist(n)%field = masterlist(f)%field + tape(t)%hlist(n)%field = allhistfldlist(f)%field ! Determine bounds @@ -1224,8 +1270,8 @@ subroutine htape_addfld (t, f, avgflag) tape(t)%hlist(n)%field%num1d_out = num1d_out ! Fields native bounds - beg1d = masterlist(f)%field%beg1d - end1d = masterlist(f)%field%end1d + beg1d = allhistfldlist(f)%field%beg1d + end1d = allhistfldlist(f)%field%end1d ! Alloccate and initialize history buffer and related info @@ -1240,7 +1286,7 @@ subroutine htape_addfld (t, f, avgflag) tape(t)%hlist(n)%hbuf(:,:) = 0._r8 tape(t)%hlist(n)%nacs(:,:) = 0 - ! Set time averaging flag based on masterlist setting or + ! Set time averaging flag based on allhistfldlist setting or ! override the default averaging flag with namelist setting if (.not. avgflag_valid(avgflag, blank_valid=.true.)) then @@ -1249,7 +1295,7 @@ subroutine htape_addfld (t, f, avgflag) end if if (avgflag == ' ') then - tape(t)%hlist(n)%avgflag = masterlist(f)%avgflag(t) + tape(t)%hlist(n)%avgflag = allhistfldlist(f)%avgflag(t) else tape(t)%hlist(n)%avgflag = avgflag end if @@ -1315,7 +1361,7 @@ subroutine hist_update_hbuf_field_1d (t, f, bounds) type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: - integer :: hpindex ! history pointer index + integer :: hpindex ! index into raw history data (clmptr_r*) arrays integer :: k ! gridcell, landunit, column or patch index integer :: beg1d,end1d ! beginning and ending indices integer :: beg1d_out,end1d_out ! beginning and ending indices on output grid @@ -1680,7 +1726,7 @@ subroutine hist_update_hbuf_field_2d (t, f, bounds, num2d) integer, intent(in) :: num2d ! size of second dimension ! ! !LOCAL VARIABLES: - integer :: hpindex ! history pointer index + integer :: hpindex ! index into raw history data (clmptr_r*) arrays integer :: k ! gridcell, landunit, column or patch index integer :: j ! level index integer :: beg1d,end1d ! beginning and ending indices @@ -2275,8 +2321,7 @@ end subroutine hfields_zero subroutine htape_create (t, histrest) ! ! !DESCRIPTION: - ! Define contents of history file t. Issue the required netcdf - ! wrapper calls to define the history file contents. + ! Define netcdf metadata of history file t. ! ! !USES: use clm_varpar , only : nlevgrnd, nlevsno, nlevlak, nlevurb, nlevmaxurbgrnd, numrad, nlevcan, nvegwcs,nlevsoi @@ -2468,6 +2513,9 @@ subroutine htape_create (t, histrest) call ncd_defdim(lnfid, 'fates_levelcwd', num_elements_fates * ncwd, dimid) call ncd_defdim(lnfid, 'fates_levelage', num_elements_fates * nlevage, dimid) call ncd_defdim(lnfid, 'fates_levagefuel', nlevage * nfsc, dimid) + call ncd_defdim(lnfid, 'fates_levclscpf', nclmax*nlevsclass*numpft_fates, dimid) + call ncd_defdim(lnfid, 'fates_levlanduse', n_landuse_cats, dimid) + call ncd_defdim(lnfid, 'fates_levlulu', n_landuse_cats * n_landuse_cats, dimid) end if if ( .not. lhistrest )then @@ -3020,6 +3068,7 @@ subroutine htape_timeconst(t, mode) use FatesInterfaceTypesMod, only : fates_hdim_scmap_levcdpf use FatesInterfaceTypesMod, only : fates_hdim_cdmap_levcdpf use FatesInterfaceTypesMod, only : fates_hdim_pftmap_levcdpf + use FatesInterfaceTypesMod, only : fates_hdim_levlanduse ! @@ -3148,6 +3197,8 @@ subroutine htape_timeconst(t, mode) long_name='FATES pft index of the combined damage-size-PFT dimension', ncid=nfid(t)) call ncd_defvar(varname='fates_levcdam', xtype=tape(t)%ncprec, dim1name='fates_levcdam', & long_name='FATES damage class lower bound', units='unitless', ncid=nfid(t)) + call ncd_defvar(varname='fates_levlanduse',xtype=ncd_int, dim1name='fates_levlanduse', & + long_name='FATES land use label', ncid=nfid(t)) end if @@ -3197,6 +3248,7 @@ subroutine htape_timeconst(t, mode) call ncd_io(varname='fates_scmap_levcdpf',data=fates_hdim_scmap_levcdpf, ncid=nfid(t), flag='write') call ncd_io(varname='fates_cdmap_levcdpf',data=fates_hdim_cdmap_levcdpf, ncid=nfid(t), flag='write') call ncd_io(varname='fates_pftmap_levcdpf',data=fates_hdim_pftmap_levcdpf, ncid=nfid(t), flag='write') + call ncd_io(varname='fates_levlanduse',data=fates_hdim_levlanduse, ncid=nfid(t), flag='write') end if endif @@ -5087,6 +5139,8 @@ character(len=max_length_filename) function set_hist_filename (hist_freq, hist_m ! ! !DESCRIPTION: ! Determine history dataset filenames. + ! Note that the first history tape is index 1 in the code but contains 'h0' in its output + ! filenames. ! ! !USES: use clm_varctl, only : caseid, inst_suffix @@ -5145,14 +5199,12 @@ subroutine hist_addfld1d (fname, units, avgflag, long_name, type1d_out, & set_noglc, set_spec, default) ! ! !DESCRIPTION: - ! Initialize a single level history field. The pointer, ptrhist, - ! is a pointer to the data type array that the history buffer will use. - ! The value of type1d passed to masterlist\_add\_fld determines which of the + ! Initialize a single level history field. The pointer inputs, ptr\_*, + ! point to the appropriate-type array storing the raw history data points. + ! The value of type1d passed to allhistfldlist\_add\_fld determines which of the ! 1d type of the output and the beginning and ending indices the history - ! buffer field). Default history contents for given field on all tapes - ! are set by calling [masterlist\_make\_active] for the appropriate tape. - ! After the masterlist is built, routine [htapes\_build] is called for an - ! initial or branch run to initialize the actual history tapes. + ! buffer field). All fields default to being written to the first history tape + ! unless 'default' is set to 'inactive'. ! ! !ARGUMENTS: character(len=*), intent(in) :: fname ! field name @@ -5196,7 +5248,7 @@ subroutine hist_addfld1d (fname, units, avgflag, long_name, type1d_out, & ! History buffer pointer - hpindex = pointer_index() + hpindex = next_history_pointer_index() if (present(ptr_lnd)) then l_type1d = grlnd @@ -5337,10 +5389,10 @@ subroutine hist_addfld1d (fname, units, avgflag, long_name, type1d_out, & if (present(l2g_scale_type)) scale_type_l2g = l2g_scale_type if (present(type1d_out)) l_type1d_out = type1d_out - ! Add field to masterlist + ! Add field to allhistfldlist - call masterlist_addfld (fname=trim(fname), numdims=1, type1d=l_type1d, & - type1d_out=l_type1d_out, type2d='unset', num2d=1, & + call allhistfldlist_addfld (fname=trim(fname), numdims=1, type1d=l_type1d, & + type1d_out=l_type1d_out, type2d=type2d_unset, num2d=1, & units=units, avgflag=avgflag, long_name=long_name, hpindex=hpindex, & p2c_scale_type=scale_type_p2c, c2l_scale_type=scale_type_c2l, & l2g_scale_type=scale_type_l2g) @@ -5352,7 +5404,7 @@ subroutine hist_addfld1d (fname, units, avgflag, long_name, type1d_out, & if (trim(l_default) == 'inactive') then return else - call masterlist_make_active (name=trim(fname), tape_index=1) + call allhistfldlist_make_active (name=trim(fname), tape_index=1) end if end subroutine hist_addfld1d @@ -5365,14 +5417,12 @@ subroutine hist_addfld2d (fname, type2d, units, avgflag, long_name, type1d_out, no_snow_behavior, default) ! ! !DESCRIPTION: - ! Initialize a single level history field. The pointer, ptrhist, - ! is a pointer to the data type array that the history buffer will use. - ! The value of type1d passed to masterlist\_add\_fld determines which of the + ! Initialize a single level history field. The pointer inputs, ptr\_*, + ! point to the appropriate-type array storing the raw history data points. + ! The value of type1d passed to allhistfldlist\_add\_fld determines which of the ! 1d type of the output and the beginning and ending indices the history - ! buffer field). Default history contents for given field on all tapes - ! are set by calling [masterlist\_make\_active] for the appropriatae tape. - ! After the masterlist is built, routine [htapes\_build] is called for an - ! initial or branch run to initialize the actual history tapes. + ! buffer field). All fields default to being written to the first history tape + ! unless 'default' is set to 'inactive'. ! ! !USES: use clm_varpar , only : nlevgrnd, nlevsno, nlevlak, numrad, nlevdecomp_full, nlevcan, nvegwcs,nlevsoi @@ -5512,6 +5562,12 @@ subroutine hist_addfld2d (fname, type2d, units, avgflag, long_name, type1d_out, num2d = num_elements_fates*nlevage case ('fates_levagefuel') num2d = nlevage*nfsc + case('fates_levclscpf') + num2d = nclmax * nclmax * numpft_fates + case ('fates_levlanduse') + num2d = n_landuse_cats + case ('fates_levlulu') + num2d = n_landuse_cats * n_landuse_cats case('cft') if (cft_size > 0) then num2d = cft_size @@ -5540,7 +5596,7 @@ subroutine hist_addfld2d (fname, type2d, units, avgflag, long_name, type1d_out, end select ! History buffer pointer - hpindex = pointer_index() + hpindex = next_history_pointer_index() if (present(ptr_lnd)) then @@ -5623,7 +5679,7 @@ subroutine hist_addfld2d (fname, type2d, units, avgflag, long_name, type1d_out, l_type1d = namep l_type1d_out = namep clmptr_ra(hpindex)%ptr => ptr_patch - + if (present(set_lake)) then do p = bounds%begp,bounds%endp l =patch%landunit(p) @@ -5673,9 +5729,9 @@ subroutine hist_addfld2d (fname, type2d, units, avgflag, long_name, type1d_out, if (present(l2g_scale_type)) scale_type_l2g = l2g_scale_type if (present(type1d_out)) l_type1d_out = type1d_out - ! Add field to masterlist + ! Add field to allhistfldlist - call masterlist_addfld (fname=trim(fname), numdims=2, type1d=l_type1d, & + call allhistfldlist_addfld (fname=trim(fname), numdims=2, type1d=l_type1d, & type1d_out=l_type1d_out, type2d=type2d, num2d=num2d, & units=units, avgflag=avgflag, long_name=long_name, hpindex=hpindex, & p2c_scale_type=scale_type_p2c, c2l_scale_type=scale_type_c2l, & @@ -5688,7 +5744,7 @@ subroutine hist_addfld2d (fname, type2d, units, avgflag, long_name, type1d_out, if (trim(l_default) == 'inactive') then return else - call masterlist_make_active (name=trim(fname), tape_index=1) + call allhistfldlist_make_active (name=trim(fname), tape_index=1) end if end subroutine hist_addfld2d @@ -5698,6 +5754,10 @@ subroutine hist_addfld_decomp (fname, type2d, units, avgflag, long_name, ptr_col ptr_patch, l2g_scale_type, default) ! + ! !DESCRIPTION: + ! Adds 1-D or 2-D history field based on column data (if ptr_col is present), + ! patch data (otherwise). + ! ! !USES: use clm_varpar , only : nlevdecomp_full use clm_varctl , only : iulog @@ -5706,7 +5766,7 @@ subroutine hist_addfld_decomp (fname, type2d, units, avgflag, long_name, ptr_col ! ! !ARGUMENTS: character(len=*), intent(in) :: fname ! field name - character(len=*), intent(in) :: type2d ! 2d output type + character(len=*), intent(in) :: type2d ! 2d output type, if 2d output is chosen character(len=*), intent(in) :: units ! units of field character(len=*), intent(in) :: avgflag ! time averaging flag character(len=*), intent(in) :: long_name ! long name of field @@ -5782,18 +5842,19 @@ subroutine hist_addfld_decomp (fname, type2d, units, avgflag, long_name, ptr_col end subroutine hist_addfld_decomp !----------------------------------------------------------------------- - integer function pointer_index () + integer function next_history_pointer_index () ! ! !DESCRIPTION: - ! Set the current pointer index and increment the value of the index. + ! Return the next free index in clmptr_r* arrays (for a new history field to write to) + ! Aka 'hpindex', e.g. field_info.hpindex. ! ! !ARGUMENTS: ! integer, save :: lastindex = 1 - character(len=*),parameter :: subname = 'pointer_index' + character(len=*),parameter :: subname = 'next_history_pointer_index' !----------------------------------------------------------------------- - pointer_index = lastindex + next_history_pointer_index = lastindex lastindex = lastindex + 1 if (lastindex > max_mapflds) then write(iulog,*) trim(subname),' ERROR: ',& @@ -5801,7 +5862,7 @@ integer function pointer_index () call endrun(msg=errMsg(sourcefile, __LINE__)) endif - end function pointer_index + end function next_history_pointer_index !----------------------------------------------------------------------- subroutine hist_add_subscript(name, dim) diff --git a/src/main/initGridCellsMod.F90 b/src/main/initGridCellsMod.F90 index a330658c1f..99303c32da 100644 --- a/src/main/initGridCellsMod.F90 +++ b/src/main/initGridCellsMod.F90 @@ -272,7 +272,8 @@ subroutine set_landunit_veg_compete (ltype, gi, li, ci, pi) SHR_ASSERT_FL(npatches_added == npatches, sourcefile, __LINE__) end subroutine set_landunit_veg_compete - + + !------------------------------------------------------------------------ subroutine set_landunit_wet_lake (ltype, gi, li, ci, pi) ! diff --git a/src/main/pftconMod.F90 b/src/main/pftconMod.F90 index 7e7ecded3c..e5379100e0 100644 --- a/src/main/pftconMod.F90 +++ b/src/main/pftconMod.F90 @@ -122,6 +122,12 @@ module pftconMod real(r8), allocatable :: taul (:,:) ! leaf transmittance: 1=vis, 2=nir real(r8), allocatable :: taus (:,:) ! stem transmittance: 1=vis, 2=nir real(r8), allocatable :: z0mr (:) ! ratio of momentum roughness length to canopy top height (-) + real(r8), allocatable :: z0v_Cr (:) ! roughness-element drag coefficient for Raupach92 parameterization (-) + real(r8), allocatable :: z0v_Cs (:) ! substrate-element drag coefficient for Raupach92 parameterization (-) + real(r8), allocatable :: z0v_c (:) ! c parameter for Raupach92 parameterization (-) + real(r8), allocatable :: z0v_cw (:) ! roughness sublayer depth coefficient for Raupach92 parameterization (-) + real(r8), allocatable :: z0v_LAIoff (:) ! leaf area index offset for Raupach92 parameterization (-) + real(r8), allocatable :: z0v_LAImax (:) ! onset of over-sheltering for Raupach92 parameterization (-) real(r8), allocatable :: displar (:) ! ratio of displacement height to canopy top height (-) real(r8), allocatable :: roota_par (:) ! CLM rooting distribution parameter [1/m] real(r8), allocatable :: rootb_par (:) ! CLM rooting distribution parameter [1/m] @@ -361,11 +367,17 @@ subroutine InitAllocate (this) allocate( this%rhos (0:mxpft,numrad) ) allocate( this%taul (0:mxpft,numrad) ) allocate( this%taus (0:mxpft,numrad) ) - allocate( this%z0mr (0:mxpft) ) - allocate( this%displar (0:mxpft) ) - allocate( this%roota_par (0:mxpft) ) - allocate( this%rootb_par (0:mxpft) ) - allocate( this%crop (0:mxpft) ) + allocate( this%z0mr (0:mxpft) ) + allocate( this%z0v_Cr (0:mxpft) ) + allocate( this%z0v_Cs (0:mxpft) ) + allocate( this%z0v_c (0:mxpft) ) + allocate( this%z0v_cw (0:mxpft) ) + allocate( this%z0v_LAIoff (0:mxpft) ) + allocate( this%z0v_LAImax (0:mxpft) ) + allocate( this%displar (0:mxpft) ) + allocate( this%roota_par (0:mxpft) ) + allocate( this%rootb_par (0:mxpft) ) + allocate( this%crop (0:mxpft) ) allocate( this%mergetoclmpft (0:mxpft) ) allocate( this%is_pft_known_to_model (0:mxpft) ) allocate( this%irrigated (0:mxpft) ) @@ -508,7 +520,7 @@ subroutine InitRead(this) use fileutils , only : getfil use ncdio_pio , only : ncd_io, ncd_pio_closefile, ncd_pio_openfile, file_desc_t use ncdio_pio , only : ncd_inqdid, ncd_inqdlen - use clm_varctl , only : paramfile, use_fates, use_flexibleCN, use_dynroot, use_biomass_heat_storage + use clm_varctl , only : paramfile, use_fates, use_flexibleCN, use_dynroot, use_biomass_heat_storage, z0param_method use spmdMod , only : masterproc use CLMFatesParamInterfaceMod, only : FatesReadPFTs use SoilBiogeochemDecompCascadeConType, only : mimics_decomp, decomp_method @@ -639,8 +651,47 @@ subroutine InitRead(this) call ncd_io('pftname',pftname, 'read', ncid, readvar=readv, posNOTonfile=.true.) if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) - call ncd_io('z0mr', this%z0mr, 'read', ncid, readvar=readv, posNOTonfile=.true.) - if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) + + + select case (z0param_method) + case ('ZengWang2007') + call ncd_io('z0mr', this%z0mr, 'read', ncid, readvar=readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) + this%z0v_Cr = 0._r8 + this%z0v_Cs = 0._r8 + this%z0v_c = 0._r8 + this%z0v_cw = 0._r8 + this%z0v_LAImax = 0._r8 + this%z0v_LAIoff = 0._r8 + + case ('Meier2022') + call ncd_io('z0v_Cr', this%z0v_Cr, 'read', ncid, readvar=readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) + + call ncd_io('z0v_Cs', this%z0v_Cs, 'read', ncid, readvar=readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) + + call ncd_io('z0v_c', this%z0v_c, 'read', ncid, readvar=readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) + + call ncd_io('z0v_cw', this%z0v_cw, 'read', ncid, readvar=readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) + + call ncd_io('z0v_LAImax', this%z0v_LAImax, 'read', ncid, readvar=readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) + + call ncd_io('z0v_LAIoff', this%z0v_LAIoff, 'read', ncid, readvar=readv, posNOTonfile=.true.) + if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) + + this%z0mr = 0._r8 + + case default + write(iulog,*) subname//' ERROR: unknown z0param_method: ', & + z0param_method + call endrun(msg = 'unknown z0param_method', & + additional_msg = errMsg(sourcefile, __LINE__)) + end select + call ncd_io('displar', this%displar, 'read', ncid, readvar=readv, posNOTonfile=.true.) if ( .not. readv ) call endrun(msg=' ERROR: error in reading in pft data'//errMsg(sourcefile, __LINE__)) @@ -1408,6 +1459,12 @@ subroutine Clean(this) deallocate( this%taul) deallocate( this%taus) deallocate( this%z0mr) + deallocate( this%z0v_Cr) + deallocate( this%z0v_Cs) + deallocate( this%z0v_c) + deallocate( this%z0v_cw) + deallocate( this%z0v_LAImax) + deallocate( this%z0v_LAIoff) deallocate( this%displar) deallocate( this%roota_par) deallocate( this%rootb_par) diff --git a/src/main/readParamsMod.F90 b/src/main/readParamsMod.F90 index 2d60b29be5..c11a741198 100644 --- a/src/main/readParamsMod.F90 +++ b/src/main/readParamsMod.F90 @@ -39,6 +39,7 @@ subroutine readParameters (photosyns_inst) use SoilBiogeochemLittVertTranspMod , only : readSoilBiogeochemLittVertTranspParams => readParams use SoilBiogeochemPotentialMod , only : readSoilBiogeochemPotentialParams => readParams use SoilBiogeochemDecompMod , only : readSoilBiogeochemDecompParams => readParams + use TillageMod , only : readTillageParams => readParams use SoilBiogeochemDecompCascadeMIMICSMod, only : readSoilBiogeochemDecompMimicsParams => readParams use SoilBiogeochemDecompCascadeBGCMod , only : readSoilBiogeochemDecompBgcParams => readParams use ch4Mod , only : readCH4Params => readParams @@ -105,6 +106,7 @@ subroutine readParameters (photosyns_inst) call readSoilBiogeochemDecompBgcParams(ncid) end if call readSoilBiogeochemDecompParams(ncid) + call readTillageParams(ncid, NLFilename_in) call readSoilBiogeochemLittVertTranspParams(ncid) call readSoilBiogeochemNitrifDenitrifParams(ncid) call readSoilBiogeochemNLeachingParams(ncid) diff --git a/src/main/restFileMod.F90 b/src/main/restFileMod.F90 index d9c23d49f3..6a574406fd 100644 --- a/src/main/restFileMod.F90 +++ b/src/main/restFileMod.F90 @@ -27,6 +27,7 @@ module restFileMod use glcBehaviorMod , only : glc_behavior_type use reweightMod , only : reweight_wrapup use IssueFixedMetadataHandler, only : write_issue_fixed_metadata, read_issue_fixed_metadata + use restUtilMod , only : excess_ice_issue ! ! !PUBLIC TYPES: implicit none @@ -592,6 +593,8 @@ subroutine restFile_write_issues_fixed(ncid, writing_finidat_interp_dest_file) ! !DESCRIPTION: ! Write metadata for issues fixed ! + ! !USES: + use clm_varctl, only : use_excess_ice ! !ARGUMENTS: type(file_desc_t), intent(inout) :: ncid ! local file id logical , intent(in) :: writing_finidat_interp_dest_file ! true if we are writing a finidat_interp_dest file @@ -606,6 +609,15 @@ subroutine restFile_write_issues_fixed(ncid, writing_finidat_interp_dest_file) ncid = ncid, & writing_finidat_interp_dest_file = writing_finidat_interp_dest_file, & issue_num = lake_dynbal_baseline_issue) + ! If running with execess ice then mark the restart file as having excess ice fixed + ! This is a permanent feature, i.e. not expected to be removed from here. + ! It would only be removed if we decided to make use_excess_ice = .true. the default. + if ( use_excess_ice ) then + call write_issue_fixed_metadata( & + ncid = ncid, & + writing_finidat_interp_dest_file = writing_finidat_interp_dest_file, & + issue_num = excess_ice_issue) + end if end subroutine restFile_write_issues_fixed diff --git a/src/main/subgridMod.F90 b/src/main/subgridMod.F90 index 645d02a603..7020f42be5 100644 --- a/src/main/subgridMod.F90 +++ b/src/main/subgridMod.F90 @@ -134,6 +134,7 @@ subroutine subgrid_get_info_natveg(gi, npatches, ncols, nlunits) ! ! !ARGUMENTS: integer, intent(in) :: gi ! grid cell index + integer, intent(out) :: npatches ! number of nat veg patches in this grid cell integer, intent(out) :: ncols ! number of nat veg columns in this grid cell integer, intent(out) :: nlunits ! number of nat veg landunits in this grid cell diff --git a/src/main/surfrdMod.F90 b/src/main/surfrdMod.F90 index 78c9d8492e..23e96e7c1a 100644 --- a/src/main/surfrdMod.F90 +++ b/src/main/surfrdMod.F90 @@ -723,6 +723,7 @@ subroutine surfrd_veg_all(begg, endg, ncid, ns, actual_numcft) ! !USES: use clm_varctl , only : create_crop_landunit, use_fates, n_dom_pfts use clm_varpar , only : natpft_lb, natpft_ub, natpft_size, cft_size, cft_lb, cft_ub + use clm_varpar , only : surfpft_lb, surfpft_ub use clm_instur , only : wt_lunit, wt_nat_patch, wt_cft, fert_cft use landunit_varcon , only : istsoil, istcrop use surfrdUtilsMod , only : convert_cft_to_pft @@ -851,7 +852,7 @@ subroutine surfrd_veg_all(begg, endg, ncid, ns, actual_numcft) ! - Pfts could be up to 16 before collapsing if create_crop_landunit = .F. ! TODO Add the same call to subroutine dynpft_interp for transient runs - call collapse_to_dominant(wt_nat_patch(begg:endg,:), natpft_lb, natpft_ub, & + call collapse_to_dominant(wt_nat_patch(begg:endg,:), surfpft_lb, surfpft_ub, & begg, endg, n_dom_pfts) end subroutine surfrd_veg_all diff --git a/src/main/surfrdUtilsMod.F90 b/src/main/surfrdUtilsMod.F90 index 0763d43a16..6b581a59c1 100644 --- a/src/main/surfrdUtilsMod.F90 +++ b/src/main/surfrdUtilsMod.F90 @@ -7,6 +7,7 @@ module surfrdUtilsMod ! !USES: #include "shr_assert.h" use shr_kind_mod , only : r8 => shr_kind_r8 + use clm_varcon , only : sum_to_1_tol use clm_varctl , only : iulog,use_fates use abortutils , only : endrun use shr_log_mod , only : errMsg => shr_log_errMsg @@ -45,13 +46,12 @@ subroutine check_sums_equal_1(arr, lb, name, caller, ier, sumto) character(len=*), intent(in) :: name ! name of array character(len=*), intent(in) :: caller ! identifier of caller, for more meaningful error messages integer, optional, intent(out):: ier ! Return an error code rather than abort - real(r8), optional, intent(out):: sumto(lb:) ! The value the array should sum to (1.0 if not provided) + real(r8), optional, intent(in):: sumto(lb:) ! The value the array should sum to (1.0 if not provided) ! ! !LOCAL VARIABLES: logical :: found integer :: nl integer :: nindx - real(r8), parameter :: eps = 1.e-13_r8 real(r8), allocatable :: TotalSum(:) integer :: ub ! upper bound of the first dimension of arr !----------------------------------------------------------------------- @@ -63,8 +63,8 @@ subroutine check_sums_equal_1(arr, lb, name, caller, ier, sumto) if( present(ier) ) ier = 0 found = .false. - do nl = lbound(arr, 1), ub - if (abs(sum(arr(nl,:)) - TotalSum(nl)) > eps) then + do nl = lb, ub + if (abs(sum(arr(nl,:)) - TotalSum(nl)) > sum_to_1_tol) then found = .true. nindx = nl exit diff --git a/src/main/test/accumul_test/CMakeLists.txt b/src/main/test/accumul_test/CMakeLists.txt index 105ef0dac1..0b06d9e87e 100644 --- a/src/main/test/accumul_test/CMakeLists.txt +++ b/src/main/test/accumul_test/CMakeLists.txt @@ -1,7 +1,6 @@ set(pfunit_sources test_accumul.pf) -create_pFUnit_test(accumul test_accumul_exe - "${pfunit_sources}" "") - -target_link_libraries(test_accumul_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(accumul + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/main/test/accumul_test/test_accumul.pf b/src/main/test/accumul_test/test_accumul.pf index 429aeac594..15d2e7403e 100644 --- a/src/main/test/accumul_test/test_accumul.pf +++ b/src/main/test/accumul_test/test_accumul.pf @@ -2,7 +2,7 @@ module test_accumul ! Tests of accumulMod - use pfunit_mod + use funit use accumulMod use unittestSubgridMod use unittestSimpleSubgridSetupsMod, only : setup_single_veg_patch diff --git a/src/main/test/atm2lnd_test/CMakeLists.txt b/src/main/test/atm2lnd_test/CMakeLists.txt index e42192b45b..51c4732205 100644 --- a/src/main/test/atm2lnd_test/CMakeLists.txt +++ b/src/main/test/atm2lnd_test/CMakeLists.txt @@ -2,7 +2,6 @@ set(pfunit_sources test_downscale_forcings.pf test_partition_precip.pf) -create_pFUnit_test(atm2lnd test_atm2lnd_exe - "${pfunit_sources}" "") - -target_link_libraries(test_atm2lnd_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(atm2lnd + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/main/test/atm2lnd_test/test_downscale_forcings.pf b/src/main/test/atm2lnd_test/test_downscale_forcings.pf index d7705f2d56..d688ad809d 100644 --- a/src/main/test/atm2lnd_test/test_downscale_forcings.pf +++ b/src/main/test/atm2lnd_test/test_downscale_forcings.pf @@ -2,7 +2,7 @@ module test_downscale_forcings ! Tests of atm2lndMod: downscale_forcings - use pfunit_mod + use funit use atm2lndMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/main/test/atm2lnd_test/test_partition_precip.pf b/src/main/test/atm2lnd_test/test_partition_precip.pf index c0d9065007..48c12c3f3c 100644 --- a/src/main/test/atm2lnd_test/test_partition_precip.pf +++ b/src/main/test/atm2lnd_test/test_partition_precip.pf @@ -2,7 +2,7 @@ module test_partition_precip ! Tests of atm2lndMod: partition_precip - use pfunit_mod + use funit use atm2lndMod use atm2lndType use shr_kind_mod, only : r8 => shr_kind_r8 diff --git a/src/main/test/clm_glclnd_test/CMakeLists.txt b/src/main/test/clm_glclnd_test/CMakeLists.txt index f7ac27caf5..12caa0d851 100644 --- a/src/main/test/clm_glclnd_test/CMakeLists.txt +++ b/src/main/test/clm_glclnd_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(clm_glclnd test_clm_glclnd_exe - "test_clm_glclnd.pf" "") - -target_link_libraries(test_clm_glclnd_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(clm_glclnd + TEST_SOURCES "test_clm_glclnd.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/main/test/clm_glclnd_test/test_clm_glclnd.pf b/src/main/test/clm_glclnd_test/test_clm_glclnd.pf index e02acc3990..7d233e1f73 100644 --- a/src/main/test/clm_glclnd_test/test_clm_glclnd.pf +++ b/src/main/test/clm_glclnd_test/test_clm_glclnd.pf @@ -2,7 +2,7 @@ module test_clm_glclnd ! Tests of clm_glclnd - use pfunit_mod + use funit use unittestSubgridMod use shr_kind_mod, only : r8 => shr_kind_r8 use lnd2glcMod diff --git a/src/main/test/filter_test/CMakeLists.txt b/src/main/test/filter_test/CMakeLists.txt index c5cd2b3eb4..291da0367b 100644 --- a/src/main/test/filter_test/CMakeLists.txt +++ b/src/main/test/filter_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(filter test_filter_exe - "test_filter_col.pf" "") - -target_link_libraries(test_filter_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(filter + TEST_SOURCES "test_filter_col.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/main/test/filter_test/test_filter_col.pf b/src/main/test/filter_test/test_filter_col.pf index 55c11c4fb6..d1d087df82 100644 --- a/src/main/test/filter_test/test_filter_col.pf +++ b/src/main/test/filter_test/test_filter_col.pf @@ -2,7 +2,7 @@ module test_filter_col ! Tests of filterColMod - use pfunit_mod + use funit use filterColMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/main/test/glcBehavior_test/CMakeLists.txt b/src/main/test/glcBehavior_test/CMakeLists.txt index caf52f4439..8a83d43b74 100644 --- a/src/main/test/glcBehavior_test/CMakeLists.txt +++ b/src/main/test/glcBehavior_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(glcBehavior test_glcBehavior_exe - "test_glcBehavior.pf" "") - -target_link_libraries(test_glcBehavior_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(glcBehavior + TEST_SOURCES "test_glcBehavior.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/main/test/glcBehavior_test/test_glcBehavior.pf b/src/main/test/glcBehavior_test/test_glcBehavior.pf index d57da25610..ff104458b1 100644 --- a/src/main/test/glcBehavior_test/test_glcBehavior.pf +++ b/src/main/test/glcBehavior_test/test_glcBehavior.pf @@ -2,7 +2,7 @@ module test_glcBehavior ! Tests of glcBehaviorMod - use pfunit_mod + use funit use glcBehaviorMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/main/test/initVertical_test/CMakeLists.txt b/src/main/test/initVertical_test/CMakeLists.txt index c6e3938fb0..8fe9648a50 100644 --- a/src/main/test/initVertical_test/CMakeLists.txt +++ b/src/main/test/initVertical_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(initVertical test_initVertical_exe - "test_initVertical.pf" "") - -target_link_libraries(test_initVertical_exe clm csm_share esmf_wrf_timemgr) \ No newline at end of file +add_pfunit_ctest(initVertical + TEST_SOURCES "test_initVertical.pf" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/main/test/initVertical_test/test_initVertical.pf b/src/main/test/initVertical_test/test_initVertical.pf index 5a52671246..0726f962d1 100644 --- a/src/main/test/initVertical_test/test_initVertical.pf +++ b/src/main/test/initVertical_test/test_initVertical.pf @@ -2,7 +2,7 @@ module test_initVertical ! Tests of initVerticalMod - use pfunit_mod + use funit use initVerticalMod use shr_kind_mod , only : r8 => shr_kind_r8 use clm_varpar, only : nlevlak, nlevgrnd, nlevdecomp_full, nlayer diff --git a/src/main/test/ncdio_utils_test/CMakeLists.txt b/src/main/test/ncdio_utils_test/CMakeLists.txt index 95ff84ac1c..27f37d5abb 100644 --- a/src/main/test/ncdio_utils_test/CMakeLists.txt +++ b/src/main/test/ncdio_utils_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(ncdio_utils test_ncdio_utils_exe - "test_ncdio_utils.pf" "") - -target_link_libraries(test_ncdio_utils_exe clm csm_share) +add_pfunit_ctest(ncdio_utils + TEST_SOURCES "test_ncdio_utils.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/main/test/ncdio_utils_test/test_ncdio_utils.pf b/src/main/test/ncdio_utils_test/test_ncdio_utils.pf index c87a89e4f7..9ee1d9ba8e 100644 --- a/src/main/test/ncdio_utils_test/test_ncdio_utils.pf +++ b/src/main/test/ncdio_utils_test/test_ncdio_utils.pf @@ -2,7 +2,7 @@ module test_ncdio_utils ! Tests of ncdio_utils - use pfunit_mod + use funit use ncdio_utils use ncdio_pio ! use the fake version of this module use shr_kind_mod, only : r8 => shr_kind_r8 diff --git a/src/main/test/subgridWeights_test/CMakeLists.txt b/src/main/test/subgridWeights_test/CMakeLists.txt index 45b4d53b01..190223929c 100644 --- a/src/main/test/subgridWeights_test/CMakeLists.txt +++ b/src/main/test/subgridWeights_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(subgridWeights test_subgridWeights_exe - "test_subgridWeights.pf" "") - -target_link_libraries(test_subgridWeights_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(subgridWeights + TEST_SOURCES "test_subgridWeights.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/main/test/subgridWeights_test/test_subgridWeights.pf b/src/main/test/subgridWeights_test/test_subgridWeights.pf index 7d29ef0510..34d5f0f660 100644 --- a/src/main/test/subgridWeights_test/test_subgridWeights.pf +++ b/src/main/test/subgridWeights_test/test_subgridWeights.pf @@ -2,7 +2,7 @@ module test_subgridWeights ! Tests of subgridWeightsMod - use pfunit_mod + use funit use unittestSubgridMod use subgridWeightsMod use shr_kind_mod, only : r8 => shr_kind_r8 diff --git a/src/main/test/surfrdUtils_test/CMakeLists.txt b/src/main/test/surfrdUtils_test/CMakeLists.txt index 4139bf6faa..b41357d646 100644 --- a/src/main/test/surfrdUtils_test/CMakeLists.txt +++ b/src/main/test/surfrdUtils_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(surfrdUtils test_surfrdUtils_exe - "test_surfrdUtils.pf" "") - -target_link_libraries(test_surfrdUtils_exe clm csm_share) +add_pfunit_ctest(surfrdUtils + TEST_SOURCES "test_surfrdUtils.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/main/test/surfrdUtils_test/test_surfrdUtils.pf b/src/main/test/surfrdUtils_test/test_surfrdUtils.pf index 065dbc9b39..98191fbe99 100644 --- a/src/main/test/surfrdUtils_test/test_surfrdUtils.pf +++ b/src/main/test/surfrdUtils_test/test_surfrdUtils.pf @@ -2,7 +2,7 @@ module test_surfrdUtils ! Tests of surfrdUtilsMod - use pfunit_mod + use funit use surfrdUtilsMod use shr_kind_mod, only : r8 => shr_kind_r8 diff --git a/src/main/test/topo_test/CMakeLists.txt b/src/main/test/topo_test/CMakeLists.txt index c01625994a..7329ab8bd7 100644 --- a/src/main/test/topo_test/CMakeLists.txt +++ b/src/main/test/topo_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(topo test_topo_exe - "test_topo.pf" "") - -target_link_libraries(test_topo_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(topo + TEST_SOURCES "test_topo.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/main/test/topo_test/test_topo.pf b/src/main/test/topo_test/test_topo.pf index 28a46b474d..196ce34763 100644 --- a/src/main/test/topo_test/test_topo.pf +++ b/src/main/test/topo_test/test_topo.pf @@ -2,7 +2,7 @@ module test_topo ! Tests of TopoMod - use pfunit_mod + use funit use TopoMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestGlcMec diff --git a/src/self_tests/test/assertions_test/CMakeLists.txt b/src/self_tests/test/assertions_test/CMakeLists.txt index de7a0febef..d36d8c7675 100644 --- a/src/self_tests/test/assertions_test/CMakeLists.txt +++ b/src/self_tests/test/assertions_test/CMakeLists.txt @@ -1,10 +1,6 @@ set (pfunit_sources test_assertions.pf) -set (extra_sources - ) - -create_pFUnit_test(assertions test_assertions_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_assertions_exe clm csm_share esmf_wrf_timemgr) +add_pfunit_ctest(assertions + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/self_tests/test/assertions_test/test_assertions.pf b/src/self_tests/test/assertions_test/test_assertions.pf index 10eaaafce6..6350048302 100644 --- a/src/self_tests/test/assertions_test/test_assertions.pf +++ b/src/self_tests/test/assertions_test/test_assertions.pf @@ -2,7 +2,7 @@ module test_assertions ! Tests of Assertions - use pfunit_mod + use funit use Assertions use shr_kind_mod , only : r8 => shr_kind_r8 use unittestUtils, only : endrun_msg diff --git a/src/soilbiogeochem/CMakeLists.txt b/src/soilbiogeochem/CMakeLists.txt index f4545a6a76..e2baa2d1b2 100644 --- a/src/soilbiogeochem/CMakeLists.txt +++ b/src/soilbiogeochem/CMakeLists.txt @@ -7,6 +7,7 @@ list(APPEND clm_sources SoilBiogeochemStateType.F90 SoilBiogeochemNitrogenStateType.F90 SoilBiogeochemNitrogenFluxType.F90 + TillageMod.F90 ) sourcelist_to_parent(clm_sources) diff --git a/src/soilbiogeochem/SoilBiogeochemCarbonFluxType.F90 b/src/soilbiogeochem/SoilBiogeochemCarbonFluxType.F90 index 114019a3d7..2cb28f04e9 100644 --- a/src/soilbiogeochem/SoilBiogeochemCarbonFluxType.F90 +++ b/src/soilbiogeochem/SoilBiogeochemCarbonFluxType.F90 @@ -6,6 +6,7 @@ module SoilBiogeochemCarbonFluxType use clm_varpar , only : ndecomp_cascade_transitions, ndecomp_pools, nlevcan use clm_varpar , only : nlevdecomp_full, nlevgrnd, nlevdecomp, nlevsoi, i_cwdl2 use clm_varcon , only : spval, ispval, dzsoi_decomp + use clm_varctl , only : use_fates,use_cn use pftconMod , only : pftcon use landunit_varcon , only : istsoil, istcrop, istdlak use ch4varcon , only : allowlakeprod @@ -14,7 +15,6 @@ module SoilBiogeochemCarbonFluxType use ColumnType , only : col use LandunitType , only : lun use SparseMatrixMultiplyMod , only : sparse_matrix_type, diag_matrix_type, vector_type - use clm_varctl , only : use_fates ! ! !PUBLIC TYPES: @@ -58,13 +58,11 @@ module SoilBiogeochemCarbonFluxType real(r8), pointer :: lithr_col (:) ! (gC/m2/s) litter heterotrophic respiration: donor-pool based definition real(r8), pointer :: somhr_col (:) ! (gC/m2/s) soil organic matter heterotrophic res: donor-pool based definition real(r8), pointer :: soilc_change_col (:) ! (gC/m2/s) FUN used soil C + real(r8), pointer :: fates_litter_flux (:) ! (gC/m2/s) A summary of the total litter + ! flux passed in from FATES. + ! This is a diagnostic for balance checks only - ! fluxes to receive carbon inputs from FATES - real(r8), pointer :: FATES_c_to_litr_c_col (:,:,:) ! total litter coming from ED. gC/m3/s - real(r8), pointer :: FATES_c_to_litr_lab_c_col (:,:) ! total labile litter coming from ED. gC/m3/s - real(r8), pointer :: FATES_c_to_litr_cel_c_col (:,:) ! total cellulose litter coming from ED. gC/m3/s - real(r8), pointer :: FATES_c_to_litr_lig_c_col (:,:) ! total lignin litter coming from ED. gC/m3/s - + contains procedure , public :: Init @@ -162,25 +160,16 @@ subroutine InitAllocate(this, bounds) allocate(this%lithr_col (begc:endc)) ; this%lithr_col (:) = nan allocate(this%somhr_col (begc:endc)) ; this%somhr_col (:) = nan allocate(this%soilc_change_col (begc:endc)) ; this%soilc_change_col (:) = nan - + + if(use_fates)then + allocate(this%fates_litter_flux(begc:endc)); this%fates_litter_flux(:) = nan + else + allocate(this%fates_litter_flux(0:0)); this%fates_litter_flux(:) = nan + end if + if(use_soil_matrixcn)then end if - if ( use_fates ) then - ! initialize these variables to be zero rather than a bad number since they are not zeroed every timestep (due to a need for them to persist) - - allocate(this%FATES_c_to_litr_c_col(begc:endc,1:nlevdecomp_full,1:ndecomp_pools)) - this%FATES_c_to_litr_c_col(begc:endc,1:nlevdecomp_full,1:ndecomp_pools) = 0._r8 - - allocate(this%FATES_c_to_litr_lab_c_col(begc:endc,1:nlevdecomp_full)) - this%FATES_c_to_litr_lab_c_col(begc:endc,1:nlevdecomp_full) = 0._r8 - - allocate(this%FATES_c_to_litr_cel_c_col(begc:endc,1:nlevdecomp_full)) - this%FATES_c_to_litr_cel_c_col(begc:endc,1:nlevdecomp_full) = 0._r8 - - allocate(this%FATES_c_to_litr_lig_c_col(begc:endc,1:nlevdecomp_full)) - this%FATES_c_to_litr_lig_c_col(begc:endc,1:nlevdecomp_full) = 0._r8 - endif allocate(this%litr_lig_c_to_n_col(begc:endc)) this%litr_lig_c_to_n_col(:)= 0._r8 @@ -315,8 +304,8 @@ subroutine InitHistory(this, bounds, carbon_type) if ( decomp_cascade_con%cascade_receiver_pool(l) /= 0 ) then data1dptr => this%decomp_cascade_ctransfer_col(:,l) fieldname = & - trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))//'C_TO_'//& - trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l)))//'C' + trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))//'_C_TO_'//& + trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l)))//'_C' longname = 'decomp. of '//trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_donor_pool(l)))//& ' C to '//trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_receiver_pool(l)))//' C' call hist_addfld1d (fname=fieldname, units='gC/m^2/s', & @@ -353,9 +342,9 @@ subroutine InitHistory(this, bounds, carbon_type) if ( decomp_cascade_con%cascade_receiver_pool(l) /= 0 ) then data2dptr => this%decomp_cascade_ctransfer_vr_col(:,:,l) fieldname = & - trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))//'C_TO_'//& + trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))//'_C_TO_'//& trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l)))& - //'C'//trim(vr_suffix) + //'_C'//trim(vr_suffix) longname = 'decomp. of '//& trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_donor_pool(l)))//& ' C to '//& @@ -423,14 +412,14 @@ subroutine InitHistory(this, bounds, carbon_type) do k = 1, ndecomp_pools ! none from CWD if ( .not. decomp_cascade_con%is_cwd(k) ) then data1dptr => this%decomp_cpools_leached_col(:,k) - fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'C_TO_LEACHING' + fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_C_TO_LEACHING' longname = trim(decomp_cascade_con%decomp_pool_name_long(k))//' C leaching loss' call hist_addfld1d (fname=fieldname, units='gC/m^2/s', & avgflag='A', long_name=longname, & ptr_col=data1dptr, default='inactive') data2dptr => this%decomp_cpools_transport_tendency_col(:,:,k) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(k))//'C_TNDNCY_VERT_TRANSPORT' + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(k))//'_C_TNDNCY_VERT_TRANSPORT' longname = trim(decomp_cascade_con%decomp_pool_name_long(k))//' C tendency due to vertical transport' call hist_addfld_decomp (fname=fieldname, units='gC/m^3/s', type2d='levdcmp', & avgflag='A', long_name=longname, & @@ -513,9 +502,9 @@ subroutine InitHistory(this, bounds, carbon_type) data2dptr => this%decomp_cascade_ctransfer_vr_col(:,:,l) fieldname = 'C13_'//& trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))& - //'C_TO_'//& + //'_C_TO_'//& trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l)))& - //'C'//trim(vr_suffix) + //'_C'//trim(vr_suffix) longname = 'C13 decomp. of '& //trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_donor_pool(l)))& //' C to '//& @@ -597,9 +586,9 @@ subroutine InitHistory(this, bounds, carbon_type) fieldname = 'C14_'//& trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))& - //'C_TO_'//& + //'_C_TO_'//& trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l)))& - //'C'//trim(vr_suffix) + //'_C'//trim(vr_suffix) longname = 'C14 decomp. of '& //trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_donor_pool(l)))//& ' C to '//trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_receiver_pool(l)))//' C' @@ -625,23 +614,6 @@ subroutine InitHistory(this, bounds, carbon_type) end do - if ( use_fates ) then - - call hist_addfld_decomp(fname='FATES_c_to_litr_lab_c', units='gC/m^3/s', type2d='levdcmp', & - avgflag='A', long_name='litter labile carbon flux from FATES to BGC', & - ptr_col=this%FATES_c_to_litr_lab_c_col) - - call hist_addfld_decomp(fname='FATES_c_to_litr_cel_c', units='gC/m^3/s', type2d='levdcmp', & - avgflag='A', long_name='litter celluluse carbon flux from FATES to BGC', & - ptr_col=this%FATES_c_to_litr_cel_c_col) - - call hist_addfld_decomp(fname='FATES_c_to_litr_lig_c', units='gC/m^3/s', type2d='levdcmp', & - avgflag='A', long_name='litter lignin carbon flux from FATES to BGC', & - ptr_col=this%FATES_c_to_litr_lig_c_col) - - endif - - end subroutine InitHistory !----------------------------------------------------------------------- @@ -694,40 +666,6 @@ subroutine Restart(this, bounds, ncid, flag) logical :: readvar !----------------------------------------------------------------------- - ! - ! if FATES is enabled, need to restart the variables used to transfer from FATES to CLM as they - ! are persistent between daily FATES dynamics calls and half-hourly CLM timesteps - ! - if ( use_fates ) then - - ptr2d => this%FATES_c_to_litr_lab_c_col - call restartvar(ncid=ncid, flag=flag, varname='FATES_c_to_litr_lab_c_col', xtype=ncd_double, & - dim1name='column', dim2name='levgrnd', switchdim=.true., & - long_name='', units='gC/m3/s', scale_by_thickness=.false., & - interpinic_flag='interp', readvar=readvar, data=ptr2d) - - ptr2d => this%FATES_c_to_litr_cel_c_col - call restartvar(ncid=ncid, flag=flag, varname='FATES_c_to_litr_cel_c_col', xtype=ncd_double, & - dim1name='column', dim2name='levgrnd', switchdim=.true., & - long_name='', units='gC/m3/s', scale_by_thickness=.false., & - interpinic_flag='interp', readvar=readvar, data=ptr2d) - - ptr2d => this%FATES_c_to_litr_lig_c_col - call restartvar(ncid=ncid, flag=flag, varname='FATES_c_to_litr_lig_c_col', xtype=ncd_double, & - dim1name='column', dim2name='levgrnd', switchdim=.true., & - long_name='', units='gC/m3/s', scale_by_thickness=.false., & - interpinic_flag='interp', readvar=readvar, data=ptr2d) - - ! Copy last 3 variables to an array of litter pools for use in do loops. - ! Repeat copy in src/utils/clmfates_interfaceMod.F90. - ! Keep the three originals to avoid backwards compatibility issues with - ! restart files. - this%FATES_c_to_litr_c_col(:,:,1) = this%FATES_c_to_litr_lab_c_col(:,:) - this%FATES_c_to_litr_c_col(:,:,2) = this%FATES_c_to_litr_cel_c_col(:,:) - this%FATES_c_to_litr_c_col(:,:,3) = this%FATES_c_to_litr_lig_c_col(:,:) - - end if - call restartvar(ncid=ncid, flag=flag, varname='ligninNratioAvg', xtype=ncd_double, & dim1name='column', & long_name='', units='', & @@ -806,13 +744,11 @@ subroutine SetValues ( this, num_column, filter_column, value_column) this%soilc_change_col(i) = value_column end do - ! NOTE: do not zero the fates to BGC C flux variables since they need to persist from the daily fates timestep s to the half-hourly BGC timesteps. I.e. FATES_c_to_litr_lab_c_col, FATES_c_to_litr_cel_c_col, FATES_c_to_litr_lig_c_col - end subroutine SetValues !----------------------------------------------------------------------- subroutine Summary(this, bounds, & - num_soilc, filter_soilc, num_soilp, filter_soilp, & + num_bgc_soilc, filter_bgc_soilc, num_soilp, filter_soilp, & soilbiogeochem_decomp_cascade_ctransfer_col, & soilbiogeochem_cwdc_col, soilbiogeochem_cwdn_col, & leafc_to_litter_patch, frootc_to_litter_patch) @@ -827,15 +763,16 @@ subroutine Summary(this, bounds, & ! !ARGUMENTS: class(soilbiogeochem_carbonflux_type) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns integer, intent(in), optional :: num_soilp ! number of patches in filter integer, intent(in), optional :: filter_soilp(:) ! filter for patches real(r8), intent(in), optional :: soilbiogeochem_cwdc_col(bounds%begc:) real(r8), intent(in), optional :: soilbiogeochem_cwdn_col(bounds%begc:) real(r8), intent(in), optional :: soilbiogeochem_decomp_cascade_ctransfer_col(bounds%begc:,1:) - real(r8), intent(in), optional :: leafc_to_litter_patch(bounds%begp:) - real(r8), intent(in), optional :: frootc_to_litter_patch(bounds%begp:) + + real(r8), intent(in), optional :: leafc_to_litter_patch(:) + real(r8), intent(in), optional :: frootc_to_litter_patch(:) ! ! !LOCAL VARIABLES: integer :: c,j,k,l,p @@ -850,16 +787,16 @@ subroutine Summary(this, bounds, & !----------------------------------------------------------------------- - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%som_c_leached_col(c) = 0._r8 end do ! vertically integrate HR and decomposition cascade fluxes do k = 1, ndecomp_cascade_transitions do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%decomp_cascade_hr_col(c,k) = & this%decomp_cascade_hr_col(c,k) + & this%decomp_cascade_hr_vr_col(c,j,k) * dzsoi_decomp(j) @@ -873,15 +810,15 @@ subroutine Summary(this, bounds, & ! total heterotrophic respiration, vertically resolved (HR) do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%hr_vr_col(c,j) = 0._r8 end do end do do k = 1, ndecomp_cascade_transitions do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%hr_vr_col(c,j) = & this%hr_vr_col(c,j) + & this%decomp_cascade_hr_vr_col(c,j,k) @@ -891,19 +828,19 @@ subroutine Summary(this, bounds, & ! add up all vertical transport tendency terms and calculate total som leaching loss as the sum of these do l = 1, ndecomp_pools - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%decomp_cpools_leached_col(c,l) = 0._r8 end do do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%decomp_cpools_leached_col(c,l) = this%decomp_cpools_leached_col(c,l) + & this%decomp_cpools_transport_tendency_col(c,j,l) * dzsoi_decomp(j) end do end do - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%som_c_leached_col(c) = this%som_c_leached_col(c) + this%decomp_cpools_leached_col(c,l) end do end do @@ -912,8 +849,8 @@ subroutine Summary(this, bounds, & associate(is_soil => decomp_cascade_con%is_soil) ! TRUE => pool is a soil pool do k = 1, ndecomp_cascade_transitions if ( is_soil(decomp_cascade_con%cascade_donor_pool(k)) ) then - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%somhr_col(c) = this%somhr_col(c) + this%decomp_cascade_hr_col(c,k) end do end if @@ -924,8 +861,8 @@ subroutine Summary(this, bounds, & associate(is_litter => decomp_cascade_con%is_litter) ! TRUE => pool is a litter pool do k = 1, ndecomp_cascade_transitions if ( is_litter(decomp_cascade_con%cascade_donor_pool(k)) ) then - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%lithr_col(c) = this%lithr_col(c) + this%decomp_cascade_hr_col(c,k) end do end if @@ -936,8 +873,8 @@ subroutine Summary(this, bounds, & associate(is_cwd => decomp_cascade_con%is_cwd) ! TRUE => pool is a cwd pool do k = 1, ndecomp_cascade_transitions if ( is_cwd(decomp_cascade_con%cascade_donor_pool(k)) ) then - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%cwdhr_col(c) = this%cwdhr_col(c) + this%decomp_cascade_hr_col(c,k) end do end if @@ -948,8 +885,8 @@ subroutine Summary(this, bounds, & associate(is_microbe => decomp_cascade_con%is_microbe) ! TRUE => pool is a microbial pool do k = 1, ndecomp_cascade_transitions if ( is_microbe(decomp_cascade_con%cascade_donor_pool(k)) ) then - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%michr_col(c) = this%michr_col(c) + this%decomp_cascade_hr_col(c,k) end do end if @@ -957,64 +894,73 @@ subroutine Summary(this, bounds, & end associate ! total heterotrophic respiration (HR) - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) - this%hr_col(c) = & - this%michr_col(c) + & - this%cwdhr_col(c) + & - this%lithr_col(c) + & - this%somhr_col(c) + this%hr_col(c) = & + this%michr_col(c) + & + this%cwdhr_col(c) + & + this%lithr_col(c) + & + this%somhr_col(c) - end do + end do ! Calculate ligninNratio ! FATES does its own calculation - if (.not. use_fates .and. decomp_method == mimics_decomp) then - do fp = 1,num_soilp - p = filter_soilp(fp) - - associate(ivt => patch%itype) ! Input: [integer (:)] patch plant type - ligninNratio_leaf_patch(p) = pftcon%lf_flig(ivt(p)) * & - pftcon%lflitcn(ivt(p)) * & - leafc_to_litter_patch(p) - ligninNratio_froot_patch(p) = pftcon%fr_flig(ivt(p)) * & - pftcon%frootcn(ivt(p)) * & - frootc_to_litter_patch(p) - end associate - end do - - call p2c(bounds, num_soilc, filter_soilc, & - ligninNratio_leaf_patch(bounds%begp:bounds%endp), & - ligninNratio_leaf_col(bounds%begc:bounds%endc)) - call p2c(bounds, num_soilc, filter_soilc, & - ligninNratio_froot_patch(bounds%begp:bounds%endp), & - ligninNratio_froot_col(bounds%begc:bounds%endc)) - call p2c(bounds, num_soilc, filter_soilc, & - leafc_to_litter_patch(bounds%begp:bounds%endp), & - leafc_to_litter_col(bounds%begc:bounds%endc)) - call p2c(bounds, num_soilc, filter_soilc, & - frootc_to_litter_patch(bounds%begp:bounds%endp), & - frootc_to_litter_col(bounds%begc:bounds%endc)) + if_mimics: if (decomp_method == mimics_decomp ) then + + if(num_soilp>0)then + do fp = 1,num_soilp + p = filter_soilp(fp) + associate(ivt => patch%itype) ! Input: [integer (:)] patch plant type + ligninNratio_leaf_patch(p) = pftcon%lf_flig(ivt(p)) * & + pftcon%lflitcn(ivt(p)) * & + leafc_to_litter_patch(p) + ligninNratio_froot_patch(p) = pftcon%fr_flig(ivt(p)) * & + pftcon%frootcn(ivt(p)) * & + frootc_to_litter_patch(p) + end associate + end do + + call p2c(bounds, num_bgc_soilc, filter_bgc_soilc, & + ligninNratio_leaf_patch(bounds%begp:bounds%endp), & + ligninNratio_leaf_col(bounds%begc:bounds%endc)) + call p2c(bounds, num_bgc_soilc, filter_bgc_soilc, & + ligninNratio_froot_patch(bounds%begp:bounds%endp), & + ligninNratio_froot_col(bounds%begc:bounds%endc)) + call p2c(bounds, num_bgc_soilc, filter_bgc_soilc, & + leafc_to_litter_patch(bounds%begp:bounds%endp), & + leafc_to_litter_col(bounds%begc:bounds%endc)) + call p2c(bounds, num_bgc_soilc, filter_bgc_soilc, & + frootc_to_litter_patch(bounds%begp:bounds%endp), & + frootc_to_litter_col(bounds%begc:bounds%endc)) + + end if ! Calculate ligninNratioAve - do fc = 1,num_soilc - c = filter_soilc(fc) - if (soilbiogeochem_cwdn_col(c) > 0._r8) then - ligninNratio_cwd = CNParamsShareInst%cwd_flig * & - (soilbiogeochem_cwdc_col(c) / soilbiogeochem_cwdn_col(c)) * & - soilbiogeochem_decomp_cascade_ctransfer_col(c,i_cwdl2) - else - ligninNratio_cwd = 0._r8 + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) + if(.not.col%is_fates(c)) then + if (soilbiogeochem_cwdn_col(c) > 0._r8) then + ligninNratio_cwd = CNParamsShareInst%cwd_flig * & + (soilbiogeochem_cwdc_col(c) / soilbiogeochem_cwdn_col(c)) * & + soilbiogeochem_decomp_cascade_ctransfer_col(c,i_cwdl2) + else + ligninNratio_cwd = 0._r8 + end if + this%litr_lig_c_to_n_col(c) = & + (ligninNratio_leaf_col(c) + ligninNratio_froot_col(c) + & + ligninNratio_cwd) / & + max(1.0e-3_r8, leafc_to_litter_col(c) + & + frootc_to_litter_col(c) + & + soilbiogeochem_decomp_cascade_ctransfer_col(c,i_cwdl2)) + !else + ! For FATES: + ! this array is currently updated here: + ! clmfates_interfaceMod.F90:wrap_update_hlmfates_dyn() end if - this%litr_lig_c_to_n_col(c) = & - (ligninNratio_leaf_col(c) + ligninNratio_froot_col(c) + & - ligninNratio_cwd) / & - max(1.0e-3_r8, leafc_to_litter_col(c) + & - frootc_to_litter_col(c) + & - soilbiogeochem_decomp_cascade_ctransfer_col(c,i_cwdl2)) end do - end if + end if if_mimics end subroutine Summary diff --git a/src/soilbiogeochem/SoilBiogeochemCarbonStateType.F90 b/src/soilbiogeochem/SoilBiogeochemCarbonStateType.F90 index a09441069a..de269a4c78 100644 --- a/src/soilbiogeochem/SoilBiogeochemCarbonStateType.F90 +++ b/src/soilbiogeochem/SoilBiogeochemCarbonStateType.F90 @@ -7,7 +7,7 @@ module SoilBiogeochemCarbonStateType use clm_varpar , only : ndecomp_cascade_transitions, ndecomp_pools, nlevcan use clm_varpar , only : nlevdecomp_full, nlevdecomp, nlevsoi use clm_varcon , only : spval, ispval, dzsoi_decomp, zisoi, zsoi, c3_r2 - use clm_varctl , only : iulog, spinup_state, use_fates + use clm_varctl , only : iulog, spinup_state, use_fates_bgc use landunit_varcon , only : istcrop, istsoil use abortutils , only : endrun use spmdMod , only : masterproc @@ -17,6 +17,7 @@ module SoilBiogeochemCarbonStateType use GridcellType , only : grc use SoilBiogeochemStateType , only : get_spinup_latitude_term use SparseMatrixMultiplyMod , only : sparse_matrix_type, vector_type + use CNVegCarbonStateType , only : cnveg_carbonstate_type ! ! !PUBLIC TYPES: implicit none @@ -30,19 +31,28 @@ module SoilBiogeochemCarbonStateType real(r8), pointer :: ctrunc_vr_col (:,:) ! (gC/m3) vertically-resolved column-level sink for C truncation ! summary (diagnostic) state variables, not involved in mass balance - real(r8), pointer :: ctrunc_col (:) ! (gC/m2) column-level sink for C truncation - real(r8), pointer :: totmicc_col (:) ! (gC/m2) total microbial carbon - real(r8), pointer :: totlitc_col (:) ! (gC/m2) total litter carbon - real(r8), pointer :: totlitc_1m_col (:) ! (gC/m2) total litter carbon to 1 meter - real(r8), pointer :: totsomc_col (:) ! (gC/m2) total soil organic matter carbon - real(r8), pointer :: totsomc_1m_col (:) ! (gC/m2) total soil organic matter carbon to 1 meter - real(r8), pointer :: cwdc_col (:) ! (gC/m2) coarse woody debris C (diagnostic) - real(r8), pointer :: decomp_cpools_1m_col (:,:) ! (gC/m2) Diagnostic: decomposing (litter, cwd, soil) c pools to 1 meter - real(r8), pointer :: decomp_cpools_col (:,:) ! (gC/m2) decomposing (litter, cwd, soil) c pools - real(r8), pointer :: dyn_cbal_adjustments_col(:) ! (gC/m2) adjustments to each column made in this timestep via dynamic column area adjustments (note: this variable only makes sense at the column-level: it is meaningless if averaged to the gridcell-level) - integer :: restart_file_spinup_state ! spinup state as read from restart file, for determining whether to enter or exit spinup mode. - real(r8) :: totvegcthresh ! threshold for total vegetation carbon to zero out decomposition pools + real(r8), pointer :: ctrunc_col (:) ! (gC/m2) column-level sink for C truncation + real(r8), pointer :: totmicc_col (:) ! (gC/m2) total microbial carbon + real(r8), pointer :: totlitc_col (:) ! (gC/m2) total litter carbon + real(r8), pointer :: totlitc_1m_col (:) ! (gC/m2) total litter carbon to 1 meter + real(r8), pointer :: totsomc_col (:) ! (gC/m2) total soil organic matter carbon + real(r8), pointer :: totsomc_1m_col (:) ! (gC/m2) total soil organic matter carbon to 1 meter + real(r8), pointer :: cwdc_col (:) ! (gC/m2) coarse woody debris C (diagnostic) + real(r8), pointer :: decomp_cpools_1m_col (:,:) ! (gC/m2) Diagnostic: decomposing (litter, cwd, soil) c pools to 1 meter + real(r8), pointer :: decomp_cpools_col (:,:) ! (gC/m2) decomposing (litter, cwd, soil) c pools + real(r8), pointer :: dyn_cbal_adjustments_col(:) ! (gC/m2) adjustments to each column made in this timestep via dynamic column + ! area adjustments (note: this variable only makes sense at the column-level: + ! it is meaningless if averaged to the gridcell-level) + integer :: restart_file_spinup_state ! spinup state as read from restart file, for determining whether to enter or exit spinup mode. + real(r8) :: totvegcthresh ! threshold for total vegetation carbon to zero out decomposition pools + + + ! Carbon totals, includes soil, cpool and vegetation + real(r8), pointer :: totc_col (:) ! (gC/m2) total column carbon, incl veg and cpool + real(r8), pointer :: totecosysc_col (:) ! (gC/m2) total ecosystem carbon, incl veg but excl cpool + real(r8), pointer :: totc_grc (:) ! (gC/m2) total gridcell carbon + ! Matrix-cn contains @@ -95,9 +105,11 @@ subroutine InitAllocate(this, bounds) ! ! !LOCAL VARIABLES: integer :: begc,endc + integer :: begg,endg !------------------------------------------------------------------------ begc = bounds%begc; endc = bounds%endc + begg = bounds%begg; endg = bounds%endg allocate( this%decomp_cpools_col (begc :endc,1:ndecomp_pools)) ; this%decomp_cpools_col (:,:) = nan allocate( this%decomp_cpools_1m_col (begc :endc,1:ndecomp_pools)) ; this%decomp_cpools_1m_col (:,:) = nan @@ -116,9 +128,8 @@ subroutine InitAllocate(this, bounds) this%decomp_soilc_vr_col(:,:)= nan allocate(this%ctrunc_col (begc :endc)) ; this%ctrunc_col (:) = nan - if ( .not. use_fates ) then - allocate(this%cwdc_col (begc :endc)) ; this%cwdc_col (:) = nan - endif + allocate(this%cwdc_col (begc :endc)) ; this%cwdc_col (:) = nan + allocate(this%totmicc_col (begc :endc)) ; this%totmicc_col (:) = nan allocate(this%totlitc_col (begc :endc)) ; this%totlitc_col (:) = nan allocate(this%totsomc_col (begc :endc)) ; this%totsomc_col (:) = nan @@ -126,6 +137,10 @@ subroutine InitAllocate(this, bounds) allocate(this%totsomc_1m_col (begc :endc)) ; this%totsomc_1m_col (:) = nan allocate(this%dyn_cbal_adjustments_col (begc:endc)) ; this%dyn_cbal_adjustments_col (:) = nan + allocate(this%totc_col (begc:endc)) ; this%totc_col (:) = nan + allocate(this%totecosysc_col (begc:endc)) ; this%totecosysc_col (:) = nan + allocate(this%totc_grc (begg:endg)) ; this%totc_grc (:) = nan + this%restart_file_spinup_state = huge(1) end subroutine InitAllocate @@ -169,7 +184,7 @@ subroutine InitHistory(this, bounds, carbon_type) do l = 1, ndecomp_pools if ( nlevdecomp_full > 1 ) then data2dptr => this%decomp_cpools_vr_col(:,1:nlevsoi,l) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'C_vr' + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'_C_vr' longname = trim(decomp_cascade_con%decomp_pool_name_history(l))//' C (vertically resolved)' call hist_addfld2d (fname=fieldname, units='gC/m^3', type2d='levsoi', & avgflag='A', long_name=longname, & @@ -177,7 +192,7 @@ subroutine InitHistory(this, bounds, carbon_type) endif data1dptr => this%decomp_cpools_col(:,l) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'C' + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'_C' longname = trim(decomp_cascade_con%decomp_pool_name_history(l))//' C' call hist_addfld1d (fname=fieldname, units='gC/m^2', & avgflag='A', long_name=longname, & @@ -185,7 +200,7 @@ subroutine InitHistory(this, bounds, carbon_type) if ( nlevdecomp_full > 1 ) then data1dptr => this%decomp_cpools_1m_col(:,l) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'C_1m' + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'_C_1m' longname = trim(decomp_cascade_con%decomp_pool_name_history(l))//' C to 1 meter' call hist_addfld1d (fname=fieldname, units='gC/m^2', & avgflag='A', long_name=longname, & @@ -249,6 +264,16 @@ subroutine InitHistory(this, bounds, carbon_type) &only makes sense at the column level: should not be averaged to gridcell', & ptr_col=this%dyn_cbal_adjustments_col, default='inactive') + this%totc_col(begc:endc) = spval + call hist_addfld1d (fname='TOTCOLC', units='gC/m^2', & + avgflag='A', long_name='total column carbon, incl veg and cpool but excl product pools', & + ptr_col=this%totc_col) + + this%totecosysc_col(begc:endc) = spval + call hist_addfld1d (fname='TOTECOSYSC', units='gC/m^2', & + avgflag='A', long_name='total ecosystem carbon, incl veg but excl cpool and product pools', & + ptr_col=this%totecosysc_col) + end if !------------------------------- @@ -268,7 +293,7 @@ subroutine InitHistory(this, bounds, carbon_type) do l = 1, ndecomp_pools if ( nlevdecomp_full > 1 ) then data2dptr => this%decomp_cpools_vr_col(:,1:nlevsoi,l) - fieldname = 'C13_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'C_vr' + fieldname = 'C13_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'_C_vr' longname = 'C13 '//trim(decomp_cascade_con%decomp_pool_name_history(l))//' C (vertically resolved)' call hist_addfld2d (fname=fieldname, units='gC13/m^3', type2d='levsoi', & avgflag='A', long_name=longname, & @@ -276,7 +301,7 @@ subroutine InitHistory(this, bounds, carbon_type) endif data1dptr => this%decomp_cpools_col(:,l) - fieldname = 'C13_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'C' + fieldname = 'C13_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'_C' longname = 'C13 '//trim(decomp_cascade_con%decomp_pool_name_history(l))//' C' call hist_addfld1d (fname=fieldname, units='gC13/m^2', & avgflag='A', long_name=longname, & @@ -336,6 +361,17 @@ subroutine InitHistory(this, bounds, carbon_type) long_name='C13 adjustments in soil carbon due to dynamic column areas; & &only makes sense at the column level: should not be averaged to gridcell', & ptr_col=this%dyn_cbal_adjustments_col, default='inactive') + + this%totc_col(begc:endc) = spval + call hist_addfld1d (fname='C13_TOTCOLC', units='gC13/m^2', & + avgflag='A', long_name='C13 total column carbon, incl veg and cpool but excl product pools', & + ptr_col=this%totc_col, default='inactive') + + this%totecosysc_col(begc:endc) = spval + call hist_addfld1d (fname='C13_TOTECOSYSC', units='gC13/m^2', & + avgflag='A', long_name='C13 total ecosystem carbon, incl veg but excl cpool and product pools', & + ptr_col=this%totecosysc_col) + endif !------------------------------- @@ -355,21 +391,21 @@ subroutine InitHistory(this, bounds, carbon_type) do l = 1, ndecomp_pools if ( nlevdecomp_full > 1 ) then data2dptr => this%decomp_cpools_vr_col(:,1:nlevsoi,l) - fieldname = 'C14_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'C_vr' + fieldname = 'C14_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'_C_vr' longname = 'C14 '//trim(decomp_cascade_con%decomp_pool_name_history(l))//' C (vertically resolved)' call hist_addfld2d (fname=fieldname, units='gC14/m^3', type2d='levsoi', & avgflag='A', long_name=longname, ptr_col=data2dptr, default='inactive') endif data1dptr => this%decomp_cpools_col(:,l) - fieldname = 'C14_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'C' + fieldname = 'C14_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'_C' longname = 'C14 '//trim(decomp_cascade_con%decomp_pool_name_history(l))//' C' call hist_addfld1d (fname=fieldname, units='gC14/m^2', & avgflag='A', long_name=longname, ptr_col=data1dptr, default='inactive') if ( nlevdecomp_full > 1 ) then data1dptr => this%decomp_cpools_1m_col(:,l) - fieldname = 'C14_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'C_1m' + fieldname = 'C14_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//'_C_1m' longname = 'C14_'//trim(decomp_cascade_con%decomp_pool_name_history(l))//' C to 1 meter' call hist_addfld1d (fname=fieldname, units='gC/m^2', & avgflag='A', long_name=longname, ptr_col=data1dptr, default='inactive') @@ -426,6 +462,17 @@ subroutine InitHistory(this, bounds, carbon_type) long_name='C14 adjustments in soil carbon due to dynamic column areas; & &only makes sense at the column level: should not be averaged to gridcell', & ptr_col=this%dyn_cbal_adjustments_col, default='inactive') + + this%totc_col(begc:endc) = spval + call hist_addfld1d (fname='C14_TOTCOLC', units='gC14/m^2', & + avgflag='A', long_name='C14 total column carbon, incl veg and cpool but excl product pools', & + ptr_col=this%totc_col, default='inactive') + + this%totecosysc_col(begc:endc) = spval + call hist_addfld1d (fname='C14_TOTECOSYSC', units='gC14/m^2', & + avgflag='A', long_name='C14 total ecosystem carbon, incl veg but excl cpool and product pools', & + ptr_col=this%totecosysc_col) + endif end subroutine InitHistory @@ -445,7 +492,7 @@ subroutine InitCold(this, bounds, ratio, c12_soilbiogeochem_carbonstate_inst) type(soilbiogeochem_carbonstate_type), intent(in), optional :: c12_soilbiogeochem_carbonstate_inst ! ! !LOCAL VARIABLES: - integer :: p,c,l,j,k + integer :: p,c,l,j,k,g integer :: fc ! filter index integer :: num_special_col ! number of good values in special_col filter integer :: special_col(bounds%endc-bounds%begc+1) ! special landunit filter - columns @@ -523,23 +570,30 @@ subroutine InitCold(this, bounds, ratio, c12_soilbiogeochem_carbonstate_inst) end if end if - if ( .not. use_fates ) then - if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then - if (present(c12_soilbiogeochem_carbonstate_inst)) then - this%cwdc_col(c) = c12_soilbiogeochem_carbonstate_inst%cwdc_col(c) * ratio - else - this%cwdc_col(c) = 0._r8 - end if - this%ctrunc_col(c) = 0._r8 - this%totmicc_col(c) = 0._r8 - this%totlitc_col(c) = 0._r8 - this%totsomc_col(c) = 0._r8 - this%totlitc_1m_col(c) = 0._r8 - this%totsomc_1m_col(c) = 0._r8 + + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + if (present(c12_soilbiogeochem_carbonstate_inst) .and. (.not.col%is_fates(c)) ) then + this%cwdc_col(c) = c12_soilbiogeochem_carbonstate_inst%cwdc_col(c) * ratio + else + this%cwdc_col(c) = 0._r8 end if + this%ctrunc_col(c) = 0._r8 + this%totmicc_col(c) = 0._r8 + this%totlitc_col(c) = 0._r8 + this%totsomc_col(c) = 0._r8 + this%totlitc_1m_col(c) = 0._r8 + this%totsomc_1m_col(c) = 0._r8 + + this%totc_col(c) = 0._r8 + this%totecosysc_col(c) = 0._r8 end if + end do + do g = bounds%begg, bounds%endg + this%totc_grc(g) = 0._r8 + end do + ! now loop through special filters and explicitly set the variables that ! have to be in place for biogeophysics @@ -815,7 +869,7 @@ subroutine SetValues ( this, num_column, filter_column, value_column) do fi = 1,num_column i = filter_column(fi) - if ( .not. use_fates ) then + if ( .not. col%is_fates(i) ) then this%cwdc_col(i) = value_column end if this%ctrunc_col(i) = value_column @@ -824,6 +878,8 @@ subroutine SetValues ( this, num_column, filter_column, value_column) this%totlitc_1m_col(i) = value_column this%totsomc_col(i) = value_column this%totsomc_1m_col(i) = value_column + this%totc_col(i) = value_column + this%totecosysc_col(i) = value_column end do do j = 1,nlevdecomp_full @@ -875,7 +931,7 @@ subroutine SetValues ( this, num_column, filter_column, value_column) end subroutine SetValues !----------------------------------------------------------------------- - subroutine Summary(this, bounds, num_allc, filter_allc) + subroutine Summary(this, bounds, num_allc, filter_allc, num_bgc_soilc, filter_bgc_soilc,cnveg_carbonstate_inst) ! ! !DESCRIPTION: ! Perform column-level carbon summary calculations @@ -883,13 +939,22 @@ subroutine Summary(this, bounds, num_allc, filter_allc) ! !ARGUMENTS: class(soilbiogeochem_carbonstate_type) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_allc ! number of columns in allc filter + integer , intent(in) :: num_allc ! number of columns in soil filter integer , intent(in) :: filter_allc(:) ! filter for all active columns + integer , intent(in) :: num_bgc_soilc ! number of columns in soil filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for all active columns + type(cnveg_carbonstate_type) , intent(inout) :: cnveg_carbonstate_inst + ! ! !LOCAL VARIABLES: integer :: c,j,k,l ! indices integer :: fc ! filter indices real(r8) :: maxdepth ! depth to integrate soil variables + integer :: num_local ! Either num_bgc_soilc or num_allc, depending + ! on if its a fates run, its different because + ! the cnveg variables are not allocated w/ fates + real(r8) :: ecovegc_col + real(r8) :: totvegc_col !----------------------------------------------------------------------- ! vertically integrate each of the decomposing C pools @@ -1056,23 +1121,53 @@ subroutine Summary(this, bounds, num_allc, filter_allc) end if end do - ! coarse woody debris carbon - if (.not. use_fates ) then - do fc = 1,num_allc + do fc = 1,num_allc + c = filter_allc(fc) + ! coarse woody debris carbon + this%cwdc_col(c) = 0._r8 + end do + + if (use_fates_bgc) then + num_local = num_bgc_soilc + else + num_local = num_allc + end if + do fc = 1,num_local + if(use_fates_bgc) then + c = filter_bgc_soilc(fc) + else c = filter_allc(fc) - this%cwdc_col(c) = 0._r8 - end do - do l = 1, ndecomp_pools - if ( decomp_cascade_con%is_cwd(l) ) then - do fc = 1,num_allc - c = filter_allc(fc) + end if + if(col%is_fates(c)) then + totvegc_col = 0._r8 + ecovegc_col = 0._r8 + else + do l = 1, ndecomp_pools + if ( decomp_cascade_con%is_cwd(l) ) then this%cwdc_col(c) = this%cwdc_col(c) + this%decomp_cpools_col(c,l) - end do - end if - end do + end if + end do + totvegc_col = cnveg_carbonstate_inst%totc_p2c_col(c) + ecovegc_col = cnveg_carbonstate_inst%totvegc_col(c) + end if + + ! total ecosystem carbon, including veg but excluding cpool (TOTECOSYSC) + this%totecosysc_col(c) = & + this%cwdc_col(c) + & + this%totmicc_col(c) + & + this%totlitc_col(c) + & + this%totsomc_col(c) + & + ecovegc_col + ! total column carbon, including veg and cpool (TOTCOLC) + this%totc_col(c) = & + this%cwdc_col(c) + & + this%totmicc_col(c) + & + this%totlitc_col(c) + & + this%totsomc_col(c) + & + this%ctrunc_col(c) + & + totvegc_col + end do - end if - end subroutine Summary !------------------------------------------------------------------------ diff --git a/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 b/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 index eec9e00f80..57bc82984e 100644 --- a/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemCompetitionMod.F90 @@ -165,7 +165,7 @@ subroutine SoilBiogeochemCompetitionInit ( bounds) end subroutine SoilBiogeochemCompetitionInit !----------------------------------------------------------------------- - subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, filter_soilp, & + subroutine SoilBiogeochemCompetition (bounds, num_bgc_soilc, filter_bgc_soilc,num_bgc_vegp, filter_bgc_vegp, & p_decomp_cn_gain, pmnf_decomp_cascade, waterstatebulk_inst, & waterfluxbulk_inst, temperature_inst,soilstate_inst, & cnveg_state_inst,cnveg_carbonstate_inst, & @@ -187,10 +187,10 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_vegp ! number of veg patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for veg patches real(r8) , intent(in) :: pmnf_decomp_cascade(bounds%begc:,1:,1:) ! potential mineral N flux from one pool to another (gN/m3/s) real(r8) , intent(in) :: p_decomp_cn_gain(bounds%begc:,1:,1:) ! C:N ratio of the flux gained by the receiver pool type(waterstatebulk_type) , intent(in) :: waterstatebulk_inst @@ -284,7 +284,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, ! calcualte nitrogen uptake profile ! nuptake_prof(:,:) = nan ! call SoilBiogelchemNitrogenUptakeProfile(bounds, & - ! nlevdecomp, num_soilc, filter_soilc, & + ! nlevdecomp, num_bgc_soilc, filter_bgc_soilc, & ! sminn_vr, dzsoi_decomp, nfixation_prof, nuptake_prof) ! column loops to resolve plant/heterotroph competition for mineral N @@ -293,24 +293,24 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, local_use_fun = use_fun - if (.not. use_nitrif_denitrif) then + if_nitrif: if (.not. use_nitrif_denitrif) then ! init sminn_tot - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_tot(c) = 0. end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_tot(c) = sminn_tot(c) + sminn_vr(c,j) * dzsoi_decomp(j) end do end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (sminn_tot(c) > 0.) then nuptake_prof(c,j) = sminn_vr(c,j) / sminn_tot(c) else @@ -320,15 +320,15 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sum_ndemand_vr(c,j) = plant_ndemand(c) * nuptake_prof(c,j) + potential_immob_vr(c,j) end do end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) l = col%landunit(c) if (sum_ndemand_vr(c,j)*dt < sminn_vr(c,j)) then @@ -376,7 +376,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, if ( local_use_fun ) then call t_startf( 'CNFUN' ) - call CNFUN(bounds,num_soilc,filter_soilc,num_soilp,filter_soilp,waterstatebulk_inst, & + call CNFUN(bounds,num_bgc_soilc,filter_bgc_soilc,num_bgc_vegp,filter_bgc_vegp,waterstatebulk_inst, & waterfluxbulk_inst,temperature_inst,soilstate_inst,cnveg_state_inst,cnveg_carbonstate_inst,& cnveg_carbonflux_inst,cnveg_nitrogenstate_inst,cnveg_nitrogenflux_inst ,& soilbiogeochem_nitrogenflux_inst,soilbiogeochem_carbonflux_inst,canopystate_inst, & @@ -390,8 +390,8 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, ! sum up N fluxes to plant do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant(c) = sminn_to_plant(c) + sminn_to_plant_vr(c,j) * dzsoi_decomp(j) if ( local_use_fun ) then if (sminn_to_plant_fun_vr(c,j).gt.sminn_to_plant_vr(c,j)) then @@ -402,19 +402,19 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, end do ! give plants a second pass to see if there is any mineral N left over with which to satisfy residual N demand. - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) residual_sminn(c) = 0._r8 end do ! sum up total N left over after initial plant and immobilization fluxes - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) residual_plant_ndemand(c) = plant_ndemand(c) - sminn_to_plant(c) end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (residual_plant_ndemand(c) > 0._r8 ) then if (nlimit(c,j) .eq. 0) then residual_sminn_vr(c,j) = max(sminn_vr(c,j) - (actual_immob_vr(c,j) + sminn_to_plant_vr(c,j) ) * dt, 0._r8) @@ -428,8 +428,8 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, ! distribute residual N to plants do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) if ( residual_plant_ndemand(c) > 0._r8 .and. residual_sminn(c) > 0._r8 .and. nlimit(c,j) .eq. 0) then sminn_to_plant_vr(c,j) = sminn_to_plant_vr(c,j) + residual_sminn_vr(c,j) * & min(( residual_plant_ndemand(c) * dt ) / residual_sminn(c), 1._r8) / dt @@ -438,13 +438,13 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, end do ! re-sum up N fluxes to plant - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant(c) = 0._r8 end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant(c) = sminn_to_plant(c) + sminn_to_plant_vr(c,j) * dzsoi_decomp(j) if ( .not. local_use_fun ) then sum_ndemand_vr(c,j) = potential_immob_vr(c,j) + sminn_to_plant_vr(c,j) @@ -459,8 +459,8 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, ! be lost to denitrification, in addition to the constant ! proportion lost in the decomposition pathways do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) if ( .not. local_use_fun ) then if ((sminn_to_plant_vr(c,j) + actual_immob_vr(c,j))*dt < sminn_vr(c,j)) then sminn_to_denit_excess_vr(c,j) = max(bdnr*((sminn_vr(c,j)/dt) - sum_ndemand_vr(c,j)),0._r8) @@ -479,15 +479,15 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, ! sum up N fluxes to immobilization do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) actual_immob(c) = actual_immob(c) + actual_immob_vr(c,j) * dzsoi_decomp(j) potential_immob(c) = potential_immob(c) + potential_immob_vr(c,j) * dzsoi_decomp(j) end do end do - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! calculate the fraction of potential growth that can be ! acheived with the N available to plants if (plant_ndemand(c) > 0.0_r8) then @@ -520,23 +520,23 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, compet_nit = params_inst%compet_nit ! init total mineral N pools - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_tot(c) = 0. end do ! sum up total mineral N pools do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_tot(c) = sminn_tot(c) + (smin_no3_vr(c,j) + smin_nh4_vr(c,j)) * dzsoi_decomp(j) end do end do ! define N uptake profile for initial vertical distribution of plant N uptake, assuming plant seeks N from where it is most abundant do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (sminn_tot(c) > 0.) then nuptake_prof(c,j) = sminn_vr(c,j) / sminn_tot(c) else @@ -547,8 +547,8 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, ! main column/vertical loop do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) l = col%landunit(c) ! first compete for nh4 @@ -630,8 +630,6 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, sum_no3_demand_scaled(c,j) = (plant_ndemand(c)*nuptake_prof(c,j))*compet_plant_no3 + & (potential_immob_vr(c,j)-actual_immob_nh4_vr(c,j))*compet_decomp_no3 + pot_f_denit_vr(c,j)*compet_denit endif - - if (sum_no3_demand(c,j)*dt < smin_no3_vr(c,j)) then @@ -655,7 +653,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, smin_no3_to_plant_vr(c,j) = smin_no3_vr(c,j)/dt - actual_immob_no3_vr(c,j) - f_denit_vr(c,j) end if endif - + else ! NO3 availability can not satisfy the sum of immobilization, denitrification, and @@ -756,7 +754,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, if ( local_use_fun ) then call t_startf( 'CNFUN' ) - call CNFUN(bounds,num_soilc,filter_soilc,num_soilp,filter_soilp,waterstatebulk_inst,& + call CNFUN(bounds,num_bgc_soilc,filter_bgc_soilc,num_bgc_vegp,filter_bgc_vegp,waterstatebulk_inst,& waterfluxbulk_inst,temperature_inst,soilstate_inst,cnveg_state_inst,cnveg_carbonstate_inst,& cnveg_carbonflux_inst,cnveg_nitrogenstate_inst,cnveg_nitrogenflux_inst ,& soilbiogeochem_nitrogenflux_inst,soilbiogeochem_carbonflux_inst,canopystate_inst, & @@ -778,20 +776,20 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, if(.not.local_use_fun)then - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! sum up N fluxes to plant after initial competition sminn_to_plant(c) = 0._r8 end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant(c) = sminn_to_plant(c) + sminn_to_plant_vr(c,j) * dzsoi_decomp(j) end do end do else - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! sum up N fluxes to plant after initial competition sminn_to_plant(c) = 0._r8 !this isn't use in fun. do j = 1, nlevdecomp @@ -815,8 +813,8 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, if (decomp_method == mimics_decomp) then do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) do k = 1, ndecomp_cascade_transitions if (cascade_receiver_pool(k) == i_cop_mic .or. & @@ -848,14 +846,14 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, if(.not.local_use_fun)then ! give plants a second pass to see if there is any mineral N left over with which to satisfy residual N demand. ! first take frm nh4 pool; then take from no3 pool - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) residual_plant_ndemand(c) = plant_ndemand(c) - sminn_to_plant(c) residual_smin_nh4(c) = 0._r8 end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (residual_plant_ndemand(c) > 0._r8 ) then if (nlimit_nh4(c,j) .eq. 0) then residual_smin_nh4_vr(c,j) = max(smin_nh4_vr(c,j) - (actual_immob_nh4_vr(c,j) + & @@ -875,13 +873,13 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, end do ! re-sum up N fluxes to plant after second pass for nh4 - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant(c) = 0._r8 end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant_vr(c,j) = smin_nh4_to_plant_vr(c,j) + smin_no3_to_plant_vr(c,j) sminn_to_plant(c) = sminn_to_plant(c) + (sminn_to_plant_vr(c,j)) * dzsoi_decomp(j) end do @@ -889,15 +887,15 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, ! ! and now do second pass for no3 - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) residual_plant_ndemand(c) = plant_ndemand(c) - sminn_to_plant(c) residual_smin_no3(c) = 0._r8 end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (residual_plant_ndemand(c) > 0._r8 ) then if (nlimit_no3(c,j) .eq. 0) then residual_smin_no3_vr(c,j) = max(smin_no3_vr(c,j) - (actual_immob_no3_vr(c,j) + & @@ -916,13 +914,13 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, end do ! re-sum up N fluxes to plant after second passes of both no3 and nh4 - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant(c) = 0._r8 end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant_vr(c,j) = smin_nh4_to_plant_vr(c,j) + smin_no3_to_plant_vr(c,j) sminn_to_plant(c) = sminn_to_plant(c) + (sminn_to_plant_vr(c,j)) * dzsoi_decomp(j) end do @@ -930,13 +928,13 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, else !use_fun !calculate maximum N available to plants. - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant(c) = 0._r8 end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant_vr(c,j) = smin_nh4_to_plant_vr(c,j) + smin_no3_to_plant_vr(c,j) sminn_to_plant(c) = sminn_to_plant(c) + (sminn_to_plant_vr(c,j)) * dzsoi_decomp(j) end do @@ -945,8 +943,8 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, ! add up fun fluxes from SMINN to plant. do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_to_plant_new(c) = sminn_to_plant_new(c) + & (sminn_to_plant_fun_no3_vr(c,j) + sminn_to_plant_fun_nh4_vr(c,j)) * dzsoi_decomp(j) @@ -956,14 +954,14 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, end if !use_f ! sum up N fluxes to immobilization - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) actual_immob(c) = 0._r8 potential_immob(c) = 0._r8 end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) actual_immob(c) = actual_immob(c) + actual_immob_vr(c,j) * dzsoi_decomp(j) potential_immob(c) = potential_immob(c) + potential_immob_vr(c,j) * dzsoi_decomp(j) end do @@ -972,8 +970,8 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! calculate the fraction of potential growth that can be ! acheived with the N available to plants ! calculate the fraction of immobilization realized (for diagnostic purposes) @@ -993,7 +991,7 @@ subroutine SoilBiogeochemCompetition (bounds, num_soilc, filter_soilc,num_soilp, end if end do ! end of column loops - end if !end of if_not_use_nitrif_denitrif + end if if_nitrif !end of if_not_use_nitrif_denitrif end associate diff --git a/src/soilbiogeochem/SoilBiogeochemDecompCascadeBGCMod.F90 b/src/soilbiogeochem/SoilBiogeochemDecompCascadeBGCMod.F90 index b65fc5f17f..f4785fc4d8 100644 --- a/src/soilbiogeochem/SoilBiogeochemDecompCascadeBGCMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemDecompCascadeBGCMod.F90 @@ -229,8 +229,6 @@ subroutine init_decompcascade_bgc(bounds, soilbiogeochem_state_inst, soilstate_i ! initialize rate constants and decomposition pathways following the decomposition cascade of the BGC model. ! written by C. Koven ! - ! !USES: - ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds type(soilbiogeochem_state_type) , intent(inout) :: soilbiogeochem_state_inst @@ -311,7 +309,7 @@ subroutine init_decompcascade_bgc(bounds, soilbiogeochem_state_inst, soilstate_i i_met_lit = i_litr_min floating_cn_ratio_decomp_pools(i_met_lit) = .true. decomp_cascade_con%decomp_pool_name_restart(i_met_lit) = 'litr1' - decomp_cascade_con%decomp_pool_name_history(i_met_lit) = 'MET_LIT' + decomp_cascade_con%decomp_pool_name_history(i_met_lit) = 'LIT_MET' decomp_cascade_con%decomp_pool_name_long(i_met_lit) = 'metabolic litter' decomp_cascade_con%decomp_pool_name_short(i_met_lit) = 'L1' is_litter(i_met_lit) = .true. @@ -326,7 +324,7 @@ subroutine init_decompcascade_bgc(bounds, soilbiogeochem_state_inst, soilstate_i i_cel_lit = i_met_lit + 1 floating_cn_ratio_decomp_pools(i_cel_lit) = .true. decomp_cascade_con%decomp_pool_name_restart(i_cel_lit) = 'litr2' - decomp_cascade_con%decomp_pool_name_history(i_cel_lit) = 'CEL_LIT' + decomp_cascade_con%decomp_pool_name_history(i_cel_lit) = 'LIT_CEL' decomp_cascade_con%decomp_pool_name_long(i_cel_lit) = 'cellulosic litter' decomp_cascade_con%decomp_pool_name_short(i_cel_lit) = 'L2' is_litter(i_cel_lit) = .true. @@ -341,7 +339,7 @@ subroutine init_decompcascade_bgc(bounds, soilbiogeochem_state_inst, soilstate_i i_lig_lit = i_cel_lit + 1 floating_cn_ratio_decomp_pools(i_lig_lit) = .true. decomp_cascade_con%decomp_pool_name_restart(i_lig_lit) = 'litr3' - decomp_cascade_con%decomp_pool_name_history(i_lig_lit) = 'LIG_LIT' + decomp_cascade_con%decomp_pool_name_history(i_lig_lit) = 'LIT_LIG' decomp_cascade_con%decomp_pool_name_long(i_lig_lit) = 'lignin litter' decomp_cascade_con%decomp_pool_name_short(i_lig_lit) = 'L3' is_litter(i_lig_lit) = .true. @@ -366,7 +364,7 @@ subroutine init_decompcascade_bgc(bounds, soilbiogeochem_state_inst, soilstate_i i_act_som = i_lig_lit + 1 floating_cn_ratio_decomp_pools(i_act_som) = .false. decomp_cascade_con%decomp_pool_name_restart(i_act_som) = 'soil1' - decomp_cascade_con%decomp_pool_name_history(i_act_som) = 'ACT_SOM' + decomp_cascade_con%decomp_pool_name_history(i_act_som) = 'SOM_ACT' decomp_cascade_con%decomp_pool_name_long(i_act_som) = 'active soil organic matter' decomp_cascade_con%decomp_pool_name_short(i_act_som) = 'S1' is_litter(i_act_som) = .false. @@ -381,7 +379,7 @@ subroutine init_decompcascade_bgc(bounds, soilbiogeochem_state_inst, soilstate_i i_slo_som = i_act_som + 1 floating_cn_ratio_decomp_pools(i_slo_som) = .false. decomp_cascade_con%decomp_pool_name_restart(i_slo_som) = 'soil2' - decomp_cascade_con%decomp_pool_name_history(i_slo_som) = 'SLO_SOM' + decomp_cascade_con%decomp_pool_name_history(i_slo_som) = 'SOM_SLO' decomp_cascade_con%decomp_pool_name_long(i_slo_som) = 'slow soil organic matter' decomp_cascade_con%decomp_pool_name_short(i_slo_som) = 'S2' is_litter(i_slo_som) = .false. @@ -396,7 +394,7 @@ subroutine init_decompcascade_bgc(bounds, soilbiogeochem_state_inst, soilstate_i i_pas_som = i_slo_som + 1 floating_cn_ratio_decomp_pools(i_pas_som) = .false. decomp_cascade_con%decomp_pool_name_restart(i_pas_som) = 'soil3' - decomp_cascade_con%decomp_pool_name_history(i_pas_som) = 'PAS_SOM' + decomp_cascade_con%decomp_pool_name_history(i_pas_som) = 'SOM_PAS' decomp_cascade_con%decomp_pool_name_long(i_pas_som) = 'passive soil organic matter' decomp_cascade_con%decomp_pool_name_short(i_pas_som) = 'S3' is_litter(i_pas_som) = .false. @@ -510,8 +508,9 @@ subroutine init_decompcascade_bgc(bounds, soilbiogeochem_state_inst, soilstate_i end subroutine init_decompcascade_bgc !----------------------------------------------------------------------- - subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & - soilstate_inst, temperature_inst, ch4_inst, soilbiogeochem_carbonflux_inst) + subroutine decomp_rate_constants_bgc(bounds, num_bgc_soilc, filter_bgc_soilc, & + soilstate_inst, temperature_inst, ch4_inst, soilbiogeochem_carbonflux_inst, & + idop) ! ! !DESCRIPTION: ! calculate rate constants and decomposition pathways for the CENTURY decomposition cascade model @@ -521,15 +520,19 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & use clm_time_manager , only : get_average_days_per_year, get_step_size use shr_const_mod , only : SHR_CONST_PI use clm_varcon , only : secspday + use TillageMod , only : get_do_tillage + use TillageMod , only : get_apply_tillage_multipliers + use landunit_varcon , only : istcrop ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns type(soilstate_type) , intent(in) :: soilstate_inst type(temperature_type) , intent(in) :: temperature_inst type(ch4_type) , intent(in) :: ch4_inst type(soilbiogeochem_carbonflux_type) , intent(inout) :: soilbiogeochem_carbonflux_inst + integer, optional , intent(in) :: idop(:) ! patch day of planting ! ! !LOCAL VARIABLES: real(r8), parameter :: eps = 1.e-6_r8 @@ -595,6 +598,10 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & errMsg(sourcefile, __LINE__)) endif + if (get_do_tillage() .and. .not. present(idop)) then + call endrun("Do not enable tillage without providing idop to decomp_rate_constants_bgc().") + end if + days_per_year = get_average_days_per_year() dt = real( get_step_size(), r8 ) @@ -619,8 +626,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & catanf_30 = catanf(30._r8) if ( spinup_state >= 1 ) then - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! if ( abs(spinup_factor(i_met_lit) - 1._r8) .gt. eps) then spinup_geogterm_l1(c) = spinup_factor(i_met_lit) * get_spinup_latitude_term(grc%latdeg(col%gridcell(c))) @@ -662,8 +669,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & ! end do else - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) spinup_geogterm_l1(c) = 1._r8 spinup_geogterm_l23(c) = 1._r8 spinup_geogterm_cwd(c) = 1._r8 @@ -686,14 +693,14 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & nlev_soildecomp_standard=5 allocate(fr(bounds%begc:bounds%endc,nlev_soildecomp_standard)) do j=1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) frw(c) = frw(c) + col%dz(c,j) end do end do do j = 1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (frw(c) /= 0._r8) then fr(c,j) = col%dz(c,j) / frw(c) else @@ -708,8 +715,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & ! limiting conditions at 25 C. do j = 1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (j==1) t_scalar(c,:) = 0._r8 if (t_soisno(c,j) >= SHR_CONST_TKFRZ) then t_scalar(c,1)=t_scalar(c,1) + & @@ -724,8 +731,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & else ! original century uses an arctangent function to calculate the temperature dependence of decomposition do j = 1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (j==1) t_scalar(c,:) = 0._r8 t_scalar(c,1)=t_scalar(c,1) +max(catanf(t_soisno(c,j)-SHR_CONST_TKFRZ)/catanf_30*fr(c,j),0.01_r8) @@ -743,8 +750,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & ! and soil moisture. Soil Biol. Biochem., 15(4):447-453. do j = 1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (j==1) w_scalar(c,:) = 0._r8 psi = min(soilpsi(c,j),maxpsi) ! decomp only if soilpsi is higher than minpsi @@ -760,8 +767,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & ! Check for anoxia w/o LCH4 now done in controlMod. do j = 1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (j==1) o_scalar(c,:) = 0._r8 @@ -790,8 +797,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & ! the base rates at 25 C, which are calibrated from microcosm studies. do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (t_soisno(c,j) >= SHR_CONST_TKFRZ) then t_scalar(c,j)= (Q10**((t_soisno(c,j)-(SHR_CONST_TKFRZ+25._r8))/10._r8)) else @@ -803,8 +810,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & else do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) t_scalar(c,j)= max(catanf(t_soisno(c,j)-SHR_CONST_TKFRZ)/catanf_30, 0.01_r8) end do end do @@ -820,8 +827,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & ! and soil moisture. Soil Biol. Biochem., 15(4):447-453. do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) psi = min(soilpsi(c,j),maxpsi) ! decomp only if soilpsi is higher than minpsi if (psi > minpsi) then @@ -838,8 +845,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & if (anoxia) then do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) o_scalar(c,j) = max(o2stress_unsat(c,j), mino2lim) end do @@ -857,8 +864,8 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & ! scale all decomposition rates by a constant to compensate for offset between original CENTURY temp func and Q10 normalization_factor = (catanf(normalization_tref)/catanf_30) / (q10**((normalization_tref-25._r8)/10._r8)) do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) t_scalar(c,j) = t_scalar(c,j) * normalization_factor end do end do @@ -867,16 +874,16 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & ! add a term to reduce decomposition rate at depth ! for now used a fixed e-folding depth do j = 1, nlevdecomp - do fc = 1, num_soilc - c = filter_soilc(fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc(fc) depth_scalar(c,j) = exp(-zsoi(j) / decomp_depth_efolding) end do end do ! calculate rate constants for all litter and som pools do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) decomp_k(c,j,i_met_lit) = k_l1 * t_scalar(c,j) * w_scalar(c,j) * & depth_scalar(c,j) * o_scalar(c,j) * spinup_geogterm_l1(c) decomp_k(c,j,i_cel_lit) = k_l2_l3 * t_scalar(c,j) * w_scalar(c,j) * & @@ -895,6 +902,12 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & decomp_k(c,j,i_cwd) = k_frag * t_scalar(c,j) * w_scalar(c,j) * & depth_scalar(c,j) * o_scalar(c,j) * spinup_geogterm_cwd(c) end if + + ! Tillage + if (get_do_tillage()) then + call get_apply_tillage_multipliers(idop, c, j, decomp_k(c,j,:)) + end if + ! Above into soil matrix if(use_soil_matrixcn)then ! same for cwd but only if fates is not enabled; fates handles CWD @@ -904,6 +917,7 @@ subroutine decomp_rate_constants_bgc(bounds, num_soilc, filter_soilc, & end if !use_soil_matrixcn end do end do + pathfrac_decomp_cascade(bounds%begc:bounds%endc,1:nlevdecomp,i_l1s1) = 1.0_r8 pathfrac_decomp_cascade(bounds%begc:bounds%endc,1:nlevdecomp,i_l2s1) = 1.0_r8 pathfrac_decomp_cascade(bounds%begc:bounds%endc,1:nlevdecomp,i_l3s2) = 1.0_r8 diff --git a/src/soilbiogeochem/SoilBiogeochemDecompCascadeConType.F90 b/src/soilbiogeochem/SoilBiogeochemDecompCascadeConType.F90 index 4f6cf04eaf..0474ab9a63 100644 --- a/src/soilbiogeochem/SoilBiogeochemDecompCascadeConType.F90 +++ b/src/soilbiogeochem/SoilBiogeochemDecompCascadeConType.F90 @@ -63,7 +63,7 @@ module SoilBiogeochemDecompCascadeConType !------------------------------------------------------------------------ subroutine decomp_cascade_par_init( NLFilename ) - use clm_varctl , only : use_fates, use_cn, use_fates_sp + use clm_varctl , only : use_cn, use_fates_bgc use clm_varpar , only : ndecomp_pools_max use spmdMod , only : masterproc, mpicom use clm_nlUtilsMod , only : find_nlgroup_name @@ -110,9 +110,9 @@ subroutine decomp_cascade_par_init( NLFilename ) if ( decomp_method == no_soil_decomp )then call endrun('When running with BGC an active soil_decomp_method must be used') end if - else if ( use_fates ) then - if ( .not. use_fates_sp .and. (decomp_method == no_soil_decomp) )then - call endrun('When running with FATES and without FATES-SP an active soil_decomp_method must be used') + else if ( use_fates_bgc ) then + if ( decomp_method == no_soil_decomp )then + call endrun('When running with FATES and without FATES-SP, an active soil_decomp_method must be used') end if else if ( decomp_method /= no_soil_decomp )then @@ -128,7 +128,7 @@ subroutine decomp_cascade_par_init( NLFilename ) ! ndecomp_pools would get the value of i_pas_som or i_cwd and ! ndecomp_cascade_transitions would get the value of i_s3s1 or i_cwdl3 ! depending on how use_fates is set. - if ( use_fates ) then + if ( use_fates_bgc ) then if (decomp_method == century_decomp) then ndecomp_pools = 6 ndecomp_cascade_transitions = 8 diff --git a/src/soilbiogeochem/SoilBiogeochemDecompCascadeMIMICSMod.F90 b/src/soilbiogeochem/SoilBiogeochemDecompCascadeMIMICSMod.F90 index f820db01b6..65091755f5 100644 --- a/src/soilbiogeochem/SoilBiogeochemDecompCascadeMIMICSMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemDecompCascadeMIMICSMod.F90 @@ -251,12 +251,12 @@ subroutine readParams ( ncid ) call ncd_io(trim(tString), params_inst%mimics_fmet(:), 'read', ncid, readvar=readv) if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) - allocate(params_inst%mimics_fchem_r(4)) + allocate(params_inst%mimics_fchem_r(2)) tString='mimics_fchem_r' call ncd_io(trim(tString), params_inst%mimics_fchem_r(:), 'read', ncid, readvar=readv) if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) - allocate(params_inst%mimics_fchem_k(4)) + allocate(params_inst%mimics_fchem_k(2)) tString='mimics_fchem_k' call ncd_io(trim(tString), params_inst%mimics_fchem_k(:), 'read', ncid, readvar=readv) if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) @@ -481,7 +481,7 @@ subroutine init_decompcascade_mimics(bounds, soilbiogeochem_state_inst, soilstat i_met_lit = i_litr_min floating_cn_ratio_decomp_pools(i_met_lit) = .true. decomp_cascade_con%decomp_pool_name_restart(i_met_lit) = 'litr1' - decomp_cascade_con%decomp_pool_name_history(i_met_lit) = 'MET_LIT' + decomp_cascade_con%decomp_pool_name_history(i_met_lit) = 'LIT_MET' decomp_cascade_con%decomp_pool_name_long(i_met_lit) = 'metabolic litter' decomp_cascade_con%decomp_pool_name_short(i_met_lit) = 'L1' is_microbe(i_met_lit) = .false. @@ -497,7 +497,7 @@ subroutine init_decompcascade_mimics(bounds, soilbiogeochem_state_inst, soilstat i_str_lit = i_met_lit + 1 floating_cn_ratio_decomp_pools(i_str_lit) = .true. decomp_cascade_con%decomp_pool_name_restart(i_str_lit) = 'litr2' - decomp_cascade_con%decomp_pool_name_history(i_str_lit) = 'STR_LIT' + decomp_cascade_con%decomp_pool_name_history(i_str_lit) = 'LIT_STR' decomp_cascade_con%decomp_pool_name_long(i_str_lit) = 'structural litter' decomp_cascade_con%decomp_pool_name_short(i_str_lit) = 'L2' is_microbe(i_str_lit) = .false. @@ -523,7 +523,7 @@ subroutine init_decompcascade_mimics(bounds, soilbiogeochem_state_inst, soilstat i_avl_som = i_str_lit + 1 floating_cn_ratio_decomp_pools(i_avl_som) = .true. decomp_cascade_con%decomp_pool_name_restart(i_avl_som) = 'soil1' - decomp_cascade_con%decomp_pool_name_history(i_avl_som) = 'AVL_SOM' + decomp_cascade_con%decomp_pool_name_history(i_avl_som) = 'SOM_AVL' decomp_cascade_con%decomp_pool_name_long(i_avl_som) = 'available soil organic matter' decomp_cascade_con%decomp_pool_name_short(i_avl_som) = 'S1' is_microbe(i_avl_som) = .false. @@ -539,7 +539,7 @@ subroutine init_decompcascade_mimics(bounds, soilbiogeochem_state_inst, soilstat i_chem_som = i_avl_som + 1 floating_cn_ratio_decomp_pools(i_chem_som) = .true. decomp_cascade_con%decomp_pool_name_restart(i_chem_som) = 'soil2' - decomp_cascade_con%decomp_pool_name_history(i_chem_som) = 'CHEM_SOM' + decomp_cascade_con%decomp_pool_name_history(i_chem_som) = 'SOM_CHEM' decomp_cascade_con%decomp_pool_name_long(i_chem_som) = 'chemically protected soil organic matter' decomp_cascade_con%decomp_pool_name_short(i_chem_som) = 'S2' is_microbe(i_chem_som) = .false. @@ -555,7 +555,7 @@ subroutine init_decompcascade_mimics(bounds, soilbiogeochem_state_inst, soilstat i_phys_som = i_chem_som + 1 floating_cn_ratio_decomp_pools(i_phys_som) = .true. decomp_cascade_con%decomp_pool_name_restart(i_phys_som) = 'soil3' - decomp_cascade_con%decomp_pool_name_history(i_phys_som) = 'PHYS_SOM' + decomp_cascade_con%decomp_pool_name_history(i_phys_som) = 'SOM_PHYS' decomp_cascade_con%decomp_pool_name_long(i_phys_som) = 'physically protected soil organic matter' decomp_cascade_con%decomp_pool_name_short(i_phys_som) = 'S3' is_microbe(i_phys_som) = .false. @@ -571,7 +571,7 @@ subroutine init_decompcascade_mimics(bounds, soilbiogeochem_state_inst, soilstat i_cop_mic = i_phys_som + 1 floating_cn_ratio_decomp_pools(i_cop_mic) = .true. decomp_cascade_con%decomp_pool_name_restart(i_cop_mic) = 'micr1' - decomp_cascade_con%decomp_pool_name_history(i_cop_mic) = 'COP_MIC' + decomp_cascade_con%decomp_pool_name_history(i_cop_mic) = 'MIC_COP' decomp_cascade_con%decomp_pool_name_long(i_cop_mic) = 'copiotrophic microbes' decomp_cascade_con%decomp_pool_name_short(i_cop_mic) = 'M1' is_microbe(i_cop_mic) = .true. @@ -587,7 +587,7 @@ subroutine init_decompcascade_mimics(bounds, soilbiogeochem_state_inst, soilstat i_oli_mic = i_cop_mic + 1 floating_cn_ratio_decomp_pools(i_oli_mic) = .true. decomp_cascade_con%decomp_pool_name_restart(i_oli_mic) = 'micr2' - decomp_cascade_con%decomp_pool_name_history(i_oli_mic) = 'OLI_MIC' + decomp_cascade_con%decomp_pool_name_history(i_oli_mic) = 'MIC_OLI' decomp_cascade_con%decomp_pool_name_long(i_oli_mic) = 'oligotrophic microbes' decomp_cascade_con%decomp_pool_name_short(i_oli_mic) = 'M2' is_microbe(i_oli_mic) = .true. @@ -752,10 +752,11 @@ subroutine init_decompcascade_mimics(bounds, soilbiogeochem_state_inst, soilstat end subroutine init_decompcascade_mimics !----------------------------------------------------------------------- - subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & + subroutine decomp_rates_mimics(bounds, num_bgc_soilc, filter_bgc_soilc, & num_soilp, filter_soilp, clm_fates, & soilstate_inst, temperature_inst, cnveg_carbonflux_inst, & - ch4_inst, soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst) + ch4_inst, soilbiogeochem_carbonflux_inst, soilbiogeochem_carbonstate_inst, & + idop) ! ! !DESCRIPTION: ! Calculate rates and decomposition pathways for the MIMICS @@ -768,13 +769,15 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & use subgridAveMod , only : p2c use PatchType , only : patch use pftconMod , only : pftname + use TillageMod , only : get_do_tillage + use TillageMod , only : get_apply_tillage_multipliers ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_soilp ! number of soil patches in filter integer , intent(in) :: filter_soilp(:) ! filter for soil patches - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns type(soilstate_type) , intent(in) :: soilstate_inst type(temperature_type) , intent(in) :: temperature_inst type(cnveg_carbonflux_type) , intent(in) :: cnveg_carbonflux_inst @@ -782,6 +785,7 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & type(soilbiogeochem_carbonflux_type) , intent(inout) :: soilbiogeochem_carbonflux_inst type(soilbiogeochem_carbonstate_type), intent(in) :: soilbiogeochem_carbonstate_inst type(hlm_fates_interface_type) , intent(inout) :: clm_fates + integer, optional , intent(in) :: idop(:) ! patch day of planting ! ! !LOCAL VARIABLES: real(r8), parameter :: eps = 1.e-6_r8 @@ -828,10 +832,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & real(r8):: mimics_fmet_p4 real(r8):: mimics_fchem_r_p1 real(r8):: mimics_fchem_r_p2 - real(r8):: mimics_fchem_r_p3 real(r8):: mimics_fchem_k_p1 real(r8):: mimics_fchem_k_p2 - real(r8):: mimics_fchem_k_p3 real(r8):: mimics_tau_mod_min real(r8):: mimics_tau_mod_max real(r8):: mimics_tau_mod_factor @@ -883,6 +885,10 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & spinup_factor => decomp_cascade_con%spinup_factor & ! Input: [real(r8) (:) ] factor for AD spinup associated with each pool ) + if (get_do_tillage() .and. .not. present(idop)) then + call endrun("Do not enable tillage without providing idop to decomp_rate_constants_mimics().") + end if + mino2lim = CNParamsShareInst%mino2lim days_per_year = get_average_days_per_year() @@ -895,8 +901,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & ! calc ref rate if ( spinup_state >= 1 ) then - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! if ( abs(spinup_factor(i_met_lit) - 1._r8) .gt. eps) then spinup_geogterm_l1(c) = spinup_factor(i_met_lit) * get_spinup_latitude_term(grc%latdeg(col%gridcell(c))) @@ -950,8 +956,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & ! end do else - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) spinup_geogterm_l1(c) = 1._r8 spinup_geogterm_l2(c) = 1._r8 spinup_geogterm_cwd(c) = 1._r8 @@ -975,14 +981,14 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & frw(bounds%begc:bounds%endc) = 0._r8 allocate(fr(bounds%begc:bounds%endc,nlev_soildecomp_standard)) do j=1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) frw(c) = frw(c) + col%dz(c,j) end do end do do j = 1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (frw(c) /= 0._r8) then fr(c,j) = col%dz(c,j) / frw(c) else @@ -1000,8 +1006,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & ! and soil moisture. Soil Biol. Biochem., 15(4):447-453. do j = 1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (j==1) w_scalar(c,:) = 0._r8 psi = min(soilpsi(c,j),maxpsi) ! decomp only if soilpsi is higher than minpsi @@ -1017,8 +1023,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & if (anoxia) then do j = 1,nlev_soildecomp_standard - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (j==1) o_scalar(c,:) = 0._r8 @@ -1042,8 +1048,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & ! and soil moisture. Soil Biol. Biochem., 15(4):447-453. do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) psi = min(soilpsi(c,j),maxpsi) ! decomp only if soilpsi is higher than minpsi if (psi > minpsi) then @@ -1059,8 +1065,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & if (anoxia) then do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) o_scalar(c,j) = max(o2stress_unsat(c,j), mino2lim) end do @@ -1074,8 +1080,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & ! Term that reduces decomposition rate at depth ! Placeholder. For now depth_scalar = 1. do j = 1, nlevdecomp - do fc = 1, num_soilc - c = filter_soilc(fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc(fc) ! Using fixed e-folding depth as in ! SoilBiogeochemDecompCascadeBGCMod.F90 ! depth_scalar(c,j) = exp(-zsoi(j) / decomp_depth_efolding) @@ -1092,10 +1098,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & mimics_fmet_p4 = params_inst%mimics_fmet(4) mimics_fchem_r_p1 = params_inst%mimics_fchem_r(1) mimics_fchem_r_p2 = params_inst%mimics_fchem_r(2) - mimics_fchem_r_p3 = params_inst%mimics_fchem_r(3) mimics_fchem_k_p1 = params_inst%mimics_fchem_k(1) mimics_fchem_k_p2 = params_inst%mimics_fchem_k(2) - mimics_fchem_k_p3 = params_inst%mimics_fchem_k(3) mimics_tau_mod_min = params_inst%mimics_tau_mod_min mimics_tau_mod_max = params_inst%mimics_tau_mod_max mimics_tau_mod_factor = params_inst%mimics_tau_mod_factor @@ -1135,7 +1139,7 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & end do ! p loop ! Calculate the column-level average - call p2c(bounds, num_soilc, filter_soilc, & + call p2c(bounds, num_bgc_soilc, filter_bgc_soilc, & annsum_npp(bounds%begp:bounds%endp), & annsum_npp_col_local(bounds%begc:bounds%endc)) else @@ -1146,8 +1150,8 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & end if fates_if ! calculate rates for all litter and som pools - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (use_fates) then annsum_npp_col_scalar = max(0._r8, annsum_npp_col_local(c)) @@ -1186,9 +1190,9 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & ! Used in the update of certain pathfrac terms that vary with time ! in the next loop fchem_m1 = min(1._r8, max(0._r8, mimics_fchem_r_p1 * & - exp(mimics_fchem_r_p2 * fmet) * mimics_fchem_r_p3)) + exp(mimics_fchem_r_p2 * fmet))) fchem_m2 = min(1._r8, max(0._r8, mimics_fchem_k_p1 * & - exp(mimics_fchem_k_p2 * fmet) * mimics_fchem_k_p3)) + exp(mimics_fchem_k_p2 * fmet))) do j = 1,nlevdecomp ! vmax ends up in units of per hour but is expected @@ -1283,6 +1287,7 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & ! The right hand side is OXIDAT in the testbed (line 1145) decomp_k(c,j,i_chem_som) = (term_1 + term_2) * w_d_o_scalars + ! Currently, mimics_densdep = 1 so as to have no effect decomp_k(c,j,i_cop_mic) = tau_m1 * & m1_conc**(mimics_densdep - 1.0_r8) * w_d_o_scalars favl = min(1.0_r8, max(0.0_r8, 1.0_r8 - fphys_m1(c,j) - fchem_m1)) @@ -1301,6 +1306,11 @@ subroutine decomp_rates_mimics(bounds, num_soilc, filter_soilc, & if (.not. use_fates) then decomp_k(c,j,i_cwd) = k_frag * w_d_o_scalars ! * spinup_geogterm_cwd(c) end if + + ! Tillage + if (get_do_tillage()) then + call get_apply_tillage_multipliers(idop, c, j, decomp_k(c,j,:)) + end if end do end do diff --git a/src/soilbiogeochem/SoilBiogeochemDecompMod.F90 b/src/soilbiogeochem/SoilBiogeochemDecompMod.F90 index a46f999143..9bd8b13008 100644 --- a/src/soilbiogeochem/SoilBiogeochemDecompMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemDecompMod.F90 @@ -11,7 +11,7 @@ module SoilBiogeochemDecompMod use shr_log_mod , only : errMsg => shr_log_errMsg use decompMod , only : bounds_type use clm_varpar , only : nlevdecomp, ndecomp_cascade_transitions, ndecomp_pools - use clm_varctl , only : use_nitrif_denitrif, use_lch4, use_fates, iulog + use clm_varctl , only : use_nitrif_denitrif, use_lch4, iulog use clm_varcon , only : dzsoi_decomp use SoilBiogeochemDecompCascadeConType , only : decomp_cascade_con, mimics_decomp, decomp_method, use_soil_matrixcn use SoilBiogeochemStateType , only : soilbiogeochem_state_type @@ -67,7 +67,7 @@ subroutine readParams ( ncid ) end subroutine readParams !----------------------------------------------------------------------- - subroutine SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, & + subroutine SoilBiogeochemDecomp (bounds, num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_state_inst, soilbiogeochem_carbonstate_inst, soilbiogeochem_carbonflux_inst, & soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & cn_decomp_pools, p_decomp_cpool_loss, pmnf_decomp_cascade, & @@ -78,8 +78,8 @@ subroutine SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, ! ! !ARGUMENT: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns type(soilbiogeochem_state_type) , intent(inout) :: soilbiogeochem_state_inst type(soilbiogeochem_carbonstate_type) , intent(in) :: soilbiogeochem_carbonstate_inst type(soilbiogeochem_carbonflux_type) , intent(inout) :: soilbiogeochem_carbonflux_inst @@ -137,13 +137,12 @@ subroutine SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, ! column loop to calculate actual immobilization and decomp rates, following ! resolution of plant/heterotroph competition for mineral N - if ( .not. use_fates) then ! calculate c:n ratios of applicable pools do l = 1, ndecomp_pools if ( floating_cn_ratio_decomp_pools(l) ) then do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if ( decomp_npools_vr(c,j,l) > 0._r8 ) then cn_decomp_pools(c,j,l) = decomp_cpools_vr(c,j,l) / decomp_npools_vr(c,j,l) end if @@ -151,8 +150,8 @@ subroutine SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, end do else do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) cn_decomp_pools(c,j,l) = initial_cn_ratio(l) end do end do @@ -170,8 +169,8 @@ subroutine SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, do k = 1, ndecomp_cascade_transitions do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (decomp_cpools_vr(c,j,cascade_donor_pool(k)) > 0._r8) then if ( pmnf_decomp_cascade(c,j,k) > 0._r8 ) then @@ -221,38 +220,21 @@ subroutine SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, end do end do end do - else - do k = 1, ndecomp_cascade_transitions - do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) - ! - decomp_cascade_hr_vr(c,j,k) = rf_decomp_cascade(c,j,k) * p_decomp_cpool_loss(c,j,k) - decomp_cascade_ctransfer_vr(c,j,k) = (1._r8 - rf_decomp_cascade(c,j,k)) * p_decomp_cpool_loss(c,j,k) - if (decomp_method == mimics_decomp) then - decomp_cascade_hr_vr(c,j,k) = min( & - p_decomp_cpool_loss(c,j,k), & - decomp_cascade_hr_vr(c,j,k) + c_overflow_vr(c,j,k)) - decomp_cascade_ctransfer_vr(c,j,k) = max(0.0_r8, p_decomp_cpool_loss(c,j,k) - decomp_cascade_hr_vr(c,j,k)) - end if - ! - end do - end do - end do - end if + + if (use_lch4) then ! Calculate total fraction of potential HR, for methane code do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) hrsum(c,j) = 0._r8 end do end do do k = 1, ndecomp_cascade_transitions do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) hrsum(c,j) = hrsum(c,j) + rf_decomp_cascade(c,j,k) * p_decomp_cpool_loss(c,j,k) end do end do @@ -261,8 +243,8 @@ subroutine SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, ! Nitrogen limitation / (low)-moisture limitation do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (phr_vr(c,j) > 0._r8) then fphr(c,j) = hrsum(c,j) / phr_vr(c,j) * w_scalar(c,j) fphr(c,j) = max(fphr(c,j), 0.01_r8) ! Prevent overflow errors for 0 respiration @@ -276,16 +258,11 @@ subroutine SoilBiogeochemDecomp (bounds, num_soilc, filter_soilc, ! vertically integrate net and gross mineralization fluxes for diagnostic output - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) do j = 1,nlevdecomp - if(.not.use_fates)then net_nmin(c) = net_nmin(c) + net_nmin_vr(c,j) * dzsoi_decomp(j) gross_nmin(c) = gross_nmin(c) + gross_nmin_vr(c,j) * dzsoi_decomp(j) - ! else - ! net_nmin(c) = 0.0_r8 - ! gross_nmin(c) = 0.0_r8 - endif end do end do diff --git a/src/soilbiogeochem/SoilBiogeochemLittVertTranspMod.F90 b/src/soilbiogeochem/SoilBiogeochemLittVertTranspMod.F90 index 616f995bd7..e58e2f22d6 100644 --- a/src/soilbiogeochem/SoilBiogeochemLittVertTranspMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemLittVertTranspMod.F90 @@ -5,7 +5,7 @@ module SoilBiogeochemLittVertTranspMod ! use shr_kind_mod , only : r8 => shr_kind_r8 use shr_log_mod , only : errMsg => shr_log_errMsg - use clm_varctl , only : iulog, use_c13, use_c14, spinup_state, use_fates, use_cn + use clm_varctl , only : iulog, use_c13, use_c14, spinup_state use clm_varcon , only : secspday use decompMod , only : bounds_type use abortutils , only : endrun @@ -81,7 +81,7 @@ subroutine readParams ( ncid ) end subroutine readParams !----------------------------------------------------------------------- - subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & + subroutine SoilBiogeochemLittVertTransp(bounds, num_bgc_soilc, filter_bgc_soilc, & active_layer_inst, soilbiogeochem_state_inst, & soilbiogeochem_carbonstate_inst, soilbiogeochem_carbonflux_inst, & c13_soilbiogeochem_carbonstate_inst, c13_soilbiogeochem_carbonflux_inst, & @@ -105,8 +105,8 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns type(active_layer_type) , intent(in) :: active_layer_inst type(soilbiogeochem_state_type) , intent(inout) :: soilbiogeochem_state_inst type(soilbiogeochem_carbonstate_type) , intent(inout) :: soilbiogeochem_carbonstate_inst @@ -182,16 +182,13 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & if ( use_c14 ) then ntype = ntype+1 endif - if ( use_fates ) then - ntype = 1 - endif spinup_term = 1._r8 epsilon = 1.e-30 !------ first get diffusivity / advection terms -------! ! use different mixing rates for bioturbation and cryoturbation, with fixed bioturbation and cryoturbation set to a maximum depth - do fc = 1, num_soilc - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc (fc) if (( max(altmax(c), altmax_lastyear(c)) <= max_altdepth_cryoturbation ) .and. & ( max(altmax(c), altmax_lastyear(c)) > 0._r8) ) then ! use mixing profile modified slightly from Koven et al. (2009): constant through active layer, linear decrease from base of active layer to zero at a fixed depth @@ -247,11 +244,9 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & source => soilbiogeochem_carbonflux_inst%decomp_cpools_sourcesink_col trcr_tendency_ptr => soilbiogeochem_carbonflux_inst%decomp_cpools_transport_tendency_col case (2) ! N - if (use_cn ) then - conc_ptr => soilbiogeochem_nitrogenstate_inst%decomp_npools_vr_col - source => soilbiogeochem_nitrogenflux_inst%decomp_npools_sourcesink_col - trcr_tendency_ptr => soilbiogeochem_nitrogenflux_inst%decomp_npools_transport_tendency_col - endif + conc_ptr => soilbiogeochem_nitrogenstate_inst%decomp_npools_vr_col + source => soilbiogeochem_nitrogenflux_inst%decomp_npools_sourcesink_col + trcr_tendency_ptr => soilbiogeochem_nitrogenflux_inst%decomp_npools_transport_tendency_col case (3) if ( use_c13 ) then ! C13 @@ -280,8 +275,8 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & if ( .not. is_cwd(s) ) then if(.not. use_soil_matrixcn .or. s .eq. 1)then do j = 1,nlevdecomp+1 - do fc = 1, num_soilc - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc (fc) ! if ( spinup_state >= 1 ) then ! increase transport (both advection and diffusion) by the same factor as accelerated decomposition for a given pool @@ -311,16 +306,16 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & ! Set Pe (Peclet #) and D/dz throughout column - do fc = 1, num_soilc ! dummy terms here - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc ! dummy terms here + c = filter_bgc_soilc (fc) conc_trcr(c,0) = 0._r8 conc_trcr(c,col%nbedrock(c)+1:nlevdecomp+1) = 0._r8 end do do j = 1,nlevdecomp+1 - do fc = 1, num_soilc - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc (fc) conc_trcr(c,j) = conc_ptr(c,j,s) @@ -384,8 +379,8 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & ! Calculate the tridiagonal coefficients do j = 0,nlevdecomp +1 - do fc = 1, num_soilc - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc (fc) ! g = cgridcell(c) if (j > 0 .and. j < nlevdecomp+1) then @@ -433,14 +428,14 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & enddo ! fc; column enddo ! j; nlevdecomp - do fc = 1, num_soilc - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc (fc) jtop(c) = 0 enddo ! subtract initial concentration and source terms for tendency calculation - do fc = 1, num_soilc - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc (fc) do j = 1, nlevdecomp if (.not. use_soil_matrixcn) then trcr_tendency_ptr(c,j,s) = 0.-(conc_trcr(c,j) + source(c,j,s)) @@ -454,15 +449,15 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & ! Solve for the concentration profile for this time step call Tridiagonal(bounds, 0, nlevdecomp+1, & jtop(bounds%begc:bounds%endc), & - num_soilc, filter_soilc, & + num_bgc_soilc, filter_bgc_soilc, & a_tri(bounds%begc:bounds%endc, :), & b_tri(bounds%begc:bounds%endc, :), & c_tri(bounds%begc:bounds%endc, :), & r_tri(bounds%begc:bounds%endc, :), & conc_trcr(bounds%begc:bounds%endc,0:nlevdecomp+1)) ! add post-transport concentration to calculate tendency term - do fc = 1, num_soilc - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc (fc) do j = 1, nlevdecomp trcr_tendency_ptr(c,j,s) = trcr_tendency_ptr(c,j,s) + conc_trcr(c,j) trcr_tendency_ptr(c,j,s) = trcr_tendency_ptr(c,j,s) / dtime @@ -471,16 +466,16 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & else ! For matrix solution set the matrix input array do j = 1,nlevdecomp - do fc =1,num_soilc - c = filter_soilc(fc) + do fc =1,num_bgc_soilc + c = filter_bgc_soilc(fc) end do end do end if !soil_matrix else ! for CWD pools, just add do j = 1,nlevdecomp - do fc = 1, num_soilc - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc (fc) if(.not. use_soil_matrixcn)then conc_trcr(c,j) = conc_ptr(c,j,s) + source(c,j,s) else @@ -498,8 +493,8 @@ subroutine SoilBiogeochemLittVertTransp(bounds, num_soilc, filter_soilc, & if (.not. use_soil_matrixcn) then do j = 1,nlevdecomp - do fc = 1, num_soilc - c = filter_soilc (fc) + do fc = 1, num_bgc_soilc + c = filter_bgc_soilc (fc) conc_ptr(c,j,s) = conc_trcr(c,j) ! Correct for small amounts of carbon that leak into bedrock if (j > col%nbedrock(c)) then diff --git a/src/soilbiogeochem/SoilBiogeochemNLeachingMod.F90 b/src/soilbiogeochem/SoilBiogeochemNLeachingMod.F90 index 02d22d6613..a646feb1d7 100644 --- a/src/soilbiogeochem/SoilBiogeochemNLeachingMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemNLeachingMod.F90 @@ -72,7 +72,7 @@ subroutine readParams ( ncid ) end subroutine readParams !----------------------------------------------------------------------- - subroutine SoilBiogeochemNLeaching(bounds, num_soilc, filter_soilc, & + subroutine SoilBiogeochemNLeaching(bounds, num_bgc_soilc, filter_bgc_soilc, & waterstatebulk_inst, waterfluxbulk_inst, & soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst) ! @@ -86,8 +86,8 @@ subroutine SoilBiogeochemNLeaching(bounds, num_soilc, filter_soilc, & ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns type(waterstatebulk_type) , intent(in) :: waterstatebulk_inst type(waterfluxbulk_type) , intent(in) :: waterfluxbulk_inst type(soilbiogeochem_nitrogenstate_type) , intent(in) :: soilbiogeochem_nitrogenstate_inst @@ -133,8 +133,8 @@ subroutine SoilBiogeochemNLeaching(bounds, num_soilc, filter_soilc, & ! calculate the total soil water tot_water(bounds%begc:bounds%endc) = 0._r8 do j = 1,nlevsoi - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) tot_water(c) = tot_water(c) + h2osoi_liq(c,j) end do end do @@ -143,21 +143,21 @@ subroutine SoilBiogeochemNLeaching(bounds, num_soilc, filter_soilc, & surface_water(bounds%begc:bounds%endc) = 0._r8 do j = 1,nlevsoi if ( zisoi(j) <= depth_runoff_Nloss) then - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) surface_water(c) = surface_water(c) + h2osoi_liq(c,j) end do elseif ( zisoi(j-1) < depth_runoff_Nloss) then - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) surface_water(c) = surface_water(c) + h2osoi_liq(c,j) * ( (depth_runoff_Nloss - zisoi(j-1)) / col%dz(c,j)) end do endif end do ! Loop through columns - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) drain_tot(c) = qflx_drain(c) end do @@ -170,8 +170,8 @@ subroutine SoilBiogeochemNLeaching(bounds, num_soilc, filter_soilc, & do j = 1,nlevdecomp ! Loop through columns - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! calculate the dissolved mineral N concentration (gN/kg water) ! assumes that 10% of mineral nitrogen is soluble @@ -203,8 +203,8 @@ subroutine SoilBiogeochemNLeaching(bounds, num_soilc, filter_soilc, & do j = 1,nlevdecomp ! Loop through columns - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! calculate the dissolved mineral N concentration (gN/kg water) ! assumes that 10% of mineral nitrogen is soluble diff --git a/src/soilbiogeochem/SoilBiogeochemNStateUpdate1Mod.F90 b/src/soilbiogeochem/SoilBiogeochemNStateUpdate1Mod.F90 index 197a1d015b..4b70459aca 100644 --- a/src/soilbiogeochem/SoilBiogeochemNStateUpdate1Mod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemNStateUpdate1Mod.F90 @@ -27,7 +27,7 @@ module SoilBiogeochemNStateUpdate1Mod contains !----------------------------------------------------------------------- - subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & + subroutine SoilBiogeochemNStateUpdate1(num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_state_inst, soilbiogeochem_nitrogenflux_inst, soilbiogeochem_nitrogenstate_inst) ! ! !DESCRIPTION: @@ -35,8 +35,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & ! variables (except for gap-phase mortality and fire fluxes) ! ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns type(soilbiogeochem_state_type) , intent(in) :: soilbiogeochem_state_inst type(soilbiogeochem_nitrogenflux_type) , intent(inout) :: soilbiogeochem_nitrogenflux_inst type(soilbiogeochem_nitrogenstate_type) , intent(inout) :: soilbiogeochem_nitrogenstate_inst @@ -63,8 +63,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & dt = get_step_size_real() do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if(use_fun)then !RF in FUN logic, the fixed N goes straight into the plant, and not into the SMINN pool. ! N deposition and fixation (put all into NH4 pool) ns%smin_nh4_vr_col(c,j) = ns%smin_nh4_vr_col(c,j) + nf%ndep_to_sminn_col(c)*dt * ndep_prof(c,j) @@ -94,8 +94,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & do j = 1, nlevdecomp ! column loop - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (.not. use_nitrif_denitrif) then ! N deposition and fixation @@ -122,8 +122,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & do k = 1, ndecomp_cascade_transitions do j = 1, nlevdecomp ! column loop - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) nf%decomp_npools_sourcesink_col(c,j,cascade_donor_pool(k)) = & nf%decomp_npools_sourcesink_col(c,j,cascade_donor_pool(k)) - & @@ -137,8 +137,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & if ( cascade_receiver_pool(k) /= 0 ) then ! skip terminal transitions do j = 1, nlevdecomp ! column loop - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) nf%decomp_npools_sourcesink_col(c,j,cascade_receiver_pool(k)) = & nf%decomp_npools_sourcesink_col(c,j,cascade_receiver_pool(k)) + & @@ -149,8 +149,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & else ! terminal transitions do j = 1, nlevdecomp ! column loop - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) nf%decomp_npools_sourcesink_col(c,j,cascade_donor_pool(k)) = & nf%decomp_npools_sourcesink_col(c,j,cascade_donor_pool(k)) - & nf%decomp_cascade_sminn_flux_vr_col(c,j,k) * dt @@ -173,8 +173,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & if ( cascade_receiver_pool(k) /= 0 ) then ! skip terminal transitions do j = 1, nlevdecomp ! column loop - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ns%sminn_vr_col(c,j) = ns%sminn_vr_col(c,j) - & (nf%sminn_to_denit_decomp_cascade_vr_col(c,j,k) + & nf%decomp_cascade_sminn_flux_vr_col(c,j,k))* dt @@ -183,8 +183,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & else do j = 1, nlevdecomp ! column loop - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ns%sminn_vr_col(c,j) = ns%sminn_vr_col(c,j) - & nf%sminn_to_denit_decomp_cascade_vr_col(c,j,k)* dt @@ -198,8 +198,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & do j = 1, nlevdecomp ! column loop - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! "bulk denitrification" ns%sminn_vr_col(c,j) = ns%sminn_vr_col(c,j) - nf%sminn_to_denit_excess_vr_col(c,j) * dt @@ -222,8 +222,8 @@ subroutine SoilBiogeochemNStateUpdate1(num_soilc, filter_soilc, & do j = 1, nlevdecomp ! column loop - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! mineralization fluxes (divert a fraction of this stream to nitrification flux, add the rest to NH4 pool) ns%smin_nh4_vr_col(c,j) = ns%smin_nh4_vr_col(c,j) + nf%gross_nmin_vr_col(c,j)*dt diff --git a/src/soilbiogeochem/SoilBiogeochemNitrifDenitrifMod.F90 b/src/soilbiogeochem/SoilBiogeochemNitrifDenitrifMod.F90 index 3993439a1c..44d013cece 100644 --- a/src/soilbiogeochem/SoilBiogeochemNitrifDenitrifMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemNitrifDenitrifMod.F90 @@ -12,7 +12,7 @@ module SoilBiogeochemNitrifDenitrifMod use clm_varpar , only : nlevdecomp use clm_varcon , only : rpi, grav use clm_varcon , only : d_con_g, d_con_w, secspday - use clm_varctl , only : use_lch4, use_fates + use clm_varctl , only : use_lch4 use abortutils , only : endrun use decompMod , only : bounds_type use SoilStatetype , only : soilstate_type @@ -74,6 +74,7 @@ subroutine readParams ( ncid ) ! ! read in constants ! + tString='surface_tension_water' call ncd_io(trim(tString),tempr, 'read', ncid, readvar=readv) if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) @@ -137,7 +138,7 @@ subroutine readParams ( ncid ) end subroutine readParams !----------------------------------------------------------------------- - subroutine SoilBiogeochemNitrifDenitrif(bounds, num_soilc, filter_soilc, & + subroutine SoilBiogeochemNitrifDenitrif(bounds, num_bgc_soilc, filter_bgc_soilc, & soilstate_inst, waterstatebulk_inst, temperature_inst, ch4_inst, & soilbiogeochem_carbonflux_inst, soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst) ! @@ -150,8 +151,8 @@ subroutine SoilBiogeochemNitrifDenitrif(bounds, num_soilc, filter_soilc, & ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns type(soilstate_type) , intent(in) :: soilstate_inst type(waterstatebulk_type) , intent(in) :: waterstatebulk_inst type(temperature_type) , intent(in) :: temperature_inst @@ -168,10 +169,11 @@ subroutine SoilBiogeochemNitrifDenitrif(bounds, num_soilc, filter_soilc, & real(r8) :: mu, sigma real(r8) :: t real(r8) :: pH(bounds%begc:bounds%endc) + real(r8) :: D0 ! temperature dependence of gaseous diffusion coefficients !debug-- put these type structure for outing to hist files real(r8) :: co2diff_con(2) ! diffusion constants for CO2 - real(r8) :: eps - real(r8) :: f_a + real(r8) :: fc_air_frac ! Air-filled fraction of soil volume at field capacity + real(r8) :: fc_air_frac_as_frac_porosity ! fc_air_frac as fraction of total porosity real(r8) :: surface_tension_water ! (J/m^2), Arah and Vinten 1995 real(r8) :: rij_kro_a ! Arah and Vinten 1995 real(r8) :: rij_kro_alpha ! Arah and Vinten 1995 @@ -182,7 +184,7 @@ subroutine SoilBiogeochemNitrifDenitrif(bounds, num_soilc, filter_soilc, & real(r8) :: r_max real(r8) :: r_min(bounds%begc:bounds%endc,1:nlevdecomp) real(r8) :: ratio_diffusivity_water_gas(bounds%begc:bounds%endc,1:nlevdecomp) - real(r8) :: om_frac + real(r8) :: om_frac, diffus_millingtonquirk, diffus_moldrup real(r8) :: anaerobic_frac_sat, r_psi_sat, r_min_sat ! scalar values in sat portion for averaging real(r8) :: organic_max ! organic matter content (kg/m3) where ! soil is assumed to act like peat @@ -232,7 +234,7 @@ subroutine SoilBiogeochemNitrifDenitrif(bounds, num_soilc, filter_soilc, & fmax_denit_carbonsubstrate_vr => soilbiogeochem_nitrogenflux_inst%fmax_denit_carbonsubstrate_vr_col , & ! Output: [real(r8) (:,:) ] fmax_denit_nitrate_vr => soilbiogeochem_nitrogenflux_inst%fmax_denit_nitrate_vr_col , & ! Output: [real(r8) (:,:) ] f_denit_base_vr => soilbiogeochem_nitrogenflux_inst%f_denit_base_vr_col , & ! Output: [real(r8) (:,:) ] - diffus => soilbiogeochem_nitrogenflux_inst%diffus_col , & ! Output: [real(r8) (:,:) ] diffusivity (unitless fraction of total diffusivity) + diffus => soilbiogeochem_nitrogenflux_inst%diffus_col , & ! Output: [real(r8) (:,:) ] diffusivity (m2/s) ratio_k1 => soilbiogeochem_nitrogenflux_inst%ratio_k1_col , & ! Output: [real(r8) (:,:) ] ratio_no3_co2 => soilbiogeochem_nitrogenflux_inst%ratio_no3_co2_col , & ! Output: [real(r8) (:,:) ] soil_co2_prod => soilbiogeochem_nitrogenflux_inst%soil_co2_prod_col , & ! Output: [real(r8) (:,:) ] (ug C / g soil / day) @@ -260,14 +262,17 @@ subroutine SoilBiogeochemNitrifDenitrif(bounds, num_soilc, filter_soilc, & co2diff_con(2) = 0.0009_r8 do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) !---------------- calculate soil anoxia state ! calculate gas diffusivity of soil at field capacity here ! use expression from methane code, but neglect OM for now - f_a = 1._r8 - watfc(c,j) / watsat(c,j) - eps = watsat(c,j)-watfc(c,j) ! Air-filled fraction of total soil volume + fc_air_frac = watsat(c,j)-watfc(c,j) ! theta_a in Riley et al. (2011) + fc_air_frac_as_frac_porosity = 1._r8 - watfc(c,j) / watsat(c,j) + ! This calculation of fc_air_frac_as_frac_porosity is algebraically equivalent to + ! fc_air_frac/watsat(c,j). In that form, it's easier to see its correspondence + ! to theta_a/theta_s in Riley et al. (2011). ! use diffusivity calculation including peat if (use_lch4) then @@ -278,9 +283,20 @@ subroutine SoilBiogeochemNitrifDenitrif(bounds, num_soilc, filter_soilc, & else om_frac = 1._r8 end if - diffus (c,j) = (d_con_g(2,1) + d_con_g(2,2)*t_soisno(c,j)) * 1.e-4_r8 * & - (om_frac * f_a**(10._r8/3._r8) / watsat(c,j)**2 + & - (1._r8-om_frac) * eps**2 * f_a**(3._r8 / bsw(c,j)) ) + + ! Diffusitivity after Moldrup et al. (2003) + ! Eq. 8 in Riley et al. (2011, Biogeosciences) + diffus_moldrup = fc_air_frac**2 * fc_air_frac_as_frac_porosity**(3._r8 / bsw(c,j)) + + ! Diffusivity after Millington & Quirk (1961) + ! Eq. 9 in Riley et al. (2011, Biogeosciences) + diffus_millingtonquirk = fc_air_frac**(10._r8/3._r8) / watsat(c,j)**2 + + ! First, get diffusivity as a unitless constant, which is what's needed to + ! calculate ratio_k1 below. + diffus (c,j) = & + (om_frac * diffus_millingtonquirk + & + (1._r8-om_frac) * diffus_moldrup ) ! calculate anoxic fraction of soils ! use rijtema and kroess model after Riley et al., 2000 @@ -372,10 +388,19 @@ subroutine SoilBiogeochemNitrifDenitrif(bounds, num_soilc, filter_soilc, & ! limit to anoxic fraction of soils pot_f_denit_vr(c,j) = f_denit_base_vr(c,j) * anaerobic_frac(c,j) - ! now calculate the ratio of N2O to N2 from denitrifictaion, following Del Grosso et al., 2000 + ! now calculate the ratio of N2O to N2 from denitrification, following Del Grosso et al., 2000 ! diffusivity constant (figure 6b) ratio_k1(c,j) = max(1.7_r8, 38.4_r8 - 350._r8 * diffus(c,j)) + ! Del Grosso et al. (2000) have diffus (their D_FC, "a relative index of gas diffusivity + ! through soil assuming a water content of field capacity") as unitless, but diffus history + ! field wants m2/s. Here, we use the same theoretical construct as for methane diffusivity + ! to convert to m2/s: We multiply by the temperature-dependent free-air diffusion rate. + ! NOTE that the coefficients for oxygen are used here; it may be more appropriate to use + ! coefficients for the gases being dealt with in this subroutine. + D0 = (d_con_g(2,1) + d_con_g(2,2)*t_soisno(c,j)) * 1.e-4_r8 + diffus(c,j) = diffus(c,j) * D0 + ! ratio function (figure 7c) if ( soil_co2_prod(c,j) > 1.0e-9_r8 ) then ratio_no3_co2(c,j) = smin_no3_massdens_vr(c,j) / soil_co2_prod(c,j) diff --git a/src/soilbiogeochem/SoilBiogeochemNitrogenFluxType.F90 b/src/soilbiogeochem/SoilBiogeochemNitrogenFluxType.F90 index 839f69379a..90213b8123 100644 --- a/src/soilbiogeochem/SoilBiogeochemNitrogenFluxType.F90 +++ b/src/soilbiogeochem/SoilBiogeochemNitrogenFluxType.F90 @@ -7,7 +7,7 @@ module SoilBiogeochemNitrogenFluxType use clm_varpar , only : nlevdecomp_full, nlevdecomp use clm_varcon , only : spval, ispval, dzsoi_decomp use decompMod , only : bounds_type - use clm_varctl , only : use_nitrif_denitrif, use_crop + use clm_varctl , only : use_nitrif_denitrif, use_crop, use_fates use CNSharedParamsMod , only : use_fun use SoilBiogeochemDecompCascadeConType , only : decomp_cascade_con, use_soil_matrixcn use abortutils , only : endrun @@ -127,8 +127,10 @@ module SoilBiogeochemNitrogenFluxType ! all n pools involved in decomposition real(r8), pointer :: decomp_npools_sourcesink_col (:,:,:) ! col (gN/m3) change in decomposing n pools ! (sum of all additions and subtractions from stateupdate1). - real(r8), pointer :: sminn_to_plant_fun_vr_col (:,:) ! col total layer soil N uptake of FUN (gN/m2/s) - + real(r8), pointer :: sminn_to_plant_fun_vr_col (:,:) ! col total layer soil N uptake of FUN (gN/m2/s) + real(r8), pointer :: fates_litter_flux (:) ! (gN/m2/s) A summary of the total litter + ! flux passed in from FATES. + ! This is a diagnostic for balance checks only ! track tradiagonal matrix contains @@ -274,6 +276,12 @@ subroutine InitAllocate(this, bounds) allocate(this%decomp_npools_sourcesink_col (begc:endc,1:nlevdecomp_full,1:ndecomp_pools)) this%decomp_npools_sourcesink_col (:,:,:) = nan + if(use_fates)then + allocate(this%fates_litter_flux(begc:endc)); this%fates_litter_flux(:) = nan + else + allocate(this%fates_litter_flux(0:0)); this%fates_litter_flux(:) = nan + end if + ! Allocate soil Matrix setug if(use_soil_matrixcn)then end if @@ -351,7 +359,7 @@ subroutine InitHistory(this, bounds) 'to '//trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l))) else fieldname = trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))& - //'N_TO_SMINN' + //'_N_TO_SMINN' longname = 'mineral N flux for decomp. of '& //trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l))) endif @@ -364,8 +372,8 @@ subroutine InitHistory(this, bounds) if ( decomp_cascade_con%cascade_receiver_pool(l) /= 0 ) then this%decomp_cascade_ntransfer_col(begc:endc,l) = spval data1dptr => this%decomp_cascade_ntransfer_col(:,l) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))//'N_TO_'//& - trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l)))//'N' + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))//'_N_TO_'//& + trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l)))//'_N' longname = 'decomp. of '//trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_donor_pool(l)))//& ' N to '//trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_receiver_pool(l)))//' N' call hist_addfld1d (fname=fieldname, units='gN/m^2', & @@ -388,7 +396,7 @@ subroutine InitHistory(this, bounds) 'to '//trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l))) else fieldname = trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))& - //'N_TO_SMINN'//trim(vr_suffix) + //'_N_TO_SMINN'//trim(vr_suffix) longname = 'mineral N flux for decomp. of '& //trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l))) endif @@ -401,9 +409,9 @@ subroutine InitHistory(this, bounds) if ( decomp_cascade_con%cascade_receiver_pool(l) /= 0 ) then this%decomp_cascade_ntransfer_vr_col(begc:endc,:,l) = spval data2dptr => this%decomp_cascade_ntransfer_vr_col(:,:,l) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))//'N_TO_'//& + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_donor_pool(l)))//'_N_TO_'//& trim(decomp_cascade_con%decomp_pool_name_history(decomp_cascade_con%cascade_receiver_pool(l)))& - //'N'//trim(vr_suffix) + //'_N'//trim(vr_suffix) longname = 'decomp. of '& //trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_donor_pool(l)))//& ' N to '//trim(decomp_cascade_con%decomp_pool_name_long(decomp_cascade_con%cascade_receiver_pool(l)))//' N' @@ -429,7 +437,7 @@ subroutine InitHistory(this, bounds) if ( .not. decomp_cascade_con%is_cwd(k) ) then this%decomp_npools_leached_col(begc:endc,k) = spval data1dptr => this%decomp_npools_leached_col(:,k) - fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'N_TO_LEACHING' + fieldname = 'M_'//trim(decomp_cascade_con%decomp_pool_name_history(k))//'_N_TO_LEACHING' longname = trim(decomp_cascade_con%decomp_pool_name_long(k))//' N leaching loss' call hist_addfld1d (fname=fieldname, units='gN/m^2/s', & avgflag='A', long_name=longname, & @@ -437,7 +445,7 @@ subroutine InitHistory(this, bounds) this%decomp_npools_transport_tendency_col(begc:endc,:,k) = spval data2dptr => this%decomp_npools_transport_tendency_col(:,:,k) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(k))//'N_TNDNCY_VERT_TRANSPORT' + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(k))//'_N_TNDNCY_VERT_TRANSPORT' longname = trim(decomp_cascade_con%decomp_pool_name_long(k))//' N tendency due to vertical transport' call hist_addfld_decomp (fname=fieldname, units='gN/m^3/s', type2d='levdcmp', & avgflag='A', long_name=longname, & @@ -688,6 +696,8 @@ subroutine InitHistory(this, bounds) end if if (use_nitrif_denitrif) then + ! NOTE that the calculation for diffusivity here uses coefficients for oxygen. + ! It may be more appropriate to use coefficients for N(2)O instead. this%diffus_col(begc:endc,:) = spval call hist_addfld_decomp (fname='diffus', units='m^2/s', type2d='levdcmp', & avgflag='A', long_name='diffusivity', & @@ -1044,7 +1054,7 @@ subroutine SetValues ( this, & end subroutine SetValues !----------------------------------------------------------------------- - subroutine Summary(this, bounds, num_soilc, filter_soilc) + subroutine Summary(this, bounds, num_bgc_soilc, filter_bgc_soilc) ! ! !USES: use clm_varpar , only: nlevdecomp, ndecomp_cascade_transitions,ndecomp_pools @@ -1053,16 +1063,16 @@ subroutine Summary(this, bounds, num_soilc, filter_soilc) ! !ARGUMENTS: class (soilbiogeochem_nitrogenflux_type) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns ! ! !LOCAL VARIABLES: integer :: c,j,k,l ! indices integer :: fc ! filter indices !----------------------------------------------------------------------- - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%denit_col(c) = 0._r8 this%supplement_to_sminn_col(c) = 0._r8 this%som_n_leached_col(c) = 0._r8 @@ -1071,8 +1081,8 @@ subroutine Summary(this, bounds, num_soilc, filter_soilc) ! vertically integrate decomposing N cascade fluxes and soil mineral N fluxes associated with decomposition cascade do k = 1, ndecomp_cascade_transitions do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%decomp_cascade_ntransfer_col(c,k) = & this%decomp_cascade_ntransfer_col(c,k) + & @@ -1090,8 +1100,8 @@ subroutine Summary(this, bounds, num_soilc, filter_soilc) ! vertically integrate each denitrification flux do l = 1, ndecomp_cascade_transitions do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%sminn_to_denit_decomp_cascade_col(c,l) = & this%sminn_to_denit_decomp_cascade_col(c,l) + & this%sminn_to_denit_decomp_cascade_vr_col(c,j,l) * dzsoi_decomp(j) @@ -1101,8 +1111,8 @@ subroutine Summary(this, bounds, num_soilc, filter_soilc) ! vertically integrate bulk denitrification and leaching flux do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%sminn_to_denit_excess_col(c) = & this%sminn_to_denit_excess_col(c) + & this%sminn_to_denit_excess_vr_col(c,j) * dzsoi_decomp(j) @@ -1115,16 +1125,16 @@ subroutine Summary(this, bounds, num_soilc, filter_soilc) ! total N denitrification (DENIT) do l = 1, ndecomp_cascade_transitions - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%denit_col(c) = & this%denit_col(c) + & this%sminn_to_denit_decomp_cascade_col(c,l) end do end do - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%denit_col(c) = & this%denit_col(c) + & this%sminn_to_denit_excess_col(c) @@ -1134,8 +1144,8 @@ subroutine Summary(this, bounds, num_soilc, filter_soilc) ! vertically integrate NO3 NH4 N2O fluxes and pools do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! nitrification and denitrification fluxes this%f_nit_col(c) = & @@ -1174,8 +1184,8 @@ subroutine Summary(this, bounds, num_soilc, filter_soilc) end do end do - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%denit_col(c) = this%f_denit_col(c) end do @@ -1183,8 +1193,8 @@ subroutine Summary(this, bounds, num_soilc, filter_soilc) ! supplementary N supplement_to_sminn do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%supplement_to_sminn_col(c) = & this%supplement_to_sminn_col(c) + & this%supplement_to_sminn_vr_col(c,j) * dzsoi_decomp(j) @@ -1193,22 +1203,22 @@ subroutine Summary(this, bounds, num_soilc, filter_soilc) ! add up all vertical transport tendency terms and calculate total som leaching loss as the sum of these do l = 1, ndecomp_pools - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%decomp_npools_leached_col(c,l) = 0._r8 end do do j = 1, nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%decomp_npools_leached_col(c,l) = & this%decomp_npools_leached_col(c,l) + & this%decomp_npools_transport_tendency_col(c,j,l) * dzsoi_decomp(j) end do end do - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) this%som_n_leached_col(c) = & this%som_n_leached_col(c) + & this%decomp_npools_leached_col(c,l) diff --git a/src/soilbiogeochem/SoilBiogeochemNitrogenStateType.F90 b/src/soilbiogeochem/SoilBiogeochemNitrogenStateType.F90 index 3e54e52436..ee889503d0 100644 --- a/src/soilbiogeochem/SoilBiogeochemNitrogenStateType.F90 +++ b/src/soilbiogeochem/SoilBiogeochemNitrogenStateType.F90 @@ -11,7 +11,7 @@ module SoilBiogeochemNitrogenStateType use clm_varpar , only : ndecomp_cascade_transitions, ndecomp_pools, nlevcan use clm_varpar , only : nlevdecomp_full, nlevdecomp, nlevsoi use clm_varcon , only : spval, dzsoi_decomp, zisoi - use clm_varctl , only : use_nitrif_denitrif + use clm_varctl , only : use_nitrif_denitrif, use_fates_bgc use SoilBiogeochemDecompCascadeConType , only : mimics_decomp, century_decomp, decomp_method, use_soil_matrixcn use clm_varctl , only : iulog, override_bgc_restart_mismatch_dump, spinup_state use landunit_varcon , only : istcrop, istsoil @@ -21,6 +21,7 @@ module SoilBiogeochemNitrogenStateType use GridcellType , only : grc use SoilBiogeochemStateType , only : get_spinup_latitude_term use SparseMatrixMultiplyMod , only : sparse_matrix_type, vector_type + use CNVegNitrogenStateType , only : cnveg_nitrogenstate_type ! ! !PUBLIC TYPES: implicit none @@ -59,7 +60,11 @@ module SoilBiogeochemNitrogenStateType real(r8), pointer :: dyn_no3bal_adjustments_col (:) ! (gN/m2) NO3 adjustments to each column made in this timestep via dynamic column area adjustments (only makes sense at the column-level: meaningless if averaged to the gridcell-level) real(r8), pointer :: dyn_nh4bal_adjustments_col (:) ! (gN/m2) NH4 adjustments to each column made in this timestep via dynamic column adjustments (only makes sense at the column-level: meaningless if averaged to the gridcell-level) real(r8) :: totvegcthresh ! threshold for total vegetation carbon to zero out decomposition pools - + + real(r8), pointer :: totn_col (:) ! (gN/m2) total column nitrogen, incl veg + real(r8), pointer :: totecosysn_col (:) ! (gN/m2) total ecosystem nitrogen, incl veg + real(r8), pointer :: totn_grc (:) ! (gN/m2) total gridcell nitrogen + ! Matrix-cn contains @@ -144,6 +149,10 @@ subroutine InitAllocate(this, bounds) allocate(this%decomp_soiln_vr_col(begc:endc,1:nlevdecomp_full)) this%decomp_soiln_vr_col(:,:)= nan + allocate(this%totn_col (begc:endc)) ; this%totn_col (:) = nan + allocate(this%totecosysn_col (begc:endc)) ; this%totecosysn_col (:) = nan + allocate(this%totn_grc (bounds%begg:bounds%endg)) ; this%totn_grc (:) = nan + end subroutine InitAllocate !------------------------------------------------------------------------ @@ -194,7 +203,7 @@ subroutine InitHistory(this, bounds) do l = 1, ndecomp_pools if ( nlevdecomp_full > 1 ) then data2dptr => this%decomp_npools_vr_col(:,:,l) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'N_vr' + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'_N_vr' longname = trim(decomp_cascade_con%decomp_pool_name_history(l))//' N (vertically resolved)' call hist_addfld2d (fname=fieldname, units='gN/m^3', type2d='levdcmp', & avgflag='A', long_name=longname, & @@ -204,7 +213,7 @@ subroutine InitHistory(this, bounds) endif data1dptr => this%decomp_npools_col(:,l) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'N' + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'_N' longname = trim(decomp_cascade_con%decomp_pool_name_history(l))//' N' call hist_addfld1d (fname=fieldname, units='gN/m^2', & avgflag='A', long_name=longname, & @@ -216,7 +225,7 @@ subroutine InitHistory(this, bounds) if ( nlevdecomp_full > 1 ) then data1dptr => this%decomp_npools_1m_col(:,l) - fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'N_1m' + fieldname = trim(decomp_cascade_con%decomp_pool_name_history(l))//'_N_1m' longname = trim(decomp_cascade_con%decomp_pool_name_history(l))//' N to 1 meter' call hist_addfld1d (fname=fieldname, units='gN/m^2', & avgflag='A', long_name=longname, & @@ -329,6 +338,17 @@ subroutine InitHistory(this, bounds) &only makes sense at the column level: should not be averaged to gridcell', & ptr_col=this%dyn_nh4bal_adjustments_col, default='inactive') end if + + this%totecosysn_col(begc:endc) = spval + call hist_addfld1d (fname='TOTECOSYSN', units='gN/m^2', & + avgflag='A', long_name='total ecosystem N, excluding product pools', & + ptr_col=this%totecosysn_col) + + this%totn_col(begc:endc) = spval + call hist_addfld1d (fname='TOTCOLN', units='gN/m^2', & + avgflag='A', long_name='total column-level N, excluding product pools', & + ptr_col=this%totn_col) + end subroutine InitHistory !----------------------------------------------------------------------- @@ -434,6 +454,21 @@ subroutine InitCold(this, bounds, & end if end do + do c = bounds%begc, bounds%endc + l = col%landunit(c) + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + ! total nitrogen pools + this%totecosysn_col(c) = 0._r8 + this%totn_col(c) = 0._r8 + end if + end do + + + do g = bounds%begg, bounds%endg + this%totn_grc(g) = 0._r8 + end do + + call this%SetValues (num_column=num_special_col, filter_column=special_col, value_column=0._r8) end subroutine InitCold @@ -756,6 +791,12 @@ subroutine SetValues ( this, num_column, filter_column, value_column ) end do end do + do fi = 1,num_column + i = filter_column(fi) + this%totecosysn_col(i) = value_column + this%totn_col(i) = value_column + end do + ! Set values for the matrix solution if(use_soil_matrixcn)then end if @@ -763,18 +804,29 @@ subroutine SetValues ( this, num_column, filter_column, value_column ) end subroutine SetValues !----------------------------------------------------------------------- - subroutine Summary(this, bounds, num_allc, filter_allc) + + subroutine Summary(this, bounds, num_allc, filter_allc, num_bgc_soilc, filter_bgc_soilc, cnveg_nitrogenstate_inst) + ! ! !ARGUMENTS: class (soilbiogeochem_nitrogenstate_type) :: this type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_allc ! number of columns in allc filter - integer , intent(in) :: filter_allc(:) ! filter for all active columns + integer , intent(in) :: num_allc ! number of bgc columns in soilc filter + integer , intent(in) :: filter_allc(:) ! filter for bgc columns + integer , intent(in) :: num_bgc_soilc ! number of bgc columns in soilc filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for bgc columns + type(cnveg_nitrogenstate_type) , intent(inout) :: cnveg_nitrogenstate_inst + ! ! !LOCAL VARIABLES: integer :: c,j,k,l ! indices integer :: fc ! lake filter indices + integer :: num_local ! we do summary on different set when fates is + ! active becuase the CN variables aren't allocated + ! this preserves B4B real(r8) :: maxdepth ! depth to integrate soil variables + real(r8) :: totvegn_col ! local total ecosys veg N, allows 0 for fates + real(r8) :: ecovegn_col ! local total veg N, allows 0 for fates !----------------------------------------------------------------------- ! vertically integrate NO3 NH4 N2O pools @@ -952,21 +1004,6 @@ subroutine Summary(this, bounds, num_allc, filter_allc) end if end do - ! total cwdn - do fc = 1,num_allc - c = filter_allc(fc) - this%cwdn_col(c) = 0._r8 - end do - do l = 1, ndecomp_pools - if ( decomp_cascade_con%is_cwd(l) ) then - do fc = 1,num_allc - c = filter_allc(fc) - this%cwdn_col(c) = this%cwdn_col(c) + & - this%decomp_npools_col(c,l) - end do - end if - end do - ! total sminn do fc = 1,num_allc c = filter_allc(fc) @@ -993,6 +1030,61 @@ subroutine Summary(this, bounds, num_allc, filter_allc) end do end do + ! total cwdn + do fc = 1,num_allc + c = filter_allc(fc) + this%cwdn_col(c) = 0._r8 + end do + + if(use_fates_bgc)then + num_local = num_bgc_soilc + else + num_local = num_allc + end if + + do fc = 1,num_local + if(use_fates_bgc) then + c = filter_bgc_soilc(fc) + else + c = filter_allc(fc) + end if + + if(col%is_fates(c)) then + totvegn_col = 0._r8 + ecovegn_col = 0._r8 + else + do l = 1, ndecomp_pools + if ( decomp_cascade_con%is_cwd(l) ) then + this%cwdn_col(c) = this%cwdn_col(c) + & + this%decomp_npools_col(c,l) + end if + end do + totvegn_col = cnveg_nitrogenstate_inst%totn_p2c_col(c) + ecovegn_col = cnveg_nitrogenstate_inst%totvegn_col(c) + end if + + ! total ecosystem nitrogen, including veg (TOTECOSYSN) + this%totecosysn_col(c) = & + this%cwdn_col(c) + & + this%totlitn_col(c) + & + this%totmicn_col(c) + & + this%totsomn_col(c) + & + this%sminn_col(c) + & + ecovegn_col + + ! total column nitrogen, including patch (TOTCOLN) + + this%totn_col(c) = & + this%cwdn_col(c) + & + this%totlitn_col(c) + & + this%totmicn_col(c) + & + this%totsomn_col(c) + & + this%sminn_col(c) + & + this%ntrunc_col(c) + & + totvegn_col + + end do + end subroutine Summary !----------------------------------------------------------------------- diff --git a/src/soilbiogeochem/SoilBiogeochemNitrogenUptakeMod.F90 b/src/soilbiogeochem/SoilBiogeochemNitrogenUptakeMod.F90 index 40c6a0bff9..a566a5882a 100644 --- a/src/soilbiogeochem/SoilBiogeochemNitrogenUptakeMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemNitrogenUptakeMod.F90 @@ -24,7 +24,7 @@ module SoilBiogeochemNitrogenUptakeMod contains !----------------------------------------------------------------------- - subroutine SoilBiogeochemNitrogenUptake(bounds, nlevdecomp, num_soilc, filter_soilc, & + subroutine SoilBiogeochemNitrogenUptake(bounds, nlevdecomp, num_bgc_soilc, filter_bgc_soilc, & sminn_vr, dzsoi_decomp, nfixation_prof, nuptake_prof) ! ! DESCRIPTION @@ -33,8 +33,8 @@ subroutine SoilBiogeochemNitrogenUptake(bounds, nlevdecomp, num_soilc, filter_so ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds integer , intent(in) :: nlevdecomp ! number of vertical layers - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns real(r8) , intent(in) :: sminn_vr(bounds%begc: , 1: ) ! soil mineral nitrogen profile real(r8) , intent(in) :: dzsoi_decomp(1: ) ! layer thickness real(r8) , intent(in) :: nfixation_prof(bounds%begc: , 1: ) ! nitrogen fixation profile @@ -51,21 +51,21 @@ subroutine SoilBiogeochemNitrogenUptake(bounds, nlevdecomp, num_soilc, filter_so SHR_ASSERT_ALL_FL((ubound(nuptake_prof) == (/bounds%endc, nlevdecomp/)) , sourcefile, __LINE__) ! init sminn_tot - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_tot(c) = 0. end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) sminn_tot(c) = sminn_tot(c) + sminn_vr(c,j) * dzsoi_decomp(j) end do end do do j = 1, nlevdecomp - do fc=1,num_soilc - c = filter_soilc(fc) + do fc=1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (sminn_tot(c) > 0.) then nuptake_prof(c,j) = sminn_vr(c,j) / sminn_tot(c) else diff --git a/src/soilbiogeochem/SoilBiogeochemPotentialMod.F90 b/src/soilbiogeochem/SoilBiogeochemPotentialMod.F90 index da46e178b7..deb9bdbf78 100644 --- a/src/soilbiogeochem/SoilBiogeochemPotentialMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemPotentialMod.F90 @@ -68,7 +68,7 @@ subroutine readParams ( ncid ) end subroutine readParams !----------------------------------------------------------------------- - subroutine SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & + subroutine SoilBiogeochemPotential (bounds, num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_state_inst, soilbiogeochem_carbonstate_inst, soilbiogeochem_carbonflux_inst, & soilbiogeochem_nitrogenstate_inst, soilbiogeochem_nitrogenflux_inst, & cn_decomp_pools, p_decomp_cpool_loss, p_decomp_cn_gain, & @@ -80,8 +80,8 @@ subroutine SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & ! ! !ARGUMENT: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns type(soilbiogeochem_state_type) , intent(inout) :: soilbiogeochem_state_inst type(soilbiogeochem_carbonstate_type) , intent(in) :: soilbiogeochem_carbonstate_inst type(soilbiogeochem_carbonflux_type) , intent(inout) :: soilbiogeochem_carbonflux_inst @@ -137,8 +137,8 @@ subroutine SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & decomp_k => soilbiogeochem_carbonflux_inst%decomp_k_col , & ! Input: [real(r8) (:,:,:) ] decomposition rate coefficient (1./sec) phr_vr => soilbiogeochem_carbonflux_inst%phr_vr_col & ! Output: [real(r8) (:,:) ] potential HR (gC/m3/s) ) - - if ( .not. use_fates ) then + + ! set initial values for potential C and N fluxes p_decomp_cpool_loss(begc:endc, :, :) = 0._r8 pmnf_decomp_cascade(begc:endc, :, :) = 0._r8 @@ -149,8 +149,8 @@ subroutine SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & do l = 1, ndecomp_pools if ( floating_cn_ratio_decomp_pools(l) ) then do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if ( decomp_npools_vr(c,j,l) > 0._r8 ) then cn_decomp_pools(c,j,l) = decomp_cpools_vr(c,j,l) / decomp_npools_vr(c,j,l) end if @@ -158,8 +158,8 @@ subroutine SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & end do else do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) cn_decomp_pools(c,j,l) = initial_cn_ratio(l) end do end do @@ -173,8 +173,8 @@ subroutine SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & do k = 1, ndecomp_cascade_transitions do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (decomp_cpools_vr(c,j,cascade_donor_pool(k)) > 0._r8 .and. & decomp_k(c,j,cascade_donor_pool(k)) > 0._r8 ) then @@ -236,8 +236,8 @@ subroutine SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & ! transitions loop). if (decomp_method == mimics_decomp) then do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ! Sum C & N fluxes from all transitions into m1 & m2 pools. ! Had to form a new loop for the summation due to the order ! necessary, ie do k as the innermost loop. @@ -293,15 +293,15 @@ subroutine SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & ! Sum up all the potential immobilization fluxes (positive pmnf flux) ! and all the mineralization fluxes (negative pmnf flux) do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) immob(c,j) = 0._r8 end do end do do k = 1, ndecomp_cascade_transitions do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) if (pmnf_decomp_cascade(c,j,k) > 0._r8) then immob(c,j) = immob(c,j) + pmnf_decomp_cascade(c,j,k) else @@ -315,42 +315,29 @@ subroutine SoilBiogeochemPotential (bounds, num_soilc, filter_soilc, & end do do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) potential_immob_vr(c,j) = immob(c,j) end do end do - else ! use_fates - ! As a first step we are making this a C-only model, so no N downregulation of fluxes. - do k = 1, ndecomp_cascade_transitions - do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) - ! - p_decomp_cpool_loss(c,j,k) = decomp_cpools_vr(c,j,cascade_donor_pool(k)) & - * decomp_k(c,j,cascade_donor_pool(k)) * pathfrac_decomp_cascade(c,j,k) - ! - end do - end do - end do - end if ! Add up potential hr for methane calculations do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) phr_vr(c,j) = 0._r8 end do end do do k = 1, ndecomp_cascade_transitions do j = 1,nlevdecomp - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) phr_vr(c,j) = phr_vr(c,j) + rf_decomp_cascade(c,j,k) * p_decomp_cpool_loss(c,j,k) end do end do end do + end associate end subroutine SoilBiogeochemPotential diff --git a/src/soilbiogeochem/SoilBiogeochemPrecisionControlMod.F90 b/src/soilbiogeochem/SoilBiogeochemPrecisionControlMod.F90 index 3740700ab1..cc349ad8bc 100644 --- a/src/soilbiogeochem/SoilBiogeochemPrecisionControlMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemPrecisionControlMod.F90 @@ -60,7 +60,7 @@ subroutine SoilBiogeochemPrecisionControlInit( soilbiogeochem_carbonstate_inst, end subroutine SoilBiogeochemPrecisionControlInit !----------------------------------------------------------------------- - subroutine SoilBiogeochemPrecisionControl(num_soilc, filter_soilc, & + subroutine SoilBiogeochemPrecisionControl(num_bgc_soilc, filter_bgc_soilc, & soilbiogeochem_carbonstate_inst, c13_soilbiogeochem_carbonstate_inst, & c14_soilbiogeochem_carbonstate_inst, soilbiogeochem_nitrogenstate_inst) @@ -70,13 +70,13 @@ subroutine SoilBiogeochemPrecisionControl(num_soilc, filter_soilc, & ! they get too small. ! ! !USES: - use clm_varctl , only : iulog, use_c13, use_c14, use_nitrif_denitrif, use_cn + use clm_varctl , only : iulog, use_c13, use_c14, use_nitrif_denitrif use clm_varpar , only : nlevdecomp use CNSharedParamsMod, only: use_fun ! ! !ARGUMENTS: - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_soilc ! number of bgc soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for bgc soil columns type(soilbiogeochem_carbonstate_type) , intent(inout) :: soilbiogeochem_carbonstate_inst type(soilbiogeochem_carbonstate_type) , intent(inout) :: c13_soilbiogeochem_carbonstate_inst type(soilbiogeochem_carbonstate_type) , intent(inout) :: c14_soilbiogeochem_carbonstate_inst @@ -106,8 +106,8 @@ subroutine SoilBiogeochemPrecisionControl(num_soilc, filter_soilc, & ) ! column loop - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) do j = 1,nlevdecomp ! initialize the column-level C and N truncation terms @@ -125,13 +125,12 @@ subroutine SoilBiogeochemPrecisionControl(num_soilc, filter_soilc, & do k = 1, ndecomp_pools if (abs(cs%decomp_cpools_vr_col(c,j,k)) < ccrit) then + cc = cc + cs%decomp_cpools_vr_col(c,j,k) cs%decomp_cpools_vr_col(c,j,k) = 0._r8 - if (use_cn) then - cn = cn + ns%decomp_npools_vr_col(c,j,k) - ns%decomp_npools_vr_col(c,j,k) = 0._r8 - endif + cn = cn + ns%decomp_npools_vr_col(c,j,k) + ns%decomp_npools_vr_col(c,j,k) = 0._r8 if ( use_c13 ) then cc13 = cc13 + c13cs%decomp_cpools_vr_col(c,j,k) @@ -150,9 +149,8 @@ subroutine SoilBiogeochemPrecisionControl(num_soilc, filter_soilc, & cs%ctrunc_vr_col(c,j) = cs%ctrunc_vr_col(c,j) + cc - if (use_cn) then - ns%ntrunc_vr_col(c,j) = ns%ntrunc_vr_col(c,j) + cn - endif + ns%ntrunc_vr_col(c,j) = ns%ntrunc_vr_col(c,j) + cn + if ( use_c13 ) then c13cs%ctrunc_vr_col(c,j) = c13cs%ctrunc_vr_col(c,j) + cc13 endif @@ -167,8 +165,8 @@ subroutine SoilBiogeochemPrecisionControl(num_soilc, filter_soilc, & if (use_nitrif_denitrif) then ! remove small negative perturbations for stability purposes, if any should arise. - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) do j = 1,nlevdecomp if (abs(ns%smin_no3_vr_col(c,j)) < ncrit/1e4_r8) then if ( ns%smin_no3_vr_col(c,j) < 0._r8 ) then diff --git a/src/soilbiogeochem/SoilBiogeochemStateType.F90 b/src/soilbiogeochem/SoilBiogeochemStateType.F90 index fcdced386d..69055aec9b 100644 --- a/src/soilbiogeochem/SoilBiogeochemStateType.F90 +++ b/src/soilbiogeochem/SoilBiogeochemStateType.F90 @@ -11,7 +11,7 @@ module SoilBiogeochemStateType use clm_varcon , only : spval, ispval, c14ratio, grlnd use landunit_varcon, only : istsoil, istcrop use clm_varpar , only : nlevsno, nlevgrnd, nlevlak - use clm_varctl , only : use_cn + use clm_varctl , only : use_cn, use_fates_bgc use clm_varctl , only : iulog use LandunitType , only : lun use ColumnType , only : col @@ -60,7 +60,7 @@ subroutine Init(this, bounds) type(bounds_type), intent(in) :: bounds call this%InitAllocate ( bounds ) - if (use_cn) then + if (use_cn .or. use_fates_bgc) then call this%InitHistory ( bounds ) end if call this%InitCold ( bounds ) @@ -103,7 +103,7 @@ subroutine InitAllocate(this, bounds) allocate(this%nue_decomp_cascade_col(1:ndecomp_cascade_transitions)); this%nue_decomp_cascade_col(:) = nan - + end subroutine InitAllocate !------------------------------------------------------------------------ @@ -132,26 +132,30 @@ subroutine InitHistory(this, bounds) begp = bounds%begp; endp= bounds%endp begc = bounds%begc; endc= bounds%endc - this%croot_prof_patch(begp:endp,:) = spval - call hist_addfld_decomp (fname='CROOT_PROF', units='1/m', type2d='levdcmp', & - avgflag='A', long_name='profile for litter C and N inputs from coarse roots', & - ptr_patch=this%croot_prof_patch, default='inactive') - - this%froot_prof_patch(begp:endp,:) = spval - call hist_addfld_decomp (fname='FROOT_PROF', units='1/m', type2d='levdcmp', & - avgflag='A', long_name='profile for litter C and N inputs from fine roots', & - ptr_patch=this%froot_prof_patch, default='inactive') - - this%leaf_prof_patch(begp:endp,:) = spval - call hist_addfld_decomp (fname='LEAF_PROF', units='1/m', type2d='levdcmp', & - avgflag='A', long_name='profile for litter C and N inputs from leaves', & - ptr_patch=this%leaf_prof_patch, default='inactive') - - this%stem_prof_patch(begp:endp,:) = spval - call hist_addfld_decomp (fname='STEM_PROF', units='1/m', type2d='levdcmp', & - avgflag='A', long_name='profile for litter C and N inputs from stems', & - ptr_patch=this%stem_prof_patch, default='inactive') - + if_usecn: if(use_cn) then + this%croot_prof_patch(begp:endp,:) = spval + call hist_addfld_decomp (fname='CROOT_PROF', units='1/m', type2d='levdcmp', & + avgflag='A', long_name='profile for litter C and N inputs from coarse roots', & + ptr_patch=this%croot_prof_patch, default='inactive') + + this%froot_prof_patch(begp:endp,:) = spval + call hist_addfld_decomp (fname='FROOT_PROF', units='1/m', type2d='levdcmp', & + avgflag='A', long_name='profile for litter C and N inputs from fine roots', & + ptr_patch=this%froot_prof_patch, default='inactive') + + this%leaf_prof_patch(begp:endp,:) = spval + call hist_addfld_decomp (fname='LEAF_PROF', units='1/m', type2d='levdcmp', & + avgflag='A', long_name='profile for litter C and N inputs from leaves', & + ptr_patch=this%leaf_prof_patch, default='inactive') + + this%stem_prof_patch(begp:endp,:) = spval + call hist_addfld_decomp (fname='STEM_PROF', units='1/m', type2d='levdcmp', & + avgflag='A', long_name='profile for litter C and N inputs from stems', & + ptr_patch=this%stem_prof_patch, default='inactive') + end if if_usecn + + ! These output variables are valid for both use_cn AND use_fates_bgc + this%nfixation_prof_col(begc:endc,:) = spval call hist_addfld_decomp (fname='NFIXATION_PROF', units='1/m', type2d='levdcmp', & avgflag='A', long_name='profile for biological N fixation', & @@ -161,7 +165,7 @@ subroutine InitHistory(this, bounds) call hist_addfld_decomp (fname='NDEP_PROF', units='1/m', type2d='levdcmp', & avgflag='A', long_name='profile for atmospheric N deposition', & ptr_col=this%ndep_prof_col, default='inactive') - + this%som_adv_coef_col(begc:endc,:) = spval call hist_addfld_decomp (fname='SOM_ADV_COEF', units='m/s', type2d='levdcmp', & avgflag='A', long_name='advection term for vertical SOM translocation', & diff --git a/src/soilbiogeochem/SoilBiogeochemVerticalProfileMod.F90 b/src/soilbiogeochem/SoilBiogeochemVerticalProfileMod.F90 index 7209bd8278..376f18742b 100644 --- a/src/soilbiogeochem/SoilBiogeochemVerticalProfileMod.F90 +++ b/src/soilbiogeochem/SoilBiogeochemVerticalProfileMod.F90 @@ -24,7 +24,7 @@ module SoilBiogeochemVerticalProfileMod contains !----------------------------------------------------------------------- - subroutine SoilBiogeochemVerticalProfile(bounds, num_soilc,filter_soilc,num_soilp,filter_soilp, & + subroutine SoilBiogeochemVerticalProfile(bounds, num_bgc_soilc,filter_bgc_soilc,num_bgc_vegp,filter_bgc_vegp, & active_layer_inst, soilstate_inst, soilbiogeochem_state_inst) ! ! !DESCRIPTION: @@ -57,10 +57,10 @@ subroutine SoilBiogeochemVerticalProfile(bounds, num_soilc,filter_soilc,num_soil ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_soilc ! number of soil columns in filter - integer , intent(in) :: filter_soilc(:) ! filter for soil columns - integer , intent(in) :: num_soilp ! number of soil patches in filter - integer , intent(in) :: filter_soilp(:) ! filter for soil patches + integer , intent(in) :: num_bgc_soilc ! number of soil columns in filter + integer , intent(in) :: filter_bgc_soilc(:) ! filter for soil columns + integer , intent(in) :: num_bgc_vegp ! number of soil patches in filter + integer , intent(in) :: filter_bgc_vegp(:) ! filter for soil patches type(active_layer_type) , intent(in) :: active_layer_inst type(soilstate_type) , intent(in) :: soilstate_inst type(soilbiogeochem_state_type) , intent(inout) :: soilbiogeochem_state_inst @@ -71,7 +71,7 @@ subroutine SoilBiogeochemVerticalProfile(bounds, num_soilc,filter_soilc,num_soil real(r8) :: rootfr_tot real(r8) :: cinput_rootfr(bounds%begp:bounds%endp, 1:nlevdecomp_full) ! pft-native root fraction used for calculating inputs real(r8) :: col_cinput_rootfr(bounds%begc:bounds%endc, 1:nlevdecomp_full) ! col-native root fraction used for calculating inputs - integer :: c, j, fc, p, fp, pi + integer :: c, j, fc, p, fp integer :: alt_ind ! debugging temp variables real(r8) :: froot_prof_sum @@ -124,21 +124,20 @@ subroutine SoilBiogeochemVerticalProfile(bounds, num_soilc,filter_soilc,num_soil cinput_rootfr(begp:endp, :) = 0._r8 col_cinput_rootfr(begc:endc, :) = 0._r8 - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) c = patch%column(p) if (patch%itype(p) /= noveg) then do j = 1, nlevdecomp cinput_rootfr(p,j) = crootfr(p,j) / dzsoi_decomp(j) end do - else cinput_rootfr(p,1) = 0. endif end do - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) c = patch%column(p) ! integrate rootfr over active layer of soil column rootfr_tot = 0._r8 @@ -168,7 +167,6 @@ subroutine SoilBiogeochemVerticalProfile(bounds, num_soilc,filter_soilc,num_soil leaf_prof(p,1) = 1./dzsoi_decomp(1) stem_prof(p,1) = 1./dzsoi_decomp(1) endif - end do !! aggregate root profile to column @@ -176,42 +174,57 @@ subroutine SoilBiogeochemVerticalProfile(bounds, num_soilc,filter_soilc,num_soil ! cinput_rootfr(bounds%begp:bounds%endp, :), & ! col_cinput_rootfr(bounds%begc:bounds%endc, :), & ! 'unity') - do pi = 1,maxsoil_patches - do fc = 1,num_soilc - c = filter_soilc(fc) - if (pi <= col%npatches(c)) then - p = col%patchi(c) + pi - 1 - do j = 1,nlevdecomp - col_cinput_rootfr(c,j) = col_cinput_rootfr(c,j) + cinput_rootfr(p,j) * patch%wtcol(p) - end do - end if + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) + c = patch%column(p) + do j = 1,nlevdecomp + col_cinput_rootfr(c,j) = col_cinput_rootfr(c,j) + cinput_rootfr(p,j) * patch%wtcol(p) end do end do + ! repeat for column-native profiles: Ndep and Nfix - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) rootfr_tot = 0._r8 surface_prof_tot = 0._r8 - ! redo column ntegration over active layer for column-native profiles - do j = 1, min(max(altmax_lastyear_indx(c), 1), nlevdecomp) - rootfr_tot = rootfr_tot + col_cinput_rootfr(c,j) * dzsoi_decomp(j) - surface_prof_tot = surface_prof_tot + surface_prof(j) * dzsoi_decomp(j) - end do - if ( (altmax_lastyear_indx(c) > 0) .and. (rootfr_tot > 0._r8) .and. (surface_prof_tot > 0._r8) ) then - do j = 1, min(max(altmax_lastyear_indx(c), 1), nlevdecomp) - nfixation_prof(c,j) = col_cinput_rootfr(c,j) / rootfr_tot - ndep_prof(c,j) = surface_prof(j)/ surface_prof_tot + if_fates: if(col%is_fates(c))then + ! For FATES, we just use the e-folding depth for both fixation and deposition + ! partially because the fixation may be free-living depending on FATES-side + ! fixation choices, and partially for simplicity + do j = 1, min(max(altmax_lastyear_indx(c), 1), nlevdecomp) + surface_prof_tot = surface_prof_tot + surface_prof(j) * dzsoi_decomp(j) end do + if ( (altmax_lastyear_indx(c) > 0) .and. (surface_prof_tot > 0._r8) ) then + do j = 1, min(max(altmax_lastyear_indx(c), 1), nlevdecomp) + nfixation_prof(c,j) = surface_prof(j)/ surface_prof_tot + ndep_prof(c,j) = surface_prof(j)/ surface_prof_tot + end do + else + nfixation_prof(c,1) = 1./dzsoi_decomp(1) + ndep_prof(c,1) = 1./dzsoi_decomp(1) + endif else - nfixation_prof(c,1) = 1./dzsoi_decomp(1) - ndep_prof(c,1) = 1./dzsoi_decomp(1) - endif + ! redo column ntegration over active layer for column-native profiles + do j = 1, min(max(altmax_lastyear_indx(c), 1), nlevdecomp) + rootfr_tot = rootfr_tot + col_cinput_rootfr(c,j) * dzsoi_decomp(j) + surface_prof_tot = surface_prof_tot + surface_prof(j) * dzsoi_decomp(j) + end do + if ( (altmax_lastyear_indx(c) > 0) .and. (rootfr_tot > 0._r8) .and. (surface_prof_tot > 0._r8) ) then + do j = 1, min(max(altmax_lastyear_indx(c), 1), nlevdecomp) + nfixation_prof(c,j) = col_cinput_rootfr(c,j) / rootfr_tot + ndep_prof(c,j) = surface_prof(j)/ surface_prof_tot + end do + else + nfixation_prof(c,1) = 1./dzsoi_decomp(1) + ndep_prof(c,1) = 1./dzsoi_decomp(1) + endif + end if if_fates end do ! check to make sure integral of all profiles = 1. - do fc = 1,num_soilc - c = filter_soilc(fc) + do fc = 1,num_bgc_soilc + c = filter_bgc_soilc(fc) ndep_prof_sum = 0. nfixation_prof_sum = 0. do j = 1, nlevdecomp @@ -237,8 +250,8 @@ subroutine SoilBiogeochemVerticalProfile(bounds, num_soilc,filter_soilc,num_soil endif end do - do fp = 1,num_soilp - p = filter_soilp(fp) + do fp = 1,num_bgc_vegp + p = filter_bgc_vegp(fp) froot_prof_sum = 0. croot_prof_sum = 0. leaf_prof_sum = 0. diff --git a/src/soilbiogeochem/TillageMod.F90 b/src/soilbiogeochem/TillageMod.F90 new file mode 100644 index 0000000000..4a24daf4c2 --- /dev/null +++ b/src/soilbiogeochem/TillageMod.F90 @@ -0,0 +1,341 @@ +module TillageMod + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! Module for soil tillage. + ! + ! !USES: + use shr_kind_mod , only : r8 => shr_kind_r8, CS => shr_kind_CS + use shr_log_mod , only : errMsg => shr_log_errMsg + use abortutils , only : endrun + use clm_varctl , only : iulog + use clm_varpar , only : ndecomp_pools + use ColumnType , only : col + use PatchType , only : patch + ! + implicit none + private + ! !PUBLIC MEMBER PROCEDURES + public :: readParams + public :: get_do_tillage + public :: get_apply_tillage_multipliers + public :: get_fraction_tilled + ! !PUBLIC DATA MEMBERS + character(len=CS), public :: tillage_mode ! off, low, high + ! + ! !PRIVATE DATA MEMBERS + integer :: tillage_intensity + integer, parameter :: tillage_off = 0 + integer, parameter :: tillage_low = 1 + integer, parameter :: tillage_high = 2 + logical :: use_original_tillage_phases ! Use buggy tillage phase determination? + real(r8), pointer :: tillage_mults_allphases(:,:) ! (ndecomp_pools, ntill_stages_max) + integer, parameter :: ntill_stages_max = 3 ! How many different tillage phases are there? (Not including all-1 phases.) + integer, parameter :: ntill_intensities_max = 2 ! How many different tillage intensities are allowed (other than "off")? + real(r8) :: max_tillage_depth ! Maximum depth to till (m) + +!============================================================================== +contains +!============================================================================== + + subroutine readParams_namelist(NLFilename) + ! + ! Read namelist parameters related to tillage. + ! + ! !USES: + use spmdMod , only : masterproc, mpicom + use clm_nlUtilsMod , only : find_nlgroup_name + use shr_mpi_mod , only : shr_mpi_bcast + ! !ARGUMENTS: + character(len=*), intent(in) :: NLFilename ! Namelist filename + ! + ! !LOCAL VARIABLES + integer :: nu_nml ! unit for namelist file + integer :: nml_error ! namelist i/o error flag + character(*), parameter :: subname = "('readParams_namelist')" + + namelist /tillage_inparm/ & + tillage_mode, & + use_original_tillage_phases, & + max_tillage_depth + + ! Default values + tillage_mode = 'off' + use_original_tillage_phases = .false. + max_tillage_depth = 0.26_r8 ! Graham et al. (2021) unintentionally used 0.32 + + ! Read tillage namelist + if (masterproc) then + open(newunit=nu_nml, file=trim(NLFilename), status='old', iostat=nml_error ) + call find_nlgroup_name(nu_nml, 'tillage_inparm', status=nml_error) + if (nml_error == 0) then + read(nu_nml, nml=tillage_inparm, iostat=nml_error) + if (nml_error /= 0) then + call endrun(subname // ':: ERROR reading tillage namelist') + end if + else + call endrun(subname // ':: ERROR finding tillage namelist') + end if + close(nu_nml) + endif + call shr_mpi_bcast(tillage_mode, mpicom) + call shr_mpi_bcast(use_original_tillage_phases , mpicom) + call shr_mpi_bcast(max_tillage_depth, mpicom) + + if (masterproc) then + write(iulog,*) ' ' + write(iulog,*) 'tillage settings:' + write(iulog,*) ' tillage_mode = ',tillage_mode + write(iulog,*) ' use_original_tillage_phases = ',use_original_tillage_phases + write(iulog,*) ' max_tillage_depth = ',max_tillage_depth + endif + + ! Assign these + if (tillage_mode == "off") then + tillage_intensity = tillage_off + else if (tillage_mode == "low") then + tillage_intensity = tillage_low + else if (tillage_mode == "high") then + tillage_intensity = tillage_high + else + call endrun(subname // ':: ERROR Unrecognized tillage_mode') + end if + + end subroutine readParams_namelist + + + subroutine readParams_netcdf(ncid) + ! !DESCRIPTION: + ! + ! Read paramfile parameters to be used in tillage. + ! + ! !USES + use ncdio_pio , only : file_desc_t, ncd_io + use clm_varpar, only : ndecomp_pools_max + use SoilBiogeochemDecompCascadeConType, only : no_soil_decomp, century_decomp, mimics_decomp, decomp_method + ! + ! !ARGUMENTS: + type(file_desc_t),intent(inout) :: ncid ! pio netCDF file id + ! + ! !LOCAL VARIABLES: + character(len=32) :: subname = 'readParams_netcdf' + character(len=100) :: errCode = 'Error reading tillage params ' + logical :: readv ! has variable been read in or not + real(r8), allocatable :: tempr(:,:,:) ! temporary to read in constant + character(len=100) :: tString ! temp. var for reading + character(len=3) :: decomp_method_str + + ! Initialize tillage multipliers as all 1, and exit if not tilling + allocate(tillage_mults_allphases(ndecomp_pools, ntill_stages_max)) + tillage_mults_allphases(:,:) = 1.0_r8 + if (.not. get_do_tillage()) then + return + end if + + ! Handle decomposition method + select case( decomp_method ) + case( no_soil_decomp ) + return + case( century_decomp ) + tString = 'bgc_till_decompk_multipliers' + case( mimics_decomp ) + tString = 'mimics_till_decompk_multipliers' + case default + write(decomp_method_str, '(I3)') decomp_method + call endrun('Bad decomp_method = '//decomp_method_str ) + end select + + ! Read off of netcdf file + allocate(tempr(ntill_intensities_max,ndecomp_pools_max,ntill_stages_max)) + call ncd_io(trim(tString), tempr, 'read', ncid, readvar = readv, posNOTonfile = .true.) + if (.not. readv) then + call endrun(msg=trim(errCode)//trim(tString)//errMsg(__FILE__, __LINE__)) + end if + + ! Save + tillage_mults_allphases = tempr(tillage_intensity,1:ndecomp_pools,:) + + end subroutine readParams_netcdf + + + subroutine readParams(ncid, NLFilename) + ! !USES + use ncdio_pio , only : file_desc_t + ! + ! !ARGUMENTS: + type(file_desc_t),intent(inout) :: ncid ! pio netCDF file id + character(len=*), intent(in) :: NLFilename ! Namelist filename + + call readParams_namelist(NLFilename) + call readParams_netcdf(ncid) + + end subroutine readParams + + + function get_do_tillage() + logical :: get_do_tillage + get_do_tillage = tillage_intensity > tillage_off + end function get_do_tillage + + + subroutine get_tillage_multipliers(tillage_mults, idop) + ! !DESCRIPTION: + ! + ! Get the tillage effective multiplier if prognostic crops are on and + ! tillage is turned on. Created by Sam Levis. Modified by Michael Graham + ! to use days past planting (idpp). + ! + ! Modified by Sam Rabin to fix a bug where idpp wasn't actually used, which + ! would affect growing seasons that crossed over into a new calendar year. + ! Previous behavior can be requested with namelist variable use_original_tillage_phases. + ! + ! Original code had two versions depending on cell's GDP, but this seems to + ! have been only an initial effort that was (a) never published and (b) not + ! completely developed. + ! + ! !USES: + use clm_time_manager, only : get_curr_calday, get_curr_days_per_year + use pftconMod , only : ntmp_corn, nirrig_tmp_corn, ntmp_soybean, nirrig_tmp_soybean + use CNPhenologyMod , only : DaysPastPlanting + ! !ARGUMENTS: + real(r8) , intent(inout) :: tillage_mults(:) ! tillage multipliers for this patch + integer , intent(in) :: idop ! patch day of planting + ! + ! !LOCAL VARIABLES: + ! + integer :: day ! julian day + integer :: idpp ! days past planting + integer :: phase ! which tillage phase are we in? + real(r8) dayspyr ! days per year + !----------------------------------------------------------------------- + + ! info from DAYCENT (Melannie Hartman CSU) + ! temp. cereals: P 30 d bef, C 15 d bef, D on day of planting + ! corn, soy : P C D & HW-7 30 d aftr + + phase = 0 + + if (use_original_tillage_phases) then + day = get_curr_calday() + if (day >= idop .and. day < idop+15) then ! based on Point Chisel Tandem Disk multipliers + phase = 1 + else if (day >= idop+15 .and. day < idop+45) then ! based on Field and Row Cultivator multipliers + phase = 2 + else if (day >= idop+45 .and. day < idop+75) then ! based on Rod Weed Row Planter + phase = 3 + end if + else + idpp = DaysPastPlanting(idop) + if (idpp < 15) then ! based on Point Chisel Tandem Disk multipliers + phase = 1 + else if (idpp < 45) then ! based on Field and Row Cultivator multipliers + phase = 2 + else if (idpp < 75) then ! based on Rod Weed Row Planter + phase = 3 + end if + end if + + tillage_mults(:) = 1._r8 + if (phase > 0) then + if (phase > ntill_stages_max) then + call endrun(msg='Tillage phase > ntill_stages_max') + end if + tillage_mults = tillage_mults_allphases(:, phase) + end if + + end subroutine get_tillage_multipliers + + + function get_fraction_tilled(layer_bottom, layer_thickness, max_tillage_depth_gft) result(fraction_tilled) + ! !ARGUMENTS + real(r8), intent(in) :: layer_bottom ! Soil layer interface (between j and j+1) depth (zisoi) + real(r8), intent(in) :: layer_thickness ! Soil layer thickness (dzsoi_decomp) + real(r8) :: max_tillage_depth_gft ! Maximum tillage depth + ! !LOCAL VARIABLES + real(r8) :: layer_top + ! !RESULT + real(r8) :: fraction_tilled ! Fraction of this layer that's within the tillage depth + + ! If the top of the layer is below the max tillage depth, do not till. + layer_top = layer_bottom - layer_thickness + if (layer_top > max_tillage_depth_gft) then + fraction_tilled = 0._r8 + return + end if + + ! Handle zero-thickness layers. This may not be necessary. + if (layer_thickness == 0._r8) then + if (layer_bottom <= max_tillage_depth_gft) then + fraction_tilled = 1._r8 + else + fraction_tilled = 0._r8 + end if + return + end if + + fraction_tilled = max(0._r8, min(1._r8, (max_tillage_depth_gft - layer_top) / layer_thickness)) + + end function get_fraction_tilled + + + subroutine get_apply_tillage_multipliers(idop, c, j, decomp_k) + ! !DESCRIPTION: + ! + ! Multiply decomposition rate constants by tillage coefficients. + ! Written by Sam Rabin, based on original code by Michael Graham. + ! + ! !USES + use pftconMod , only : npcropmin + use clm_varcon, only : zisoi, dzsoi_decomp + use landunit_varcon , only : istcrop + use PatchType , only : patch + ! + ! !ARGUMENTS: + integer , intent(in) :: idop(:) ! patch day of planting + integer , intent(in) :: c ! index of column this is being called for + integer , intent(in) :: j ! index of soil layer this is being called for + real(r8), dimension(:), intent(inout) :: decomp_k ! Output: [real(r8) (:) ] rate constant for decomposition (1./sec) + ! + ! !LOCAL VARIABLES + integer :: p + real :: sumwt ! sum of all patch weights, to check + real(r8), dimension(ndecomp_pools) :: tillage_mults + real(r8), dimension(ndecomp_pools) :: tillage_mults_1patch + real(r8) :: fraction_tilled ! Fraction of this layer that's within the tillage depth + + ! Skip tillage if column is inactive or this layer doesn't get tilled + fraction_tilled = get_fraction_tilled(zisoi(j), dzsoi_decomp(j), max_tillage_depth) + if (.not. col%active(c) .or. fraction_tilled == 0._r8 .or. col%lun_itype(c) /= istcrop) then + return + end if + + ! Initialize tillage multipliers to 0. We will loop through all patches in column, + ! adding patch-weighted multipliers to this. + tillage_mults(:) = 0.0_r8 + + sumwt = 0.0_r8 + do p = col%patchi(c),col%patchf(c) + if (patch%active(p) .and. patch%wtcol(p) /= 0._r8) then + if (patch%itype(p) < npcropmin) then + ! Do not till generic crops + tillage_mults_1patch(:) = 1._r8 + else + call get_tillage_multipliers(tillage_mults_1patch, idop(p)) + end if + tillage_mults = tillage_mults + tillage_mults_1patch * patch%wtcol(p) + sumwt = sumwt + patch%wtcol(p) + end if + end do + if (abs(1.0_r8 - sumwt) > 1.e-6_r8) then + call endrun('ERROR Active crop patch weights does not sum to 1') + end if + + ! Adjust tillage_mults to consider fraction of this layer that's within tillage depth. + tillage_mults = tillage_mults * fraction_tilled & + + 1._r8 * (1._r8 - fraction_tilled) + + ! Apply + decomp_k = decomp_k * tillage_mults(:) + + end subroutine get_apply_tillage_multipliers + +end module TillageMod diff --git a/src/soilbiogeochem/test/ACSpinup_test/CMakeLists.txt b/src/soilbiogeochem/test/ACSpinup_test/CMakeLists.txt index 17f01d6d22..c74163bd52 100644 --- a/src/soilbiogeochem/test/ACSpinup_test/CMakeLists.txt +++ b/src/soilbiogeochem/test/ACSpinup_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(acspinup test_acspinup_exe - "test_acspinup.pf" "") - -target_link_libraries(test_acspinup_exe clm csm_share) +add_pfunit_ctest(acspinup + TEST_SOURCES "test_acspinup.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/soilbiogeochem/test/ACSpinup_test/test_acspinup.pf b/src/soilbiogeochem/test/ACSpinup_test/test_acspinup.pf index df9c4f80fb..b3de19f4ad 100644 --- a/src/soilbiogeochem/test/ACSpinup_test/test_acspinup.pf +++ b/src/soilbiogeochem/test/ACSpinup_test/test_acspinup.pf @@ -2,7 +2,7 @@ module test_acspinup ! Tests of the acspinup functions in SoilBiogeochemStateType - use pfunit_mod + use funit use shr_kind_mod , only : r8 => shr_kind_r8 use SoilBiogeochemStateType, only : get_spinup_latitude_term diff --git a/src/soilbiogeochem/test/CMakeLists.txt b/src/soilbiogeochem/test/CMakeLists.txt index 988cb531b5..22d8fba60f 100644 --- a/src/soilbiogeochem/test/CMakeLists.txt +++ b/src/soilbiogeochem/test/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(ACSpinup_test) +add_subdirectory(tillage_test) diff --git a/src/soilbiogeochem/test/tillage_test/CMakeLists.txt b/src/soilbiogeochem/test/tillage_test/CMakeLists.txt new file mode 100644 index 0000000000..fbc550dd03 --- /dev/null +++ b/src/soilbiogeochem/test/tillage_test/CMakeLists.txt @@ -0,0 +1,3 @@ +add_pfunit_ctest(tillage + TEST_SOURCES "test_tillage.pf" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/soilbiogeochem/test/tillage_test/test_tillage.pf b/src/soilbiogeochem/test/tillage_test/test_tillage.pf new file mode 100644 index 0000000000..77721aedd4 --- /dev/null +++ b/src/soilbiogeochem/test/tillage_test/test_tillage.pf @@ -0,0 +1,54 @@ +module test_tillage + + ! Tests of the functions in TillageMod + + use funit + + use shr_kind_mod , only : r8 => shr_kind_r8 + use TillageMod, only : get_fraction_tilled + + implicit none + save + + real(r8), parameter :: tol = 1.e-8_r8 + +contains + + @Test + subroutine test_get_fraction_tilled() + + integer, parameter :: nlayers = 5 + real(r8) :: zisoi(nlayers) ! Depth of soil interfaces (bottom of each layer) + real(r8) :: dzsoi_decomp(nlayers) ! Thickness of soil layers + integer :: j ! Soil layer + real(r8), parameter :: max_tillage_depth = 0.21_r8 + + zisoi = [0.01_r8, 0.05_r8, 0.1_r8, 0.5_r8, 1._r8] + dzsoi_decomp = [zisoi(1) - 0._r8, & + zisoi(2) - zisoi(1), & + zisoi(3) - zisoi(2), & + zisoi(4) - zisoi(3), & + zisoi(5) - zisoi(4)] + + @assertEqual(1._r8, get_fraction_tilled(zisoi(1), dzsoi_decomp(1), max_tillage_depth)) + @assertEqual(1._r8, get_fraction_tilled(zisoi(2), dzsoi_decomp(2), max_tillage_depth)) + @assertEqual(1._r8, get_fraction_tilled(zisoi(3), dzsoi_decomp(3), max_tillage_depth)) + @assertEqual(0.275_r8, get_fraction_tilled(zisoi(4), dzsoi_decomp(4), max_tillage_depth), tolerance=tol) + @assertEqual(0._r8, get_fraction_tilled(zisoi(5), dzsoi_decomp(5), max_tillage_depth)) + + end subroutine test_get_fraction_tilled + + + @Test + + subroutine test_get_fraction_tilled_0thickness() + real(r8), parameter :: max_tillage_depth = 0.5_r8 + + @assertEqual(1._r8, get_fraction_tilled(0.4_r8, 0._r8, max_tillage_depth)) + @assertEqual(1._r8, get_fraction_tilled(0.5_r8, 0._r8, max_tillage_depth)) + @assertEqual(0._r8, get_fraction_tilled(0.6_r8, 0._r8, max_tillage_depth)) + end subroutine test_get_fraction_tilled_0thickness + + + +end module test_tillage diff --git a/src/unit_test_shr/test/unittestArray_test/CMakeLists.txt b/src/unit_test_shr/test/unittestArray_test/CMakeLists.txt index 9a18380545..9889d96e0a 100644 --- a/src/unit_test_shr/test/unittestArray_test/CMakeLists.txt +++ b/src/unit_test_shr/test/unittestArray_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(unittestArray test_unittestArray_exe - "test_unittestArray.pf" "") - -target_link_libraries(test_unittestArray_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(unittestArray + TEST_SOURCES "test_unittestArray.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/unit_test_shr/test/unittestArray_test/test_unittestArray.pf b/src/unit_test_shr/test/unittestArray_test/test_unittestArray.pf index b11d62e612..f48fd7ad52 100644 --- a/src/unit_test_shr/test/unittestArray_test/test_unittestArray.pf +++ b/src/unit_test_shr/test/unittestArray_test/test_unittestArray.pf @@ -2,7 +2,7 @@ module test_unittestArray ! Tests of unittestArrayMod - use pfunit_mod + use funit use unittestArrayMod use unittestSubgridMod use unittestSimpleSubgridSetupsMod diff --git a/src/unit_test_shr/test/unittestFilterBuilder_test/CMakeLists.txt b/src/unit_test_shr/test/unittestFilterBuilder_test/CMakeLists.txt index c767479aee..698cd525b6 100644 --- a/src/unit_test_shr/test/unittestFilterBuilder_test/CMakeLists.txt +++ b/src/unit_test_shr/test/unittestFilterBuilder_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(unittestFilterBuilder test_filterBuilder_exe - "test_filterBuilder.pf" "") - -target_link_libraries(test_filterBuilder_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(unittestFilterBuilder + TEST_SOURCES "test_filterBuilder.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/unit_test_shr/test/unittestFilterBuilder_test/test_filterBuilder.pf b/src/unit_test_shr/test/unittestFilterBuilder_test/test_filterBuilder.pf index 58674110a9..5f017c10b8 100644 --- a/src/unit_test_shr/test/unittestFilterBuilder_test/test_filterBuilder.pf +++ b/src/unit_test_shr/test/unittestFilterBuilder_test/test_filterBuilder.pf @@ -2,7 +2,7 @@ module test_filterBuilder ! Tests of unittestFilterBuilder - use pfunit_mod + use funit use unittestFilterBuilderMod implicit none diff --git a/src/unit_test_shr/test/unittestSubgrid_test/CMakeLists.txt b/src/unit_test_shr/test/unittestSubgrid_test/CMakeLists.txt index 2e0bc4f03e..e6bf0e58b4 100644 --- a/src/unit_test_shr/test/unittestSubgrid_test/CMakeLists.txt +++ b/src/unit_test_shr/test/unittestSubgrid_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(unittestSubgrid test_unittestSubgrid_exe - "test_unittestSubgrid.pf" "") - -target_link_libraries(test_unittestSubgrid_exe clm csm_share) \ No newline at end of file +add_pfunit_ctest(unittestSubgrid + TEST_SOURCES "test_unittestSubgrid.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/unit_test_shr/test/unittestSubgrid_test/test_unittestSubgrid.pf b/src/unit_test_shr/test/unittestSubgrid_test/test_unittestSubgrid.pf index a0bea34dc4..c8ae194249 100644 --- a/src/unit_test_shr/test/unittestSubgrid_test/test_unittestSubgrid.pf +++ b/src/unit_test_shr/test/unittestSubgrid_test/test_unittestSubgrid.pf @@ -2,7 +2,7 @@ module test_unittestSubgrid ! Tests of unittestSubgridMod - use pfunit_mod + use funit use unittestSubgridMod use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/src/unit_test_shr/unittestWaterTypeFactory.F90 b/src/unit_test_shr/unittestWaterTypeFactory.F90 index cc3510e881..f5f417f9ce 100644 --- a/src/unit_test_shr/unittestWaterTypeFactory.F90 +++ b/src/unit_test_shr/unittestWaterTypeFactory.F90 @@ -161,7 +161,8 @@ subroutine create_water_type(this, water_inst, & h2osno_col = col_array(0._r8), & snow_depth_col = col_array(0._r8), & watsat_col = l_watsat_col, & - t_soisno_col = l_t_soisno_col) + t_soisno_col = l_t_soisno_col, & + NLFilename = ' ') end subroutine create_water_type subroutine teardown(this, water_inst) diff --git a/src/unit_test_stubs/CMakeLists.txt b/src/unit_test_stubs/CMakeLists.txt index 38abfb1633..2d7fe23378 100644 --- a/src/unit_test_stubs/CMakeLists.txt +++ b/src/unit_test_stubs/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(csm_share) add_subdirectory(dyn_subgrid) add_subdirectory(main) +add_subdirectory(share_esmf) add_subdirectory(utils) sourcelist_to_parent(clm_sources) diff --git a/src/unit_test_stubs/share_esmf/CMakeLists.txt b/src/unit_test_stubs/share_esmf/CMakeLists.txt new file mode 100644 index 0000000000..5eb2d42415 --- /dev/null +++ b/src/unit_test_stubs/share_esmf/CMakeLists.txt @@ -0,0 +1,5 @@ +list(APPEND clm_sources + ExcessIceStreamType.F90 + ) + +sourcelist_to_parent(clm_sources) diff --git a/src/unit_test_stubs/share_esmf/ExcessIceStreamType.F90 b/src/unit_test_stubs/share_esmf/ExcessIceStreamType.F90 new file mode 100644 index 0000000000..60bac5ad28 --- /dev/null +++ b/src/unit_test_stubs/share_esmf/ExcessIceStreamType.F90 @@ -0,0 +1,79 @@ +module ExcessIceStreamType + + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! Stub module for Excess ice streams + ! + ! !USES + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_cl + use shr_log_mod , only : errMsg => shr_log_errMsg + use abortutils , only : endrun + use decompMod , only : bounds_type + + ! !PUBLIC TYPES: + implicit none + private + + public :: UseExcessIceStreams ! If streams will be used + + type, public :: excessicestream_type + contains + + ! !PUBLIC MEMBER FUNCTIONS: + procedure, public :: Init ! Initialize and read data in + procedure, public :: CalcExcessIce ! Calculate excess ice ammount + end type excessicestream_type + ! ! PRIVATE DATA: + + logical :: namelist_read = .false. + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + +!============================================================================== +contains +!============================================================================== + + subroutine Init(this, bounds, NLFilename) + ! + ! arguments + implicit none + class(excessicestream_type) :: this + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! Namelist filename + + namelist_read = .true. + + end subroutine Init + + subroutine CalcExcessIce(this,bounds,exice_bulk_init) + + ! only transfers grid values to columns + implicit none + class(excessicestream_type) :: this + type(bounds_type), intent(in) :: bounds + real(r8) , intent(inout) :: exice_bulk_init(bounds%begc:bounds%endc) + ! + ! !LOCAL VARIABLES: + call endrun(msg=' ERROR CalcExcessIce stub is being called and should NOT be'//errMsg(sourcefile, __LINE__)) + + end subroutine CalcExcessIce + + logical function UseExcessIceStreams() + ! + ! !DESCRIPTION: + ! Return true if + ! + ! !USES: + ! + ! !ARGUMENTS: + implicit none + ! + ! !LOCAL VARIABLES: + if ( .not. namelist_read ) then + call endrun(msg=' ERROR UseExcessIceStreams being called, but namelist has not been read yet'//errMsg(sourcefile, __LINE__)) + end if + UseExcessIceStreams = .false. +end function UseExcessIceStreams + +end module ExcessIceStreamType diff --git a/src/unit_test_stubs/utils/restUtilMod_stub.F90.in b/src/unit_test_stubs/utils/restUtilMod_stub.F90.in index b6e3ba4f19..135c977bf6 100644 --- a/src/unit_test_stubs/utils/restUtilMod_stub.F90.in +++ b/src/unit_test_stubs/utils/restUtilMod_stub.F90.in @@ -21,8 +21,12 @@ module restUtilMod public :: restartvar + public :: RestartExcessIceIssue + public :: set_missing_from_template + public :: CallRestartvarDimOK + contains !----------------------------------------------------------------------- @@ -149,4 +153,57 @@ contains end subroutine set_missing_from_template + !----------------------------------------------------------------------- + subroutine RestartExcessIceIssue(ncid, flag, excess_ice_on_restart) + ! + ! !DESCRIPTION: + ! + ! !USES: + ! + ! !ARGUMENTS: + type(file_desc_t), intent(inout) :: ncid ! netcdf id + character(len=*) , intent(in) :: flag ! 'read' or 'write' + logical, intent(out) :: excess_ice_on_restart ! If excess ice is on the restart file + ! + ! !LOCAL VARIABLES: + integer :: attribute_value + + character(len=*), parameter :: subname = 'RestartExcessIceIssue' + !----------------------------------------------------------------------- + + excess_ice_on_restart = .false. + + end subroutine RestartExcessIceIssue + + + !----------------------------------------------------------------------- + logical function CallRestartvarDimOK (ncid, flag, dimname) + ! + ! !DESCRIPTION: + ! Answer whether to call restartvar(), if necessary checking whether + ! a dimension exists in the restart file + ! + ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2022-02-02) + ! Used in Restart(). Even though restartvar() can safely be called for a + ! non-existent variable, it gives an error for a non-existent dimension, so + ! check whether the dimension exists before trying to read. The need for this + ! function arose because we recently added the mxsowings and mxharvests + ! dimensions to the restart file. + ! + ! !USES: + use ncdio_pio + ! + ! !ARGUMENTS: + type(file_desc_t), intent(inout) :: ncid + character(len=*) , intent(in) :: flag + character(len=*) , intent(in) :: dimname + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + + CallRestartvarDimOK = .false. + + end function CallRestartvarDimOK + + end module restUtilMod diff --git a/src/utils/clm_time_manager.F90 b/src/utils/clm_time_manager.F90 index bbf3d8bb29..955d98057a 100644 --- a/src/utils/clm_time_manager.F90 +++ b/src/utils/clm_time_manager.F90 @@ -6,7 +6,11 @@ module clm_time_manager use spmdMod , only: masterproc use clm_varctl , only: iulog use clm_varcon , only: isecspday - use ESMF + use ESMF , only: ESMF_Clock, ESMF_Calendar, ESMF_MAXSTR, ESMF_Time, ESMF_TimeInterval + use ESMF , only: ESMF_TimeIntervalSet, ESMF_TimeSet, ESMF_TimeGet, ESMF_ClockGet + use ESMF , only: operator(==), operator(+), operator(<=), operator(>=) + use ESMF , only: operator(>), operator(<), operator(-) + use ESMF , only: ESMF_KIND_I8, ESMF_TimeIntervalGet implicit none private @@ -38,6 +42,7 @@ module clm_time_manager get_prev_calday, &! return calendar day at beginning of current timestep get_calday, &! return calendar day from input date get_calendar, &! return calendar + get_doy_tomorrow, &! return next day of year get_average_days_per_year,&! return the average number of days per year for the given calendar get_curr_days_per_year, &! return the days per year for year as of the end of the current time step get_prev_days_per_year, &! return the days per year for year as of the beginning of the current time step @@ -55,6 +60,8 @@ module clm_time_manager is_beg_curr_year, &! return true on first timestep in current year is_end_curr_year, &! return true on last timestep in current year is_perpetual, &! return true if perpetual calendar is in use + is_doy_in_interval, &! return true if day of year is in the provided interval + is_today_in_doy_interval, &! return true if today's day of year is in the provided interval is_near_local_noon, &! return true if near local noon is_restart, &! return true if this is a restart run update_rad_dtime, &! track radiation interval via nstep @@ -93,10 +100,11 @@ module clm_time_manager ref_ymd = uninit_int, &! reference date for time coordinate in yearmmdd format ref_tod = 0 ! reference time of day for time coordinate in seconds type(ESMF_Calendar), target, save :: tm_cal ! calendar - type(ESMF_Clock), save :: tm_clock ! model clock + type(ESMF_Clock), save :: tm_clock ! model clock + integer, save :: tm_clock_step_size_sec ! Cache of clock timestep. type(ESMF_Time), save :: tm_perp_date ! perpetual date - ! Data required to restart time manager: + ! Data required to restart time manager (only set if timemgr_restart_io is called): integer, save :: rst_step_sec = uninit_int ! timestep size seconds integer, save :: rst_start_ymd = uninit_int ! start date integer, save :: rst_start_tod = uninit_int ! start time of day @@ -245,6 +253,8 @@ subroutine init_clock( start_date, ref_date, curr_date ) !--------------------------------------------------------------------------------- ! Purpose: Initialize the clock based on the start_date, ref_date and curr_date ! + use ESMF , only : ESMF_ClockCreate, ESMF_ClockAdvance + type(ESMF_Time), intent(in) :: start_date ! start date for run type(ESMF_Time), intent(in) :: ref_date ! reference date for time coordinate type(ESMF_Time), intent(in) :: curr_date ! current date (equal to start_date) @@ -303,6 +313,12 @@ subroutine init_clock( start_date, ref_date, curr_date ) call ESMF_ClockGet(tm_clock, currTime=current ) call chkrc(rc, sub//': error return from ESMF_ClockGet') end do + + + ! Cache step size, we query it a lot. + call ESMF_TimeIntervalGet(step_size, s=tm_clock_step_size_sec, rc=rc) + call chkrc(rc, sub//': error return from ESMF_ClockTimeIntervalGet') + end subroutine init_clock !========================================================================================= @@ -553,6 +569,8 @@ subroutine init_calendar( ) !--------------------------------------------------------------------------------- ! Initialize calendar + use ESMF , only : ESMF_CalKind_Flag, ESMF_CALKIND_NOLEAP + use ESMF , only : ESMF_CALKIND_GREGORIAN, ESMF_CalendarCreate ! ! Local variables ! @@ -652,6 +670,7 @@ end subroutine timemgr_print subroutine advance_timestep() ! Increment the timestep number. + use ESMF , only : ESMF_ClockAdvance character(len=*), parameter :: sub = 'clm::advance_timestep' integer :: rc @@ -687,16 +706,10 @@ integer function get_step_size() ! Return the step size in seconds. character(len=*), parameter :: sub = 'clm::get_step_size' - type(ESMF_TimeInterval) :: step_size ! timestep size - integer :: rc if ( .not. check_timemgr_initialized(sub) ) return - call ESMF_ClockGet(tm_clock, timeStep=step_size, rc=rc) - call chkrc(rc, sub//': error return from ESMF_ClockGet') - - call ESMF_TimeIntervalGet(step_size, s=get_step_size, rc=rc) - call chkrc(rc, sub//': error return from ESMF_ClockTimeIntervalGet') + get_step_size = tm_clock_step_size_sec end function get_step_size @@ -1262,6 +1275,34 @@ end function get_calendar !========================================================================================= + function get_doy_tomorrow(doy_today) result(doy_tomorrow) + + !--------------------------------------------------------------------------------- + ! Given a day of the year (doy_today), return the next day of the year + + integer, intent(in) :: doy_today + integer :: doy_tomorrow + integer :: days_in_year + character(len=*), parameter :: sub = 'clm::get_doy_tomorrow' + + ! Use get_prev_days_per_year() instead of get_curr_days_per_year() because the latter, in the last timestep of a year, actually returns the number of days in the NEXT year. + days_in_year = get_prev_days_per_year() + + if ( doy_today < 1 .or. doy_today > days_in_year )then + write(iulog,*) 'doy_today = ', doy_today + write(iulog,*) 'days_in_year = ', days_in_year + call shr_sys_abort( sub//': error doy_today out of range' ) + end if + + if (doy_today == days_in_year) then + doy_tomorrow = 1 + else + doy_tomorrow = doy_today + 1 + end if + end function get_doy_tomorrow + + !========================================================================================= + real(r8) function get_average_days_per_year() !--------------------------------------------------------------------------------- @@ -1749,6 +1790,58 @@ end function is_perpetual !========================================================================================= + logical function is_doy_in_interval(start, end, doy) + + ! Return true if day of year is in the provided interval. + ! Does not treat leap years differently from normal years. + ! Arguments + integer, intent(in) :: start ! start of interval (day of year) + integer, intent(in) :: end ! end of interval (day of year) + integer, intent(in) :: doy ! day of year to query + + ! Local variables + logical :: window_crosses_newyear + + character(len=*), parameter :: sub = 'clm::is_doy_in_interval' + + window_crosses_newyear = end < start + + if (window_crosses_newyear .and. & + (doy >= start .or. doy <= end)) then + is_doy_in_interval = .true. + else if (.not. window_crosses_newyear .and. & + (doy >= start .and. doy <= end)) then + is_doy_in_interval = .true. + else + is_doy_in_interval = .false. + end if + + end function is_doy_in_interval + + !========================================================================================= + + logical function is_today_in_doy_interval(start, end) + + ! Return true if today's day of year is in the provided interval. + ! Does not treat leap years differently from normal years. + ! Arguments + integer, intent(in) :: start ! start of interval (day of year) + integer, intent(in) :: end ! end of interval (day of year) + + ! Local variable(s) + integer :: doy_today + + character(len=*), parameter :: sub = 'clm::is_today_in_doy_interval' + + ! Get doy of beginning of current timestep + doy_today = get_prev_calday() + + is_today_in_doy_interval = is_doy_in_interval(start, end, doy_today) + + end function is_today_in_doy_interval + + !========================================================================================= + subroutine timemgr_datediff(ymd1, tod1, ymd2, tod2, days) ! Calculate the difference (ymd2,tod2) - (ymd1,tod1) and return the result in days. @@ -1782,6 +1875,7 @@ end subroutine timemgr_datediff !========================================================================================= subroutine chkrc(rc, mes) + use ESMF , only : ESMF_SUCCESS integer, intent(in) :: rc ! return code from time management library character(len=*), intent(in) :: mes ! error message if ( rc == ESMF_SUCCESS ) return @@ -1886,6 +1980,7 @@ subroutine timemgr_reset() ! does not explicitly initialize all variables). ! ! !USES: + use ESMF , only : ESMF_ClockDestroy ! ! !ARGUMENTS: ! @@ -1963,6 +2058,7 @@ subroutine for_test_set_curr_date(yr, mon, day, tod) ! *** Should only be used in unit tests!!! *** ! ! !USES: + use ESMF , only : ESMF_ClockSet ! ! !ARGUMENTS: integer, intent(in) :: yr ! year diff --git a/src/utils/clmfates_interfaceMod.F90 b/src/utils/clmfates_interfaceMod.F90 index 9a04a9d66f..7039884847 100644 --- a/src/utils/clmfates_interfaceMod.F90 +++ b/src/utils/clmfates_interfaceMod.F90 @@ -7,11 +7,9 @@ module CLMFatesInterfaceMod ! ! This is also the only location where CLM code is allowed to see FATES memory ! structures. - ! The routines here, that call FATES library routines, will not pass any types defined - ! by the driving land model (HLM). - ! - ! either native type arrays (int,real,log, etc) or packed into fates boundary condition - ! structures. + ! The routines here, that call FATES library routines, cannot pass most types defined + ! by the driving land model (HLM), only native type arrays (int,real,log, etc), implementations + ! of fates abstract classes, and references into fates boundary condition structures. ! ! Note that CLM/ALM does use Shared Memory Parallelism (SMP), where processes such as ! the update of state variables are forked. However, IO is not assumed to be @@ -48,6 +46,7 @@ module CLMFatesInterfaceMod use CNProductsMod , only : cn_products_type use clm_varctl , only : iulog use clm_varctl , only : fates_parteh_mode + use PRTGenericMod , only : prt_cnp_flex_allom_hyp use clm_varctl , only : use_fates use clm_varctl , only : fates_spitfire_mode use clm_varctl , only : use_fates_tree_damage @@ -60,6 +59,8 @@ module CLMFatesInterfaceMod use clm_varctl , only : use_fates_fixed_biogeog use clm_varctl , only : use_fates_nocomp use clm_varctl , only : use_fates_sp + use clm_varctl , only : use_fates_luh + use clm_varctl , only : fates_seeddisp_cadence use clm_varctl , only : fates_inventory_ctrl_filename use clm_varctl , only : use_nitrif_denitrif use clm_varctl , only : use_lch4 @@ -67,6 +68,7 @@ module CLMFatesInterfaceMod use clm_varcon , only : spval use clm_varcon , only : denice use clm_varcon , only : ispval + use clm_varcon , only : sum_to_1_tol use clm_varpar , only : surfpft_lb,surfpft_ub use clm_varpar , only : numrad use clm_varpar , only : ivis @@ -80,6 +82,8 @@ module CLMFatesInterfaceMod use SolarAbsorbedType , only : solarabs_type use SoilBiogeochemCarbonFluxType, only : soilbiogeochem_carbonflux_type use SoilBiogeochemCarbonStateType, only : soilbiogeochem_carbonstate_type + use SoilBiogeochemNitrogenFluxType, only : soilbiogeochem_nitrogenflux_type + use SoilBiogeochemNitrogenStateType, only : soilbiogeochem_nitrogenstate_type use FrictionVelocityMod , only : frictionvel_type use clm_time_manager , only : is_restart, is_first_restart_step use ncdio_pio , only : file_desc_t, ncd_int, ncd_double @@ -94,9 +98,11 @@ module CLMFatesInterfaceMod use spmdMod , only : masterproc use decompMod , only : get_proc_bounds, & get_proc_clumps, & + get_proc_global, & get_clump_bounds use SoilBiogeochemDecompCascadeConType , only : mimics_decomp, decomp_method use SoilBiogeochemDecompCascadeConType , only : no_soil_decomp, century_decomp + use SoilWaterRetentionCurveMod, only : soil_water_retention_curve_type use GridCellType , only : grc use ColumnType , only : col use LandunitType , only : lun @@ -105,6 +111,7 @@ module CLMFatesInterfaceMod use shr_log_mod , only : errMsg => shr_log_errMsg use clm_varcon , only : dzsoi_decomp use FuncPedotransferMod, only: get_ipedof + use CLMFatesParamInterfaceMod, only: fates_param_reader_ctsm_impl ! use SoilWaterPlantSinkMod, only : Compute_EffecRootFrac_And_VertTranSink_Default ! Used FATES Modules @@ -122,13 +129,20 @@ module CLMFatesInterfaceMod use FatesInterfaceMod , only : UpdateFatesRMeansTStep use FatesInterfaceMod , only : InitTimeAveragingGlobals + use FatesParametersInterface, only : fates_param_reader_type + use FatesParametersInterface, only : fates_parameters_type + + use FatesInterfaceMod , only : DetermineGridCellNeighbors + use FatesHistoryInterfaceMod, only : fates_hist use FatesRestartInterfaceMod, only : fates_restart_interface_type - use EDTypesMod , only : ed_patch_type + use FatesPatchMod , only : fates_patch_type use PRTGenericMod , only : num_elements use FatesInterfaceTypesMod, only : hlm_stepsize use FatesInterfaceTypesMod, only : fates_maxPatchesPerSite + use FatesInterfaceTypesMod, only : hlm_num_luh2_states + use FatesInterfaceTypesMod, only : fates_dispersal_cadence_none use EDMainMod , only : ed_ecosystem_dynamics use EDMainMod , only : ed_update_site use EDInitMod , only : zero_site @@ -136,7 +150,7 @@ module CLMFatesInterfaceMod use EDInitMod , only : init_patches use EDInitMod , only : set_site_properties use EDPftVarcon , only : EDpftvarcon_inst - use EDSurfaceRadiationMod , only : ED_SunShadeFracs, ED_Norman_Radiation + use FatesRadiationDriveMod, only : FatesSunShadeFracs, FatesNormalizedCanopyRadiation use EDBtranMod , only : btran_ed, & get_active_suction_layers use EDCanopyStructureMod , only : canopy_summarization, update_hlm_dynamics @@ -158,7 +172,15 @@ module CLMFatesInterfaceMod use dynHarvestMod , only : dynHarvest_interp_resolve_harvesttypes use FatesConstantsMod , only : hlm_harvest_area_fraction use FatesConstantsMod , only : hlm_harvest_carbon + use FatesDispersalMod , only : lneighbors, dispersal_type, IsItDispersalTime + use perf_mod , only : t_startf, t_stopf + + use dynFATESLandUseChangeMod, only : num_landuse_transition_vars, num_landuse_state_vars + use dynFATESLandUseChangeMod, only : landuse_transitions, landuse_states + use dynFATESLandUseChangeMod, only : landuse_transition_varnames, landuse_state_varnames + use dynFATESLandUseChangeMod, only : dynFatesLandUseInterp + implicit none type, public :: f2hmap_type @@ -197,6 +219,9 @@ module CLMFatesInterfaceMod ! fates_fire_data_method determines the fire data passed from HLM to FATES class(fates_fire_base_type), allocatable :: fates_fire_data_method + ! Type structure that holds allocatable arrays for mpi-based seed dispersal + type(dispersal_type) :: fates_seed + contains procedure, public :: init @@ -224,7 +249,10 @@ module CLMFatesInterfaceMod procedure, public :: wrap_hydraulics_drive procedure, public :: WrapUpdateFatesRmean procedure, public :: wrap_WoodProducts - + procedure, public :: WrapGlobalSeedDispersal + procedure, public :: WrapUpdateFatesSeedInOut + procedure, public :: UpdateCLitterFluxes + procedure, public :: UpdateNLitterFluxes end type hlm_fates_interface_type ! hlm_bounds_to_fates_bounds is not currently called outside the interface. @@ -266,6 +294,7 @@ subroutine CLMFatesGlobals1(surf_numpft,surf_numcft,maxsoil_patches) integer :: pass_sp integer :: pass_masterproc logical :: verbose_output + type(fates_param_reader_ctsm_impl) :: var_reader call t_startf('fates_globals1') @@ -309,6 +338,7 @@ subroutine CLMFatesGlobals1(surf_numpft,surf_numcft,maxsoil_patches) end if + ! The following call reads in the parameter file ! and then uses that to determine the number of patches ! FATES requires. We pass that to CLM here @@ -317,7 +347,7 @@ subroutine CLMFatesGlobals1(surf_numpft,surf_numcft,maxsoil_patches) ! and allocations on the FATES side, which require ! some allocations from CLM (like soil layering) - call SetFatesGlobalElements1(use_fates,surf_numpft,surf_numcft) + call SetFatesGlobalElements1(use_fates,surf_numpft,surf_numcft,var_reader) maxsoil_patches = fates_maxPatchesPerSite @@ -353,6 +383,9 @@ subroutine CLMFatesGlobals2() integer :: pass_is_restart integer :: pass_cohort_age_tracking integer :: pass_tree_damage + integer :: pass_use_luh + integer :: pass_num_luh_states + integer :: pass_num_luh_transitions call t_startf('fates_globals2') @@ -371,6 +404,7 @@ subroutine CLMFatesGlobals2() call set_fates_ctrlparms('soilwater_ipedof',ival=get_ipedof(0)) call set_fates_ctrlparms('parteh_mode',ival=fates_parteh_mode) + call set_fates_ctrlparms('seeddisp_cadence',ival=fates_seeddisp_cadence) ! CTSM-FATES is not fully coupled (yet) ! So lets tell fates to use the RD competition mechanism @@ -491,6 +525,19 @@ subroutine CLMFatesGlobals2() call set_fates_ctrlparms('num_lu_harvest_cats',ival=pass_num_lu_harvest_cats) call set_fates_ctrlparms('use_logging',ival=pass_logging) + if(use_fates_luh) then + pass_use_luh = 1 + pass_num_luh_states = num_landuse_state_vars + pass_num_luh_transitions = num_landuse_transition_vars + else + pass_use_luh = 0 + pass_num_luh_states = 0 + pass_num_luh_transitions = 0 + end if + call set_fates_ctrlparms('use_luh2',ival=pass_use_luh) + call set_fates_ctrlparms('num_luh2_states',ival=pass_num_luh_states) + call set_fates_ctrlparms('num_luh2_transitions',ival=pass_num_luh_transitions) + if(use_fates_inventory_init) then pass_inventory_init = 1 else @@ -556,14 +603,14 @@ subroutine init(this, bounds_proc ) ! is not turned on ! --------------------------------------------------------------------------------- + use spmdMod, only : npes + use decompMod, only : procinfo use FatesInterfaceTypesMod, only : numpft_fates => numpft use FatesParameterDerivedMod, only : param_derived use subgridMod, only : natveg_patch_exists use clm_instur , only : wt_nat_patch use FATESFireFactoryMod , only: create_fates_fire_data_method - implicit none - ! Input Arguments class(hlm_fates_interface_type), intent(inout) :: this type(bounds_type),intent(in) :: bounds_proc @@ -582,6 +629,7 @@ subroutine init(this, bounds_proc ) type(bounds_type) :: bounds_clump integer :: nmaxcol integer :: ndecomp + integer :: numg ! Initialize the FATES communicators with the HLM ! This involves to stages @@ -594,6 +642,19 @@ subroutine init(this, bounds_proc ) ! Parameter Routines call param_derived%Init( numpft_fates ) + + ! Initialize dispersal + if (fates_seeddisp_cadence /= fates_dispersal_cadence_none) then + + ! Initialize fates global seed dispersal array for all nodes + call get_proc_global(ng=numg) + call this%fates_seed%init(npes,numg,procinfo%ncells,numpft_fates) + + ! Initialize the array of nearest neighbors for fates-driven grid cell communications + ! This must be called after surfrd_get_data and decompInit_lnd + call DetermineGridCellNeighbors(lneighbors,this%fates_seed,numg) + end if + nclumps = get_proc_clumps() allocate(this%fates(nclumps)) allocate(this%f2hmap(nclumps)) @@ -693,7 +754,9 @@ subroutine init(this, bounds_proc ) ndecomp = col%nbedrock(c) - call allocate_bcin(this%fates(nc)%bc_in(s),col%nbedrock(c),ndecomp, num_harvest_inst,surfpft_lb,surfpft_ub) + call allocate_bcin(this%fates(nc)%bc_in(s),col%nbedrock(c),ndecomp, & + num_harvest_inst, num_landuse_state_vars, num_landuse_transition_vars, & + surfpft_lb,surfpft_ub) call allocate_bcout(this%fates(nc)%bc_out(s),col%nbedrock(c),ndecomp) call zero_bcs(this%fates(nc),s) @@ -712,10 +775,10 @@ subroutine init(this, bounds_proc ) this%fates(nc)%bc_in(s)%pft_areafrac(ft)=wt_nat_patch(g,m) end do - if(abs(sum(this%fates(nc)%bc_in(s)%pft_areafrac(surfpft_lb:surfpft_ub))-1.0_r8).gt.1.0e-9)then - write(iulog,*) 'pft_area error in interfc ',s, sum(this%fates(nc)%bc_in(s)%pft_areafrac(:))-1.0_r8 + if (abs(sum(this%fates(nc)%bc_in(s)%pft_areafrac(surfpft_lb:surfpft_ub)) - 1.0_r8) > sum_to_1_tol) then + write(iulog,*) 'pft_area error in interfc ', s, sum(this%fates(nc)%bc_in(s)%pft_areafrac(:)) - 1.0_r8 call endrun(msg=errMsg(sourcefile, __LINE__)) - endif + end if end do !site ! Initialize site-level static quantities dictated by the HLM @@ -770,7 +833,6 @@ subroutine check_hlm_active(this, nc, bounds_clump) ! in handy when we have dynamic sites in FATES ! --------------------------------------------------------------------------------- - implicit none class(hlm_fates_interface_type), intent(inout) :: this integer :: nc type(bounds_type),intent(in) :: bounds_clump @@ -808,7 +870,8 @@ end subroutine check_hlm_active subroutine dynamics_driv(this, nc, bounds_clump, & atm2lnd_inst, soilstate_inst, temperature_inst, active_layer_inst, & waterstatebulk_inst, waterdiagnosticbulk_inst, wateratm2lndbulk_inst, & - canopystate_inst, soilbiogeochem_carbonflux_inst, frictionvel_inst) + canopystate_inst, soilbiogeochem_carbonflux_inst, frictionvel_inst, & + soil_water_retention_curve) ! This wrapper is called daily from clm_driver ! This wrapper calls ed_driver, which is the daily dynamics component of FATES @@ -820,7 +883,7 @@ subroutine dynamics_driv(this, nc, bounds_clump, & use subgridMod, only : natveg_patch_exists ! !ARGUMENTS: - implicit none + class(hlm_fates_interface_type), intent(inout) :: this type(bounds_type),intent(in) :: bounds_clump type(atm2lnd_type) , intent(in) :: atm2lnd_inst @@ -834,11 +897,13 @@ subroutine dynamics_driv(this, nc, bounds_clump, & type(canopystate_type) , intent(inout) :: canopystate_inst type(soilbiogeochem_carbonflux_type), intent(inout) :: soilbiogeochem_carbonflux_inst type(frictionvel_type) , intent(inout) :: frictionvel_inst + class(soil_water_retention_curve_type), intent(in) :: soil_water_retention_curve ! !LOCAL VARIABLES: integer :: s ! site index integer :: g ! grid-cell index (HLM) integer :: c ! column index (HLM) + integer :: j ! Soil layer index integer :: ifp ! patch index ft integer :: p ! HLM patch index integer :: nlevsoil ! number of soil layers at the site @@ -849,6 +914,7 @@ subroutine dynamics_driv(this, nc, bounds_clump, & integer :: ier integer :: begg,endg real(r8) :: harvest_rates(bounds_clump%begg:bounds_clump%endg,num_harvest_inst) + real(r8) :: s_node, smp_node ! local for relative water content and potential logical :: after_start_of_harvest_ts integer :: iharv !----------------------------------------------------------------------- @@ -885,7 +951,7 @@ subroutine dynamics_driv(this, nc, bounds_clump, & lnfm24 = this%fates_fire_data_method%GetLight24() end if - if (fates_spitfire_mode .eq. anthro_suppression) then + if (fates_spitfire_mode == anthro_suppression) then allocate(gdp_lf_col(bounds_clump%begc:bounds_clump%endc), stat=ier) if (ier /= 0) then call endrun(msg="allocation error for gdp"//& @@ -909,7 +975,7 @@ subroutine dynamics_driv(this, nc, bounds_clump, & end do ! ifp - if (fates_spitfire_mode .eq. anthro_suppression) then + if (fates_spitfire_mode == anthro_suppression) then ! Placeholder for future fates use of gdp - comment out before integration !this%fates(nc)%bc_in(s)%gdp = gdp_lf_col(c) ! k US$/capita(g) end if @@ -933,6 +999,24 @@ subroutine dynamics_driv(this, nc, bounds_clump, & this%fates(nc)%bc_in(s)%max_rooting_depth_index_col = & min(nlevsoil, active_layer_inst%altmax_lastyear_indx_col(c)) + nlevsoil = this%fates(nc)%bc_in(s)%nlevsoil + do j = 1,nlevsoil + this%fates(nc)%bc_in(s)%tempk_sl(j) = temperature_inst%t_soisno_col(c,j) + end do + + call get_active_suction_layers(this%fates(nc)%nsites, & + this%fates(nc)%sites, & + this%fates(nc)%bc_in, & + this%fates(nc)%bc_out) + + do j = 1,nlevsoil + if(this%fates(nc)%bc_out(s)%active_suction_sl(j)) then + s_node = max(waterstatebulk_inst%h2osoi_vol_col(c,j)/soilstate_inst%eff_porosity_col(c,j) ,0.01_r8) + call soil_water_retention_curve%soil_suction(c,j,s_node, soilstate_inst, smp_node) + this%fates(nc)%bc_in(s)%smp_sl(j) = smp_node + end if + end do + do ifp = 1, this%fates(nc)%sites(s)%youngest_patch%patchno !for vegetated patches ! Mapping between IFP space (1,2,3) and HLM P space (looping by IFP) @@ -987,19 +1071,24 @@ subroutine dynamics_driv(this, nc, bounds_clump, & this%fates(nc)%bc_in(s)%hlm_harvest_catnames(1:num_harvest_inst) = harvest_varnames(1:num_harvest_inst) ! also pass the units that the harvest rates are specified in - if (trim(harvest_units) .eq. trim(unitless_units)) then + if (trim(harvest_units) == trim(unitless_units)) then this%fates(nc)%bc_in(s)%hlm_harvest_units = hlm_harvest_area_fraction - else if (trim(harvest_units) .eq. trim(mass_units)) then + else if (trim(harvest_units) == trim(mass_units)) then this%fates(nc)%bc_in(s)%hlm_harvest_units = hlm_harvest_carbon else write(iulog,*) 'units field not one of the specified options.' write(iulog,*) harvest_units call endrun(msg=errMsg(sourcefile, __LINE__)) end if - - endif + if (use_fates_luh) then + this%fates(nc)%bc_in(s)%hlm_luh_states = landuse_states(:,g) + this%fates(nc)%bc_in(s)%hlm_luh_state_names = landuse_state_varnames + this%fates(nc)%bc_in(s)%hlm_luh_transitions = landuse_transitions(:,g) + this%fates(nc)%bc_in(s)%hlm_luh_transition_names = landuse_transition_varnames + end if + end do ! Nutrient uptake fluxes have been accumulating with each short @@ -1007,6 +1096,11 @@ subroutine dynamics_driv(this, nc, bounds_clump, & ! structures into the cohort structures. call UnPackNutrientAquisitionBCs(this%fates(nc)%sites, this%fates(nc)%bc_in) + ! Distribute any seeds from neighboring gridcells into the current gridcell + ! Global seed availability array populated by WrapGlobalSeedDispersal call + if (fates_seeddisp_cadence /= fates_dispersal_cadence_none) then + call this%WrapUpdateFatesSeedInOut(bounds_clump) + end if ! --------------------------------------------------------------------------------- ! Flush arrays to values defined by %flushval (see registry entry in @@ -1014,6 +1108,7 @@ subroutine dynamics_driv(this, nc, bounds_clump, & ! --------------------------------------------------------------------------------- call fates_hist%flush_hvars(nc,upfreq_in=1) + call fates_hist%flush_hvars(nc,upfreq_in=5) ! --------------------------------------------------------------------------------- ! Part II: Call the FATES model now that input boundary conditions have been @@ -1028,47 +1123,10 @@ subroutine dynamics_driv(this, nc, bounds_clump, & call ed_update_site(this%fates(nc)%sites(s), & this%fates(nc)%bc_in(s), & - this%fates(nc)%bc_out(s)) - + this%fates(nc)%bc_out(s), & + is_restarting = .false.) enddo - ! --------------------------------------------------------------------------------- - ! Part III: Process FATES output into the dimensions and structures that are part - ! of the HLMs API. (column, depth, and litter fractions) - ! --------------------------------------------------------------------------------- - - if ( decomp_method /= no_soil_decomp )then - do s = 1, this%fates(nc)%nsites - c = this%f2hmap(nc)%fcolumn(s) - - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_lab_c_col(c,1:nlevdecomp) = 0.0_r8 - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_cel_c_col(c,1:nlevdecomp) = 0.0_r8 - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_lig_c_col(c,1:nlevdecomp) = 0.0_r8 - - nld_si = this%fates(nc)%bc_in(s)%nlevdecomp - - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_lab_c_col(c,1:nld_si) = & - this%fates(nc)%bc_out(s)%litt_flux_lab_c_si(1:nld_si) - - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_cel_c_col(c,1:nld_si) = & - this%fates(nc)%bc_out(s)%litt_flux_cel_c_si(1:nld_si) - - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_lig_c_col(c,1:nld_si) = & - this%fates(nc)%bc_out(s)%litt_flux_lig_c_si(1:nld_si) - - ! Copy last 3 variables to an array of litter pools for use in do loops - ! and repeat copy in soilbiogeochem/SoilBiogeochemCarbonFluxType.F90. - ! Keep the three originals to avoid backwards compatibility issues with - ! restart files. - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_c_col(c,1:nld_si,1) = & - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_lab_c_col(c,1:nld_si) - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_c_col(c,1:nld_si,2) = & - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_cel_c_col(c,1:nld_si) - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_c_col(c,1:nld_si,3) = & - soilbiogeochem_carbonflux_inst%FATES_c_to_litr_lig_c_col(c,1:nld_si) - - end do - end if ! --------------------------------------------------------------------------------- @@ -1088,7 +1146,8 @@ subroutine dynamics_driv(this, nc, bounds_clump, & ! --------------------------------------------------------------------------------- call fates_hist%update_history_dyn( nc, & this%fates(nc)%nsites, & - this%fates(nc)%sites) + this%fates(nc)%sites, & + this%fates(nc)%bc_in ) if (masterproc) then write(iulog, *) 'clm: leaving fates model', bounds_clump%begg, & @@ -1100,8 +1159,159 @@ subroutine dynamics_driv(this, nc, bounds_clump, & return end subroutine dynamics_driv - ! ------------------------------------------------------------------------------------ + ! =============================================================================== + + subroutine UpdateNLitterFluxes(this,soilbiogeochem_nitrogenflux_inst,ci,c) + + use clm_varpar, only : i_met_lit + + class(hlm_fates_interface_type), intent(inout) :: this + type(soilbiogeochem_nitrogenflux_type) , intent(inout) :: soilbiogeochem_nitrogenflux_inst + integer , intent(in) :: ci ! clump index + integer , intent(in) :: c ! column index + + integer :: s ! site index + real(r8) :: dtime + integer :: i_lig_lit, i_cel_lit ! indices for lignan and cellulose + + dtime = get_step_size_real() + s = this%f2hmap(ci)%hsites(c) + + associate(nf_soil => soilbiogeochem_nitrogenflux_inst) + + nf_soil%decomp_npools_sourcesink_col(c,:,:) = 0._r8 + + if ( .not. use_fates_sp ) then + + ! (gC/m3/timestep) + !nf_soil%decomp_npools_sourcesink_col(c,1:nlevdecomp,i_met_lit) = & + ! nf_soil%decomp_npools_sourcesink_col(c,1:nlevdecomp,i_met_lit) + & + ! this%fates(ci)%bc_out(s)%litt_flux_lab_n_si(1:nlevdecomp)*dtime + + ! Used for mass balance checking (gC/m2/s) + !nf_soil%fates_litter_flux(c) = sum(this%fates(ci)%bc_out(s)%litt_flux_lab_n_si(1:nlevdecomp) * & + ! this%fates(ci)%bc_in(s)%dz_decomp_sisl(1:nlevdecomp)) + + i_cel_lit = i_met_lit + 1 + + !nf_soil%decomp_npools_sourcesink_col(c,1:nlevdecomp,i_cel_lit) = & + ! nf_soil%decomp_npools_sourcesink_col(c,1:nlevdecomp,i_cel_lit) + & + ! this%fates(ci)%bc_out(s)%litt_flux_cel_n_si(1:nlevdecomp)*dtime + + !nf_soil%fates_litter_flux(c) = nf_soil%fates_litter_flux(c) + & + ! sum(this%fates(ci)%bc_out(s)%litt_flux_cel_n_si(1:nlevdecomp) * & + ! this%fates(ci)%bc_in(s)%dz_decomp_sisl(1:nlevdecomp)) + + if (decomp_method == mimics_decomp) then + ! Mimics has a structural pool, which is cellulose and lignan + i_lig_lit = i_cel_lit + elseif(decomp_method == century_decomp ) then + ! CENTURY has a separate lignan pool from cellulose + i_lig_lit = i_cel_lit + 1 + end if + + !nf_soil%decomp_npools_sourcesink_col(c,1:nlevdecomp,i_lig_lit) = & + ! nf_soil%decomp_npools_sourcesink_col(c,1:nlevdecomp,i_lig_lit) + & + ! this%fates(ci)%bc_out(s)%litt_flux_lig_n_si(1:nlevdecomp)*dtime + + !nf_soil%fates_litter_flux(c) = nf_soil%fates_litter_flux(c) + & + ! sum(this%fates(ci)%bc_out(s)%litt_flux_lig_n_si(1:nlevdecomp) * & + ! this%fates(ci)%bc_in(s)%dz_decomp_sisl(1:nlevdecomp)) + + nf_soil%fates_litter_flux = 0._r8 + + else + + ! In SP mode their is no mass flux between the two + nf_soil%fates_litter_flux = 0._r8 + + end if + + end associate + + return + end subroutine UpdateNLitterFluxes + + ! =========================================================== + + subroutine UpdateCLitterFluxes(this,soilbiogeochem_carbonflux_inst,ci,c) + + use clm_varpar, only : i_met_lit + + class(hlm_fates_interface_type), intent(inout) :: this + type(soilbiogeochem_carbonflux_type) , intent(inout) :: soilbiogeochem_carbonflux_inst + integer , intent(in) :: ci ! clump index + integer , intent(in) :: c ! column index + + integer :: s ! site index + real(r8) :: dtime + integer :: i_lig_lit, i_cel_lit ! indices for lignan and cellulose + + dtime = get_step_size_real() + s = this%f2hmap(ci)%hsites(c) + + associate(cf_soil => soilbiogeochem_carbonflux_inst) + + ! This is zeroed in CNDriverNoLeaching -> soilbiogeochem_carbonflux_inst%SetValues() + ! Which is called prior to this call, which is later in the CNDriverNoLeaching() + ! routine. + ! cf_soil%decomp_cpools_sourcesink_col(c,:,:) = 0._r8 + + if ( .not. use_fates_sp ) then + + + call FluxIntoLitterPools(this%fates(ci)%sites(s), & + this%fates(ci)%bc_in(s), & + this%fates(ci)%bc_out(s)) + + ! (gC/m3/timestep) + cf_soil%decomp_cpools_sourcesink_col(c,1:nlevdecomp,i_met_lit) = & + cf_soil%decomp_cpools_sourcesink_col(c,1:nlevdecomp,i_met_lit) + & + this%fates(ci)%bc_out(s)%litt_flux_lab_c_si(1:nlevdecomp)*dtime + + ! Used for mass balance checking (gC/m2/s) + cf_soil%fates_litter_flux(c) = sum(this%fates(ci)%bc_out(s)%litt_flux_lab_c_si(1:nlevdecomp) * & + this%fates(ci)%bc_in(s)%dz_decomp_sisl(1:nlevdecomp)) + + i_cel_lit = i_met_lit + 1 + + cf_soil%decomp_cpools_sourcesink_col(c,1:nlevdecomp,i_cel_lit) = & + cf_soil%decomp_cpools_sourcesink_col(c,1:nlevdecomp,i_cel_lit) + & + this%fates(ci)%bc_out(s)%litt_flux_cel_c_si(1:nlevdecomp)*dtime + + cf_soil%fates_litter_flux(c) = cf_soil%fates_litter_flux(c) + & + sum(this%fates(ci)%bc_out(s)%litt_flux_cel_c_si(1:nlevdecomp) * & + this%fates(ci)%bc_in(s)%dz_decomp_sisl(1:nlevdecomp)) + + if (decomp_method == mimics_decomp) then + ! Mimics has a structural pool, which is cellulose and lignan + i_lig_lit = i_cel_lit + elseif(decomp_method == century_decomp ) then + ! CENTURY has a separate lignan pool from cellulose + i_lig_lit = i_cel_lit + 1 + end if + + cf_soil%decomp_cpools_sourcesink_col(c,1:nlevdecomp,i_lig_lit) = & + cf_soil%decomp_cpools_sourcesink_col(c,1:nlevdecomp,i_lig_lit) + & + this%fates(ci)%bc_out(s)%litt_flux_lig_c_si(1:nlevdecomp)*dtime + + cf_soil%fates_litter_flux(c) = cf_soil%fates_litter_flux(c) + & + sum(this%fates(ci)%bc_out(s)%litt_flux_lig_c_si(1:nlevdecomp) * & + this%fates(ci)%bc_in(s)%dz_decomp_sisl(1:nlevdecomp)) + + else + ! In SP mode their is no mass flux between the two + + cf_soil%fates_litter_flux = 0._r8 + end if + + end associate + return + end subroutine UpdateCLitterFluxes + + ! =================================================================================== + subroutine wrap_update_hlmfates_dyn(this, nc, bounds_clump, & waterdiagnosticbulk_inst, canopystate_inst, & soilbiogeochem_carbonflux_inst, is_initing_from_restart) @@ -1112,13 +1322,13 @@ subroutine wrap_update_hlmfates_dyn(this, nc, bounds_clump, & ! provides boundary conditions (such as vegetation fractional coverage) ! --------------------------------------------------------------------------------- - implicit none class(hlm_fates_interface_type), intent(inout) :: this type(bounds_type),intent(in) :: bounds_clump integer , intent(in) :: nc type(waterdiagnosticbulk_type) , intent(inout) :: waterdiagnosticbulk_inst type(canopystate_type) , intent(inout) :: canopystate_inst type(soilbiogeochem_carbonflux_type), intent(inout) :: soilbiogeochem_carbonflux_inst + ! is this being called during a read from restart sequence (if so then use the restarted fates ! snow depth variable rather than the CLM variable). @@ -1131,6 +1341,7 @@ subroutine wrap_update_hlmfates_dyn(this, nc, bounds_clump, & integer :: c ! column index integer :: g ! grid cell + logical :: dispersal_flag ! local flag to pass to the inside of the site loop real(r8) :: areacheck call t_startf('fates_wrap_update_hlmfates_dyn') @@ -1165,7 +1376,7 @@ subroutine wrap_update_hlmfates_dyn(this, nc, bounds_clump, & ! Canopy diagnostics for FATES call canopy_summarization(this%fates(nc)%nsites, & this%fates(nc)%sites, & - this%fates(nc)%bc_in) + this%fates(nc)%bc_in) ! Canopy diagnostic outputs for HLM call update_hlm_dynamics(this%fates(nc)%nsites, & @@ -1227,9 +1438,24 @@ subroutine wrap_update_hlmfates_dyn(this, nc, bounds_clump, & patch%is_bareground(bounds_clump%begp:bounds_clump%endp) = .false. patch%wt_ed(bounds_clump%begp:bounds_clump%endp) = 0.0_r8 - do s = 1,this%fates(nc)%nsites + if (fates_seeddisp_cadence /= fates_dispersal_cadence_none) then + ! Zero the outgoing_local seed values prior to populating with the most recent seed update + this%fates_seed%outgoing_local(:,:) = 0._r8 + + ! check the dispersal time once outside of the loop and set a flag to pass in + dispersal_flag = .false. + if (IsItDispersalTime()) dispersal_flag = .true. + end if + + do s = 1,this%fates(nc)%nsites c = this%f2hmap(nc)%fcolumn(s) + g = col%gridcell(c) + + ! Accumulate seeds from sites to the gridcell local outgoing buffer + if (fates_seeddisp_cadence /= fates_dispersal_cadence_none) then + if (dispersal_flag) this%fates_seed%outgoing_local(:,g) = this%fates(nc)%sites(s)%seed_out(:) + end if ! Other modules may have AI's we only flush values ! that are on the naturally vegetated columns @@ -1332,7 +1558,8 @@ end subroutine wrap_update_hlmfates_dyn subroutine restart( this, bounds_proc, ncid, flag, waterdiagnosticbulk_inst, & waterstatebulk_inst, canopystate_inst, soilstate_inst, & - active_layer_inst, soilbiogeochem_carbonflux_inst) + active_layer_inst, soilbiogeochem_carbonflux_inst, & + soilbiogeochem_nitrogenflux_inst) ! --------------------------------------------------------------------------------- ! The ability to restart the model is handled through three different types of calls @@ -1356,8 +1583,6 @@ subroutine restart( this, bounds_proc, ncid, flag, waterdiagnosticbulk_inst, & use EDMainMod, only : ed_update_site use FatesInterfaceTypesMod, only: fates_maxElementsPerSite - implicit none - ! Arguments class(hlm_fates_interface_type), intent(inout) :: this @@ -1370,7 +1595,8 @@ subroutine restart( this, bounds_proc, ncid, flag, waterdiagnosticbulk_inst, & type(soilstate_type) , intent(inout) :: soilstate_inst type(active_layer_type) , intent(in) :: active_layer_inst type(soilbiogeochem_carbonflux_type), intent(inout) :: soilbiogeochem_carbonflux_inst - + type(soilbiogeochem_nitrogenflux_type), intent(inout) :: soilbiogeochem_nitrogenflux_inst + ! Locals type(bounds_type) :: bounds_clump integer :: nc @@ -1388,7 +1614,6 @@ subroutine restart( this, bounds_proc, ncid, flag, waterdiagnosticbulk_inst, & integer :: nvar integer :: ivar logical :: readvar - logical, save :: initialized = .false. call t_startf('fates_restart') @@ -1576,16 +1801,17 @@ subroutine restart( this, bounds_proc, ncid, flag, waterdiagnosticbulk_inst, & this%fates(nc)%bc_in(s)%max_rooting_depth_index_col = & min(this%fates(nc)%bc_in(s)%nlevsoil, active_layer_inst%altmax_lastyear_indx_col(c)) + ! When restarting the model, this subroutine has several + ! procedures that are incremental or don't need to be performed for + ! during the restart sequence. For the prior, we don't want the restarted + ! run to call these routines more than would had been called during + ! a continuous simulation period, as it would change results. So + ! we pass in the "is_restarting=.true." flag so we can bypass those procedures + call ed_update_site( this%fates(nc)%sites(s), & this%fates(nc)%bc_in(s), & - this%fates(nc)%bc_out(s) ) - - ! This call sends internal fates variables into the - ! output boundary condition structures. Note: this is called - ! internally in fates dynamics as well. - call FluxIntoLitterPools(this%fates(nc)%sites(s), & - this%fates(nc)%bc_in(s), & - this%fates(nc)%bc_out(s)) + this%fates(nc)%bc_out(s), & + is_restarting = .true. ) end do @@ -1659,18 +1885,24 @@ subroutine restart( this, bounds_proc, ncid, flag, waterdiagnosticbulk_inst, & ! ------------------------------------------------------------------------ call fates_hist%flush_hvars(nc,upfreq_in=1) do s = 1,this%fates(nc)%nsites - call fates_hist%zero_site_hvars(this%fates(nc)%sites(s), & + call fates_hist%zero_site_hvars(this%fates(nc)%sites(s), & upfreq_in=1) end do - call fates_hist%update_history_dyn( nc, & - this%fates(nc)%nsites, & - this%fates(nc)%sites) + call fates_hist%update_history_dyn( nc, & + this%fates(nc)%nsites, & + this%fates(nc)%sites, & + this%fates(nc)%bc_in) end if end do !$OMP END PARALLEL DO + ! Disperse seeds + if (fates_seeddisp_cadence /= fates_dispersal_cadence_none) then + call this%WrapGlobalSeedDispersal(is_restart_flag=.true.) + end if + end if call t_stopf('fates_restart') @@ -1700,9 +1932,9 @@ subroutine init_coldstart(this, waterstatebulk_inst, waterdiagnosticbulk_inst, & real(r8) :: vol_ice real(r8) :: eff_porosity integer :: nlevsoil ! Number of soil layers at each site - integer :: j + integer :: i, j integer :: s - integer :: c + integer :: c, g integer :: p ! HLM patch index integer :: ft ! plant functional type @@ -1792,6 +2024,18 @@ subroutine init_coldstart(this, waterstatebulk_inst, waterdiagnosticbulk_inst, & call HydrSiteColdStart(this%fates(nc)%sites,this%fates(nc)%bc_in) end if + do s = 1,this%fates(nc)%nsites + c = this%f2hmap(nc)%fcolumn(s) + g = col%gridcell(c) + + if (use_fates_luh) then + this%fates(nc)%bc_in(s)%hlm_luh_states = landuse_states(:,g) + this%fates(nc)%bc_in(s)%hlm_luh_state_names = landuse_state_varnames + this%fates(nc)%bc_in(s)%hlm_luh_transitions = landuse_transitions(:,g) + this%fates(nc)%bc_in(s)%hlm_luh_transition_names = landuse_transition_varnames + end if + end do + ! Newly initialized patches need a starting temperature call init_patches(this%fates(nc)%nsites, this%fates(nc)%sites, & this%fates(nc)%bc_in) @@ -1808,14 +2052,8 @@ subroutine init_coldstart(this, waterstatebulk_inst, waterdiagnosticbulk_inst, & call ed_update_site(this%fates(nc)%sites(s), & this%fates(nc)%bc_in(s), & - this%fates(nc)%bc_out(s)) - - ! This call sends internal fates variables into the - ! output boundary condition structures. Note: this is called - ! internally in fates dynamics as well. - call FluxIntoLitterPools(this%fates(nc)%sites(s), & - this%fates(nc)%bc_in(s), & - this%fates(nc)%bc_out(s)) + this%fates(nc)%bc_out(s), & + is_restarting = .false.) end do @@ -1831,12 +2069,13 @@ subroutine init_coldstart(this, waterstatebulk_inst, waterdiagnosticbulk_inst, & ! ------------------------------------------------------------------------ call fates_hist%flush_hvars(nc,upfreq_in=1) do s = 1,this%fates(nc)%nsites - call fates_hist%zero_site_hvars(this%fates(nc)%sites(s), & + call fates_hist%zero_site_hvars(this%fates(nc)%sites(s), & upfreq_in=1) end do - call fates_hist%update_history_dyn( nc, & - this%fates(nc)%nsites, & - this%fates(nc)%sites) + call fates_hist%update_history_dyn( nc, & + this%fates(nc)%nsites, & + this%fates(nc)%sites, & + this%fates(nc)%bc_in) @@ -1858,8 +2097,6 @@ subroutine wrap_sunfrac(this,nc,atm2lnd_inst,canopystate_inst) ! of the canopy that is exposed to sun. ! --------------------------------------------------------------------------------- - implicit none - ! Input Arguments class(hlm_fates_interface_type), intent(inout) :: this @@ -1881,7 +2118,7 @@ subroutine wrap_sunfrac(this,nc,atm2lnd_inst,canopystate_inst) ! this is the order increment of patch ! on the site - type(ed_patch_type), pointer :: cpatch ! c"urrent" patch INTERF-TODO: SHOULD + type(fates_patch_type), pointer :: cpatch ! c"urrent" patch INTERF-TODO: SHOULD ! BE HIDDEN AS A FATES PRIVATE call t_startf('fates_wrapsunfrac') @@ -1913,7 +2150,7 @@ subroutine wrap_sunfrac(this,nc,atm2lnd_inst,canopystate_inst) ! as well as total patch sun/shade fraction output boundary condition ! ------------------------------------------------------------------------------- - call ED_SunShadeFracs(this%fates(nc)%nsites, & + call FatesSunShadeFracs(this%fates(nc)%nsites, & this%fates(nc)%sites, & this%fates(nc)%bc_in, & this%fates(nc)%bc_out) @@ -1996,8 +2233,6 @@ subroutine wrap_btran(this,nc,fn,filterc,soilstate_inst, & use SoilWaterRetentionCurveMod, only : soil_water_retention_curve_type - implicit none - ! Arguments class(hlm_fates_interface_type), intent(inout) :: this integer , intent(in) :: nc @@ -2162,12 +2397,13 @@ subroutine wrap_photosynthesis(this, nc, bounds, fn, filterp, & use shr_log_mod , only : errMsg => shr_log_errMsg use abortutils , only : endrun use decompMod , only : bounds_type - use clm_varcon , only : rgas, tfrz, namep + use clm_varcon , only : tfrz, namep use clm_varctl , only : iulog - use pftconMod , only : pftcon use PatchType , only : patch use quadraticMod , only : quadratic - use EDtypesMod , only : ed_patch_type, ed_cohort_type, ed_site_type + use EDtypesMod , only : ed_site_type + use FatesPatchMod, only : fates_patch_type + use FatesCohortMod , only : fates_cohort_type ! ! !ARGUMENTS: @@ -2323,50 +2559,56 @@ end subroutine wrap_accumulatefluxes ! ====================================================================================== - subroutine wrap_WoodProducts(this, bounds_clump, fc, filterc, c_products_inst) + subroutine wrap_WoodProducts(this, bounds_clump, num_soilc, filter_soilc, & + c_products_inst, n_products_inst) ! !ARGUMENTS: class(hlm_fates_interface_type), intent(inout) :: this type(bounds_type) , intent(in) :: bounds_clump - integer , intent(in) :: fc ! size of column filter - integer , intent(in) :: filterc(fc) ! column filter + integer , intent(in) :: num_soilc ! size of column filter + integer , intent(in) :: filter_soilc(:) ! column filter type(cn_products_type) , intent(inout) :: c_products_inst + type(cn_products_type) , intent(inout) :: n_products_inst ! Locals - integer :: s,c,icc,g - integer :: nc - - ! This wrapper is not active. This is just place-holder code until - ! harvest-product flux is fully implemented. RGK-05-2022 + integer :: s,c,g,fc + integer :: ci ! Clump index + + ci = bounds_clump%clump_index - !associate( & - ! prod10c => c_products_inst%hrv_deadstem_to_prod10_grc, & - ! prod100c => c_products_inst%hrv_deadstem_to_prod100_grc) + ! Loop over columns + do fc = 1, num_soilc + + c = filter_soilc(fc) + g = col%gridcell(c) + s = this%f2hmap(ci)%hsites(c) - ! nc = bounds_clump%clump_index - ! Loop over columns - do icc = 1,fc - c = filterc(icc) - g = col%gridcell(c) - s = this%f2hmap(nc)%hsites(c) - - ! Shijie: Pass harvested wood products to ELM variable - ! prod10c(g) = prod10c(g) + & - ! this%fates(nc)%bc_out(s)%hrv_deadstemc_to_prod10c - ! prod100c(g) = prod100c(g) + & - ! this%fates(nc)%bc_out(s)%hrv_deadstemc_to_prod100c - - ! RGK: THere is also a patch level variable - !do ifp = 1,this%fates(nc)%sites(s)%youngest_patch%patchno - ! p = ifp+col%patchi(c) - ! hrv_deadstemc_to_prod10c(p) = - ! hrv_deadstemc_to_prod100c(p) - !end do + ! Shijie: Pass harvested wood products to CLM product pools + c_products_inst%hrv_deadstem_to_prod10_grc(g) = & + c_products_inst%hrv_deadstem_to_prod10_grc(g) + & + this%fates(ci)%bc_out(s)%hrv_deadstemc_to_prod10c + + c_products_inst%hrv_deadstem_to_prod100_grc(g) = & + c_products_inst%hrv_deadstem_to_prod100_grc(g) + & + this%fates(ci)%bc_out(s)%hrv_deadstemc_to_prod100c + + ! If N cycling is on + if(fates_parteh_mode == prt_cnp_flex_allom_hyp ) then + + !n_products_inst%hrv_deadstem_to_prod10_grc(g) = & + ! n_products_inst%hrv_deadstem_to_prod10_grc(g) + & + ! this%fates(ci)%bc_out(s)%hrv_deadstemc_to_prod10c + + !n_products_inst%hrv_deadstem_to_prod100_grc(g) = & + ! n_products_inst%hrv_deadstem_to_prod100_grc(g) + & + ! this%fates(ci)%bc_out(s)%hrv_deadstemc_to_prod100c + + end if + - end do + end do - ! end associate - return + return end subroutine wrap_WoodProducts ! ====================================================================================== @@ -2427,7 +2669,7 @@ subroutine wrap_canopy_radiation(this, bounds_clump, nc, & end do end do - call ED_Norman_Radiation(this%fates(nc)%nsites, & + call FatesNormalizedCanopyRadiation(this%fates(nc)%nsites, & this%fates(nc)%sites, & this%fates(nc)%bc_in, & this%fates(nc)%bc_out) @@ -2464,6 +2706,138 @@ end subroutine wrap_canopy_radiation ! ====================================================================================== + subroutine WrapGlobalSeedDispersal(this,is_restart_flag) + + ! Call mpi procedure to provide the global seed output distribution array to every gridcell. + ! This could be conducted with a more sophisticated halo-type structure or distributed graph. + + use decompMod, only : procinfo + use spmdMod, only : MPI_REAL8, MPI_SUM, mpicom + use FatesDispersalMod, only : lneighbors, neighbor_type, dispersal_type + use FatesInterfaceTypesMod, only : numpft_fates => numpft + + ! Arguments + class(hlm_fates_interface_type), intent(inout) :: this + logical, optional :: is_restart_flag + + ! Local + integer :: numg ! total number of gridcells across all processors + integer :: ier ! error code + integer :: g ! gridcell index + + logical :: set_restart_flag ! local logical variable to pass to IsItDispersalTime + ! if optional is_restart_flag is true +#ifdef _OPENMP + logical, external :: omp_in_parallel +#endif + + type (neighbor_type), pointer :: neighbor + + ! Check to see if we are not in a threaded region. Fail the run if this returns true. +#ifdef _OPENMP + if (omp_in_parallel()) then + call endrun(msg='clmfates interface error: MPI routine called within threaded region'//& + errMsg(sourcefile, __LINE__)) + end if +#endif + + ! This should only be run once per day + if(is_beg_curr_day()) then + + ! If WrapGlobalSeedDispersal is being called at the end a fates restart call, + ! pass .false. to the set_dispersed_flag to avoid updating the + ! global dispersal date + set_restart_flag = .true. + if (present(is_restart_flag)) then + if (is_restart_flag) set_restart_flag = .false. + end if + + call t_startf('fates-seed-mpi_reduce') + + if (IsItDispersalTime(setdispersedflag=set_restart_flag)) then + + ! Re-initialize incoming seed buffer for this time step + this%fates_seed%incoming_global(:,:) = 0._r8 + this%fates_seed%outgoing_global(:,:) = 0._r8 + + ! Distribute outgoing seed data across all MPI tasks + ! This method of grid cell communications is inefficient in that it creates global information + ! across all processes. A future update should communicate to the minimum set of neighbors. + call MPI_Allgatherv(this%fates_seed%outgoing_local, procinfo%ncells*numpft_fates, MPI_REAL8, & + this%fates_seed%outgoing_global, this%fates_seed%ncells_array*numpft_fates, this%fates_seed%begg_array*numpft_fates, & + MPI_REAL8, mpicom, ier) + if (ier /= 0) then + call endrun(msg='clmfates interface error: MPI_Allgatherv failed'//& + errMsg(sourcefile, __LINE__)) + end if + + ! zero outgoing local for all gridcells outside threaded region now that we've passed them out + this%fates_seed%outgoing_local(:,:) = 0._r8 + + ! Calculate the current gridcell incoming seed for each gridcell index + ! This should be conducted outside of a threaded region to provide access to + ! the neighbor%gindex which might not be available in via the clumped index + call get_proc_global(ng=numg) + do g = 1, numg + neighbor => lneighbors(g)%first_neighbor + do while (associated(neighbor)) + ! This also applies the same neighborhood distribution scheme to all pfts + ! This needs to have a per pft density probability value + this%fates_seed%incoming_global(:,g) = this%fates_seed%incoming_global(:,g) + & + this%fates_seed%outgoing_global(:,neighbor%gindex) * & + neighbor%density_prob(:) / lneighbors(g)%neighbor_count + neighbor => neighbor%next_neighbor + end do + end do + end if + end if + + call t_stopf('fates-seed-mpi_reduce') + + end subroutine WrapGlobalSeedDispersal + + ! ====================================================================================== + + subroutine WrapUpdateFatesSeedInOut(this,bounds_clump) + + ! This subroutine passes the globally dispersed seed via WrapGlobalSeedDispersal, incoming_global + ! to the fates local process seed_in site object. It also resets the fates seed_out + ! in preparation for fates to update the seeds being dispersed out. + + ! Arguments + class(hlm_fates_interface_type), intent(inout) :: this + type(bounds_type), intent(in) :: bounds_clump + + integer :: g ! global index of the host gridcell + integer :: c ! global index of the host column + integer :: s ! FATES site index + integer :: nc ! clump index + + call t_startf('fates-seed-disperse') + + nc = bounds_clump%clump_index + + ! Check that it is the beginning of the current dispersal time step + if (IsItDispersalTime()) then + do s = 1, this%fates(nc)%nsites + c = this%f2hmap(nc)%fcolumn(s) + g = col%gridcell(c) + + ! assuming equal area for all sites, seed_id_global in [kg/grid/day], seed_in in [kg/site/day] + this%fates(nc)%sites(s)%seed_in(:) = this%fates_seed%incoming_global(:,g) + this%fates(nc)%sites(s)%seed_out(:) = 0._r8 ! reset seed_out + end do + else + ! if it is not the dispersing time, pass in zero + this%fates(nc)%sites(s)%seed_in(:) = 0._r8 + end if + + call t_stopf('fates-seed-disperse') + + end subroutine WrapUpdateFatesSeedInOut + + ! ====================================================================================== + subroutine wrap_update_hifrq_hist(this, bounds_clump, & soilbiogeochem_carbonflux_inst, & soilbiogeochem_carbonstate_inst) @@ -2512,7 +2886,7 @@ subroutine wrap_update_hifrq_hist(this, bounds_clump, & this%fates(nc)%sites, & this%fates(nc)%bc_in, & dtime) - + end associate call t_stopf('fates_wrap_update_hifrq_hist') @@ -2726,7 +3100,7 @@ subroutine WrapUpdateFatesRmean(this, nc, temperature_inst) end do end do - call UpdateFatesRMeansTStep(this%fates(nc)%sites,this%fates(nc)%bc_in) + call UpdateFatesRMeansTStep(this%fates(nc)%sites,this%fates(nc)%bc_in,this%fates(nc)%bc_out) end subroutine WrapUpdateFatesRmean @@ -2745,7 +3119,8 @@ subroutine init_history_io(this,bounds_proc) use FatesIOVariableKindMod, only : site_can_r8, site_cnlf_r8, site_cnlfpft_r8 use FatesIOVariableKindMod, only : site_height_r8, site_elem_r8, site_elpft_r8 use FatesIOVariableKindMod, only : site_elcwd_r8, site_elage_r8, site_agefuel_r8 - use FatesIOVariableKindMod, only : site_cdpf_r8, site_cdsc_r8 + use FatesIOVariableKindMod, only : site_cdpf_r8, site_cdsc_r8, site_clscpf_r8 + use FatesIOVariableKindMod, only : site_landuse_r8, site_lulu_r8 use FatesIODimensionsMod, only : fates_bounds_type @@ -2844,11 +3219,12 @@ subroutine init_history_io(this,bounds_proc) case(site_soil_r8, site_size_pft_r8, site_size_r8, site_pft_r8, & site_age_r8, site_height_r8, site_coage_r8,site_coage_pft_r8, & - site_fuel_r8, site_cwdsc_r8, & + site_fuel_r8, site_cwdsc_r8, site_clscpf_r8, & site_can_r8,site_cnlf_r8, site_cnlfpft_r8, site_scag_r8, & site_scagpft_r8, site_agepft_r8, site_elem_r8, site_elpft_r8, & site_elcwd_r8, site_elage_r8, site_agefuel_r8, & - site_cdsc_r8, site_cdpf_r8) + site_cdsc_r8, site_cdpf_r8, & + site_landuse_r8, site_lulu_r8) d_index = fates_hist%dim_kinds(dk_index)%dim2_index @@ -2960,7 +3336,7 @@ subroutine ComputeRootSoilFlux(this, bounds_clump, num_filterc, filterc, & end if end do - if(num_filter_fates .ne. this%fates(nc)%nsites )then + if(num_filter_fates /= this%fates(nc)%nsites )then write(iulog,*) 'The HLM list of natural veg columns during root water transfer' write(iulog,*) 'is not the same size as the fates site list?' call endrun(msg=errMsg(sourcefile, __LINE__)) @@ -2985,35 +3361,6 @@ subroutine ComputeRootSoilFlux(this, bounds_clump, num_filterc, filterc, & end subroutine ComputeRootSoilFlux - ! ====================================================================================== -! -! THIS WAS MOVED TO WRAP_HYDRAULICS_DRIVE() -! -! subroutine TransferPlantWaterStorage(this, bounds_clump, nc, waterstate_inst) -! -! implicit none -! class(hlm_fates_interface_type), intent(inout) :: this -! type(bounds_type),intent(in) :: bounds_clump -! integer,intent(in) :: nc -! type(waterstate_type) , intent(inout) :: waterstate_inst -! -! ! locals -! integer :: s -! integer :: c -! -! if (.not. (use_fates .and. use_fates_planthydro) ) return -! -! do s = 1, this%fates(nc)%nsites -! c = this%f2hmap(nc)%fcolumn(s) -! waterstate_inst%total_plant_stored_h2o_col(c) = & -! this%fates(nc)%bc_out(s)%plant_stored_h2o_si -! end do -! return -!end subroutine TransferPlantWaterStorage - - - - ! ====================================================================================== subroutine wrap_hydraulics_drive(this, bounds_clump, nc, & @@ -3021,7 +3368,6 @@ subroutine wrap_hydraulics_drive(this, bounds_clump, nc, & fn, filterp, solarabs_inst, energyflux_inst) - implicit none class(hlm_fates_interface_type), intent(inout) :: this type(bounds_type),intent(in) :: bounds_clump integer,intent(in) :: nc @@ -3133,17 +3479,16 @@ end subroutine wrap_hydraulics_drive subroutine hlm_bounds_to_fates_bounds(hlm, fates) - use FatesIODimensionsMod, only : fates_bounds_type + use FatesIODimensionsMod, only : fates_bounds_type use FatesInterfaceTypesMod, only : nlevsclass, nlevage, nlevcoage use FatesInterfaceTypesMod, only : nlevheight use FatesInterfaceTypesMod, only : nlevdamage - use EDtypesMod, only : nfsc - use FatesLitterMod, only : ncwd - use EDtypesMod, only : nlevleaf, nclmax + use FatesLitterMod, only : nfsc + use FatesLitterMod, only : ncwd + use EDParamsMod, only : nlevleaf, nclmax use FatesInterfaceTypesMod, only : numpft_fates => numpft - + use FatesConstantsMod, only : n_landuse_cats - implicit none type(bounds_type), intent(in) :: hlm type(fates_bounds_type), intent(out) :: fates @@ -3227,6 +3572,15 @@ subroutine hlm_bounds_to_fates_bounds(hlm, fates) fates%cdam_begin = 1 fates%cdam_end = nlevdamage + + fates%clscpf_begin = 1 + fates%clscpf_end = numpft_fates * nlevsclass * nclmax + + fates%landuse_begin = 1 + fates%landuse_end = n_landuse_cats + + fates%lulu_begin = 1 + fates%lulu_end = n_landuse_cats * n_landuse_cats call t_stopf('fates_hlm2fatesbnds') @@ -3292,4 +3646,6 @@ subroutine GetAndSetTime() end subroutine GetAndSetTime + !----------------------------------------------------------------------- + end module CLMFatesInterfaceMod diff --git a/src/utils/clmfates_paraminterfaceMod.F90 b/src/utils/clmfates_paraminterfaceMod.F90 index 01a3328747..ea27f563bf 100644 --- a/src/utils/clmfates_paraminterfaceMod.F90 +++ b/src/utils/clmfates_paraminterfaceMod.F90 @@ -2,15 +2,33 @@ module CLMFatesParamInterfaceMod ! NOTE(bja, 2017-01) this code can not go into the main clm-fates ! interface module because of circular dependancies with pftvarcon. - use shr_kind_mod, only : r8 => shr_kind_r8 - use FatesGlobals, only : fates_log + use shr_kind_mod, only : r8 => shr_kind_r8, SHR_KIND_CL + use FatesGlobals, only : fates_log + use FatesParametersInterface, only : fates_parameters_type + use FatesParametersInterface, only : fates_param_reader_type + use EDParamsMod, only : FatesRegisterParams, FatesReceiveParams + use SFParamsMod, only : SpitFireRegisterParams, SpitFireReceiveParams + use PRTInitParamsFATESMod, only : PRTRegisterParams, PRTReceiveParams + use FatesSynchronizedParamsMod, only : FatesSynchronizedParamsInst implicit none + public :: fates_param_reader_ctsm_impl + ! + type, extends(fates_param_reader_type) :: fates_param_reader_ctsm_impl + private - ! NOTE(bja, 2017-01) these methods can NOT be part of the clmi-fates - ! nterface type because they are called before the instance is + ! !PRIVATE MEMBER DATA: + + contains + ! !PUBLIC MEMBER FUNCTIONS: + procedure, public :: Read ! Read params from disk + + end type fates_param_reader_ctsm_impl + + + ! NOTE(bja, 2017-01) these methods can NOT be part of the clm-fates + ! interface type because they are called before the instance is ! initialized. - public :: FatesReadParameters public :: FatesReadPFTs private :: ParametersFromNetCDF private :: SetParameterDimensions @@ -23,54 +41,6 @@ module CLMFatesParamInterfaceMod contains - !----------------------------------------------------------------------- - subroutine FatesReadParameters() - - use clm_varctl, only : use_fates, paramfile, fates_paramfile - use spmdMod, only : masterproc - - use FatesParametersInterface, only : fates_parameters_type - - use EDParamsMod, only : FatesRegisterParams, FatesReceiveParams - use SFParamsMod, only : SpitFireRegisterParams, SpitFireReceiveParams - use PRTInitParamsFATESMod, only : PRTRegisterParams, PRTReceiveParams - use FatesSynchronizedParamsMod, only : FatesSynchronizedParamsInst - - implicit none - - character(len=32) :: subname = 'FatesReadParameters' - class(fates_parameters_type), allocatable :: fates_params - logical :: is_host_file - - if (use_fates) then - if (masterproc) then - write(fates_log(), *) 'clmfates_interfaceMod.F90::'//trim(subname)//' :: CLM reading ED/FATES '//' parameters ' - end if - - allocate(fates_params) - call fates_params%Init() - call FatesRegisterParams(fates_params) - call SpitFireRegisterParams(fates_params) - call PRTRegisterParams(fates_params) - call FatesSynchronizedParamsInst%RegisterParams(fates_params) - - is_host_file = .false. - call ParametersFromNetCDF(fates_paramfile, is_host_file, fates_params) - - is_host_file = .true. - call ParametersFromNetCDF(paramfile, is_host_file, fates_params) - - call FatesReceiveParams(fates_params) - call SpitFireReceiveParams(fates_params) - call PRTReceiveParams(fates_params) - call FatesSynchronizedParamsInst%ReceiveParams(fates_params) - - call fates_params%Destroy() - deallocate(fates_params) - end if - - end subroutine FatesReadParameters - !----------------------------------------------------------------------- subroutine FatesReadPFTs() @@ -211,7 +181,7 @@ subroutine ParametersFromNetCDF(filename, is_host_file, fates_params) call SetParameterDimensions(ncid, is_host_file, fates_params) max_dim_size = fates_params%GetMaxDimensionSize() allocate(data(max_dim_size, max_dim_size)) - + num_params = fates_params%num_params() do i = 1, num_params call fates_params%GetMetaData(i, name, dimension_shape, dimension_sizes, dimension_names, is_host_param) @@ -243,4 +213,23 @@ subroutine ParametersFromNetCDF(filename, is_host_file, fates_params) end subroutine ParametersFromNetCDF !----------------------------------------------------------------------- + subroutine Read(this, fates_params ) + ! + ! !DESCRIPTION: + ! Read 'fates_params' parameters from storage. + ! + ! USES + use clm_varctl, only : fname_len, paramfile, fates_paramfile + ! !ARGUMENTS: + class(fates_param_reader_ctsm_impl) :: this + class(fates_parameters_type), intent(inout) :: fates_params + !----------------------------------------------------------------------- + logical :: is_host_file = .false. + + call ParametersFromNetCDF(fates_paramfile, is_host_file, fates_params) + + end subroutine Read + + !----------------------------------------------------------------------- + end module CLMFatesParamInterfaceMod diff --git a/src/utils/restUtilMod.F90.in b/src/utils/restUtilMod.F90.in index 4271271097..1bece885e4 100644 --- a/src/utils/restUtilMod.F90.in +++ b/src/utils/restUtilMod.F90.in @@ -19,6 +19,7 @@ module restUtilMod implicit none save private + integer, parameter, public :: excess_ice_issue = 1787 ! save ! !----------------------------------------------------------------------- @@ -88,9 +89,15 @@ module restUtilMod end interface set_missing_vals_to_constant public :: set_missing_vals_to_constant + public :: RestartExcessIceIssue + private :: missing_field_possibly_abort private :: write_interpinic_flag + ! Answer whether to call restartvar(), if necessary checking whether + ! a dimension exists in the restart file + public :: CallRestartvarDimOK + character(len=*), parameter, private :: sourcefile = & __FILE__ @@ -740,5 +747,78 @@ contains end subroutine write_interpinic_flag + !----------------------------------------------------------------------- + subroutine RestartExcessIceIssue(ncid, flag, excess_ice_on_restart) + ! + ! !DESCRIPTION: + ! Is excess ice on the originating restart file? This is important to have + ! because the init_interp process copies the cold-start values to the + ! interpolated file if they aren't there, and we need to know if good values + ! exist on the originating restart file. + ! + ! !USES: + use ncdio_pio , only : file_desc_t + use IssueFixedMetadataHandler, only : read_issue_fixed_metadata + ! + ! !ARGUMENTS: + type(file_desc_t), intent(inout) :: ncid ! netcdf id + character(len=*) , intent(in) :: flag ! 'read' or 'write' + logical, intent(out) :: excess_ice_on_restart ! If excess ice is on the restart file + ! + ! !LOCAL VARIABLES: + integer :: attribute_value + + + character(len=*), parameter :: subname = 'RestartExcessIceIssue' + !----------------------------------------------------------------------- + + excess_ice_on_restart = .false. + ! The write of the issue metadata is in restFileMod::: restFile_write_issues_fixed + if (flag == 'read' )then + call read_issue_fixed_metadata( & + ncid = ncid, & + issue_num = excess_ice_issue, & + attribute_value = attribute_value) + if (attribute_value == 0) then + excess_ice_on_restart = .false. + else + excess_ice_on_restart = .true. + end if + + end if + + end subroutine RestartExcessIceIssue + !----------------------------------------------------------------------- + logical function CallRestartvarDimOK (ncid, flag, dimname) + ! + ! !DESCRIPTION: + ! Answer whether to call restartvar(), if necessary checking whether + ! a dimension exists in the restart file + ! + ! BACKWARDS_COMPATIBILITY(wjs/ssr, 2022-02-02) + ! Used in Restart(). Even though restartvar() can safely be called for a + ! non-existent variable, it gives an error for a non-existent dimension, so + ! check whether the dimension exists before trying to read. The need for this + ! function arose because we recently added the mxsowings and mxharvests + ! dimensions to the restart file. + ! + ! !USES: + use ncdio_pio + ! + ! !ARGUMENTS: + type(file_desc_t), intent(inout) :: ncid + character(len=*) , intent(in) :: flag + character(len=*) , intent(in) :: dimname + ! + ! !LOCAL VARIABLES: + !----------------------------------------------------------------------- + + if (flag == 'read') then + call check_dim(ncid, dimname, dimexist=CallRestartvarDimOK) + else + CallRestartvarDimOK = .true. + end if + + end function CallRestartvarDimOK end module restUtilMod diff --git a/src/utils/test/annual_flux_dribbler_test/CMakeLists.txt b/src/utils/test/annual_flux_dribbler_test/CMakeLists.txt index 74923ce9a7..a0a60e7431 100644 --- a/src/utils/test/annual_flux_dribbler_test/CMakeLists.txt +++ b/src/utils/test/annual_flux_dribbler_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(annual_flux_dribbler test_annual_flux_dribbler_exe - "test_annual_flux_dribbler.pf" "") - -target_link_libraries(test_annual_flux_dribbler_exe clm csm_share esmf_wrf_timemgr) +add_pfunit_ctest(annual_flux_dribbler + TEST_SOURCES "test_annual_flux_dribbler.pf" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/utils/test/annual_flux_dribbler_test/test_annual_flux_dribbler.pf b/src/utils/test/annual_flux_dribbler_test/test_annual_flux_dribbler.pf index bd9f8e489a..79b9718547 100644 --- a/src/utils/test/annual_flux_dribbler_test/test_annual_flux_dribbler.pf +++ b/src/utils/test/annual_flux_dribbler_test/test_annual_flux_dribbler.pf @@ -2,7 +2,7 @@ module test_annual_flux_dribbler ! Tests of AnnualFluxDribbler - use pfunit_mod + use funit use AnnualFluxDribbler use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSubgridMod diff --git a/src/utils/test/array_utils_test/CMakeLists.txt b/src/utils/test/array_utils_test/CMakeLists.txt index 11cbd47e5a..21c7d91d81 100644 --- a/src/utils/test/array_utils_test/CMakeLists.txt +++ b/src/utils/test/array_utils_test/CMakeLists.txt @@ -3,10 +3,6 @@ set (pfunit_sources test_convert_to_logical.pf ) -set (extra_sources - ) - -create_pFUnit_test(array_utils test_array_utils_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_array_utils_exe clm csm_share esmf_wrf_timemgr) +add_pfunit_ctest(array_utils + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/utils/test/array_utils_test/test_convert_to_logical.pf b/src/utils/test/array_utils_test/test_convert_to_logical.pf index 6e726d443a..3a7ba7b4de 100644 --- a/src/utils/test/array_utils_test/test_convert_to_logical.pf +++ b/src/utils/test/array_utils_test/test_convert_to_logical.pf @@ -2,7 +2,7 @@ module test_convert_to_logical ! Tests of array_utils: convert_to_logical - use pfunit_mod + use funit use array_utils use shr_kind_mod , only : r8 => shr_kind_r8 use unittestUtils, only : endrun_msg diff --git a/src/utils/test/array_utils_test/test_find_k_max_indices.pf b/src/utils/test/array_utils_test/test_find_k_max_indices.pf index 084f5fc415..e125491085 100644 --- a/src/utils/test/array_utils_test/test_find_k_max_indices.pf +++ b/src/utils/test/array_utils_test/test_find_k_max_indices.pf @@ -2,7 +2,7 @@ module test_find_k_max_indices ! Tests of array_utils: find_k_max_indices - use pfunit_mod + use funit use array_utils use shr_kind_mod , only : r8 => shr_kind_r8 use unittestUtils, only : endrun_msg diff --git a/src/utils/test/clm_time_manager_test/CMakeLists.txt b/src/utils/test/clm_time_manager_test/CMakeLists.txt index 3651eaf984..f34e77dfc9 100644 --- a/src/utils/test/clm_time_manager_test/CMakeLists.txt +++ b/src/utils/test/clm_time_manager_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(clm_time_manager test_clm_time_manager_exe - "test_clm_time_manager.pf" "") - -target_link_libraries(test_clm_time_manager_exe clm csm_share esmf_wrf_timemgr) +add_pfunit_ctest(clm_time_manager + TEST_SOURCES "test_clm_time_manager.pf" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/utils/test/clm_time_manager_test/test_clm_time_manager.pf b/src/utils/test/clm_time_manager_test/test_clm_time_manager.pf index 9e49bad266..df8a59de4b 100644 --- a/src/utils/test/clm_time_manager_test/test_clm_time_manager.pf +++ b/src/utils/test/clm_time_manager_test/test_clm_time_manager.pf @@ -2,7 +2,7 @@ module test_clm_time_manager ! Tests of clm_time_manager - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use clm_time_manager use unittestTimeManagerMod, only : unittest_timemgr_setup, unittest_timemgr_teardown @@ -15,7 +15,10 @@ module test_clm_time_manager save real(r8), parameter :: tol = 1.e-13_r8 - integer, parameter :: dtime = 1800 + integer, parameter :: secs_in_day_int = 86400 + real(r8), parameter :: secs_in_day_r8 = 86400._r8 + integer, parameter :: dtime_int = 1800 + real(r8), parameter :: dtime_r8 = 1800._r8 @TestCase type, extends(TestCase) :: TestTimeManager @@ -42,11 +45,11 @@ contains class(TestTimeManager), intent(inout) :: this integer :: step_size - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) step_size = get_step_size() - @assertEqual(dtime, step_size) + @assertEqual(dtime_int, step_size) end subroutine getStepSize_returnsCorrectValue @Test @@ -54,7 +57,7 @@ contains class(TestTimeManager), intent(inout) :: this real(r8) :: calday - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) calday = get_calday(101, 0) @@ -66,7 +69,7 @@ contains class(TestTimeManager), intent(inout) :: this real(r8) :: calday - call unittest_timemgr_setup(dtime=dtime, use_gregorian_calendar=.true.) + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar=.true.) calday = get_calday(41231, 43200) @@ -78,7 +81,7 @@ contains class(TestTimeManager), intent(inout) :: this real(r8) :: calday - call unittest_timemgr_setup(dtime=dtime, use_gregorian_calendar=.true.) + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar=.true.) calday = get_calday(41231, 43200, reuse_day_365_for_day_366=.true.) @@ -90,7 +93,7 @@ contains class(TestTimeManager), intent(inout) :: this real(r8) :: calday - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_date(yr=2000, mon=1, day=1, tod=0) @@ -104,7 +107,7 @@ contains class(TestTimeManager), intent(inout) :: this real(r8) :: calday - call unittest_timemgr_setup(dtime=dtime, use_gregorian_calendar=.true.) + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar=.true.) call set_date(yr=2000, mon=12, day=31, tod=43200) @@ -118,7 +121,7 @@ contains class(TestTimeManager), intent(inout) :: this real(r8) :: calday - call unittest_timemgr_setup(dtime=dtime, use_gregorian_calendar=.true.) + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar=.true.) call set_date(yr=2000, mon=12, day=31, tod=43200) @@ -133,12 +136,12 @@ contains real(r8) :: calday real(r8) :: calday_expected - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_date(yr=2000, mon=1, day=1, tod=0) calday = get_prev_calday() - calday_expected = 366._r8 - dtime/86400._r8 + calday_expected = 366._r8 - dtime_int/secs_in_day_r8 @assertEqual(calday_expected, calday, tolerance=tol) end subroutine getPrevCalday_jan1Time0_returnsCorrectValue @@ -149,12 +152,12 @@ contains real(r8) :: calday real(r8) :: calday_expected - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_date(yr=2000, mon=1, day=2, tod=0) calday = get_prev_calday() - calday_expected = 2._r8 - dtime/86400._r8 + calday_expected = 2._r8 - dtime_int/secs_in_day_r8 @assertEqual(calday_expected, calday, tolerance=tol) end subroutine getPrevCalday_jan2Time0_returnsCorrectValue @@ -164,7 +167,7 @@ contains class(TestTimeManager), intent(inout) :: this real(r8) :: yearfrac - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_date(yr=2, mon=1, day=1, tod=0) @@ -179,7 +182,7 @@ contains real(r8) :: yearfrac real(r8) :: yearfrac_expected - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_date(yr=2, mon=3, day=1, tod=43200) @@ -195,7 +198,7 @@ contains real(r8) :: yearfrac real(r8) :: yearfrac_expected - call unittest_timemgr_setup(dtime=dtime, use_gregorian_calendar=.true.) + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar=.true.) call set_date(yr=2000, mon=12, day=31, tod=43200) @@ -209,16 +212,15 @@ contains subroutine getPrevYearfrac_atYearBoundary_returnsLargeValue(this) class(TestTimeManager), intent(inout) :: this real(r8) :: yearfrac - integer, parameter :: secs_in_day = 86400 real(r8) :: yearfrac_expected - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_date(yr=2, mon=1, day=1, tod=0) yearfrac = get_prev_yearfrac() - yearfrac_expected = (365._r8 - real(dtime, r8) / real(secs_in_day, r8)) / 365._r8 + yearfrac_expected = (365._r8 - dtime_r8 / secs_in_day_r8) / 365._r8 @assertEqual(yearfrac_expected, yearfrac) end subroutine getPrevYearfrac_atYearBoundary_returnsLargeValue @@ -226,16 +228,15 @@ contains subroutine getPrevYearfrac_inMiddleOfYear_returnsCorrectValue(this) class(TestTimeManager), intent(inout) :: this real(r8) :: yearfrac - integer, parameter :: secs_in_day = 86400 real(r8) :: yearfrac_expected - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_date(yr=2, mon=3, day=1, tod=43200) yearfrac = get_prev_yearfrac() - yearfrac_expected = (59.5_r8 - real(dtime, r8) / real(secs_in_day, r8)) / 365._r8 + yearfrac_expected = (59.5_r8 - dtime_r8 / secs_in_day_r8) / 365._r8 @assertEqual(yearfrac_expected, yearfrac) end subroutine getPrevYearfrac_inMiddleOfYear_returnsCorrectValue @@ -243,16 +244,15 @@ contains subroutine getPrevYearfrac_leapYearInMiddleOfYear_returnsCorrectValue(this) class(TestTimeManager), intent(inout) :: this real(r8) :: yearfrac - integer, parameter :: secs_in_day = 86400 real(r8) :: yearfrac_expected - call unittest_timemgr_setup(dtime=dtime, use_gregorian_calendar = .true.) + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar = .true.) call set_date(yr=2000, mon=3, day=1, tod=43200) yearfrac = get_prev_yearfrac() - yearfrac_expected = (60.5_r8 - real(dtime, r8) / real(secs_in_day, r8)) / 366._r8 + yearfrac_expected = (60.5_r8 - dtime_r8 / secs_in_day_r8) / 366._r8 @assertEqual(yearfrac_expected, yearfrac) end subroutine getPrevYearfrac_leapYearInMiddleOfYear_returnsCorrectValue @@ -261,10 +261,9 @@ contains ! This ensures that the correct year is used in determining the number of days in the year class(TestTimeManager), intent(inout) :: this real(r8) :: yearfrac - integer, parameter :: secs_in_day = 86400 real(r8) :: yearfrac_expected - call unittest_timemgr_setup(dtime=dtime, use_gregorian_calendar = .true.) + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar = .true.) call set_date(yr=2000, mon=1, day=1, tod=0) @@ -272,7 +271,7 @@ contains ! In the following, note that we have 365 and not 366, because the prev_yearfrac uses ! year 1999, which is not a leap year: - yearfrac_expected = (365._r8 - real(dtime, r8) / real(secs_in_day, r8)) / 365._r8 + yearfrac_expected = (365._r8 - dtime_r8 / secs_in_day_r8) / 365._r8 @assertEqual(yearfrac_expected, yearfrac) end subroutine getPrevYearfrac_leapYearAtYearBoundary_returnsCorrectValue @@ -281,7 +280,7 @@ contains class(TestTimeManager), intent(inout) :: this integer :: nstep - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) nstep = get_nstep() @@ -294,7 +293,7 @@ contains integer, parameter :: expected_nstep = 3 integer :: nstep - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_nstep(expected_nstep) @@ -308,9 +307,9 @@ contains class(TestTimeManager), intent(inout) :: this logical :: is_beg - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) - call set_date(yr=2, mon=1, day=1, tod=dtime) + call set_date(yr=2, mon=1, day=1, tod=dtime_int) is_beg = is_beg_curr_year() @@ -322,9 +321,9 @@ contains class(TestTimeManager), intent(inout) :: this logical :: is_beg - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) - call set_date(yr=2, mon=1, day=2, tod=dtime) + call set_date(yr=2, mon=1, day=2, tod=dtime_int) is_beg = is_beg_curr_year() @@ -336,7 +335,7 @@ contains class(TestTimeManager), intent(inout) :: this logical :: is_end - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_date(yr=2, mon=1, day=1, tod=0) @@ -350,7 +349,7 @@ contains class(TestTimeManager), intent(inout) :: this logical :: is_end - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_date(yr=2, mon=1, day=2, tod=0) @@ -364,7 +363,7 @@ contains class(TestTimeManager), intent(inout) :: this logical :: is_first - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) is_first = is_first_step() @@ -376,7 +375,7 @@ contains class(TestTimeManager), intent(inout) :: this logical :: is_first - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) call set_nstep(1) @@ -390,7 +389,7 @@ contains class(TestTimeManager), intent(inout) :: this integer :: nstep - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) nstep = 100 call set_nstep(nstep) @@ -419,7 +418,7 @@ contains real(r8) :: londeg integer :: expected - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) ! Check for local noon at Greenich londeg = 0.0_r8 @@ -446,7 +445,7 @@ contains integer :: secs real(r8) :: londeg - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) londeg = 0.0_r8 do while ( londeg <= 360.0_r8 ) @@ -470,16 +469,16 @@ contains real(r8) :: londeg integer :: expected - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) ! Check for local noon at Greenich for 1 time step ahead londeg = 0.0_r8 - secs = 3600*12 + dtime + secs = 3600*12 + dtime_int call set_date(yr=2018, mon=9, day=3, tod=secs) - expected = secs - dtime - @assertEqual( expected, get_local_time( londeg, offset=-dtime ) ) + expected = secs - dtime_int + @assertEqual( expected, get_local_time( londeg, offset=-dtime_int ) ) londeg = 360.0_r8 - @assertEqual( expected, get_local_time( londeg, offset=-dtime ) ) + @assertEqual( expected, get_local_time( londeg, offset=-dtime_int ) ) end subroutine check_local_time_woffset @@ -490,7 +489,7 @@ contains integer :: secs real(r8) :: londeg - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) ! Check for local noon at Greenich will be true from 11 to 1pm londeg = 0.0_r8 @@ -528,7 +527,7 @@ contains integer :: secs logical :: check - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) londeg = 0.0_r8 secs = get_local_time( londeg ) @@ -549,7 +548,7 @@ contains real(r8) :: londeg integer :: secs - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) londeg = -200.0_r8 secs = get_local_time( londeg ) @@ -567,7 +566,7 @@ contains real(r8) :: londeg integer :: secs - call unittest_timemgr_setup(dtime=dtime) + call unittest_timemgr_setup(dtime=dtime_int) londeg = 400.0_r8 secs = get_local_time( londeg ) @@ -577,4 +576,134 @@ contains end subroutine bad_hilontolocal_time + @Test + subroutine check_is_doy_in_interval_startend(this) + class(TestTimeManager), intent(inout) :: this + + integer, parameter :: start = 100 + integer, parameter :: end = 300 + + @assertTrue(is_doy_in_interval(start, end, start)) + @assertTrue(is_doy_in_interval(start, end, end)) + @assertTrue(is_doy_in_interval(start, end, 200)) + @assertFalse(is_doy_in_interval(start, end, 35)) + @assertFalse(is_doy_in_interval(start, end, 350)) + + end subroutine check_is_doy_in_interval_startend + + @Test + subroutine check_is_doy_in_interval_endstart(this) + class(TestTimeManager), intent(inout) :: this + + integer, parameter :: start = 300 + integer, parameter :: end = 100 + + @assertTrue(is_doy_in_interval(start, end, start)) + @assertTrue(is_doy_in_interval(start, end, end)) + @assertFalse(is_doy_in_interval(start, end, 200)) + @assertTrue(is_doy_in_interval(start, end, 35)) + @assertTrue(is_doy_in_interval(start, end, 350)) + + end subroutine check_is_doy_in_interval_endstart + + @Test + subroutine check_is_doy_in_interval_sameday(this) + class(TestTimeManager), intent(inout) :: this + + integer, parameter :: start = 300 + integer, parameter :: end = 300 + + @assertTrue(is_doy_in_interval(start, end, start)) + @assertTrue(is_doy_in_interval(start, end, end)) + @assertFalse(is_doy_in_interval(start, end, 200)) + @assertFalse(is_doy_in_interval(start, end, 350)) + + end subroutine check_is_doy_in_interval_sameday + + @Test + subroutine check_is_today_in_doy_interval(this) + class(TestTimeManager), intent(inout) :: this + + integer :: start, end + + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar=.true.) + + start = 100 ! April 10 + end = 300 ! October 27 + + ! Test well before interval + call set_date(yr=2009, mon=3, day=25, tod=0) + @assertFalse(is_today_in_doy_interval(start, end)) + + ! Test last timestep before interval + call set_date(yr=2009, mon=4, day=10, tod=0) + @assertFalse(is_today_in_doy_interval(start, end)) + + ! Test first timestep of interval + call set_date(yr=2009, mon=4, day=10, tod=dtime_int) + @assertTrue(is_today_in_doy_interval(start, end)) + + ! Test well within interval + call set_date(yr=2009, mon=7, day=24, tod=0) + @assertTrue(is_today_in_doy_interval(start, end)) + + ! Test last timestep of interval + call set_date(yr=2009, mon=10, day=28, tod=0) + @assertTrue(is_today_in_doy_interval(start, end)) + + ! Test first timestep after interval + call set_date(yr=2009, mon=10, day=28, tod=dtime_int) + @assertFalse(is_today_in_doy_interval(start, end)) + + end subroutine check_is_today_in_doy_interval + + @Test + subroutine check_get_doy_tomorrow_noleap(this) + class(TestTimeManager), intent(inout) :: this + character(len=256) :: expected_msg + integer :: doy_tomorrow ! Dummy needed for exception tests + integer :: days_in_year + + ! We don't care about the actual date here; we just want to enable get_prev_days_per_year() + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar=.true.) + call set_date(yr=2009, mon=10, day=28, tod=dtime_int) + + ! Use get_prev_days_per_year() instead of get_curr_days_per_year() because the latter, in the last timestep of a year, actually returns the number of days in the NEXT year. + days_in_year = get_prev_days_per_year() + + @assertEqual(get_doy_tomorrow(1), 2) + @assertEqual(get_doy_tomorrow(150), 151) + @assertEqual(get_doy_tomorrow(days_in_year), 1) + + doy_tomorrow = get_doy_tomorrow(days_in_year + 1) + expected_msg = endrun_msg("clm::get_doy_tomorrow: error doy_today out of range" ) + @assertExceptionRaised(expected_msg) + + end subroutine check_get_doy_tomorrow_noleap + + @Test + subroutine check_get_doy_tomorrow_leap(this) + class(TestTimeManager), intent(inout) :: this + character(len=256) :: expected_msg + integer :: doy_tomorrow ! Dummy needed for exception tests + integer :: days_in_year + + call unittest_timemgr_setup(dtime=dtime_int, use_gregorian_calendar=.true.) + + ! Ensure that things work even in the last timestep of a leap year... + call set_date(yr=2008, mon=12, day=31, tod=secs_in_day_int - dtime_int) + ! Use get_prev_days_per_year() instead of get_curr_days_per_year() because the latter, in the last timestep of a year, actually returns the number of days in the NEXT year. + days_in_year = get_prev_days_per_year() + @assertEqual(get_doy_tomorrow(days_in_year), 1) + doy_tomorrow = get_doy_tomorrow(days_in_year + 1) + expected_msg = endrun_msg("clm::get_doy_tomorrow: error doy_today out of range") + @assertExceptionRaised(expected_msg) + + ! ... as well as in the first timestep after a leap year + call set_date(yr=2009, mon=1, day=1, tod=0) + @assertEqual(get_doy_tomorrow(1), 2) + + end subroutine check_get_doy_tomorrow_leap + + end module test_clm_time_manager diff --git a/src/utils/test/matrix_test/CMakeLists.txt b/src/utils/test/matrix_test/CMakeLists.txt index a9c8456eea..50ed6673d8 100644 --- a/src/utils/test/matrix_test/CMakeLists.txt +++ b/src/utils/test/matrix_test/CMakeLists.txt @@ -1,10 +1,6 @@ set (pfunit_sources test_matrix.pf) -set (extra_sources - ) - -create_pFUnit_test(matrix test_matrix_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_matrix_exe clm csm_share) +add_pfunit_ctest(matrix + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share) diff --git a/src/utils/test/matrix_test/test_matrix.pf b/src/utils/test/matrix_test/test_matrix.pf index 5ffa8726b3..67fc6ded20 100644 --- a/src/utils/test/matrix_test/test_matrix.pf +++ b/src/utils/test/matrix_test/test_matrix.pf @@ -2,7 +2,7 @@ module test_matrix ! Tests of Matrix: inverse - use pfunit_mod + use funit use MatrixMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestUtils, only : endrun_msg @@ -20,7 +20,7 @@ module test_matrix integer, parameter :: ndims = 20 - real(r8), parameter :: tol = 1.e-15_r8 + real(r8), parameter :: tol = 1.e-14_r8 contains diff --git a/src/utils/test/numerics_test/CMakeLists.txt b/src/utils/test/numerics_test/CMakeLists.txt index 83bf0cc1d0..19d2c67451 100644 --- a/src/utils/test/numerics_test/CMakeLists.txt +++ b/src/utils/test/numerics_test/CMakeLists.txt @@ -1,10 +1,6 @@ set (pfunit_sources test_truncate_small_values.pf) -set (extra_sources - ) - -create_pFUnit_test(numerics test_numerics_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_numerics_exe clm csm_share esmf_wrf_timemgr) +add_pfunit_ctest(numerics + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/utils/test/numerics_test/test_truncate_small_values.pf b/src/utils/test/numerics_test/test_truncate_small_values.pf index e4673d5388..c1faf8e622 100644 --- a/src/utils/test/numerics_test/test_truncate_small_values.pf +++ b/src/utils/test/numerics_test/test_truncate_small_values.pf @@ -2,7 +2,7 @@ module test_truncate_small_values ! Tests of NumericsMod: truncate_small_values - use pfunit_mod + use funit use NumericsMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestSimpleSubgridSetupsMod diff --git a/src/utils/test/quadratic_test/CMakeLists.txt b/src/utils/test/quadratic_test/CMakeLists.txt index 50d6c92f1a..daea7647cc 100644 --- a/src/utils/test/quadratic_test/CMakeLists.txt +++ b/src/utils/test/quadratic_test/CMakeLists.txt @@ -1,4 +1,3 @@ -create_pFUnit_test(quadratic test_quadratic_exe - "test_quadratic.pf" "") - -target_link_libraries(test_quadratic_exe clm csm_share) +add_pfunit_ctest(quadratic + TEST_SOURCES "test_quadratic.pf" + LINK_LIBRARIES clm csm_share) diff --git a/src/utils/test/quadratic_test/test_quadratic.pf b/src/utils/test/quadratic_test/test_quadratic.pf index 34ecc12eb7..7acfbae6e9 100644 --- a/src/utils/test/quadratic_test/test_quadratic.pf +++ b/src/utils/test/quadratic_test/test_quadratic.pf @@ -2,7 +2,7 @@ module test_quadratic ! Tests of quadratic - use pfunit_mod + use funit use quadraticMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestUtils, only : endrun_msg @@ -176,8 +176,6 @@ contains b = 4.0_r8 c = 4.0_r8 + 100.0_r8*epsilon(b) call quadratic (a, b, c, r1, r2) - call check_root(a, b, c, r1) - call check_root(a, b, c, r2) expected_msg = endrun_msg( & 'quadratic ERROR: Quadratic solution error: b^2 - 4ac is negative') @assertExceptionRaised(expected_msg) diff --git a/src/utils/test/sparse_matrix_test/CMakeLists.txt b/src/utils/test/sparse_matrix_test/CMakeLists.txt index 7ba943b272..ba8a7966c8 100644 --- a/src/utils/test/sparse_matrix_test/CMakeLists.txt +++ b/src/utils/test/sparse_matrix_test/CMakeLists.txt @@ -1,10 +1,6 @@ set (pfunit_sources test_sparse_matrix.pf) -set (extra_sources - ) - -create_pFUnit_test(sparse_matrix test_sparse_matrix_exe - "${pfunit_sources}" "${extra_sources}") - -target_link_libraries(test_sparse_matrix_exe clm csm_share) +add_pfunit_ctest(sparse_matrix + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share) diff --git a/src/utils/test/sparse_matrix_test/test_sparse_matrix.pf b/src/utils/test/sparse_matrix_test/test_sparse_matrix.pf index 9e69fdace5..2f8bc1ca7b 100644 --- a/src/utils/test/sparse_matrix_test/test_sparse_matrix.pf +++ b/src/utils/test/sparse_matrix_test/test_sparse_matrix.pf @@ -2,7 +2,7 @@ module test_sparse_matrix ! Tests of Sparse Matrix Multiply module - use pfunit_mod + use funit use SparseMatrixMultiplyMod use shr_kind_mod , only : r8 => shr_kind_r8 use unittestUtils, only : endrun_msg diff --git a/test/tools/README b/test/tools/README index a2acbcae40..c545f625b8 100644 --- a/test/tools/README +++ b/test/tools/README @@ -11,7 +11,7 @@ To use... ./test_driver.sh -i -on cheyenne +on Derecho qcmd -l walltime=08:00:00 -- ./test_driver.sh -i >& run.out & @@ -33,7 +33,7 @@ To run neon-specific tests, please use login nodes: env CLM_INPUT_TESTS=`pwd`/tests_pretag_nompi_neon ./test_driver.sh -i > & run_neon.out & -Intended for use on NCAR machines cheyenne, geyser (DAV) and hobart. +Intended for use on NCAR machines Derecho, Casper (DAV) and izumi. II. RUNNING test_driver.sh TOOLS TESTING: diff --git a/test/tools/README.testnames b/test/tools/README.testnames index 11d9e23d4c..f42864facc 100644 --- a/test/tools/README.testnames +++ b/test/tools/README.testnames @@ -43,6 +43,7 @@ m is the resolution 9 -- 4x5 a -- NEON YELL b -- NEON KONA +c -- NEON OSBS d -- region1 c -- single point from the 0.9x1.25 grid g -- unused diff --git a/test/tools/input_tests_master b/test/tools/input_tests_master index f3e46d50b5..784df20ea5 100644 --- a/test/tools/input_tests_master +++ b/test/tools/input_tests_master @@ -24,17 +24,10 @@ bli58 TBLscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_10x15_crp_1850-200 smi64 TSMscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_5x5_amazon_hirespft_2005^tools__ds bli64 TBLscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_5x5_amazon_hirespft_2005^tools__ds -smi74 TSMscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_1x1_brazil_1850-2000^tools__ds -bli74 TBLscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_1x1_brazil_1850-2000^tools__ds -smi78 TSMscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_1x1_brazil_1850^tools__ds -bli78 TBLscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_1x1_brazil_1850^tools__ds -smiT4 TSMscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_1x1_numaIA_crp_2000^tools__ds -bliT4 TBLscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_1x1_numaIA_crp_2000^tools__ds -smiT2 TSMscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_1x1_numaIA_crp_SSP5-8.5_1850-2100^tools__s -bliT2 TBLscript_tools.sh mksurfdata_map mksurfdata.pl mksrfdt_1x1_numaIA_crp_SSP5-8.5_1850-2100^tools__s - -sm0a1 TSMscript_tools.sh site_and_regional run_neon.py run_neon_OSBS -bl0a1 TBLscript_tools.sh site_and_regional run_neon.py run_neon_OSBS +sm0c1 TSMscript_tools.sh site_and_regional run_neon.py run_neon_OSBS +bl0c1 TBLscript_tools.sh site_and_regional run_neon.py run_neon_OSBS +sm0a1 TSMscript_tools.sh site_and_regional run_neon.py run_neon_YELL_PRISM +bl0a1 TBLscript_tools.sh site_and_regional run_neon.py run_neon_YELL_PRISM smba1 TSMscript_tools.sh site_and_regional subset_data subset_data_YELL blba1 TBLscript_tools.sh site_and_regional subset_data subset_data_YELL @@ -54,5 +47,3 @@ smi#2 TSMscript_tools.sh mkmapdata mkmapdata.sh mkmapdata_ne30np4 bli#2 TBLscript_tools.sh mkmapdata mkmapdata.sh mkmapdata_ne30np4 smi59 TSMscript_tools.sh mkmapdata mkmapdata.sh mkmapdata_if10 bli59 TBLscript_tools.sh mkmapdata mkmapdata.sh mkmapdata_if10 -smi79 TSMscript_tools.sh mkmapdata mkmapdata.sh mkmapdata_i1x1_brazil -bli79 TBLscript_tools.sh mkmapdata mkmapdata.sh mkmapdata_i1x1_brazil diff --git a/test/tools/nl_files/mkmapdata_i1x1_brazil b/test/tools/nl_files/mkmapdata_i1x1_brazil deleted file mode 100644 index bb54d468dc..0000000000 --- a/test/tools/nl_files/mkmapdata_i1x1_brazil +++ /dev/null @@ -1 +0,0 @@ --t regional -r 1x1_brazil --fast diff --git a/test/tools/nl_files/mksrfdt_1x1_brazil_1850 b/test/tools/nl_files/mksrfdt_1x1_brazil_1850 deleted file mode 100644 index 2330bd082e..0000000000 --- a/test/tools/nl_files/mksrfdt_1x1_brazil_1850 +++ /dev/null @@ -1 +0,0 @@ --l CSMDATA -r 1x1_brazil -y 1850-2000 -exedir EXEDIR diff --git a/test/tools/nl_files/mksrfdt_1x1_brazil_1850-2000 b/test/tools/nl_files/mksrfdt_1x1_brazil_1850-2000 deleted file mode 100644 index 2330bd082e..0000000000 --- a/test/tools/nl_files/mksrfdt_1x1_brazil_1850-2000 +++ /dev/null @@ -1 +0,0 @@ --l CSMDATA -r 1x1_brazil -y 1850-2000 -exedir EXEDIR diff --git a/test/tools/nl_files/mksrfdt_1x1_numaIA_crp_2000 b/test/tools/nl_files/mksrfdt_1x1_numaIA_crp_2000 deleted file mode 100644 index 03304f81eb..0000000000 --- a/test/tools/nl_files/mksrfdt_1x1_numaIA_crp_2000 +++ /dev/null @@ -1 +0,0 @@ --l CSMDATA -r 1x1_numaIA -y 2000 -exedir EXEDIR diff --git a/test/tools/nl_files/mksrfdt_1x1_numaIA_crp_SSP5-8.5_1850-2100 b/test/tools/nl_files/mksrfdt_1x1_numaIA_crp_SSP5-8.5_1850-2100 deleted file mode 100644 index ed83434075..0000000000 --- a/test/tools/nl_files/mksrfdt_1x1_numaIA_crp_SSP5-8.5_1850-2100 +++ /dev/null @@ -1 +0,0 @@ --l CSMDATA -r 1x1_numaIA -y 1850-2100 -ssp_rcp SSP5-8.5 -exedir EXEDIR diff --git a/test/tools/nl_files/mksrfdt_1x1_vancouverCAN_2000 b/test/tools/nl_files/mksrfdt_1x1_vancouverCAN_2000 deleted file mode 100644 index a446e82fcd..0000000000 --- a/test/tools/nl_files/mksrfdt_1x1_vancouverCAN_2000 +++ /dev/null @@ -1 +0,0 @@ --l CSMDATA -r 1x1_vancouverCAN -no-crop -y 2000 -exedir EXEDIR diff --git a/test/tools/nl_files/modify_data_YELL b/test/tools/nl_files/modify_data_YELL index e76322cdeb..159c92ae63 100644 --- a/test/tools/nl_files/modify_data_YELL +++ b/test/tools/nl_files/modify_data_YELL @@ -1 +1 @@ ---neon_site YELL --surf_dir CSMDATA/lnd/clm2/surfdata_map/NEON --out_dir EXEDIR +--neon_site YELL --surf_dir CSMDATA/lnd/clm2/surfdata_map/NEON --out_dir EXEDIR --inputdata-dir CSMDATA diff --git a/test/tools/nl_files/run_neon_OSBS b/test/tools/nl_files/run_neon_OSBS index c49fb77783..0c274b13ad 100644 --- a/test/tools/nl_files/run_neon_OSBS +++ b/test/tools/nl_files/run_neon_OSBS @@ -1 +1 @@ ---verbose --run-type ad --setup-only +--verbose --run-type ad --setup-only --neon-site OSBS diff --git a/test/tools/nl_files/run_neon_YELL_PRISM b/test/tools/nl_files/run_neon_YELL_PRISM new file mode 100644 index 0000000000..f5ebdf9fdf --- /dev/null +++ b/test/tools/nl_files/run_neon_YELL_PRISM @@ -0,0 +1 @@ +--verbose --run-type transient --setup-only --neon-site YELL --prism --neon-version v2 --experiment toolstest diff --git a/test/tools/nl_files/subset_data_KONA b/test/tools/nl_files/subset_data_KONA index cb743f2b45..c3be007869 100644 --- a/test/tools/nl_files/subset_data_KONA +++ b/test/tools/nl_files/subset_data_KONA @@ -1 +1 @@ -point --lon 263.38956 --lat 39.1082 --site KONA --dompft 17 19 23 45 --pctpft 28 12 32 28 --crop --create-domain --create-surface --outdir EXEDIR/KONA_user-mod_and_data --user-mods-dir EXEDIR/KONA_user-mod_and_data --verbose +point --lon 263.38956 --lat 39.1082 --site KONA --dompft 17 19 23 45 --pctpft 28 12 32 28 --crop --create-domain --create-surface --outdir EXEDIR/KONA_user-mod_and_data --user-mods-dir EXEDIR/KONA_user-mod_and_data --verbose --inputdata-dir CSMDATA diff --git a/test/tools/nl_files/subset_data_US-UMB b/test/tools/nl_files/subset_data_US-UMB index 499b5f53fd..935b0dc99d 100644 --- a/test/tools/nl_files/subset_data_US-UMB +++ b/test/tools/nl_files/subset_data_US-UMB @@ -1 +1 @@ -point --lon 275.28626 --lat 45.5598 --site 1x1_US-UMB --dompft 7 --cap-saturation --uniform-snowpack --create-surface --outdir EXEDIR/US-UMB_user-mod_and_data --user-mods-dir EXEDIR/US-UMB_user-mod_and_data --verbose +point --lon 275.28626 --lat 45.5598 --site 1x1_US-UMB --dompft 7 --cap-saturation --uniform-snowpack --create-surface --outdir EXEDIR/US-UMB_user-mod_and_data --user-mods-dir EXEDIR/US-UMB_user-mod_and_data --verbose --inputdata-dir CSMDATA diff --git a/test/tools/nl_files/subset_data_YELL b/test/tools/nl_files/subset_data_YELL index 5e142713df..8295830c25 100644 --- a/test/tools/nl_files/subset_data_YELL +++ b/test/tools/nl_files/subset_data_YELL @@ -1 +1 @@ -point --lon 250.45804 --lat 44.95597 --site YELL --dompft 1 --crop --create-domain --create-surface --outdir EXEDIR/YELL_user-mod_and_data --user-mods-dir EXEDIR/YELL_user-mod_and_data --verbose +point --lon 250.45804 --lat 44.95597 --site YELL --dompft 1 --crop --create-domain --create-surface --outdir EXEDIR/YELL_user-mod_and_data --user-mods-dir EXEDIR/YELL_user-mod_and_data --verbose --inputdata-dir CSMDATA diff --git a/test/tools/nl_files/subset_data_f09_US_pt b/test/tools/nl_files/subset_data_f09_US_pt index 4acdfeabd4..bf6d5e2861 100644 --- a/test/tools/nl_files/subset_data_f09_US_pt +++ b/test/tools/nl_files/subset_data_f09_US_pt @@ -1 +1 @@ -point --lon 257.5 --lat 43.822 --site 1x1_ --include-nonveg --crop --create-landuse --create-datm --create-user-mods --datm-syr 2000 --datm-eyr 2000 --create-surface --outdir EXEDIR/f09_US_pt_user-mod_and_data --user-mods-dir EXEDIR/f09_US_pt_user-mod_and_data --verbose +point --lon 257.5 --lat 43.822 --site 1x1_ --include-nonveg --crop --create-landuse --create-datm --create-user-mods --datm-syr 2000 --datm-eyr 2000 --create-surface --outdir EXEDIR/f09_US_pt_user-mod_and_data --user-mods-dir EXEDIR/f09_US_pt_user-mod_and_data --verbose --inputdata-dir CSMDATA diff --git a/test/tools/nl_files/subset_data_region1 b/test/tools/nl_files/subset_data_region1 index c1c5607239..fce83f0e2e 100644 --- a/test/tools/nl_files/subset_data_region1 +++ b/test/tools/nl_files/subset_data_region1 @@ -1 +1 @@ -region --lat1 -40 --lat2 15 --lon1 275 --lon2 330 --create-domain --create-surface --create-landuse --verbose --overwrite --reg test1 +region --lat1 -40 --lat2 15 --lon1 275 --lon2 330 --create-domain --create-surface --create-landuse --verbose --overwrite --reg test1 --inputdata-dir CSMDATA diff --git a/test/tools/test_driver.sh b/test/tools/test_driver.sh index 1b3c141d79..6b1811c8ab 100755 --- a/test/tools/test_driver.sh +++ b/test/tools/test_driver.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh # # test_driver.sh: driver script for the offline testing of CLM of tools # @@ -26,15 +26,74 @@ hostname=`hostname` echo $hostname case $hostname in - ##cheyenne - cheyenne* | r*i*n*) - submit_script="test_driver_cheyenne${cur_time}.sh" + ##Derecho + derecho* | dec*) + submit_script="test_driver_derecho${cur_time}.sh" ##vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv writing to batch script vvvvvvvvvvvvvvvvvvv cat > ./${submit_script} << EOF #!/bin/sh # +interactive="YES" +input_file="tests_pretag_derecho_nompi" +c_threads=128 + +export INITMODULES="/glade/u/apps/derecho/23.06/spack/opt/spack/lmod/8.7.20/gcc/7.5.0/pdxb/lmod/lmod/init/sh" +. \$INITMODULES + +module --force purge +module load ncarenv +module load craype +module load intel +module load mkl +module load ncarcompilers +module load netcdf +module load nco +module load ncl + +#omp threads +if [ -z "\$CLM_THREADS" ]; then #threads NOT set on command line + export CLM_THREADS=\$c_threads +fi + +# Stop on first failed test +if [ -z "\$CLM_SOFF" ]; then #CLM_SOFF NOT set + export CLM_SOFF=FALSE +fi + +export CESM_MACH="derecho" +export CESM_COMP="intel" + +export NETCDF_DIR=\$NETCDF +export INC_NETCDF=\$NETCDF/include +export LIB_NETCDF=\$NETCDF/lib +export MAKE_CMD="gmake -j " +export CFG_STRING="" +export TOOLS_MAKE_STRING="USER_FC=ifort USER_LINKER=ifort USER_CPPDEFS=-DLINUX" +export MACH_WORKSPACE=\$SCRATCH +export CPRNC_EXE="$CESMDATAROOT/cprnc/cprnc" +dataroot="$CESMDATAROOT/inputdata" +export TOOLSLIBS="" +export REGRID_PROC=1 +export TOOLS_CONF_STRING="--mpilib mpi-serial" + + +echo_arg="" + +EOF +#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ writing to batch script ^^^^^^^^^^^^^^^^^^^ + ;; + + ##cheyenne + cheyenne* | r*i*n*) + submit_script="test_driver_cheyenne${cur_time}.sh" + +#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv writing to batch script vvvvvvvvvvvvvvvvvvv +at > ./${submit_script} << EOF +!/bin/sh + + interactive="YES" input_file="tests_pretag_cheyenne_nompi" c_threads=36 @@ -54,8 +113,6 @@ module load nco module load ncl module load conda -$CESMDATAROOT/py_env_create -conda activate ctsm_py ##omp threads @@ -119,8 +176,6 @@ module load openmpi module load nco module load conda module load ncl -$CESMDATAROOT/py_env_create -conda activate ctsm_py ##omp threads @@ -220,8 +275,6 @@ module load compiler/intel module load tool/nco module load tool/netcdf module load lang/python -$CESMDATAROOT/py_env_create -conda activate ctsm_py export NETCDF_DIR=\$NETCDF_PATH export INC_NETCDF=\${NETCDF_PATH}/include @@ -303,8 +356,6 @@ module load compiler/intel module load tool/nco module load tool/netcdf module load lang/python -$CESMDATAROOT/py_env_create -conda activate ctsm_py export NETCDF_DIR=\$NETCDF_PATH export INC_NETCDF=\${NETCDF_PATH}/include @@ -322,7 +373,7 @@ EOF ;; * ) - echo "Only setup to work on: cheyenne, hobart and izumi" + echo "Only setup to work on: derecho, cheyenne, hobart and izumi" exit @@ -380,6 +431,13 @@ else fi fi +# Setup conda environement +conda activate ctsm_pylib +if [ \$? -ne 0 ]; then + echo "ERROR: Trouble activating the ctsm_pylib conda environment, be sure it's setup with \$CLM_ROOT/py_env_create, then rerun" + exit 4 +fi + ##output files clm_log=\${initdir}/td.\${JOBID}.log if [ -f \$clm_log ]; then @@ -634,7 +692,7 @@ case $arg1 in * ) echo "" echo "**********************" - echo "usage on cheyenne, hobart, and izumi: " + echo "usage on derecho, cheyenne, hobart, and izumi: " echo "./test_driver.sh -i" echo "" echo "valid arguments: " diff --git a/test/tools/tests_posttag_hobart_nompi b/test/tools/tests_posttag_hobart_nompi index 9f07863e4d..d3cbccecdf 100644 --- a/test/tools/tests_posttag_hobart_nompi +++ b/test/tools/tests_posttag_hobart_nompi @@ -1,4 +1,3 @@ smc#4 blc#4 smi54 bli54 smi57 bli57 -smiT4 bliT4 diff --git a/test/tools/tests_posttag_izumi_nompi b/test/tools/tests_posttag_izumi_nompi index 62687a7e3d..3e84fb8459 100644 --- a/test/tools/tests_posttag_izumi_nompi +++ b/test/tools/tests_posttag_izumi_nompi @@ -1,3 +1,2 @@ smi54 bli54 smi57 bli57 -smiT4 bliT4 diff --git a/test/tools/tests_posttag_nompi_regression b/test/tools/tests_posttag_nompi_regression index 1395aebe11..b665409c51 100644 --- a/test/tools/tests_posttag_nompi_regression +++ b/test/tools/tests_posttag_nompi_regression @@ -5,7 +5,3 @@ smi53 bli53 smi54 bli54 smi57 bli57 smi58 bli58 -smi74 bli74 -smi78 bli78 -smiT4 bliT4 -smiT2 bliT2 diff --git a/test/tools/tests_pretag_cheyenne_nompi b/test/tools/tests_pretag_cheyenne_nompi index 19e96594bf..6ce4972915 100644 --- a/test/tools/tests_pretag_cheyenne_nompi +++ b/test/tools/tests_pretag_cheyenne_nompi @@ -1,4 +1,3 @@ -smi79 bli79 smc#4 blc#4 smg54 blg54 smba1 blba1 @@ -10,6 +9,3 @@ smi64 bli64 smi54 bli54 smi57 bli57 smi58 bli58 -smi74 bli74 -smiT4 bliT4 -smiT2 bliT2 diff --git a/test/tools/tests_pretag_derecho_nompi b/test/tools/tests_pretag_derecho_nompi new file mode 100644 index 0000000000..5fdaf335ae --- /dev/null +++ b/test/tools/tests_pretag_derecho_nompi @@ -0,0 +1,9 @@ +smba1 blba1 +smbd1 blbd1 +sm0a1 bl0a1 +sm0c1 bl0c1 +smaa2 blaa2 +smba1 blba1 +smb81 blb81 +smbc1 blbc1 +smbd1 blbd1 diff --git a/test/tools/tests_pretag_nompi_neon b/test/tools/tests_pretag_nompi_neon index 43167e71c0..e5fa27e6c4 100644 --- a/test/tools/tests_pretag_nompi_neon +++ b/test/tools/tests_pretag_nompi_neon @@ -1,4 +1,5 @@ sm0a1 bl0a1 +sm0c1 bl0c1 smaa2 blaa2 smba1 blba1 smbb1 blbb1 diff --git a/tools/README b/tools/README index a35cff8b7c..2aaecc3bd8 100644 --- a/tools/README +++ b/tools/README @@ -8,6 +8,8 @@ I. General directory structure: $CTSMROOT/tools mksurfdata_map --- Create surface datasets. + crop_calendars --- Regrid and process GGCMI sowing and harvest date files for use in CTSM. + mkmapgrids ------- Create regular lat/lon SCRIP grid files needed by mkmapdata mkmapdata -------- Create SCRIP mapping data from SCRIP grid files (uses ESMF) mkprocdata_map --- Convert output unstructured grids into a 2D format that diff --git a/tools/contrib/README b/tools/contrib/README index 43884abe69..4cf9c68fc4 100644 --- a/tools/contrib/README +++ b/tools/contrib/README @@ -10,7 +10,7 @@ The python scripts require the following settings before running on cheyenne: module load conda ../../py_env_create -conda activate ctsm_py +conda activate ctsm_pylib Brief description of scripts: diff --git a/tools/contrib/add_tillage_to_paramsfile.py b/tools/contrib/add_tillage_to_paramsfile.py new file mode 100644 index 0000000000..ebea329ef6 --- /dev/null +++ b/tools/contrib/add_tillage_to_paramsfile.py @@ -0,0 +1,171 @@ +import xarray as xr +import numpy as np +import os +import subprocess +import argparse +import sys + +def make_dataarray(np_array, decomp_model, ntill_intensities_max, ndecomp_pools_max, ntill_stages_max): + intensities_dim = xr.IndexVariable( + dims = "ntill_intensities_max", + data = np.arange(ntill_intensities_max), + ) + pools_dim = xr.IndexVariable( + dims = "ndecomp_pools_max", + data = np.arange(ndecomp_pools_max), + ) + stages_dim = xr.IndexVariable( + dims = "ntill_stages_max", + data = np.arange(ntill_stages_max), + ) + + # Name DataArray + if decomp_model.lower() == "mimics": + da_name = "mimics" + elif decomp_model.lower() == "century": + da_name = "bgc" + da_name += "_till_decompk_multipliers" + + da = xr.DataArray( + data = np_array, + dims = { + "ntill_intensities_max": intensities_dim, + "ndecomp_pools_max": pools_dim, + "ntill_stages_max": stages_dim + }, + name = da_name, + attrs = { + "long_name": f"Value by which decomp_k should be multiplied during tillage with {decomp_model} soil", + "units": "unitless", + } + ) + + # netCDF variable needs dimensions reversed from how they're specified in code + da = da.transpose() + + return da + +def main(file_in, + file_out): + # Get git info + thisDir = os.path.dirname(__file__) + git_status = subprocess.run( + ["git", "status"], + capture_output=True, + ) + git_status = git_status.stdout.decode() + repo_is_clean = "working tree clean" in git_status + if not repo_is_clean: + print("WARNING: Repo not clean; will not save params file.") + print(git_status) + git_log = subprocess.run( + ["git", "log", "-1"], + capture_output=True, + ) + git_log = git_log.stdout.decode() + + + # Set up dimensions + ds0 = xr.open_dataset(file_in) + ntill_intensities_max = 2 + ndecomp_pools_max = ds0.dims["ndecomp_pools_max"] + ntill_stages_max = 3 + tillage_shape_ips = (ntill_intensities_max, ndecomp_pools_max, ntill_stages_max) + tillage_shape_ps = (ndecomp_pools_max, ntill_stages_max) + + + # Fill CENTURY array + # Define pool indices + i_litr_min = 0 # 1 in FORTRAN, but Python is 0-indexed + i_met_lit = i_litr_min + i_cel_lit = i_met_lit + 1 + i_lig_lit = i_cel_lit + 1 + i_act_som = i_lig_lit + 1 + i_slo_som = i_act_som + 1 + i_pas_som = i_slo_som + 1 + tillage_century = np.full(tillage_shape_ips, 1.0) + tillage_century_lo = np.full(tillage_shape_ps, 1.0) + tillage_century_lo[i_act_som,:] = np.array([1.0, 1.0, 1.0]) + tillage_century_lo[i_slo_som,:] = np.array([3.0, 1.6, 1.3]) + tillage_century_lo[i_pas_som,:] = np.array([3.0, 1.6, 1.3]) + tillage_century_lo[i_cel_lit,:] = np.array([1.5, 1.5, 1.1]) + tillage_century_lo[i_lig_lit,:] = np.array([1.5, 1.5, 1.1]) + tillage_century[0,:,:] = tillage_century_lo + tillage_century_hi = np.full(tillage_shape_ps, 1.0) + tillage_century_hi[i_act_som,:] = np.array([1.2, 1.0, 1.0]) + tillage_century_hi[i_slo_som,:] = np.array([4.8, 3.5, 2.5]) + tillage_century_hi[i_pas_som,:] = np.array([4.8, 3.5, 2.5]) + tillage_century_hi[i_cel_lit,:] = np.array([1.8, 1.5, 1.1]) + tillage_century_hi[i_lig_lit,:] = np.array([1.8, 1.5, 1.1]) + tillage_century[1,:,:] = tillage_century_hi + + + # Fill MIMICS array + i_litr_min = 1 + i_met_lit = i_litr_min + i_str_lit = i_met_lit + 1 + i_avl_som = i_str_lit + 1 + i_chem_som = i_avl_som + 1 + i_phys_som = i_chem_som + 1 + tillage_mimics = np.full(tillage_shape_ips, 1.0) + tillage_mimics[:,i_avl_som,:] = tillage_century[:,i_act_som,:] + tillage_mimics[:,i_chem_som,:] = tillage_century[:,i_slo_som,:] + tillage_mimics[:,i_phys_som,:] = tillage_century[:,i_pas_som,:] + if not np.array_equal(tillage_century[:,i_cel_lit,:], tillage_century[:,i_lig_lit,:]): + raise RuntimeError("How to combine 2 CENTURY litter pools into 1 MIMICS litter pool?") + tillage_mimics[:,i_str_lit,:] = tillage_century[:,i_cel_lit,:] + + # Make DataArrays + tillage_century_da = make_dataarray( + tillage_century, "CENTURY", + ntill_intensities_max, ndecomp_pools_max, ntill_stages_max) + tillage_mimics_da = make_dataarray( + tillage_mimics, "MIMICS", + ntill_intensities_max, ndecomp_pools_max, ntill_stages_max) + + if not repo_is_clean: + raise RuntimeError("Clean up git repo before trying to save!") + + ds1 = ds0.copy() + ds0.close() + + ds1[tillage_century_da.name] = tillage_century_da + ds1[tillage_mimics_da.name] = tillage_mimics_da + ds1.attrs['latest_git_log'] = git_log + + ds1.to_netcdf(file_out, format="NETCDF3_CLASSIC") + + +if __name__ == "__main__": + ############################### + ### Process input arguments ### + ############################### + parser = argparse.ArgumentParser( + description="Adds tillage parameters to a CLM parameter file (netCDF)." + ) + + # Define arguments + parser.add_argument( + "-i", + "--input-file", + help="Parameter file (netCDF) to which you wish to add tillage parameters.", + type=str, + required=True, + ) + parser.add_argument( + "-o", + "--output-file", + help="Output parameter file.", + type=str, + required=True, + ) + + # Get arguments + args = parser.parse_args(sys.argv[1:]) + + ########### + ### Run ### + ########### + main(os.path.realpath(args.input_file), + os.path.realpath(args.output_file), + ) \ No newline at end of file diff --git a/tools/contrib/ssp_anomaly_forcing_smooth b/tools/contrib/ssp_anomaly_forcing_smooth index 94658f3d54..362e47c67d 100755 --- a/tools/contrib/ssp_anomaly_forcing_smooth +++ b/tools/contrib/ssp_anomaly_forcing_smooth @@ -3,12 +3,12 @@ ssp_anomaly_forcing_smooth -Create anomoly forcing datasets for SSP scenarios that can be used by CESM datm model +Create anomaly forcing datasets for SSP scenarios that can be used by CESM datm model load proper modules first, i.e. ../../py_env_create -conda activate ctsm_py +conda activate ctsm_pylib """ import sys @@ -21,6 +21,169 @@ import numpy as np import netCDF4 as netcdf4 +# Adds global attributes, returning hdir and fdir +def add_global_attributes(ds, historydate, histdir, sspdir, num_ens, climo_year, climo_base_nyrs, dpath, dfile, hist_yrstart, hist_yrend, ssp_yrstart, ssp_yrend, timetag): + ds.Created_on = timetag + + ds.title = "anomaly forcing data" + ds.note1 = ( + "Anomaly/scale factors calculated relative to " + + str(climo_year - (climo_base_nyrs - 1) / 2) + + "-" + + str(climo_year + (climo_base_nyrs - 1) / 2) + ) + ds.history = historydate + ": created by " + sys.argv[0] + stdout = os.popen("git describe") + ds.gitdescribe = stdout.read().rstrip() + ds.Source = "CMIP6 CESM simulations" + ds.Conventions = "CF-1.0" + ds.comment = ( + "Monthly scale factors for given SSP scenario compared to a climatology based on" + + " data centered on " + + str(climo_year) + + " over the range given in note1" + ) + ds.number_of_ensemble_members = str(num_ens) + ds.Created_by = getuser() + + for nens in range(num_ens): + hdir = dpath + histdir[nens] + dfile + fdir = dpath + sspdir[nens] + dfile + if nens == 0: + ds.Created_from_historical_dirs = hdir + ds.Created_from_scenario_dirs = fdir + else: + ds.Created_from_historical_dirs += ", " + hdir + ds.Created_from_scenario_dirs += ", " + fdir + + ds.History_years = str(hist_yrstart) + "," + str(hist_yrend) + ds.Scenario_years = str(ssp_yrstart) + "," + str(ssp_yrend) + ds.institution = "National Center for Atmospheric Research" + return hdir,fdir + + +def create_fill_latlon(ds, data, var_name): + + ds.createDimension(var_name, int(data.size)) + wl = ds.createVariable(var_name, np.float64, (var_name,)) + + if var_name == "lat": + wl.units = "degrees_north" + wl.long_name = "Latitude" + elif var_name == "lon": + wl.units = "degrees_east" + wl.long_name = "Longitude" + wl.mode = "time-invariant" + + wl[:] = data + + return ds + + +def create_fill_time(ds, time, ntime, ssp_time_units=None, ssp_time_longname=None, adj_time=False): + if ntime is not None: + ntime = int(ntime) + ds.createDimension("time", ntime) + + wtime = ds.createVariable("time", np.float64, ("time",)) + + if ssp_time_units is not None: + wtime.units = ssp_time_units + if ssp_time_longname is not None: + wtime.long_name = ssp_time_longname + wtime.calendar = "noleap" + + # adjust time to middle of month + if adj_time: + wtime_offset = 15 - time[0] + wtime[:] = time + wtime_offset + else: + wtime[:] = time + + return ds + + +def create_fill_ancillary_vars(ds, landfrac, landmask, area): + + wmask = ds.createVariable("landmask", np.int32, ("lat", "lon")) + warea = ds.createVariable("area", np.float64, ("lat", "lon")) + wfrac = ds.createVariable("landfrac", np.float64, ("lat", "lon")) + + warea.units = "km2" + wfrac.units = "unitless" + wmask.units = "unitless" + + warea.long_name = "Grid cell area" + wfrac.long_name = "Grid cell land fraction" + wmask.long_name = "Grid cell land mask" + + warea.mode = "time-invariant" + wfrac.mode = "time-invariant" + wmask.mode = "time-invariant" + + # write to file -------------------------------------------- + wmask[:, :] = landmask + wfrac[:, :] = landfrac + warea[:, :] = area + + return ds + + +def add_to_dataset(ds, var_name, data, units=None, mode=None, historical_source_files=None, scenario_source_files=None, long_name=None, cell_methods=None): + dims = ("time", "lat", "lon") + data_type = np.float64 + + wvar = ds.createVariable( + var_name, + data_type, + dims, + fill_value=data_type(1.0e36), + ) + + wvar[:, :, :] = data + + if units is not None: + wvar.units = units + if mode is not None: + wvar.mode = mode + if historical_source_files is not None: + wvar.historical_source_files = historical_source_files + if scenario_source_files is not None: + wvar.scenario_source_files = scenario_source_files + if long_name is not None: + wvar.long_name = long_name + if cell_methods is not None: + wvar.cell_methods = cell_methods + + return ds + + +def create_fill_forcing(ds, field_out, units, anomsf, field_out_wind, f, hdir, fdir, histfiles, sspfiles, long_name, anom_fld): + + historical_source_files = "".join(histfiles).replace(hdir, "") + scenario_source_files = "".join(sspfiles).replace(fdir, "") + mode = "time-dependent" + + if field_out[f] == "sfcWind": + long_name = str(long_name) + " U component " + anomsf[f] + var_name = field_out_wind[0] + data = anom_fld / np.sqrt(2) + else: + long_name = str(long_name) + " " + anomsf[f] + var_name = field_out[f] + data = anom_fld + # Was missing cell_methods attribute in original + ds = add_to_dataset(ds, var_name, data, units=units[f], mode=mode, historical_source_files=historical_source_files, scenario_source_files=scenario_source_files, long_name=long_name) + + if field_out[f] == "sfcWind": + long_name = long_name.replace("U component", "V component") + var_name = field_out_wind[1] + # Was missing mode attribute in original + ds = add_to_dataset(ds, var_name, data, units=units[f], historical_source_files=historical_source_files, scenario_source_files=scenario_source_files, long_name=long_name, cell_methods="time: mean") + + return ds + + parser = argparse.ArgumentParser(description="Create anomaly forcing") parser.add_argument( "sspnum", @@ -31,16 +194,25 @@ parser.add_argument( ) parser.add_argument( "--write_climo", + "--write-climo", help="write out climatology files and exit", action="store_true", default=False, ) parser.add_argument( "--print_ssps", + "--print-ssps", help="Just print out directory names and exit", action="store_true", default=False, ) +parser.add_argument( + "--output_dir", "--output-dir", + help="Top-level output directory (default: ./anomaly_forcing/). Sub-directory will be created for the selected scenario.", + type=str, + default=os.path.join(".", "anomaly_forcing"), +) + args = parser.parse_args() if args.sspnum == 0: @@ -48,7 +220,7 @@ if args.sspnum == 0: # ------------------------------------------------------- -print("Create anomoly forcing data that can be used by CTSM in CESM") +print("Create anomaly forcing data that can be used by CTSM in CESM") # Input and output directories make sure they exist datapath = "/glade/campaign/collections/cmip/CMIP6/timeseries-cmip6/" # Path on casper @@ -109,15 +281,13 @@ _v2 is just used for restart files that have been spatially interpolated """ -spath = "./" if os.path.exists(datapath): print("Input data directory:" + datapath) else: sys.exit("Could not find input directory: " + datapath) -if os.path.exists(spath): - print("Output data directory:" + spath) -else: - sys.exit("Could not find output directory: " + spath) +if not os.path.exists(args.output_dir): + os.makedirs(args.output_dir) +print("Output data directory:" + args.output_dir) # Settings to run with today = datetime.date.today() @@ -165,9 +335,6 @@ if args.print_ssps: sspnum = args.sspnum -# hist_case needed? -hist_case = "b.e21.BHIST.f09_g17.CMIP6-historical.010" - if sspnum == 1: # SSP1-26 ssptag = "SSP1-2.6" @@ -196,25 +363,15 @@ if num_ens != len(histdir): print("number of ensemble members not the same") sys.exit("number of members different") -# test w/ 1 ensemble member -num_ens = 3 - # Setup output directory -sspoutdir = "anomaly_forcing/CMIP6-" + ssptag +sspoutdir = "CMIP6-" + ssptag -outdir = spath + sspoutdir +outdir = os.path.join(args.output_dir, sspoutdir) if not os.path.exists(outdir): os.makedirs(outdir) print("Output specific data directory :" + outdir) -# historical files are split by 50 year periods; use last period -hist_suffix = ["200001-201412.nc"] # not standardized?! -# hist_suffix = ['-201412.nc'] -# projections are split 2015/2064 2065/2100 -ssp_suffix = ["201501-206412.nc", "206501-210012.nc"] -# ssp_suffix = ['-206412.nc','-210012.nc'] - climo_year = 2015 # ten years on either side (21 years total) climo_base_nyrs = 21 @@ -244,7 +401,11 @@ field_out_wind = ["uas", "vas"] nfields = len(field_in) +output_format = "NETCDF3_64BIT_DATA" + # -- Loop over forcing fields ------------------------------------ + + for f in range(nfields): # -- Loop over ensemble members ------------------------------ @@ -482,71 +643,31 @@ for f in range(nfields): if write_climo: # Use NetCDF4 format, because using older NetCDF formats are too slow w = netcdf4.Dataset( - outdir + field_out[f] + "_climo" + creationdate + ".nc", + os.path.join(outdir, field_out[f] + "_climo" + creationdate + ".nc"), "w", - format="NETCDF3_64BIT_DATA" + format=output_format, ) - w.createDimension("lat", int(nlat)) - w.createDimension("lon", int(nlon)) - w.createDimension("time", int(nmo)) - - wtime = w.createVariable("time", np.float64, ("time",)) - wlat = w.createVariable("lat", np.float64, ("lat",)) - wlon = w.createVariable("lon", np.float64, ("lon",)) - wvar = w.createVariable( - field_out[f], - np.float64, - ("time", "lat", "lon"), - fill_value=np.float64(1.0e36), - ) - wtime[ - :, - ] = time[0:12] - wlon[ - :, - ] = lon - wlat[ - :, - ] = lat - wvar[:, :, :] = climo + w = create_fill_latlon(w, lat, "lat") + w = create_fill_latlon(w, lon, "lon") + w = create_fill_time(w, time[0:12], nmo) + + add_to_dataset(w, field_out[f], climo) w.close() # Use NetCDF4 format, because using older NetCDF formats are too slow w = netcdf4.Dataset( - outdir + field_out[f] + "_smooth" + creationdate + ".nc", + os.path.join(outdir, field_out[f] + "_smooth" + creationdate + ".nc"), "w", - format="NETCDF3_64BIT_DATA" - ) - w.createDimension("lat", int(nlat)) - w.createDimension("lon", int(nlon)) - w.createDimension("time", int(tm)) - - wtime = w.createVariable("time", np.float64, ("time",)) - wlat = w.createVariable("lat", np.float64, ("lat",)) - wlon = w.createVariable("lon", np.float64, ("lon",)) - wvar = w.createVariable( - field_out[f], - np.float64, - ("time", "lat", "lon"), - fill_value=np.float64(1.0e36), + format=output_format, ) - wvar2 = w.createVariable( - "smooth_" + field_out[f], - np.float64, - ("time", "lat", "lon"), - fill_value=np.float64(1.0e36), - ) - - wtime[:] = time - wlon[ - :, - ] = lon - wlat[ - :, - ] = lat - wvar[:, :, :] = temp_fld - wvar2[:, :, :] = stemp_fld + w = create_fill_latlon(w, lat, "lat") + w = create_fill_latlon(w, lon, "lon") + w = create_fill_time(w, time, tm) + + add_to_dataset(w, field_out[f], temp_fld) + add_to_dataset(w, "smooth_" + field_out[f], stemp_fld) w.close() + print("Exit early after writing out climatology\n\n") sys.exit() @@ -556,9 +677,9 @@ for f in range(nfields): # Use NetCDF4 format, because using older NetCDF formats are too slow # Will need to convert to CDF5 format at the end, as we can't seem to # output in CDF5 format using netCDF4 python interfaces - outfilename = outdir + "/" + "af.allvars" + outfile_suffix + outfilename = os.path.join(outdir, "af.allvars" + outfile_suffix) print("Creating: " + outfilename) - outfile = netcdf4.Dataset(outfilename, "w", format="NETCDF3_64BIT_DATA") + outfile = netcdf4.Dataset(outfilename, "w", format=output_format) # creation date on the file command = 'date "+%Y/%m/%d"' @@ -566,148 +687,21 @@ for f in range(nfields): x = x2.communicate() timetag = x[0].decode("utf-8").strip() - outfile.Created_on = timetag + # Add global attributes and get hdir/fdir + hdir, fdir = add_global_attributes(outfile, historydate, histdir, sspdir, num_ens, climo_year, climo_base_nyrs, dpath, dfile, hist_yrstart, hist_yrend, ssp_yrstart, ssp_yrend, timetag) - outfile.title = "anomaly forcing data" - outfile.note1 = ( - "Anomaly/scale factors calculated relative to " - + str(climo_year - (climo_base_nyrs - 1) / 2) - + "-" - + str(climo_year + (climo_base_nyrs - 1) / 2) - ) - outfile.history = historydate + ": created by " + sys.argv[0] - stdout = os.popen("git describe") - outfile.gitdescribe = stdout.read().rstrip() - outfile.Source = "CMIP6 CESM simulations" - outfile.Conventions = "CF-1.0" - outfile.comment = ( - "Monthly scale factors for given SSP scenario compared to a climatology based on" - + " data centered on " - + str(climo_year) - + " over the range given in note1" - ) - outfile.number_of_ensemble_members = str(num_ens) - outfile.Created_by = getuser() - - for nens in range(num_ens): - hdir = dpath + histdir[nens] + dfile - fdir = dpath + sspdir[nens] + dfile - if nens == 0: - outfile.Created_from_historical_dirs = hdir - outfile.Created_from_scenario_dirs = fdir - else: - outfile.Created_from_historical_dirs += ", " + hdir - outfile.Created_from_scenario_dirs += ", " + fdir - - outfile.History_years = str(hist_yrstart) + "," + str(hist_yrend) - outfile.Scenario_years = str(ssp_yrstart) + "," + str(ssp_yrend) - outfile.institution = "National Center for Atmospheric Research" - - outfile.createDimension("lat", size=int(nlat)) - outfile.createDimension("lon", size=int(nlon)) - outfile.createDimension("time", None) - - wtime = outfile.createVariable("time", np.float64, ("time",)) - wlat = outfile.createVariable("lat", np.float64, ("lat",)) - wlon = outfile.createVariable("lon", np.float64, ("lon",)) - wmask = outfile.createVariable("landmask", np.int32, ("lat", "lon")) - warea = outfile.createVariable("area", np.float64, ("lat", "lon")) - wfrac = outfile.createVariable("landfrac", np.float64, ("lat", "lon")) - wtime.units = ssp_time_units - wlon.units = "degrees_east" - wlat.units = "degrees_north" - warea.units = "km2" - wfrac.units = "unitless" - wmask.units = "unitless" + # Create dimensions + outfile = create_fill_latlon(outfile, lat, "lat") + outfile = create_fill_latlon(outfile, lon, "lon") + outfile = create_fill_time(outfile, ssp_time, None, ssp_time_units=ssp_time_units, ssp_time_longname=ssp_time_longname, adj_time=True) - # wtime.long_name = 'Months since January '+str(fut_yrstart) - wtime.long_name = ssp_time_longname - wlon.long_name = "Longitude" - wlat.long_name = "Latitude" - warea.long_name = "Grid cell area" - wfrac.long_name = "Grid cell land fraction" - wmask.long_name = "Grid cell land mask" - wlon.mode = "time-invariant" - wlat.mode = "time-invariant" - warea.mode = "time-invariant" - wfrac.mode = "time-invariant" - wmask.mode = "time-invariant" - - wtime.calendar = "noleap" - - # write to file -------------------------------------------- - # wtime_offset = 0 - # adjust time to middle of month - # wtime_offset = -15 - wtime_offset = 15 - ssp_time[0] - wtime[:] = ssp_time + wtime_offset - wtime.calendar = "noleap" - wlon[:] = lon - wlat[:] = lat - wmask[:, :] = landmask - wfrac[:, :] = landfrac - warea[:, :] = area + # Create and fill ancillary variables + outfile = create_fill_ancillary_vars(outfile, landfrac, landmask, area) # -- End if on open file - if field_out[f] == "sfcWind": - wvar = outfile.createVariable( - field_out_wind[0], - np.float64, - ("time", "lat", "lon"), - fill_value=np.float64(1.0e36), - ) - else: - wvar = outfile.createVariable( - field_out[f], - np.float64, - ("time", "lat", "lon"), - fill_value=np.float64(1.0e36), - ) - wvar.units = units[f] - wvar.mode = "time-dependent" - - # write to file -------------------------------------------- - if field_out[f] == "sfcWind": - wvar.long_name = str(long_name) + " U component " + anomsf[f] - else: - wvar.long_name = str(long_name) + " " + anomsf[f] - - if field_out[f] == "sfcWind": - wvar[:, :, :] = anom_fld / np.sqrt(2) - else: - wvar[:, :, :] = anom_fld - - # List of source files - wvar.historical_source_files = "".join(histfiles).replace(hdir, "") - wvar.scenario_source_files = "".join(sspfiles).replace(fdir, "") - - # create second wind field for V component - if field_out[f] == "sfcWind": - command = 'date "+%y%m%d"' - x2 = subprocess.Popen(command, stdout=subprocess.PIPE, shell="True") - x = x2.communicate() - timetag = x[0].decode("utf-8").strip() - - wvar = outfile.createVariable( - field_out_wind[1], - np.float64, - ("time", "lat", "lon"), - fill_value=np.float64(1.0e36), - ) - wvar.units = units[f] - wvar.cell_methods = "time: mean" - wvar.long_name = str(long_name) + " V component " + anomsf[f] - - # write to file -------------------------------------------- - wvar[:, :, :] = anom_fld / np.sqrt(2) - - # List of source files - wvar.historical_source_files = "".join(histfiles).replace(hdir, "") - wvar.scenario_source_files = "".join(sspfiles).replace(fdir, "") - - # -- end if statement for write for V field -------- + outfile = create_fill_forcing(outfile, field_out, units, anomsf, field_out_wind, f, hdir, fdir, histfiles, sspfiles, long_name, anom_fld) # -- End Loop over forcing fields ------------------------------------ outfile.close() -print("\n\nSuccessfully made anomoly forcing datasets\n") +print("\n\nSuccessfully made anomaly forcing datasets\n") diff --git a/tools/crop_calendars/process_ggcmi_shdates b/tools/crop_calendars/process_ggcmi_shdates new file mode 100755 index 0000000000..587d790e32 --- /dev/null +++ b/tools/crop_calendars/process_ggcmi_shdates @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +""" +For description and instructions, please see README. Raw GGCMI sowing and harvest dates are on Derecho and Casper at /glade/campaign/cgd/tss/people/samrabin/raw_ggcmi3_v1.01_nc4/ +""" + +import os +import sys + +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, + os.pardir, + 'python') +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.crop_calendars.process_ggcmi_shdates import main + +if __name__ == "__main__": + main() + diff --git a/tools/crop_calendars/regrid_ggcmi_shdates b/tools/crop_calendars/regrid_ggcmi_shdates new file mode 100755 index 0000000000..33dafa12c2 --- /dev/null +++ b/tools/crop_calendars/regrid_ggcmi_shdates @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +""" +For description and instructions, please see README. Raw GGCMI sowing and harvest dates are on Derecho and Casper at /glade/campaign/cgd/tss/people/samrabin/raw_ggcmi3_v1.01_nc4/ +""" + +import os +import sys + +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, + os.pardir, + 'python') +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.crop_calendars.regrid_ggcmi_shdates import main + +if __name__ == "__main__": + main() + diff --git a/tools/mksurfdata_map/Makefile.data b/tools/mksurfdata_map/Makefile.data index d0c000ba63..1609b35a75 100644 --- a/tools/mksurfdata_map/Makefile.data +++ b/tools/mksurfdata_map/Makefile.data @@ -53,13 +53,35 @@ else endif MKSURFDATA = $(BATCHJOBS) $(PWD)/mksurfdata.pl +SUBSETDATA = $(PWD)/../site_and_regional/subset_data +MODIFYSURF = $(PWD)/../modify_input_files/fsurdat_modifier --overwrite +CDATE = $(shell date +%y%m%d) + +# subset_data options +# +SUBSETDATA_POINT = $(SUBSETDATA) point --silent --overwrite --uniform-snowpack --cap-saturation --crop --outdir . +SUBSETDATA_POINT_ALLLU = $(SUBSETDATA_POINT) --include-nonveg +SUBSETDATA_POINT_URBAN = $(SUBSETDATA_POINT) --include-nonveg + +# Subset data sites... +SUBSETDATA_1X1_BRAZIL := --lat -7 --lon -55 --site 1x1_brazil +SUBSETDATA_1X1_NUMAIA := --lat 40.6878 --lon 267.0228 --site 1x1_numaIA +SUBSETDATA_1X1_SMALL := --lat 40.6878 --lon 267.0228 --site 1x1_smallvilleIA \ + --dompft 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 \ + --pctpft 6.5 1.5 1.6 1.7 1.8 1.9 1.5 1.6 1.7 1.8 1.9 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 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 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 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 +# NOTE: The 1850 smallvilleIA site is constructed to start with 100% natural vegetation, so we can test transition to crops +SUBSETDATA_1X1_SMALL1850 := --lat 40.6878 --lon 267.0228 --site 1x1_smallvilleIA --dompft 13 --pctpft 100 + +SUBSETDATA_1X1_MEXICOCITY := --lat 19.5 --lon 260.5 --site 1x1_mexicocityMEX --out-surface surfdata_1x1_mexicocityMEX_hist_78pfts_CMIP6_simyr2000.nc +SUBSETDATA_1X1_VANCOUVER := --lat 49.5 --lon 236.5 --site 1x1_vancouverCAN --out-surface surfdata_1x1_vancouverCAN_hist_78pfts_CMIP6_simyr2000.nc +SUBSETDATA_1X1_URBALPHA := --lat -37.7308 --lon 0 --site 1x1_urbanc_alpha --out-surface surfdata_1x1_urbanc_alpha_hist_78pfts_CMIP6_simyr2000.nc # f19 and f09 are standard resolutions, f10 is used for testing, f45 is used for FATES # ne30np4 is standard resolution for SE dycore in CAM, C96 is standard for fv3 dycore # The ne30np4 series (including pg2, pg3, pg4) are standard for SE dycore # The variable resolution grids for ARCTIC, ARCTICGRIS and CONUS are also standard STANDARD_RES_NO_CROP = 0.9x1.25,1.9x2.5,10x15 -STANDARD_RES = 0.9x1.25,1.9x2.5,10x15,4x5,ne30np4,C96,ne30pg2,ne30pg3,ne30pg4,ne120np4pg3,ne0np4ARCTICGRISne30x8,ne0np4ARCTICne30x4,ne0np4CONUSne30x8 +STANDARD_RES = 0.9x1.25,1.9x2.5,10x15,4x5,ne30np4,C96,ne30pg2,ne30pg3,ne30pg4,ne120np4pg3,ne0np4ARCTICGRISne30x8,ne0np4ARCTICne30x4,ne0np4CONUSne30x8,ne3np4.pg3,ne5np4.pg3,ne16np4.pg3,mpasa480,mpasa120 # For future CMIP6 scenarios: SSP-RCP's FUTURE_RES = 0.9x1.25,1.9x2.5,10x15 @@ -91,14 +113,24 @@ CROP = \ crop-global-historical \ crop-global-transient \ crop-global-future - all : standard tropics crop urban landuse-timeseries +all-subset : \ + 1x1_brazil-tropics-present \ + crop-tropics-historical \ + crop-tropics-transient \ + crop-numa-present \ + crop-numa-historical \ + crop-smallville \ + crop-smallville-historical \ + urban-present urban-alpha + DEBUG: @echo "HOST := $(HOST)" @echo "PROJECT := $(PROJECT)" @echo "BATCHJOBS := $(BATCHJOBS)" @echo "BACKGROUND := $(BACKGROUND)" + # # standard # @@ -119,14 +151,18 @@ global-present-nldas : FORCE # tropics : $(TROPICS) -crop-tropics-present : FORCE - $(MKSURFDATA) -glc_nec 10 -y 2000 -res 5x5_amazon,1x1_brazil $(BACKGROUND) +crop-tropics-present : brazil-tropics-present + $(MKSURFDATA) -glc_nec 10 -y 2000 -res 5x5_amazon $(BACKGROUND) + +1x1_brazil-tropics-present : FORCE + $(SUBSETDATA_POINT_ALLLU) --create-surface $(SUBSETDATA_1X1_BRAZIL) + crop-tropics-historical : FORCE - $(MKSURFDATA) -glc_nec 10 -y 1850 -res 1x1_brazil $(BACKGROUND) + $(SUBSETDATA_POINT_ALLLU) --create-surface $(SUBSETDATA_1X1_BRAZIL) --cfg-file default_data_1850.cfg crop-tropics-transient : FORCE - $(MKSURFDATA) -glc_nec 10 -no_surfdata -y 1850-2000 -res 1x1_brazil $(BACKGROUND) + $(SUBSETDATA_POINT_ALLLU) --create-landuse $(SUBSETDATA_1X1_BRAZIL) # # crop @@ -143,16 +179,13 @@ crop-global-present-f05 : FORCE $(MKSURFDATA) -glc_nec 10 -y 1850,2000 -res 0.47x0.63 $(BACKGROUND) crop-numa-present : FORCE - $(MKSURFDATA) -glc_nec 10 -y 2000 -r 1x1_numaIA $(BACKGROUND) + $(SUBSETDATA_POINT_ALLLU) --create-surface $(SUBSETDATA_1X1_NUMAIA) crop-numa-historical : FORCE - $(MKSURFDATA) -glc_nec 10 -y 1850 -r 1x1_numaIA $(BACKGROUND) + $(SUBSETDATA_POINT_ALLLU) --create-surface $(SUBSETDATA_1X1_NUMAIA) --cfg-file default_data_1850.cfg crop-smallville : FORCE - $(MKSURFDATA) -glc_nec 10 -y 2000 -r 1x1_smallvilleIA \ - -pft_idx 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78 \ - -pft_frc 6.5,1.5,1.6,1.7,1.8,1.9,1.5,1.6,1.7,1.8,1.9,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,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,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,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5 \ - $(BACKGROUND) + $(SUBSETDATA_POINT) --create-surface $(SUBSETDATA_1X1_SMALL) crop-global-present-ne16np4 : FORCE $(MKSURFDATA) -glc_nec 10 -y 2000 -res ne16np4 $(BACKGROUND) @@ -165,7 +198,7 @@ crop-global-present-ne120np4 : FORCE # adds crop (to make sure that it works properly to add crop in a grid cell # where there used to be no crop). crop-smallville-historical : FORCE - $(MKSURFDATA) -glc_nec 10 -y 1850 -r 1x1_smallvilleIA -pft_idx 13 -pft_frc 100 $(BACKGROUND) + $(SUBSETDATA_POINT) --create-surface $(SUBSETDATA_1X1_SMALL1850) --cfg-file default_data_1850.cfg # Setup the historical case for SSP5-8.5 so that historical can be used to go into the future. crop-global-historical : FORCE @@ -229,20 +262,31 @@ crop-global-SSP5-8.5 : FORCE # urban : urban-present urban-alpha -urban-present : FORCE - $(MKSURFDATA) -y 2000 -no-crop -glc_nec 10 -r 1x1_vancouverCAN,1x1_mexicocityMEX $(BACKGROUND) +urban-present : mexicocity vancouver + +mexicocity : FORCE + $(SUBSETDATA_POINT_URBAN) --create-surface $(SUBSETDATA_1X1_MEXICOCITY) + $(MODIFYSURF) modify_1x1_mexicocityMEX.cfg -i surfdata_1x1_mexicocityMEX_hist_78pfts_CMIP6_simyr2000.nc -o surfdata_1x1_mexicocityMEX_hist_78pfts_CMIP6_simyr2000_c$(CDATE).nc + $(RM) surfdata_1x1_mexicocityMEX_hist_78pfts_CMIP6_simyr2000.nc + +vancouver : FORCE + $(SUBSETDATA_POINT_URBAN) --create-surface $(SUBSETDATA_1X1_VANCOUVER) + $(MODIFYSURF) modify_1x1_vancouverCAN.cfg -i surfdata_1x1_vancouverCAN_hist_78pfts_CMIP6_simyr2000.nc -o surfdata_1x1_vancouverCAN_hist_78pfts_CMIP6_simyr2000_c$(CDATE).nc + $(RM) surfdata_1x1_vancouverCAN_hist_78pfts_CMIP6_simyr2000.nc # NOTE(bja, 2015-01) skip abort on invalid data necessary as of 2015-01. See # /glade/p/cesm/cseg/inputdata/lnd/clm2/surfdata_map/README_c141219 urban-alpha : FORCE - $(MKSURFDATA) -y 2000 -no-crop -glc_nec 10 -r 1x1_urbanc_alpha -urban_skip_abort_on_invalid_data_check $(BACKGROUND) - + $(SUBSETDATA_POINT_URBAN) --create-surface $(SUBSETDATA_1X1_URBALPHA) + $(MODIFYSURF) modify_1x1_urbanc_alpha.cfg -i surfdata_1x1_urbanc_alpha_hist_78pfts_CMIP6_simyr2000.nc -o surfdata_1x1_urbanc_alpha_hist_78pfts_CMIP6_simyr2000_c$(CDATE).nc + $(RM) surfdata_1x1_urbanc_alpha_hist_78pfts_CMIP6_simyr2000.nc # # landuse timeseries # landuse-timeseries : landuse-timeseries-smallville +# NOTE: TODO: This needs to be chagned to use subset_data when transient configurations are resolved (see Issue #1673 landuse-timeseries-smallville : FORCE $(MKSURFDATA) -no_surfdata -glc_nec 10 -y 1850-1855 -r 1x1_smallvilleIA \ -pft_idx 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78 \ diff --git a/tools/mksurfdata_map/default_data_1850.cfg b/tools/mksurfdata_map/default_data_1850.cfg new file mode 100644 index 0000000000..311aeef13d --- /dev/null +++ b/tools/mksurfdata_map/default_data_1850.cfg @@ -0,0 +1,29 @@ +[main] +clmforcingindir = /glade/p/cesmdata/inputdata + +[datm_gswp3] +dir = /glade/p/cgd/tss/CTSM_datm_forcing_data/atm_forcing.datm7.GSWP3.0.5d.v1.c170516 +domain = domain.lnd.360x720_gswp3.0v1.c170606.nc +solardir = Solar +precdir = Precip +tpqwdir = TPHWL +solartag = clmforc.GSWP3.c2011.0.5x0.5.Solr. +prectag = clmforc.GSWP3.c2011.0.5x0.5.Prec. +tpqwtag = clmforc.GSWP3.c2011.0.5x0.5.TPQWL. +solarname = CLMGSWP3v1.Solar +precname = CLMGSWP3v1.Precip +tpqwname = CLMGSWP3v1.TPQW + +[surfdat] +dir = lnd/clm2/surfdata_map/release-clm5.0.18 +surfdat_16pft = surfdata_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr1850_c190214.nc +surfdat_78pft = surfdata_0.9x1.25_hist_78pfts_CMIP6_simyr1850_c190214.nc + +[landuse] +dir = lnd/clm2/surfdata_map/release-clm5.0.18 +landuse_16pft = landuse.timeseries_0.9x1.25_hist_16pfts_Irrig_CMIP6_simyr1850-2015_c190214.nc +landuse_78pft = landuse.timeseries_0.9x1.25_hist_78pfts_CMIP6_simyr1850-2015_c190214.nc + +[domain] +file = share/domains/domain.lnd.fv0.9x1.25_gx1v7.151020.nc + diff --git a/tools/mksurfdata_map/modify_1x1_mexicocityMEX.cfg b/tools/mksurfdata_map/modify_1x1_mexicocityMEX.cfg new file mode 100644 index 0000000000..6eab73a159 --- /dev/null +++ b/tools/mksurfdata_map/modify_1x1_mexicocityMEX.cfg @@ -0,0 +1,138 @@ +[modify_fsurdat_basic_options] + +lat_dimname = lsmlat +lon_dimname = lsmlon + +# idealized (bool) +# When user wants existing values in fsurdat to persist in all except the +# variables that they explicitly request to change, then set this to False. +# When user wants idealized representation of the land by resetting all +# fsurdat variables, some through this file and others by using hardwired +# defaults, then set this to True. +idealized = False + +# subgrid section to set the PCT_* variables +process_subgrid_section = True +# Variable list section to set specific variable names +process_var_list_section = True + +# Boundaries of user-defined rectangle to apply changes (float) +# If lat_1 > lat_2, the code creates two rectangles, one in the north and +# one in the south. +# If lon_1 > lon_2, the rectangle wraps around the 0-degree meridian. +# Alternatively, user may specify a custom area in a .nc landmask_file +# below. If set, this will override the lat/lon settings. +# ----------------------------------- +# (Use a grid that includes the entire globe as we are just setting a single point) +# southernmost latitude for rectangle +lnd_lat_1 = -90 +# northernmost latitude for rectangle +lnd_lat_2 = 90 +# westernmost longitude for rectangle +lnd_lon_1 = 0 +# easternmost longitude for rectangle +lnd_lon_2 = 360 +# user-defined mask in a file, as alternative to setting lat/lon values +landmask_file = UNSET + +# PFT/CFT to be set to 100% according to user-defined mask. +# If idealized = True and dom_pft = UNSET, the latter defaults to 0 +# (bare soil). Valid values range from 0 to a max value (int) that one can +# obtain from the fsurdat_in file using ncdump (or method preferred by user). +# The max valid value will equal (lsmpft - 1) and will also equal the last +# value of cft(cft). Cannot be set with evenly_split_cropland = True. +dom_pft = UNSET + +# If True, evenly split each gridcell's cropland among all crop types (CFTs). +# Can only be True if dom_pft is UNSET. +evenly_split_cropland = False + +# LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_pft +# If dom_pft = 0, the next four default to 0 (space-delimited list +# of floats without brackets). +lai = UNSET +sai = UNSET +hgt_top = UNSET +hgt_bot = UNSET + +# SOIL_COLOR accepts integer values from 1 to 20 (see CTSM Technote for info). +# if idealized = True and soil_color = UNSET, soil_color = 15. +soil_color = UNSET + +# STD_ELEV (standard deviation of elevation) value (in meters) over the +# user_defined mask (float). +# if idealized = True and std_elev = UNSET, std_elev = 0. +std_elev = UNSET + +# FMAX (maximum fractional saturated area) value (fraction) over the +# user_defined mask (float). +# if idealized = True and max_sat_area = UNSET, max_sat_area = 0. +max_sat_area = UNSET + +# Set non-vegetation landunits to 0 (bool). +zero_nonveg = False + +# Include other land units besides vegetated +include_nonveg = True + +# Section for subgrid_fractions +[modify_fsurdat_subgrid_fractions] +# If subgrid_fractions = True this section will be enabled + +# NOTE: PCT_URBAN must be a list of three floats that sum to the total urban area +PCT_URBAN = 100.0 0.0 0.0 +PCT_CROP = 0.0 +PCT_NATVEG= 0.0 +PCT_GLACIER= 0.0 +PCT_WETLAND= 0.0 +PCT_LAKE = 0.0 + +# Section with a list of variables to prcoess +[modify_fsurdat_variable_list] +# IMPORTANT NOTE: Config file strings are case inssentive! +# +# As such it will check for variable names both in lower case and upper case. +# +# If variable_list = True this section will be enabled +# Can't specify PFT as they are in dom_pft +# Add variables on the file and assign a new value +# can't specify soil_color, max_sat_area or other things that are above. + +# Variables on numurbl which is 3 +CANYON_HWR = 1.18 1.18 1.18 +EM_IMPROAD = 0.95 0.95 0.95 +EM_PERROAD = 0.95 0.95 0.95 +EM_ROOF = 0.9 0.9 0.9 +EM_WALL = 0.85 0.85 0.85 +HT_ROOF = 18.8 18.8 18.8 +THICK_ROOF = 0.185 0.185 0.185 +THICK_WALL = 0.45 0.45 0.45 +T_BUILDING_MIN = 200.0 200.0 200.0 +WIND_HGT_CANYON = 9.4 9.4 9.4 +WTLUNIT_ROOF = 0.55 0.55 0.55 +WTROAD_PERV = 0.04 0.04 0.04 +# NOTE: This variable is integer rather than float +NLEV_IMPROAD = 5 5 5 + +# Variables on numrad which is 2 +ALB_IMPROAD_DIR = 0.08 0.08 +ALB_IMPROAD_DIF = 0.08 0.08 +ALB_PERROAD_DIR = 0.08 0.08 +ALB_PERROAD_DIF = 0.08 0.08 +ALB_ROOF_DIR = 0.2 0.2 +ALB_ROOF_DIF = 0.2 0.2 +ALB_WALL_DIR = 0.25 0.25 +ALB_WALL_DIF = 0.25 0.25 + +# Variabls on nlevurb which is 5 +TK_ROOF = 0.20 0.93 0.93 0.03 0.16 +TK_WALL = 0.88 0.88 0.88 0.88 0.88 +TK_IMPROAD = 0.82 0.82 2.10 2.10 2.10 +CV_ROOF = 1760000.0 1500000.0 1500000.0 250000.0 870000.0 +CV_WALL = 1540000.0 1540000.0 1540000.0 1540000.0 1540000.0 +CV_IMPROAD = 1740000.0 1740000.0 2000000.0 2000000.0 2000000.0 + +# Natural and Crop PFT's don't really need to be set, since they have zero area, but +# it looks better to do so +PCT_NAT_PFT = 100. 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 +PCT_CFT = 100. 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 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 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 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 0.0 0.0 0.0 0.0 0.0 0.0 0.0 diff --git a/tools/mksurfdata_map/modify_1x1_urbanc_alpha.cfg b/tools/mksurfdata_map/modify_1x1_urbanc_alpha.cfg new file mode 100644 index 0000000000..d704b629bd --- /dev/null +++ b/tools/mksurfdata_map/modify_1x1_urbanc_alpha.cfg @@ -0,0 +1,138 @@ +[modify_fsurdat_basic_options] + +lat_dimname = lsmlat +lon_dimname = lsmlon + +# idealized (bool) +# When user wants existing values in fsurdat to persist in all except the +# variables that they explicitly request to change, then set this to False. +# When user wants idealized representation of the land by resetting all +# fsurdat variables, some through this file and others by using hardwired +# defaults, then set this to True. +idealized = False + +# subgrid section to set the PCT_* variables +process_subgrid_section = True +# Variable list section to set specific variable names +process_var_list_section = True + +# Boundaries of user-defined rectangle to apply changes (float) +# If lat_1 > lat_2, the code creates two rectangles, one in the north and +# one in the south. +# If lon_1 > lon_2, the rectangle wraps around the 0-degree meridian. +# Alternatively, user may specify a custom area in a .nc landmask_file +# below. If set, this will override the lat/lon settings. +# ----------------------------------- +# (Use a grid that includes the entire globe as we are just setting a single point) +# southernmost latitude for rectangle +lnd_lat_1 = -90 +# northernmost latitude for rectangle +lnd_lat_2 = 90 +# westernmost longitude for rectangle +lnd_lon_1 = 0 +# easternmost longitude for rectangle +lnd_lon_2 = 360 +# user-defined mask in a file, as alternative to setting lat/lon values +landmask_file = UNSET + +# PFT/CFT to be set to 100% according to user-defined mask. +# If idealized = True and dom_pft = UNSET, the latter defaults to 0 +# (bare soil). Valid values range from 0 to a max value (int) that one can +# obtain from the fsurdat_in file using ncdump (or method preferred by user). +# The max valid value will equal (lsmpft - 1) and will also equal the last +# value of cft(cft). Cannot be set with evenly_split_cropland = True. +dom_pft = UNSET + +# If True, evenly split each gridcell's cropland among all crop types (CFTs). +# Can only be True if dom_pft is UNSET. +evenly_split_cropland = False + +# LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_pft +# If dom_pft = 0, the next four default to 0 (space-delimited list +# of floats without brackets). +lai = UNSET +sai = UNSET +hgt_top = UNSET +hgt_bot = UNSET + +# SOIL_COLOR accepts integer values from 1 to 20 (see CTSM Technote for info). +# if idealized = True and soil_color = UNSET, soil_color = 15. +soil_color = UNSET + +# STD_ELEV (standard deviation of elevation) value (in meters) over the +# user_defined mask (float). +# if idealized = True and std_elev = UNSET, std_elev = 0. +std_elev = UNSET + +# FMAX (maximum fractional saturated area) value (fraction) over the +# user_defined mask (float). +# if idealized = True and max_sat_area = UNSET, max_sat_area = 0. +max_sat_area = UNSET + +# Set non-vegetation landunits to 0 (bool). +zero_nonveg = False + +# Include other land units besides vegetated +include_nonveg = True + +# Section for subgrid_fractions +[modify_fsurdat_subgrid_fractions] +# If subgrid_fractions = True this section will be enabled + +# NOTE: PCT_URBAN must be a list of three floats that sum to the total urban area +PCT_URBAN = 100.0 0.0 0.0 +PCT_CROP = 0.0 +PCT_NATVEG= 0.0 +PCT_GLACIER= 0.0 +PCT_WETLAND= 0.0 +PCT_LAKE = 0.0 + +# Section with a list of variables to prcoess +[modify_fsurdat_variable_list] +# IMPORTANT NOTE: Config file strings are case inssentive! +# +# As such it will check for variable names both in lower case and upper case. +# +# If variable_list = True this section will be enabled +# Can't specify PFT as they are in dom_pft +# Add variables on the file and assign a new value +# can't specify soil_color, max_sat_area or other things that are above. + +# Variables on numurbl which is 3 +CANYON_HWR = 0.42 0.42 0.42 +EM_IMPROAD = 0.973 0.973 0.973 +EM_PERROAD = 0.973 0.973 0.973 +EM_ROOF = 0.973 0.973 0.973 +EM_WALL = 0.973 0.973 0.973 +HT_ROOF = 6.4 6.4 6.4 +THICK_ROOF = 0.1141 0.1141 0.1141 +THICK_WALL = 0.1489 0.1489 0.1489 +T_BUILDING_MIN = 200.0 200.0 200.0 +WIND_HGT_CANYON = 3.2 3.2 3.2 +WTLUNIT_ROOF = 0.445 0.445 0.445 +WTROAD_PERV = 0.68 0.68 0.68 +# NOTE: This variable is integer rather than float +NLEV_IMPROAD = 4 4 4 + +# Variables on numrad which is 2 +ALB_IMPROAD_DIR = 0.15 0.15 +ALB_IMPROAD_DIF = 0.15 0.15 +ALB_PERROAD_DIR = 0.15 0.15 +ALB_PERROAD_DIF = 0.15 0.15 +ALB_ROOF_DIR = 0.21 0.21 +ALB_ROOF_DIF = 0.21 0.21 +ALB_WALL_DIR = 0.21 0.21 +ALB_WALL_DIF = 0.21 0.21 + +# Variabls on nlevurb which is 5 +TK_ROOF = 6.530 0.025 0.230 0.160 0.00 +TK_WALL = 0.610 0.430 0.024 0.160 0.00 +TK_IMPROAD = 1.170 0.300 0.300 0.420 0.00 +CV_ROOF = 2070000.0 7100.0 1500000.0 670000.0 0.0 +CV_WALL = 1250000.0 1400000.0 1300.0 670000.0 0.0 +CV_IMPROAD = 1140000.0 1050000.0 1050000.0 1290000.0 0.0 + +# Natural and Crop PFT's don't really need to be set, since they have zero area, but +# it looks better to do so +PCT_NAT_PFT = 100. 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 +PCT_CFT = 100. 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 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 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 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 0.0 0.0 0.0 0.0 0.0 0.0 0.0 diff --git a/tools/mksurfdata_map/modify_1x1_vancouverCAN.cfg b/tools/mksurfdata_map/modify_1x1_vancouverCAN.cfg new file mode 100644 index 0000000000..f46593d653 --- /dev/null +++ b/tools/mksurfdata_map/modify_1x1_vancouverCAN.cfg @@ -0,0 +1,138 @@ +[modify_fsurdat_basic_options] + +lat_dimname = lsmlat +lon_dimname = lsmlon + +# idealized (bool) +# When user wants existing values in fsurdat to persist in all except the +# variables that they explicitly request to change, then set this to False. +# When user wants idealized representation of the land by resetting all +# fsurdat variables, some through this file and others by using hardwired +# defaults, then set this to True. +idealized = False + +# subgrid section to set the PCT_* variables +process_subgrid_section = True +# Variable list section to set specific variable names +process_var_list_section = True + +# Boundaries of user-defined rectangle to apply changes (float) +# If lat_1 > lat_2, the code creates two rectangles, one in the north and +# one in the south. +# If lon_1 > lon_2, the rectangle wraps around the 0-degree meridian. +# Alternatively, user may specify a custom area in a .nc landmask_file +# below. If set, this will override the lat/lon settings. +# ----------------------------------- +# (Use a grid that includes the entire globe as we are just setting a single point) +# southernmost latitude for rectangle +lnd_lat_1 = -90 +# northernmost latitude for rectangle +lnd_lat_2 = 90 +# westernmost longitude for rectangle +lnd_lon_1 = 0 +# easternmost longitude for rectangle +lnd_lon_2 = 360 +# user-defined mask in a file, as alternative to setting lat/lon values +landmask_file = UNSET + +# PFT/CFT to be set to 100% according to user-defined mask. +# If idealized = True and dom_pft = UNSET, the latter defaults to 0 +# (bare soil). Valid values range from 0 to a max value (int) that one can +# obtain from the fsurdat_in file using ncdump (or method preferred by user). +# The max valid value will equal (lsmpft - 1) and will also equal the last +# value of cft(cft). Cannot be set with evenly_split_cropland = True. +dom_pft = UNSET + +# If True, evenly split each gridcell's cropland among all crop types (CFTs). +# Can only be True if dom_pft is UNSET. +evenly_split_cropland = False + +# LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_pft +# If dom_pft = 0, the next four default to 0 (space-delimited list +# of floats without brackets). +lai = UNSET +sai = UNSET +hgt_top = UNSET +hgt_bot = UNSET + +# SOIL_COLOR accepts integer values from 1 to 20 (see CTSM Technote for info). +# if idealized = True and soil_color = UNSET, soil_color = 15. +soil_color = UNSET + +# STD_ELEV (standard deviation of elevation) value (in meters) over the +# user_defined mask (float). +# if idealized = True and std_elev = UNSET, std_elev = 0. +std_elev = UNSET + +# FMAX (maximum fractional saturated area) value (fraction) over the +# user_defined mask (float). +# if idealized = True and max_sat_area = UNSET, max_sat_area = 0. +max_sat_area = UNSET + +# Set non-vegetation landunits to 0 (bool). +zero_nonveg = False + +# Include other land units besides vegetated +include_nonveg = True + +# Section for subgrid_fractions +[modify_fsurdat_subgrid_fractions] +# If subgrid_fractions = True this section will be enabled + +# NOTE: PCT_URBAN must be a list of three floats that sum to the total urban area +PCT_URBAN = 100.0 0.0 0.0 +PCT_CROP = 0.0 +PCT_NATVEG= 0.0 +PCT_GLACIER= 0.0 +PCT_WETLAND= 0.0 +PCT_LAKE = 0.0 + +# Section with a list of variables to prcoess +[modify_fsurdat_variable_list] +# IMPORTANT NOTE: Config file strings are case inssentive! +# +# As such it will check for variable names both in lower case and upper case. +# +# If variable_list = True this section will be enabled +# Can't specify PFT as they are in dom_pft +# Add variables on the file and assign a new value +# can't specify soil_color, max_sat_area or other things that are above. + +# Variables on numurbl which is 3 +CANYON_HWR = 0.39 0.39 0.39 +EM_IMPROAD = 0.95 0.95 0.95 +EM_PERROAD = 0.95 0.95 0.95 +EM_ROOF = 0.92 0.92 0.92 +EM_WALL = 0.90 0.90 0.90 +HT_ROOF = 5.8 5.8 5.8 +THICK_ROOF = 0.070 0.070 0.070 +THICK_WALL = 0.20 0.20 0.20 +T_BUILDING_MIN = 200.0 200.0 200.0 +WIND_HGT_CANYON = 2.9 2.9 2.9 +WTLUNIT_ROOF = 0.51 0.51 0.51 +WTROAD_PERV = 0.11 0.11 0.11 +# NOTE: This variable is integer rather than float +NLEV_IMPROAD = 5 5 5 + +# Variables on numrad which is 2 +ALB_IMPROAD_DIR = 0.08 0.08 +ALB_IMPROAD_DIF = 0.08 0.08 +ALB_PERROAD_DIR = 0.08 0.08 +ALB_PERROAD_DIF = 0.08 0.08 +ALB_ROOF_DIR = 0.12 0.12 +ALB_ROOF_DIF = 0.12 0.12 +ALB_WALL_DIR = 0.50 0.50 +ALB_WALL_DIF = 0.50 0.50 + +# Variabls on nlevurb which is 5 +TK_ROOF = 1.40 1.40 1.40 0.03 1.51 +TK_WALL = 1.51 1.51 0.67 0.67 1.51 +TK_IMPROAD = 0.82 0.82 2.10 2.10 2.10 +CV_ROOF = 1760000.0 1760000.0 1760000.0 40000.0 2210000.0 +CV_WALL = 2110000.0 2110000.0 1000000.0 1000000.0 2110000.0 +CV_IMPROAD = 1740000.0 1740000.0 2000000.0 2000000.0 2000000.0 + +# Natural and Crop PFT's don't really need to be set, since they have zero area, but +# it looks better to do so +PCT_NAT_PFT = 100. 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 +PCT_CFT = 100. 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 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 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 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 0.0 0.0 0.0 0.0 0.0 0.0 0.0 diff --git a/tools/mksurfdata_map/src/test/mkdomain_test/test_mkdomain.pf b/tools/mksurfdata_map/src/test/mkdomain_test/test_mkdomain.pf index c54e299356..fd6a2e1e7b 100644 --- a/tools/mksurfdata_map/src/test/mkdomain_test/test_mkdomain.pf +++ b/tools/mksurfdata_map/src/test/mkdomain_test/test_mkdomain.pf @@ -2,7 +2,7 @@ module test_mkdomain ! Tests of mkdomainMod - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use mkgridmapMod, only : gridmap_type, for_test_create_gridmap diff --git a/tools/mksurfdata_map/src/test/mkgridmap_test/test_mkgridmap.pf b/tools/mksurfdata_map/src/test/mkgridmap_test/test_mkgridmap.pf index c8eda9d007..a15944384f 100644 --- a/tools/mksurfdata_map/src/test/mkgridmap_test/test_mkgridmap.pf +++ b/tools/mksurfdata_map/src/test/mkgridmap_test/test_mkgridmap.pf @@ -2,7 +2,7 @@ module test_mkgridmap ! Tests of mkgridmapMod - use pfunit_mod + use funit use mkgridmapMod use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/tools/mksurfdata_map/src/test/mkindexmap_test/test_mkindexmap.pf b/tools/mksurfdata_map/src/test/mkindexmap_test/test_mkindexmap.pf index 98e9590478..7f6f1d6745 100644 --- a/tools/mksurfdata_map/src/test/mkindexmap_test/test_mkindexmap.pf +++ b/tools/mksurfdata_map/src/test/mkindexmap_test/test_mkindexmap.pf @@ -2,7 +2,7 @@ module test_mkindexmap ! Tests of mkindexmapMod - use pfunit_mod + use funit use mkindexmapMod use mkgridmapMod, only : gridmap_type, for_test_create_gridmap, gridmap_clean use shr_kind_mod , only : r8 => shr_kind_r8 diff --git a/tools/mksurfdata_map/src/test/mkpctPftType_test/test_mkpctPftType.pf b/tools/mksurfdata_map/src/test/mkpctPftType_test/test_mkpctPftType.pf index 47e7e90f48..1652a742a2 100644 --- a/tools/mksurfdata_map/src/test/mkpctPftType_test/test_mkpctPftType.pf +++ b/tools/mksurfdata_map/src/test/mkpctPftType_test/test_mkpctPftType.pf @@ -2,7 +2,7 @@ module test_mkpctPftType ! Tests of pct_pft_type - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use mkpctPftTypeMod diff --git a/tools/mksurfdata_map/src/test/mkpftUtils_test/test_adjust_total_veg_area.pf b/tools/mksurfdata_map/src/test/mkpftUtils_test/test_adjust_total_veg_area.pf index 345c1a7370..1223e5bc68 100644 --- a/tools/mksurfdata_map/src/test/mkpftUtils_test/test_adjust_total_veg_area.pf +++ b/tools/mksurfdata_map/src/test/mkpftUtils_test/test_adjust_total_veg_area.pf @@ -2,7 +2,7 @@ module test_adjust_total_veg_area ! Tests of mkpftUtilsMod: adjust_total_veg_area - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use mkpctPftTypeMod, only : pct_pft_type diff --git a/tools/mksurfdata_map/src/test/mkpftUtils_test/test_convert_from_p2g.pf b/tools/mksurfdata_map/src/test/mkpftUtils_test/test_convert_from_p2g.pf index 53548e4e6c..3227031726 100644 --- a/tools/mksurfdata_map/src/test/mkpftUtils_test/test_convert_from_p2g.pf +++ b/tools/mksurfdata_map/src/test/mkpftUtils_test/test_convert_from_p2g.pf @@ -2,7 +2,7 @@ module test_convert_from_p2g ! Tests of mkpftUtilsMod: convert_from_p2g - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use mkpctPftTypeMod, only : pct_pft_type diff --git a/tools/mksurfdata_map/src/test/mkpftmod_test/test_pftInit.pf b/tools/mksurfdata_map/src/test/mkpftmod_test/test_pftInit.pf index 1ddb143961..a555e3e8ca 100644 --- a/tools/mksurfdata_map/src/test/mkpftmod_test/test_pftInit.pf +++ b/tools/mksurfdata_map/src/test/mkpftmod_test/test_pftInit.pf @@ -2,7 +2,7 @@ module test_pftInit ! Tests of mkpftMod: pft_override functions - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use mkpftMod diff --git a/tools/mksurfdata_map/src/test/mkpftmod_test/test_pft_oride.pf b/tools/mksurfdata_map/src/test/mkpftmod_test/test_pft_oride.pf index 97cfc66d1e..bc9cda88de 100644 --- a/tools/mksurfdata_map/src/test/mkpftmod_test/test_pft_oride.pf +++ b/tools/mksurfdata_map/src/test/mkpftmod_test/test_pft_oride.pf @@ -2,7 +2,7 @@ module test_pft_oride ! Tests of mkpftMod: pft_override functions - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use mkpftMod diff --git a/tools/mksurfdata_map/src/test/mkpftmod_test/test_pftrun.pf b/tools/mksurfdata_map/src/test/mkpftmod_test/test_pftrun.pf index 389748764b..77c8f0e623 100644 --- a/tools/mksurfdata_map/src/test/mkpftmod_test/test_pftrun.pf +++ b/tools/mksurfdata_map/src/test/mkpftmod_test/test_pftrun.pf @@ -2,7 +2,7 @@ module test_pftrun ! Tests of mkpftMod: pft_override functions - use pfunit_mod + use funit use shr_kind_mod, only : r8 => shr_kind_r8 use mkpftMod diff --git a/tools/mksurfdata_map/src/test/mksoilUtils_test/test_dominant_soil_color.pf b/tools/mksurfdata_map/src/test/mksoilUtils_test/test_dominant_soil_color.pf index b506549d87..011f20d70c 100644 --- a/tools/mksurfdata_map/src/test/mksoilUtils_test/test_dominant_soil_color.pf +++ b/tools/mksurfdata_map/src/test/mksoilUtils_test/test_dominant_soil_color.pf @@ -2,7 +2,7 @@ module test_dominant_soil_color ! Tests of mksoilUtilsMod: dominant_soil_color - use pfunit_mod + use funit use mksoilUtilsMod use shr_kind_mod , only : r8 => shr_kind_r8 use mkgridmapMod, only : gridmap_type, gridmap_clean, for_test_create_gridmap diff --git a/tools/mksurfdata_map/src/unit_test_stubs/abort.F90 b/tools/mksurfdata_map/src/unit_test_stubs/abort.F90 index aa1d8b76c2..8f56fc82fc 100644 --- a/tools/mksurfdata_map/src/unit_test_stubs/abort.F90 +++ b/tools/mksurfdata_map/src/unit_test_stubs/abort.F90 @@ -18,7 +18,7 @@ subroutine abort() ! call assertExceptionRaised ! ! then this will result in the given pFUnit test failing. - use pfunit_mod, only : throw + use funit, only : throw implicit none call throw("ABORTED:") diff --git a/tools/modify_input_files/README.fsurdat_modifier b/tools/modify_input_files/README.fsurdat_modifier index 8ab90dae1e..885227c928 100644 --- a/tools/modify_input_files/README.fsurdat_modifier +++ b/tools/modify_input_files/README.fsurdat_modifier @@ -27,12 +27,19 @@ tools/modify_input_files/modify_fsurdat_template.cfg Instructions ------------ -To run on Cheyenne/Casper/Izumi +To run on various machines: 1) (Un)load, execute, and activate the following: +1a) First step to activate conda on your system +1a) Casper: module unload python module load conda +1a) Izumi: +module load python +1a) Derecho (nothing needs to be done for this step) +1a) Elsewhere (do what's needed to activate conda) +1b) On all systems ./py_env_create -conda activate ctsm_py +conda activate ctsm_pylib (Use "deactivate" to reverse the latter.) 2) Copy, then modify the configure file named modify_fsurdat_template.cfg, which contains all the arguments needed by the script. diff --git a/tools/modify_input_files/README.mesh_mask_modifier b/tools/modify_input_files/README.mesh_mask_modifier index 13d98b9e7f..4e25e73826 100644 --- a/tools/modify_input_files/README.mesh_mask_modifier +++ b/tools/modify_input_files/README.mesh_mask_modifier @@ -10,10 +10,17 @@ tools/modify_input_files/modify_mesh_template.cfg Instructions ------------ -To run on Cheyenne/Casper/Izumi +To run on various machines: 1) (Un)load, execute, and activate the following: +1a) First step to activate conda on your system +1a) Casper: module unload python module load conda +1a) Izumi: +module load python +1a) Derecho (nothing needs to be done +1a) Elsewhere (do what's needed to activate conda) +1b) On all systems ./py_env_create conda activate ctsm_py (Use "deactivate" to reverse the latter.) @@ -49,7 +56,7 @@ In your copy of the CTSM (say, ~user/ctsm), go to the appropriate tool: Enter the following (or similar) selections in modify_fill_indianocean.cfg: -mesh_mask_in = /glade/p/cesmdata/cseg/inputdata/share/meshes/fv0.9x1.25_141008_polemod_ESMFmesh.nc +mesh_mask_in = /glade/campaign/cesm/cesmdata/cseg/inputdata/share/meshes/fv0.9x1.25_141008_polemod_ESMFmesh.nc mesh_mask_out = fv0.9x1.25_141008_polemod_ESMFmesh_modified.nc landmask_file = .../path_to_your_copy_of/fill_indianocean.nc diff --git a/tools/modify_input_files/modify_fsurdat_template.cfg b/tools/modify_input_files/modify_fsurdat_template.cfg index 0694174666..1dfb33ce53 100644 --- a/tools/modify_input_files/modify_fsurdat_template.cfg +++ b/tools/modify_input_files/modify_fsurdat_template.cfg @@ -1,4 +1,4 @@ -[modify_input] +[modify_fsurdat_basic_options] # ------------------------------------------------------------------------ # .cfg file with inputs for fsurdat_modifier. @@ -37,7 +37,14 @@ fsurdat_out = FILL_THIS_IN # ORGANIC = 0 idealized = False -# Boundaries of user-defined rectangle (float) + +# Process the optional section that handles modifying subgrid fractions +process_subgrid_section = False + +# Process the optional section that handles modifying an arbitrary list of variables +process_var_list_section = False + +# Boundaries of user-defined rectangle to apply changes (float) # If lat_1 > lat_2, the code creates two rectangles, one in the north and # one in the south. # If lon_1 > lon_2, the rectangle wraps around the 0-degree meridian. @@ -65,9 +72,13 @@ lon_dimname = UNSET # (bare soil). Valid values range from 0 to a max value (int) that one can # obtain from the fsurdat_in file using ncdump (or method preferred by user). # The max valid value will equal (lsmpft - 1) and will also equal the last -# value of cft(cft). +# value of cft(cft). Cannot be set with evenly_split_cropland = True. dom_pft = UNSET +# If True, evenly split each gridcell's cropland among all crop types (CFTs). +# Can only be True if dom_pft is UNSET. +evenly_split_cropland = False + # LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_pft # If dom_pft = 0, the next four default to 0 (space-delimited list # of floats without brackets). diff --git a/tools/site_and_regional/README b/tools/site_and_regional/README index d6cc55eac8..7b36b7d2b9 100644 --- a/tools/site_and_regional/README +++ b/tools/site_and_regional/README @@ -3,11 +3,11 @@ $CTSMROOT/tools/site_and_regional/README The purpose of this directory is to contain all of the scripts that involve creating CTSM input data files for single site as well as regional cases. -The python scripts require the following settings before running on cheyenne: +The python scripts require the following settings before running: -module load conda +(Do what's needed to make conda available on your system) ../../py_env_create -conda activate ctsm_py +conda activate ctsm_pylib Brief description of scripts: diff --git a/tools/site_and_regional/default_data.cfg b/tools/site_and_regional/default_data.cfg index f689c99044..0425aba133 100644 --- a/tools/site_and_regional/default_data.cfg +++ b/tools/site_and_regional/default_data.cfg @@ -1,8 +1,8 @@ [main] -clmforcingindir = /glade/p/cesmdata/inputdata +clmforcingindir = /glade/campaign/cesm/cesmdata/cseg/inputdata [datm_gswp3] -dir = /glade/p/cgd/tss/CTSM_datm_forcing_data/atm_forcing.datm7.GSWP3.0.5d.v1.c170516 +dir = atm/datm7/atm_forcing.datm7.GSWP3.0.5d.v1.c170516 domain = domain.lnd.360x720_gswp3.0v1.c170606.nc solardir = Solar precdir = Precip diff --git a/tools/site_and_regional/modify_singlept_site_neon b/tools/site_and_regional/modify_singlept_site_neon new file mode 100755 index 0000000000..1b790a74ca --- /dev/null +++ b/tools/site_and_regional/modify_singlept_site_neon @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +This is a just top-level skeleton script that calls +modify_singlept_site_neon.py. +The original code (modify_singlept_site_neon.py) is located under +python/ctsm/site_and_regional folder. + +For full instructions on how to run the code and different options, +please check python/ctsm/site_and_regional/modify_singlept_site_neon.py file. + +This script is for modifying surface dataset at neon sites +using data available from the neon server. + +After creating a single point surface data file from a global +surface data file using subset_data.py, use this script to +overwrite some fields with site-specific data for neon sites. + +This script will do the following: +- Download neon data for the specified site if it does not exist + in the specified directory : (i.e. ../../../neon_surf_files). +- Modify surface dataset with downloaded data. + +---------------------------------------------------------------- +To see all available options for modifying surface datasets at +tower sites: + ./modify_singlept_site_neon --help +---------------------------------------------------------------- +Instructions for running using conda python environments: +../../py_env_create +conda activate ctsm_pylib +""" + +import os +import sys + +# -- add python/ctsm to path +_CTSM_PYTHON = os.path.join( + os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, "python" +) +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.site_and_regional.modify_singlept_site_neon import main + +if __name__ == "__main__": + main() diff --git a/tools/site_and_regional/neon_s3_upload b/tools/site_and_regional/neon_gcs_upload similarity index 80% rename from tools/site_and_regional/neon_s3_upload rename to tools/site_and_regional/neon_gcs_upload index 447886e936..40afef8e74 100755 --- a/tools/site_and_regional/neon_s3_upload +++ b/tools/site_and_regional/neon_gcs_upload @@ -9,8 +9,7 @@ import os, sys _CTSM_PYTHON = os.path.abspath(os.path.join(os.path.dirname(__file__), "..","..",'python')) sys.path.insert(1, _CTSM_PYTHON) -import boto3 -from botocore.exceptions import ClientError +from google.cloud import storage import glob import datetime from ctsm import add_cime_to_path @@ -60,7 +59,7 @@ def get_parser(args, description, valid_neon_sites): dest="file_date", required = False, type = datetime.date.fromisoformat, - default = datetime.datetime.strptime("0268-01-01",'%Y-%m-%d')) + default = datetime.datetime.strptime("0318-01-01",'%Y-%m-%d')) parser.add_argument('--upload-finidat', @@ -97,28 +96,24 @@ def get_parser(args, description, valid_neon_sites): return neon_sites, args.output_root, args.file_date, args.upload_finidat, args.upload_history -def upload_file(file_name, bucket, object_name=None): - """Upload a file to an S3 bucket +def upload_blob(bucket_name, source_file_name, destination_blob_name): + """Uploads a file to the bucket.""" + # The ID of your GCS bucket + # bucket_name = "your-bucket-name" + # The path to your file to upload + # source_file_name = "local/path/to/file" + # The ID of your GCS object + # destination_blob_name = "storage-object-name" - :param file_name: File to upload - :param bucket: Bucket to upload to - :param object_name: S3 object name. If not specified then file_name is used - :return: True if file was uploaded, else False - """ + storage_client = storage.Client() + bucket = storage_client.bucket(bucket_name) + blob = bucket.blob(destination_blob_name) - # If S3 object_name was not specified, use file_name - if object_name is None: - object_name = os.path.basename(file_name) + blob.upload_from_filename(source_file_name) - # Upload the file - s3_client = boto3.client('s3') - try: - logger.info("Uploading file {} to {}".format(file_name, object_name)) - response = s3_client.upload_file(file_name, bucket, object_name) - except ClientError as e: - logger.error(e) - return False - return True + print( + f"File {source_file_name} uploaded to {destination_blob_name}." + ) def main(description): """ @@ -126,10 +121,10 @@ def main(description): from there, """ - if not os.path.isfile(os.path.join(os.getenv("HOME"),".aws","credentials")): - raise FileNotFoundError("User account must have valid aws credentials to run this script.") - cesmroot = path_to_ctsm_root() + os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = os.path.join(os.environ["HOME"],'.uploadGCSkey.json') + #os.path.join(os.environ["HOME"],"gcwriter") + # Get the list of supported neon sites from usermods valid_neon_sites = glob.glob(os.path.join(cesmroot,"cime_config","usermods_dirs","NEON","[!d]*")) valid_neon_sites = [v.split('/')[-1] for v in valid_neon_sites] @@ -149,8 +144,8 @@ def main(description): logger.warning("Could not find file {}".format(finidat_file)) continue newfile = basefile.replace(".postad.",".{}.".format(filedatestamp)) + upload_blob("neon-ncar-artifacts", finidat_file, os.path.join("NEON","lnd","ctsm","initdata",newfile) ) - upload_file(finidat_file, 'neon-ncar-transfer', os.path.join("NEON","lnd","ctsm","initdata",newfile)) if upload_history: logger.info("Upload history for {}".format(site)) case_path = os.path.join(output_root, site+".transient") @@ -161,7 +156,7 @@ def main(description): archive_dir = os.path.join(case.get_value("DOUT_S_ROOT"),"lnd","hist") for histfile in glob.iglob(archive_dir + "/*.h1.*"): newfile = os.path.basename(histfile) - upload_file(histfile, 'neon-ncar-transfer', os.path.join("NEON","archive",site,"lnd","hist",newfile)) + upload_blob("neon-ncar-artifacts", histfile, os.path.join("NEON","archive",site,"lnd","hist",newfile)) diff --git a/tools/site_and_regional/neon_sites_dompft.csv b/tools/site_and_regional/neon_sites_dompft.csv index ad5d14e53d..7ce02ad785 100644 --- a/tools/site_and_regional/neon_sites_dompft.csv +++ b/tools/site_and_regional/neon_sites_dompft.csv @@ -1,48 +1,48 @@ ,Site,Domain,Lat,Lon,pft,start_year,end_year -1,BART,1,44.06516,-71.28834,7,2018,2021 -2,HARV,1,42.53562,-72.17562,7,2018,2021 -3,BLAN,2,39.06044,-78.07115,7,2018,2021 -4,SCBI,2,38.89209,-78.13764,7,2018,2021 -5,SERC,2,38.89124,-76.55884,7,2018,2021 -6,DSNY,3,28.12919,-81.43394,14,2018,2021 -7,JERC,3,31.19608,-84.46647,1,2018,2021 -8,OSBS,3,29.68819,-81.99345,1,2018,2021 -9,GUAN,4,17.96882,-66.86888,6,2019,2021 -10,LAJA,4,18.02184,-67.07608,14,2019,2021 -11,STEI,5,45.5076,-89.5888,7,2018,2021 -12,TREE,5,45.49266,-89.58748,7,2018,2021 -13,UNDE,5,46.14103,-89.3221,7,2018,2021 -14,KONA,6,39.10828,-96.61044,18,2018,2021 -15,KONZ,6,39.1007,-96.56227,14,2018,2021 -16,UKFS,6,39.04168,-95.20495,7,2018,2021 -17,GRSM,7,35.68839,-83.50185,7,2018,2021 -18,MLBS,7,37.37783,-80.52425,7,2018,2019 -19,ORNL,7,35.57525,-84.16581,7,2018,2021 -20,DELA,8,32.54092,-87.80341,7,2018,2021 -21,LENO,8,31.8531,-88.16103,7,2021,2021 -22,TALL,8,32.95106,-87.3941,1,2018,2021 -23,DCFS,9,47.15919,-99.11251,13,2018,2021 -24,NOGP,9,46.76846,-100.91832,13,2018,2021 -25,WOOD,9,47.12833,-99.23907,13,2018,2021 -26,CPER,10,40.81297,-104.74455,14,2018,2021 -27,RMNP,10,40.27707,-105.54524,1,2018,2021 -28,STER,10,40.45984,-103.03008,18,2018,2021 -29,CLBJ,11,33.40143,-97.56725,7,2018,2021 -30,OAES,11,35.41062,-99.06044,14,2018,2021 -31,YELL,12,44.95597,-110.54196,1,2019,2021 -32,MOAB,13,38.25136,-109.38882,14,2018,2020 -33,NIWO,13,40.05236,-105.58324,12,2018,2021 -34,JORN,14,32.59052,-106.84377,14,2018,2021 -35,SRER,14,31.91068,-110.83549,9,2018,2021 -36,ONAQ,15,35.68839,-83.50185,9,2018,2019 +1,BART, 1, 44.06516, -71.28834,7,2018,2021 +2,HARV, 1, 42.53562, -72.17562,7,2018,2021 +3,BLAN, 2, 39.033698, -78.041788,7,2018,2021 +4,SCBI, 2, 38.89209, -78.13764,7,2018,2021 +5,SERC, 2, 38.89124, -76.55884,7,2018,2021 +6,DSNY, 3, 28.12919, -81.43394,14,2018,2021 +7,JERC, 3, 31.19608, -84.46647,1,2018,2021 +8,OSBS, 3, 29.68819, -81.99345,1,2018,2021 +9,GUAN, 4, 17.96882, -66.86888,6,2019,2021 +10,LAJA, 4, 18.02184, -67.07608,14,2019,2021 +11,STEI, 5, 45.50760, -89.58880,7,2018,2021 +12,TREE, 5, 45.49266, -89.58748,7,2018,2021 +13,UNDE, 5, 46.23391, -89.537254,7,2018,2021 +14,KONA, 6, 39.10828, -96.61044,19,2018,2021 +15,KONZ, 6, 39.10070, -96.56227,14,2018,2021 +16,UKFS, 6, 39.04168, -95.20495,7,2018,2021 +17,GRSM, 7, 35.68839, -83.50185,7,2018,2021 +18,MLBS, 7, 37.37783, -80.52425,7,2018,2019 +19,ORNL, 7, 35.964128, -84.282588,7,2018,2021 +20,DELA, 8, 32.54092, -87.80341,7,2018,2021 +21,LENO, 8, 31.85310, -88.16103,7,2021,2021 +22,TALL, 8, 32.95106, -87.39410,1,2018,2021 +23,DCFS, 9, 47.15919, -99.11251,13,2018,2021 +24,NOGP, 9, 46.76846, -100.91832,13,2018,2021 +25,WOOD, 9, 47.12833, -99.23907,13,2018,2021 +26,CPER,10, 40.81297, -104.74455,14,2018,2021 +27,RMNP,10, 40.27707, -105.54524,1,2018,2021 +28,STER,10, 40.45984, -103.03008,19,2018,2021 +29,CLBJ,11, 33.40143, -97.56725,7,2018,2021 +30,OAES,11, 35.41062, -99.06044,14,2018,2021 +31,YELL,12, 44.95597, -110.54196,1,2019,2021 +32,MOAB,13, 38.25136, -109.38882,14,2018,2020 +33,NIWO,13, 40.05236, -105.58324,12,2018,2021 +34,JORN,14, 32.59052, -106.84377,14,2018,2021 +35,SRER,14, 31.91068, -110.83549,9,2018,2021 +36,ONAQ,15, 40.17760, -112.45245,9,2018,2019 37,ABBY,16,45.762378,-122.329672,1,2018,2021 -38,WREF,16,45.81637,-121.95838,1,2019,2021 -39,SJER,17,37.107117,-119.733,13,2019,2021 -40,SOAP,17,37.03269,-119.2621,1,2018,2021 +38,WREF,16, 45.81637, -121.95838,1,2019,2021 +39,SJER,17,37.107117, -119.73300,13,2019,2021 +40,SOAP,17,37.032690, -119.26210,1,2018,2021 41,TEAK,17,37.006472,-119.005758,1,2019,2021 -42,TOOL,17,68.66045,-149.370128,1,2020,2021 +42,TOOL,17, 68.66045,-149.370128,11,2020,2021 43,BARR,18,71.281711,-156.650219,12,2019,2021 -44,BONA,19,65.15333,-147.50194,2,2018,2021 -45,DEJU,19,63.87983,-145.74765,2,2018,2021 -46,HEAL,19,63.8798,-149.21539,12,2018,2021 -47,PUUM,20,19.55309,-155.31731,4,2020,2020 +44,BONA,19, 65.15333, -147.50194,2,2018,2021 +45,DEJU,19, 63.87983, -145.74765,2,2018,2021 +46,HEAL,19, 63.87980, -149.21539,12,2018,2021 +47,PUUM,20, 19.55309, -155.31731,4,2020,2020 diff --git a/tools/site_and_regional/neon_surf_wrapper b/tools/site_and_regional/neon_surf_wrapper new file mode 100755 index 0000000000..306d38a774 --- /dev/null +++ b/tools/site_and_regional/neon_surf_wrapper @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +""" +This is a just top-level skeleton script that calls +neon_surf_wrapper.py. +The original code (neon_surf_wrapper.py) is located under +python/ctsm/site_and_regional folder. + +For full instructions on how to run the code and different options, +please check python/ctsm/site_and_regional/neon_surf_wrapper.py file. + +This script is a simple wrapper for neon sites that performs the +following: + 1) For neon sites, subset surface dataset from global dataset + (i.e. ./subset_data.py ) + 2) Download neon and update the created surface dataset + based on the downloaded neon data. + (i.e. modify_singlept_site_neon.py) + +---------------------------------------------------------------- +Instructions for running using conda python environments: +../../py_env_create +conda activate ctsm_pylib +""" + +import os +import sys + +# -- add python/ctsm to path +_CTSM_PYTHON = os.path.join( + os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, "python" +) +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.site_and_regional.neon_surf_wrapper import main + +if __name__ == "__main__": + main() diff --git a/tools/site_and_regional/neon_surf_wrapper.py b/tools/site_and_regional/neon_surf_wrapper.py deleted file mode 100755 index df58d3ab36..0000000000 --- a/tools/site_and_regional/neon_surf_wrapper.py +++ /dev/null @@ -1,110 +0,0 @@ -#! /usr/bin/env python3 -""" -|------------------------------------------------------------------| -|--------------------- Instructions -----------------------------| -|------------------------------------------------------------------| -This script is a simple wrapper for neon sites that performs the -following: - 1) For neon sites, subset surface dataset from global dataset - (i.e. ./subset_data.py ) - 2) Download neon and update the created surface dataset - based on the downloaded neon data. - (i.e. modify_singlept_site_neon.py) - -Instructions for running on Cheyenne/Casper: -load the following into your local environment - module load python - ncar_pylib - -""" -# TODO -# Automatic downloading of missing files if they are missing -#-[ ] Download neon sites and dom pft file -#-[ ] Make sure verbose works for printing out commands running - -# Import libraries -from __future__ import print_function - -import os -import sys -import tqdm -import logging -import argparse -import subprocess - -import pandas as pd -#import tqdm as tqdm - - - -def get_parser(): - """ - Get parser object for this script. - """ - parser = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter) - - parser.print_usage = parser.print_help - - parser.add_argument('-v','--verbose', - help='Verbose mode will print more information. ', - action="store_true", - dest="verbose", - default=False) - - - return parser - - -def execute(command): - """ - Function for running a command on shell. - Args: - command (str): - command that we want to run. - Raises: - Error with the return code from shell. - """ - print ('\n',' >> ',*command,'\n') - - try: - subprocess.check_call(command, stdout=open(os.devnull, "w"), stderr=subprocess.STDOUT) - - except subprocess.CalledProcessError as e: - #raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) - #print (e.ouput) - print (e) - - - - - - -def main(): - - args = get_parser().parse_args() - - if args.verbose: - logging.basicConfig(level=logging.DEBUG) - - - neon_sites = pd.read_csv('neon_sites_dompft.csv') - - - for i, row in tqdm.tqdm(neon_sites.iterrows()): - lat = row['Lat'] - lon = row['Lon'] - site = row['Site'] - pft = row['pft'] - print ("Now processing site :", site) - command = ['./subset_data','point','--lat',str(lat),'--lon',str(lon),'--site',site,'--dompft',str(pft),'--crop', - '--create-surface','--uniform-snowpack','--cap-saturation','--verbose'] - execute(command) - - command = ['./modify_singlept_site_neon.py','--neon_site',site, '--surf_dir', - 'subset_data_single_point'] - execute(command) - -if __name__ == "__main__": - main() - diff --git a/tools/site_and_regional/run_neon b/tools/site_and_regional/run_neon new file mode 100755 index 0000000000..ffc3be2af7 --- /dev/null +++ b/tools/site_and_regional/run_neon @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +""" +This is a just top-level skeleton script that calls +run_neon.py. +The original code (run_neon.py) is located under +python/ctsm/site_and_regional folder. + +For full instructions on how to run the code and different options, +please check python/ctsm/site_and_regional/run_neon.py file. + +This script first creates and builds a generic base case. +Next, it will clone the base_case for different neon sites and run +types to reduce the need to build ctsm everytime. + +This script will do the following: + 1) Create a generic base case for cloning. + 2) Make the case for the specific neon site(s). + 3) Make changes to the case, for: + a. AD spinup + b. post-AD spinup + c. transient + #--------------- + d. SASU or Matrix spinup + 4) Build and submit the case. + +---------------------------------------------------------------- +To see all available options for running tower sites: + ./run_neon --help +---------------------------------------------------------------- +Instructions for running using conda python environments: +../../py_env_create +conda activate ctsm_pylib +""" + +import os +import sys + +# -- add python/ctsm to path +_CTSM_PYTHON = os.path.join( + os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, "python" +) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=import-error, wrong-import-position +from ctsm.site_and_regional.run_neon import main + +if __name__ == "__main__": + main(__doc__) diff --git a/tools/site_and_regional/run_neon.py b/tools/site_and_regional/run_neon.py deleted file mode 100755 index e0aae36274..0000000000 --- a/tools/site_and_regional/run_neon.py +++ /dev/null @@ -1,814 +0,0 @@ -#! /usr/bin/env python3 - -""" -|------------------------------------------------------------------| -|--------------------- Instructions -----------------------------| -|------------------------------------------------------------------| -This is a wrapper script for running CTSM simulation for one or more -neon sites. - -This script is only for neon site and we will develop a more general -code later. - -This script first creates and builds a generic base case. -Next, it will clone the base_case for different neon sites and run -types to reduce the need to build ctsm everytime. - -This script will do the following: - 1) Create a generic base case for cloning. - 2) Make the case for the specific neon site(s). - 3) Make changes to the case, for: - a. AD spinup - b. post-AD spinup - c. transient - #--------------- - d. SASU or Matrix spinup - 4) Build and submit the case. - -------------------------------------------------------------------- -Instructions for running on Cheyenne/Casper: - -load the following into your local environment - module load python - ncar_pylib - -To remove NPL from your environment on Cheyenne/Casper: - deactivate - -------------------------------------------------------------------- -To see the available options: - ./run_neon.py --help -------------------------------------------------------------------- -""" -# TODO (NS) -# - [ ] -# - [ ] Case dependency and the ability to check case status -# - [ ] If Case dependency works we don't need finidat given explicilty for post-ad and transient. - -# - [ ] checkout_externals instead of using env varaiable -# - [ ] wget the fields available and run for those available - -# - [ ] Matrix spin-up if (SASU) Eric merged it in -# - [ ] Make sure both AD and SASU are not on at the same time - -# - [ ] Make sure CIME and other dependencies is checked out. - - -# Import libraries - -import os -import sys -import time -import shutil -import logging -import requests -import argparse -import re -import subprocess -import pandas as pd -import glob -import datetime -from getpass import getuser - -# Get the ctsm util tools and then the cime tools. -_CTSM_PYTHON = os.path.abspath( - os.path.join(os.path.dirname(__file__), "..", "..", "python") -) -sys.path.insert(1, _CTSM_PYTHON) - -from ctsm import add_cime_to_path -from ctsm.path_utils import path_to_ctsm_root -from ctsm.download_utils import download_file - -import CIME.build as build -from standard_script_setup import * -from CIME.case import Case -from CIME.utils import safe_copy, expect, symlink_force -from argparse import RawTextHelpFormatter -from CIME.locked_files import lock_file, unlock_file - -logger = logging.getLogger(__name__) - - -def get_parser(args, description, valid_neon_sites): - """ - Get parser object for this script. - """ - parser = argparse.ArgumentParser( - description=description, formatter_class=argparse.RawDescriptionHelpFormatter - ) - - CIME.utils.setup_standard_logging_options(parser) - - parser.print_usage = parser.print_help - - parser.add_argument( - "--neon-sites", - help="4-letter neon site code.", - action="store", - required=False, - choices=valid_neon_sites + ["all"], - dest="neon_sites", - default=["OSBS"], - nargs="+", - ) - - parser.add_argument( - "--base-case", - help=""" - Root Directory of base case build - [default: %(default)s] - """, - action="store", - dest="base_case_root", - type=str, - required=False, - default=None, - ) - - parser.add_argument( - "--output-root", - help=""" - Root output directory of cases - [default: %(default)s] - """, - action="store", - dest="output_root", - type=str, - required=False, - default="CIME_OUTPUT_ROOT as defined in cime", - ) - - parser.add_argument( - "--overwrite", - help=""" - overwrite existing case directories - [default: %(default)s] - """, - action="store_true", - dest="overwrite", - required=False, - default=False, - ) - - parser.add_argument( - "--setup-only", - help=""" - Only setup the requested cases, do not build or run - [default: %(default)s] - """, - action="store_true", - dest="setup_only", - required=False, - default=False, - ) - - parser.add_argument( - "--rerun", - help=""" - If the case exists but does not appear to be complete, restart it. - [default: %(default)s] - """, - action="store_true", - dest="rerun", - required=False, - default=False, - ) - - parser.add_argument( - "--no-batch", - help=""" - Run locally, do not use batch queueing system (if defined for Machine) - [default: %(default)s] - """, - action="store_true", - dest="no_batch", - required=False, - default=False, - ) - - parser.add_argument( - "--run-type", - help=""" - Type of run to do - [default: %(default)s] - """, - choices=["ad", "postad", "transient", "sasu"], - default="transient", - ) - - parser.add_argument( - "--run-length", - help=""" - How long to run (modified ISO 8601 duration) - [default: %(default)s] - """, - required=False, - type=str, - default="0Y", - ) - - parser.add_argument( - "--start-date", - help=""" - Start date for running CTSM simulation in ISO format. - [default: %(default)s] - """, - action="store", - dest="start_date", - required=False, - type=datetime.date.fromisoformat, - default=datetime.datetime.strptime("2018-01-01", "%Y-%m-%d"), - ) - - parser.add_argument( - "--end-date", - help=""" - End date for running CTSM simulation in ISO format. - [default: %(default)s] - """, - action="store", - dest="end_date", - required=False, - type=datetime.date.fromisoformat, - default=datetime.datetime.strptime("2021-01-01", "%Y-%m-%d"), - ) - - parser.add_argument( - "--run-from-postad", - help=""" - For transient runs only - should we start from the postad spinup or finidat? - By default start from finidat, if this flag is used the postad run must be available. - """, - action="store_true", - required=False, - default=False, - ) - parser.add_argument( - "--neon-version", - help=""" - Neon data version to use for this simulation. - [default: use the latest data available] - """, - action="store", - dest="user_version", - required = False, - type = str, - choices= ['v1','v2'], - ) - - - args = CIME.utils.parse_args_and_handle_standard_logging_options(args, parser) - - if "all" in args.neon_sites: - neon_sites = valid_neon_sites - else: - neon_sites = args.neon_sites - for site in neon_sites: - if site not in valid_neon_sites: - raise ValueError("Invalid site name {}".format(site)) - - if "CIME_OUTPUT_ROOT" in args.output_root: - args.output_root = None - - if args.run_length == "0Y": - if args.run_type == "ad": - run_length = "100Y" - elif args.run_type == "postad": - run_length = "100Y" - else: - # The transient run length is set by cdeps atm buildnml to the last date of the available tower data - # this value is not used - run_length = "4Y" - - run_length = parse_isoduration(run_length) - base_case_root = None - if args.base_case_root: - base_case_root = os.path.abspath(args.base_case_root) - - # Reduce output level for this script unless --debug or --verbose is provided on the command line - if not args.debug and not args.verbose: - root_logger = logging.getLogger() - root_logger.setLevel(logging.WARN) - - return ( - neon_sites, - args.output_root, - args.run_type, - args.overwrite, - run_length, - base_case_root, - args.run_from_postad, - args.setup_only, - args.no_batch, - args.rerun, - args.user_version, - ) - - -def get_isosplit(s, split): - if split in s: - n, s = s.split(split) - else: - n = 0 - return n, s - - -def parse_isoduration(s): - """ - simple ISO 8601 duration parser, does not account for leap years and assumes 30 day months - """ - # Remove prefix - s = s.split("P")[-1] - - # Step through letter dividers - years, s = get_isosplit(s, "Y") - months, s = get_isosplit(s, "M") - days, s = get_isosplit(s, "D") - - # Convert all to timedelta - dt = datetime.timedelta(days=int(days) + 365 * int(years) + 30 * int(months)) - return int(dt.total_seconds() / 86400) - - -class NeonSite: - """ - A class for encapsulating neon sites. - - ... - - Attributes - ---------- - - Methods - ------- - """ - - def __init__(self, name, start_year, end_year, start_month, end_month, finidat): - self.name = name - self.start_year = int(start_year) - self.end_year = int(end_year) - self.start_month = int(start_month) - self.end_month = int(end_month) - self.cesmroot = path_to_ctsm_root() - self.finidat = finidat - - def __str__(self): - return ( - str(self.__class__) - + "\n" - + "\n".join((str(item) + " = " for item in (self.__dict__))) - ) - - def build_base_case( - self, cesmroot, output_root, res, compset, overwrite=False, setup_only=False - ): - """ - Function for building a base_case to clone. - To spend less time on building ctsm for the neon cases, - all the other cases are cloned from this case - - Args: - self: - The NeonSite object - base_root (str): - root of the base_case CIME - res (str): - base_case resolution or gridname - compset (str): - base case compset - overwrite (bool) : - Flag to overwrite the case if exists - """ - print("---- building a base case -------") - self.base_case_root = output_root - user_mods_dirs = [ - os.path.join(cesmroot, "cime_config", "usermods_dirs", "NEON", self.name) - ] - if not output_root: - output_root = os.getcwd() - case_path = os.path.join(output_root, self.name) - - logger.info("base_case_name : {}".format(self.name)) - logger.info("user_mods_dir : {}".format(user_mods_dirs[0])) - - if overwrite and os.path.isdir(case_path): - print("Removing the existing case at: {}".format(case_path)) - shutil.rmtree(case_path) - - with Case(case_path, read_only=False) as case: - if not os.path.isdir(case_path): - print("---- creating a base case -------") - - case.create( - case_path, - cesmroot, - compset, - res, - run_unsupported=True, - answer="r", - output_root=output_root, - user_mods_dirs=user_mods_dirs, - driver="nuopc", - ) - - print("---- base case created ------") - - # --change any config for base_case: - # case.set_value("RUN_TYPE","startup") - print("---- base case setup ------") - case.case_setup() - else: - case.case_setup(reset=True) - case_path = case.get_value("CASEROOT") - - if setup_only: - return case_path - - print("---- base case build ------") - # always walk through the build process to make sure it's up to date. - t0 = time.time() - build.case_build(case_path, case=case) - t1 = time.time() - total = t1 - t0 - print("Time required to building the base case: {} s.".format(total)) - # update case_path to be the full path to the base case - return case_path - - def diff_month(self): - d1 = datetime.datetime(self.end_year, self.end_month, 1) - d2 = datetime.datetime(self.start_year, self.start_month, 1) - return (d1.year - d2.year) * 12 + d1.month - d2.month - - def run_case( - self, - base_case_root, - run_type, - run_length, - user_version, - overwrite=False, - setup_only=False, - no_batch=False, - rerun=False, - ): - user_mods_dirs = [ - os.path.join( - self.cesmroot, "cime_config", "usermods_dirs", "NEON", self.name - ) - ] - expect( - os.path.isdir(base_case_root), - "Error base case does not exist in {}".format(base_case_root), - ) - # -- if user gives a version: - if user_version: - version = user_version - else: - version = 'latest' - - print ("using this version:", version) - - case_root = os.path.abspath( - os.path.join(base_case_root, "..", self.name + "." + run_type) - ) - rundir = None - if os.path.isdir(case_root): - if overwrite: - print("---- removing the existing case -------") - shutil.rmtree(case_root) - elif rerun: - with Case(case_root, read_only=False) as case: - rundir = case.get_value("RUNDIR") - if os.path.isfile(os.path.join(rundir, "ESMF_Profile.summary")): - print( - "Case {} appears to be complete, not rerunning.".format( - case_root - ) - ) - elif not setup_only: - print("Resubmitting case {}".format(case_root)) - case.submit(no_batch=no_batch) - return - else: - logger.warning( - "Case already exists in {}, not overwritting.".format(case_root) - ) - return - - if run_type == "postad": - adcase_root = case_root.replace(".postad", ".ad") - if not os.path.isdir(adcase_root): - logger.warning( - "postad requested but no ad case found in {}".format(adcase_root) - ) - return - - if not os.path.isdir(case_root): - # read_only = False should not be required here - with Case(base_case_root, read_only=False) as basecase: - print("---- cloning the base case in {}".format(case_root)) - basecase.create_clone( - case_root, keepexe=True, user_mods_dirs=user_mods_dirs - ) - - with Case(case_root, read_only=False) as case: - # in order to avoid the complication of leap years we always set the run_length in units of days. - case.set_value("STOP_OPTION", "ndays") - case.set_value("STOP_N", run_length) - case.set_value("REST_OPTION", "end") - case.set_value("CONTINUE_RUN", False) - case.set_value("NEONVERSION", version) - if run_type == "ad": - case.set_value("CLM_FORCE_COLDSTART", "on") - case.set_value("CLM_ACCELERATED_SPINUP", "on") - case.set_value("RUN_REFDATE", "0018-01-01") - case.set_value("RUN_STARTDATE", "0018-01-01") - case.set_value("RESUBMIT", 1) - else: - case.set_value("CLM_FORCE_COLDSTART", "off") - case.set_value("CLM_ACCELERATED_SPINUP", "off") - case.set_value("RUN_TYPE", "hybrid") - - if run_type == "postad": - self.set_ref_case(case) - - if run_type == "transient": - if self.finidat: - case.set_value("RUN_TYPE", "startup") - else: - if not self.set_ref_case(case): - return - case.set_value("STOP_OPTION", "nmonths") - case.set_value("STOP_N", self.diff_month()) - case.set_value("DATM_YR_ALIGN", self.start_year) - case.set_value("DATM_YR_START", self.start_year) - case.set_value("DATM_YR_END", self.end_year) - case.set_value("CALENDAR", "GREGORIAN") - case.set_value("RESUBMIT", 0) - else: - # for the spinup we want the start and end on year boundaries - if self.start_month == 1: - case.set_value("DATM_YR_ALIGN", self.start_year) - case.set_value("DATM_YR_START", self.start_year) - elif self.start_year + 1 <= self.end_year: - case.set_value("DATM_YR_ALIGN", self.start_year + 1) - case.set_value("DATM_YR_START", self.start_year + 1) - if self.end_month == 12: - case.set_value("DATM_YR_END", self.end_year) - else: - case.set_value("DATM_YR_END", self.end_year - 1) - - # Let's no be so clevar with start / end dates - #case.set_value("DATM_YR_ALIGN", int(args.start_date[0:4])) - #case.set_value("DATM_YR_START", int(args.start_date[0:4])) - #case.set_value("DATM_YR_END", int(args.end_date[0:4])) - - if not rundir: - rundir = case.get_value("RUNDIR") - - self.modify_user_nl(case_root, run_type, rundir) - - case.create_namelists() - # explicitly run check_input_data - case.check_all_input_data() - if not setup_only: - case.submit(no_batch=no_batch) - - def set_ref_case(self, case): - rundir = case.get_value("RUNDIR") - case_root = case.get_value("CASEROOT") - if case_root.endswith(".postad"): - ref_case_root = case_root.replace(".postad", ".ad") - root = ".ad" - else: - ref_case_root = case_root.replace(".transient", ".postad") - root = ".postad" - if not os.path.isdir(ref_case_root): - logger.warning( - "ERROR: spinup must be completed first, could not find directory {}".format( - ref_case_root - ) - ) - return False - - with Case(ref_case_root) as refcase: - refrundir = refcase.get_value("RUNDIR") - case.set_value("RUN_REFDIR", refrundir) - case.set_value("RUN_REFCASE", os.path.basename(ref_case_root)) - refdate = None - for reffile in glob.iglob( - refrundir + "/{}{}.clm2.r.*.nc".format(self.name, root) - ): - m = re.search("(\d\d\d\d-\d\d-\d\d)-\d\d\d\d\d.nc", reffile) - if m: - refdate = m.group(1) - symlink_force(reffile, os.path.join(rundir, os.path.basename(reffile))) - logger.info("Found refdate of {}".format(refdate)) - if not refdate: - logger.warning("Could not find refcase for {}".format(case_root)) - return False - - for rpfile in glob.iglob(refrundir + "/rpointer*"): - safe_copy(rpfile, rundir) - if not os.path.isdir(os.path.join(rundir, "inputdata")) and os.path.isdir( - os.path.join(refrundir, "inputdata") - ): - symlink_force( - os.path.join(refrundir, "inputdata"), os.path.join(rundir, "inputdata") - ) - - case.set_value("RUN_REFDATE", refdate) - if case_root.endswith(".postad"): - case.set_value("RUN_STARTDATE", refdate) - else: - case.set_value( - "RUN_STARTDATE", - "{yr:04d}-{mo:02d}-01".format(yr=self.start_year, mo=self.start_month), - ) - return True - - def modify_user_nl(self, case_root, run_type, rundir): - user_nl_fname = os.path.join(case_root, "user_nl_clm") - user_nl_lines = None - if run_type == "transient": - if self.finidat: - user_nl_lines = [ - "finidat = '{}/inputdata/lnd/ctsm/initdata/{}'".format( - rundir, self.finidat - ) - ] - else: - user_nl_lines = [ - "hist_fincl2 = ''", - "hist_mfilt = 20", - "hist_nhtfrq = -8760", - "hist_empty_htapes = .true.", - "hist_fincl1 = 'TOTECOSYSC', 'TOTECOSYSN', 'TOTSOMC', 'TOTSOMN', 'TOTVEGC', 'TOTVEGN', 'TLAI', 'GPP', 'CPOOL', 'NPP', 'TWS', 'H2OSNO'", - ] - - if user_nl_lines: - with open(user_nl_fname, "a") as fd: - for line in user_nl_lines: - fd.write("{}\n".format(line)) - - -def check_neon_listing(valid_neon_sites): - """ - A function to download and parse neon listing file. - """ - listing_file = "listing.csv" - url = "https://storage.neonscience.org/neon-ncar/listing.csv" - - download_file(url, listing_file) - available_list = parse_neon_listing(listing_file, valid_neon_sites) - return available_list - - -def parse_neon_listing(listing_file, valid_neon_sites): - """ - A function to parse neon listing file - and find neon sites with the dates - where data is available. - - Args: - listing_file (str): downloaded listing file - - Returns: - available_list : - list of neon_site objects that is found - on the downloaded listing file. - """ - - # pd.set_option("display.max_rows", None, "display.max_columns", None) - - available_list = [] - - df = pd.read_csv(listing_file) - - # check for finidat files for transient run - finidatlist = df[df["object"].str.contains("lnd/ctsm")] - - # -- filter lines with atm/cdep - df = df[df["object"].str.contains("atm/cdeps/")] - - # -- split the object str to extract site name - df = df["object"].str.split("/", expand=True) - - # -- groupby site name - grouped_df = df.groupby(8) - for key, item in grouped_df: - # -- check if it is a valid neon site - if any(key in x for x in valid_neon_sites): - site_name = key - tmp_df = grouped_df.get_group(key) - - # -- filter files only ending with YYYY-MM.nc - tmp_df = tmp_df[tmp_df[9].str.contains("\d\d\d\d-\d\d.nc")] - - # -- find all the data versions - versions = tmp_df[7].unique() - #print ("all versions available for ", site_name,":", *versions) - latest_version = tmp_df[7].iloc[-1] - #print ("latests version available for ", site_name,":", latest_version) - - tmp_df = tmp_df[tmp_df[7].str.contains(latest_version)] - # -- remove .nc from the file names - tmp_df[9] = tmp_df[9].str.replace(".nc", "") - - - tmp_df2 = tmp_df[9].str.split("-", expand=True) - - # ignore any prefix in file name and just get year - tmp_df2[0] = tmp_df2[0].str.slice(-4) - - # -- figure out start_year and end_year - start_year = tmp_df2[0].iloc[0] - end_year = tmp_df2[0].iloc[-1] - - # -- figure out start_month and end_month - start_month = tmp_df2[1].iloc[0] - end_month = tmp_df2[1].iloc[-1] - - logger.debug("Valid neon site " + site_name + " found!") - logger.debug("File version {}".format(latest_version)) - logger.debug("start_year={}".format(start_year)) - logger.debug("end_year={}".format(end_year)) - logger.debug("start_month={}".format(start_month)) - logger.debug("end_month={}".format(end_month)) - finidat = None - for line in finidatlist["object"]: - if site_name in line: - finidat = line.split(",")[0].split("/")[-1] - - neon_site = NeonSite( - site_name, start_year, end_year, start_month, end_month, finidat - ) - logger.debug(neon_site) - available_list.append(neon_site) - - return available_list - - -def main(description): - cesmroot = path_to_ctsm_root() - # Get the list of supported neon sites from usermods - valid_neon_sites = glob.glob( - os.path.join(cesmroot, "cime_config", "usermods_dirs", "NEON", "[!d]*") - ) - valid_neon_sites = sorted([v.split("/")[-1] for v in valid_neon_sites]) - - ( - site_list, - output_root, - run_type, - overwrite, - run_length, - base_case_root, - run_from_postad, - setup_only, - no_batch, - rerun, - user_version - ) = get_parser(sys.argv, description, valid_neon_sites) - - if output_root: - logger.debug("output_root : " + output_root) - if not os.path.exists(output_root): - os.makedirs(output_root) - - # -- check neon listing file for available data: - available_list = check_neon_listing(valid_neon_sites) - - # ================================= - # -- all neon sites can be cloned from one generic case - # -- so no need to define a base_case for every site. - - res = "CLM_USRDAT" - compset = "I1PtClm51Bgc" - - # -- Looping over neon sites - - for neon_site in available_list: - if neon_site.name in site_list: - if run_from_postad: - neon_site.finidat = None - if not base_case_root: - base_case_root = neon_site.build_base_case( - cesmroot, output_root, res, compset, overwrite, setup_only - ) - logger.info("-----------------------------------") - logger.info("Running CTSM for neon site : {}".format(neon_site.name)) - neon_site.run_case( - base_case_root, - run_type, - run_length, - user_version, - overwrite, - setup_only, - no_batch, - rerun, - ) - - -if __name__ == "__main__": - main(__doc__) diff --git a/tools/site_and_regional/subset_data b/tools/site_and_regional/subset_data index bb582b21f8..d19525c97d 100755 --- a/tools/site_and_regional/subset_data +++ b/tools/site_and_regional/subset_data @@ -16,15 +16,10 @@ To run this script the following packages are required: ---------------------------------------------------------------- To see all available options for single-point/regional subsetting: ./subset_data --help - ---------------------------------------------------------------- -Instructions for running on Cheyenne/Casper: - load the following into your local environment - module load python - ncar_pylib - -To remove from your environment on Cheyenne/Casper: - deactivate +Instructions for running using conda python environments: +../../py_env_create +conda activate ctsm_py """ import os @@ -39,4 +34,4 @@ sys.path.insert(1, _CTSM_PYTHON) from ctsm.subset_data import main if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/tools/toolchain/gen_mksurf_namelist b/tools/toolchain/gen_mksurf_namelist deleted file mode 100755 index ecd225dd19..0000000000 --- a/tools/toolchain/gen_mksurf_namelist +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python - -# 2020-11-08 Negin Sobhani - -""" -|------------------------------------------------------------------| -|--------------------- Instructions -----------------------------| -|------------------------------------------------------------------| -This is a just top-level skeleton script that calls -gen_mksurf_namelist.py. -The original code (./gen_mksurf_namelist.py) is located under -python/ctsm folder. - -This Python script is part of the simplified toolchain for creating -the surface dataset for ctsm cases. -This script should be used as the first step of the new toolchain. -It will automatically create namelist (control file) that is -needed for creating surface dataset and requisite intermediate files for -running CTSM cases. -For transient cases, it will also create a txt file that includes the -landuse files for every year. - -------------------------------------------------------------------- -Instructions for running on Cheyenne/Casper: - -load the following into your local environment: - - module load python - ncar_pylib -------------------------------------------------------------------- -To see the available options: - ./gen_mksurf_namelist.py --help - -To run the script: - ./gen_mksurf_namelist.py - -To remove NPL(ncar_pylib) from your environment on Cheyenne/Casper: - deactivate -------------------------------------------------------------------- -""" - -#TODO (NS) - -# -[x] Add default values in the help page. -# -[x] Add info for help page note for end_year -- by default is start_year -# -[ ] Possibly remove year --years and range options -# Currently comment them out. - -# -[ ] maybe a verbose option and removing debug -# -[x] --debug mode is not working... - -# -[ ] add error check for hi-res and years if they are 1850 and 2005. - -# -[x] different path for each range of years for transient cases. -# default should be picked based on the year. 1850 - 2015 --> -# /glade/p/cesm/cseg/inputdata/lnd/clm2/rawdata/pftcftdynharv.0.25x0.25.LUH2.histsimyr1850-2015.c170629/ -# 850-1850 --> -# pftcftdynharv.0.25x0.25.LUH2.histsimyr0850-1849.c171012 - -# -[ ] hirespft data only for 2005? - -# -- Import libraries -import os -import sys - -# -- add python/ctsm to path -_CTSM_PYTHON = os.path.join( - os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, "python" - ) -sys.path.insert(1, _CTSM_PYTHON) - -from ctsm.gen_mksurf_namelist import main - -if __name__ == "__main__": - main() - -