diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 8708f8e0c2..58a1869f9d 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -30,3 +30,4 @@ d866510188d26d51bcd6d37239283db690af7e82 8a168bb0895f4f2421608dd2589398e13a6663e6 183fc26a6691bbdf87f515dc47924a64be3ced9b 6fccf682eaf718615407d9bacdd3903b8786a03d +2500534eb0a83cc3aff94b30fb62e915054030bf diff --git a/Externals.cfg b/Externals.cfg index a17f8e2ec6..c09bd4041e 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_96 +tag = cismwrap_2_1_97 externals = Externals_CISM.cfg required = True @@ -16,14 +16,14 @@ required = True local_path = components/rtm protocol = git repo_url = https://github.com/ESCOMP/RTM -tag = rtm1_0_78 +tag = rtm1_0_79 required = True [mosart] local_path = components/mosart protocol = git repo_url = https://github.com/ESCOMP/MOSART -tag = mosart1_0_48 +tag = mosart1_0_49 required = True [mizuRoute] @@ -34,7 +34,7 @@ hash = 34723c2 required = True [ccs_config] -tag = ccs_config_cesm0.0.84 +tag = ccs_config_cesm0.0.92 protocol = git repo_url = https://github.com/ESMCI/ccs_config_cesm.git local_path = ccs_config @@ -44,18 +44,18 @@ required = True local_path = cime protocol = git repo_url = https://github.com/ESMCI/cime -tag = cime6.0.175 +tag = cime6.0.217_httpsbranch03 required = True [cmeps] -tag = cmeps0.14.43 +tag = cmeps0.14.50 protocol = git repo_url = https://github.com/ESCOMP/CMEPS.git local_path = components/cmeps required = True [cdeps] -tag = cdeps1.0.24 +tag = cdeps1.0.28 protocol = git repo_url = https://github.com/ESCOMP/CDEPS.git local_path = components/cdeps @@ -70,7 +70,7 @@ local_path = components/cpl7 required = True [share] -tag = share1.0.17 +tag = share1.0.18 protocol = git repo_url = https://github.com/ESCOMP/CESM_share local_path = share diff --git a/Externals_CLM.cfg b/Externals_CLM.cfg index a6fae66356..69d13ca73b 100644 --- a/Externals_CLM.cfg +++ b/Externals_CLM.cfg @@ -1,8 +1,8 @@ [fates] local_path = src/fates protocol = git -repo_url = https://github.com/NGEET/fates -tag = sci.1.71.0_api.33.0.0 +repo_url = https://github.com/rgknox/fates +tag = sci.1.72.2_api.34.0.0 required = True [externals_description] diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 18999dd5af..f62a16ffa1 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -784,7 +784,8 @@ sub setup_cmdl_fates_mode { 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","fates_seeddisp_cadence","use_fates_luh","fluh_timeseries" ); + "fates_parteh_mode","use_fates_tree_damage","fates_history_dimlevel","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)) ) { @@ -1578,6 +1579,7 @@ sub process_namelist_inline_logic { setup_logic_glacier($opts, $nl_flags, $definition, $defaults, $nl, $envxml_ref); setup_logic_dynamic_plant_nitrogen_alloc($opts, $nl_flags, $definition, $defaults, $nl, $physv); setup_logic_luna($opts, $nl_flags, $definition, $defaults, $nl, $physv); + setup_logic_hillslope($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_o3_veg_stress_method($opts, $nl_flags, $definition, $defaults, $nl,$physv); setup_logic_hydrstress($opts, $nl_flags, $definition, $defaults, $nl); setup_logic_dynamic_roots($opts, $nl_flags, $definition, $defaults, $nl, $physv); @@ -2042,7 +2044,7 @@ sub setup_logic_snicar_methods { 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.' ); + my %supportedSettings = ( 'snicar_solarspec' => "'mid_latitude_winter'", 'snicar_dust_optics' => "'sahara'", 'snicar_numrad_snw' => '5', '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); @@ -2060,13 +2062,13 @@ sub setup_logic_snicar_methods { $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 + # snicar_snobc_intmix and snicar_snodst_intmix cannot both be true, however, they can both be false 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)) { + my $val2 = $supportedSettings{$key2}; # supported value for this option + if (($var1 eq $var2) && ($var2 ne $val2)) { $log->warning("$key1 = $var1 and $key2 = $var2 do not work together!"); } } @@ -2712,6 +2714,8 @@ sub setup_logic_do_transient_pfts { $cannot_be_true = "$var cannot be combined with use_cndv"; } elsif (&value_is_true($nl->get_value('use_fates'))) { $cannot_be_true = "$var cannot be combined with use_fates"; + } elsif (&value_is_true($nl->get_value('use_hillslope'))) { + $cannot_be_true = "$var cannot be combined with use_hillslope"; } if ($cannot_be_true) { @@ -2787,6 +2791,8 @@ sub setup_logic_do_transient_crops { # do_transient_crops. However, this hasn't been tested, so to be safe, # we are not allowing this combination for now. $cannot_be_true = "$var has not been tested with FATES, so for now these two options cannot be combined"; + } elsif (&value_is_true($nl->get_value('use_hillslope'))) { + $cannot_be_true = "$var cannot be combined with use_hillslope"; } if ($cannot_be_true) { @@ -2882,6 +2888,8 @@ sub setup_logic_do_transient_lakes { if (&value_is_true($nl->get_value($var))) { if (&value_is_true($nl->get_value('collapse_urban'))) { $log->fatal_error("$var cannot be combined with collapse_urban"); + } elsif (&value_is_true($nl->get_value('use_hillslope'))) { + $log->fatal_error("$var cannot be combined with use_hillslope"); } if ($n_dom_pfts > 0 || $n_dom_landunits > 0 || $toosmall_soil > 0 || $toosmall_crop > 0 || $toosmall_glacier > 0 || $toosmall_lake > 0 || $toosmall_wetland > 0 || $toosmall_urban > 0) { $log->fatal_error("$var cannot be combined with any of the of the following > 0: n_dom_pfts > 0, n_dom_landunit > 0, toosmall_soil > 0._r8, toosmall_crop > 0._r8, toosmall_glacier > 0._r8, toosmall_lake > 0._r8, toosmall_wetland > 0._r8, toosmall_urban > 0._r8"); @@ -2945,6 +2953,8 @@ sub setup_logic_do_transient_urban { if (&value_is_true($nl->get_value($var))) { if (&value_is_true($nl->get_value('collapse_urban'))) { $log->fatal_error("$var cannot be combined with collapse_urban"); + } elsif (&value_is_true($nl->get_value('use_hillslope'))) { + $log->fatal_error("$var cannot be combined with use_hillslope"); } if ($n_dom_pfts > 0 || $n_dom_landunits > 0 || $toosmall_soil > 0 || $toosmall_crop > 0 || $toosmall_glacier > 0 || $toosmall_lake > 0 || $toosmall_wetland > 0 || $toosmall_urban > 0) { $log->fatal_error("$var cannot be combined with any of the of the following > 0: n_dom_pfts > 0, n_dom_landunit > 0, toosmall_soil > 0._r8, toosmall_crop > 0._r8, toosmall_glacier > 0._r8, toosmall_lake > 0._r8, toosmall_wetland > 0._r8, toosmall_urban > 0._r8"); @@ -3274,12 +3284,8 @@ sub setup_logic_hydrology_switches { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_subgrid_fluxes'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'snow_cover_fraction_method'); my $subgrid = $nl->get_value('use_subgrid_fluxes' ); - my $origflag = $nl->get_value('origflag' ); my $h2osfcflag = $nl->get_value('h2osfcflag' ); my $scf_method = $nl->get_value('snow_cover_fraction_method'); - if ( $origflag == 1 && &value_is_true($subgrid) ) { - $log->fatal_error("if origflag is ON, use_subgrid_fluxes can NOT also be on!"); - } if ( $h2osfcflag == 1 && ! &value_is_true($subgrid) ) { $log->fatal_error("if h2osfcflag is ON, use_subgrid_fluxes can NOT be off!"); } @@ -3303,9 +3309,6 @@ sub setup_logic_hydrology_switches { if ( defined($use_vic) && defined($lower) && (&value_is_true($use_vic)) && $lower != 3 && $lower != 4) { $log->fatal_error( "If use_vichydro is on -- lower_boundary_condition can only be table or aquifer" ); } - if ( defined($origflag) && defined($use_vic) && (&value_is_true($use_vic)) && $origflag == 1 ) { - $log->fatal_error( "If use_vichydro is on -- origflag can NOT be equal to 1" ); - } if ( defined($h2osfcflag) && defined($lower) && $h2osfcflag == 0 && $lower != 4 ) { $log->fatal_error( "If h2osfcflag is 0 lower_boundary_condition can only be aquifer" ); } @@ -3487,6 +3490,28 @@ sub setup_logic_luna { #------------------------------------------------------------------------------- +sub setup_logic_hillslope { + # + # Hillslope model + # + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_hillslope' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'downscale_hillslope_meteorology' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'hillslope_head_gradient_method' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'hillslope_transmissivity_method' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'hillslope_pft_distribution_method' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'hillslope_soil_profile_method' ); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_hillslope_routing', 'use_hillslope'=>$nl_flags->{'use_hillslope'} ); + my $use_hillslope = $nl->get_value('use_hillslope'); + my $use_hillslope_routing = $nl->get_value('use_hillslope_routing'); + if ( (! &value_is_true($use_hillslope)) && &value_is_true($use_hillslope_routing) ) { + $log->fatal_error("Cannot turn on use_hillslope_routing when use_hillslope is off\n" ); + } +} + +#------------------------------------------------------------------------------- + sub setup_logic_hydrstress { # # Plant hydraulic stress model @@ -4265,7 +4290,6 @@ sub setup_logic_soil_resis { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'soil_resis_method' ); } -#------------------------------------------------------------------------------- sub setup_logic_canopyfluxes { # @@ -4471,7 +4495,8 @@ sub setup_logic_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","fates_seeddisp_cadence", - "use_fates_logging","fates_parteh_mode", "use_fates_cohort_age_tracking","use_fates_tree_damage","use_fates_luh" ); + "use_fates_logging","fates_parteh_mode", "use_fates_cohort_age_tracking","use_fates_tree_damage", + "use_fates_luh","fates_history_dimlevel" ); 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'} ); @@ -4641,6 +4666,7 @@ sub write_output_files { # CLM component 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 @@ -4650,7 +4676,7 @@ sub write_output_files { soilhydrology_inparm luna friction_velocity mineral_nitrogen_dynamics soilwater_movement_inparm rooting_profile_inparm soil_resis_inparm bgc_shared canopyfluxes_inparm aerosol - clmu_inparm clm_soilstate_inparm clm_nitrogen clm_snowhydrology_inparm + clmu_inparm clm_soilstate_inparm clm_nitrogen clm_snowhydrology_inparm hillslope_hydrology_inparm hillslope_properties_inparm cnprecision_inparm clm_glacier_behavior crop_inparm irrigation_inparm surfacealbedo_inparm water_tracers_inparm tillage_inparm); diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index f79ad1a76b..682955b204 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -449,7 +449,9 @@ attributes from the config_cache.xml file (with keys converted to upper-case). 1.e9 SwensonLawrence2012 + Jordan1991 +Sturm1997 -lnd/clm2/paramdata/ctsm51_params.c240207b.nc -lnd/clm2/paramdata/clm50_params.c240207b.nc -lnd/clm2/paramdata/clm45_params.c240207b.nc +lnd/clm2/paramdata/ctsm51_params.c240208.nc +lnd/clm2/paramdata/clm50_params.c240208.nc +lnd/clm2/paramdata/clm45_params.c240208.nc @@ -619,6 +621,18 @@ attributes from the config_cache.xml file (with keys converted to upper-case). -6.d+2 -6.d+1 + + +.false. +.false. +.false. +.false. +Darcy +LayerSum +Standard +Uniform +.true. + .false. .true. @@ -1689,7 +1703,8 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 mid_latitude_winter sahara .false. -.false. +.false. +.true. .true. .false. @@ -2889,7 +2904,7 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 .false. 1 0 - +2,2 .true. .false. .true. diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 8760724b48..a2e9fb86da 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -777,6 +777,19 @@ Full pathname to the inventory initialization control file. (Only relevant if FATES is on). + + Setting for what types of FATES history to be allocate and + calculated at the dynamics timestep (1st integer) and the + model timestep (2nd integer). This must be consistent with + hist_fincl*, ie output variables must not be listed if the + output level is not enabled. + 0 = no fates history variables are calculated or allocated + 1 = only time x space (3d) fates history variables allowed + 2 = multiplexed dimensioned fates history is also allowed + (Only relevant if FATES is on) + + @@ -800,6 +813,41 @@ LUNA operates on C3 and non-crop vegetation (see vcmax_opt for how other veg is LUNA: Leaf Utilization of Nitrogen for Assimilation + +Toggle to turn on the hillslope model + + + +Toggle to turn on meteorological downscaling in hillslope model + + + +Toggle to turn on surface water routing in the hillslope hydrology model + + + +Method for calculating hillslope saturated head gradient + + + +Method for calculating transmissivity of hillslope columns + + + +Method for distributing pfts across hillslope columns + + + +Method for distributing soil thickness across hillslope columns + + Toggle to turn on the plant hydraulic stress model @@ -2542,12 +2590,6 @@ If surface water is active or not (deprecated -- will be removed) - -Use original CLM4 soil hydraulic properties -(deprecated -- will be removed) - - diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index cb2456ee32..76612736be 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -163,10 +163,10 @@ sub cat_and_create_namelistinfile { # # Figure out number of tests that will run # -my $ntests = 2080; +my $ntests = 1611; if ( defined($opts{'compare'}) ) { - $ntests += 1410; + $ntests += 1145; } plan( tests=>$ntests ); @@ -190,8 +190,7 @@ sub cat_and_create_namelistinfile { &make_config_cache($phys); my $DOMFILE = "$inputdata_rootdir/atm/datm7/domain.lnd.T31_gx3v7.090928.nc"; -my $real_par_file = "user_nl_ctsm_real_parameters"; -my $bldnml = "../build-namelist -verbose -csmdata $inputdata_rootdir -configuration clm -structure standard -glc_nec 10 -no-note -output_reals $real_par_file"; +my $bldnml = "../build-namelist -verbose -csmdata $inputdata_rootdir -configuration clm -structure standard -glc_nec 10 -no-note"; if ( $opts{'test'} ) { $bldnml .= " -test"; } @@ -201,7 +200,7 @@ sub cat_and_create_namelistinfile { system( "/bin/rm $tempfile" ); } -my @files = ( "lnd_in", $tempfile, $real_par_file ); +my @files = ( "lnd_in", $tempfile ); my $cwd = `pwd`; chomp( $cwd ); my $cfiles = NMLTest::CompFiles->new( $cwd, @files ); @@ -269,13 +268,11 @@ sub cat_and_create_namelistinfile { $cfiles->copyfiles( "most_options", $mode ); # Compare to default $cfiles->doNOTdodiffonfile( "lnd_in", "default", $mode ); - $cfiles->doNOTdodiffonfile( "$real_par_file", "default", $mode ); $cfiles->doNOTdodiffonfile( "$tempfile", "default", $mode ); $cfiles->comparefiles( "default", $mode ); # Compare to baseline if ( defined($opts{'compare'}) ) { $cfiles->dodiffonfile( "lnd_in", "most_options", $mode ); - $cfiles->dodiffonfile( "$real_par_file", "most_options", $mode ); $cfiles->doNOTdodiffonfile( "$tempfile", "most_options", $mode ); $cfiles->comparefiles( "most_options", $mode, $opts{'compare'} ); } @@ -358,7 +355,6 @@ sub cat_and_create_namelistinfile { } if ( defined($opts{'compare'}) ) { $cfiles->doNOTdodiffonfile( "$tempfile", "$base_options $options", $mode ); - $cfiles->dodiffonfile( "$real_par_file", "$base_options $options", $mode ); $cfiles->comparefiles( "$base_options $options", $mode, $opts{'compare'} ); } if ( defined($opts{'generate'}) ) { @@ -408,7 +404,6 @@ sub cat_and_create_namelistinfile { if ( defined($opts{'compare'}) ) { $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); $cfiles->dodiffonfile( "lnd_in", "$options", $mode ); - $cfiles->dodiffonfile( "$real_par_file", "$options", $mode ); $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); } if ( defined($opts{'generate'}) ) { @@ -445,7 +440,6 @@ sub cat_and_create_namelistinfile { if ( defined($opts{'compare'}) ) { $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); $cfiles->dodiffonfile( "lnd_in", "$options", $mode ); - $cfiles->dodiffonfile( "$real_par_file", "$options", $mode ); $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); } if ( defined($opts{'generate'}) ) { @@ -487,7 +481,6 @@ sub cat_and_create_namelistinfile { if ( defined($opts{'compare'}) ) { $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); $cfiles->dodiffonfile( "lnd_in", "$options", $mode ); - $cfiles->dodiffonfile( "$real_par_file", "$options", $mode ); $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); } if ( defined($opts{'generate'}) ) { @@ -812,21 +805,6 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm4_5", }, - "-vic with origflag=1" =>{ options=>"-vichydro -envxml_dir .", - namelst=>"origflag=1", - GLC_TWO_WAY_COUPLING=>"FALSE", - phys=>"clm4_5", - }, - "l_bnd=flux with origflag=0"=>{ options=>"-envxml_dir .", - namelst=>"origflag=0, lower_boundary_condition=1", - GLC_TWO_WAY_COUPLING=>"FALSE", - phys=>"clm4_5", - }, - "l_bnd=zflux with origflag=0"=>{ options=>"-envxml_dir .", - namelst=>"origflag=0, lower_boundary_condition=2", - GLC_TWO_WAY_COUPLING=>"FALSE", - phys=>"clm4_5", - }, "bedrock with l_bnc=flux" =>{ options=>"-envxml_dir .", namelst=>"use_bedrock=.true., lower_boundary_condition=1", GLC_TWO_WAY_COUPLING=>"FALSE", @@ -1421,7 +1399,6 @@ sub cat_and_create_namelistinfile { $cfiles->shownmldiff( "default", "standard" ); if ( defined($opts{'compare'}) ) { $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); - $cfiles->dodiffonfile( "$real_par_file", "$options", $mode ); $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); } @@ -1506,7 +1483,6 @@ sub cat_and_create_namelistinfile { $cfiles->shownmldiff( "default", "standard" ); if ( defined($opts{'compare'}) ) { $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); - $cfiles->dodiffonfile( "$real_par_file", "$options", $mode ); $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); } if ( defined($opts{'generate'}) ) { @@ -1580,7 +1556,6 @@ sub cat_and_create_namelistinfile { $cfiles->shownmldiff( "default", "standard" ); if ( defined($opts{'compare'}) ) { $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); - $cfiles->dodiffonfile( "$real_par_file", "$options", $mode ); $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); } if ( defined($opts{'generate'}) ) { @@ -1607,7 +1582,6 @@ sub cat_and_create_namelistinfile { $cfiles->shownmldiff( "default", "standard" ); if ( defined($opts{'compare'}) ) { $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); - $cfiles->dodiffonfile( "$real_par_file", "$options", $mode ); $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); } if ( defined($opts{'generate'}) ) { @@ -1790,7 +1764,6 @@ sub cleanup { my $type = shift; print "Cleanup files created\n"; - system( "/bin/rm env_run.xml $real_par_file" ); if ( defined($type) ) { if ( $type eq "config" ) { system( "/bin/rm config_cache.xml" ); diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index 55d1363e6b..8a18fc2901 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -29,12 +29,6 @@ - - - FAIL - #2268 - - @@ -58,41 +52,6 @@ - - - FAIL - 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 @@ -147,6 +106,13 @@ #2310 + + + + FAIL + #2373 + + @@ -192,20 +158,6 @@ - - - FAIL - ESMCI/ccs_config_cesm#130 - - - - - - FAIL - ESMCI/ccs_config_cesm#130 - - - PEND diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 90f8b4a6a7..3209068d46 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -2013,12 +2013,8 @@ - - - @@ -2137,9 +2133,6 @@ - @@ -2152,10 +2145,7 @@ - - @@ -2166,9 +2156,6 @@ - @@ -2203,12 +2190,8 @@ - - - @@ -2222,10 +2205,6 @@ - @@ -2238,10 +2217,7 @@ - - - @@ -2633,10 +2609,6 @@ - @@ -2656,10 +2628,7 @@ - - @@ -2994,9 +2963,6 @@ - @@ -3023,10 +2989,7 @@ - - @@ -3480,4 +3443,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cime_config/testdefs/testmods_dirs/clm/FatesColdAllVars/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/FatesColdAllVars/user_nl_clm index 7f5ece27c8..a426c775b0 100644 --- a/cime_config/testdefs/testmods_dirs/clm/FatesColdAllVars/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/FatesColdAllVars/user_nl_clm @@ -3,75 +3,57 @@ hist_mfilt = 365 hist_nhtfrq = -24 hist_empty_htapes = .false. fates_spitfire_mode = 1 +fates_history_dimlevel = 2,2 +use_fates_tree_damage = .true. 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', -'FATES_SECONDARY_FOREST_VEGC', 'FATES_SECONDAREA_ANTHRODIST_AP', -'FATES_SECONDAREA_DIST_AP', 'FATES_STOMATAL_COND_AP', 'FATES_LBLAYER_COND_AP', -'FATES_NPP_AP', 'FATES_GPP_AP', 'FATES_PARSUN_Z_CLLL', 'FATES_PARSHA_Z_CLLL', -'FATES_PARSUN_Z_CLLLPF', 'FATES_PARSHA_Z_CLLLPF', 'FATES_PARSUN_Z_CL', -'FATES_PARSHA_Z_CL', 'FATES_LAISUN_Z_CLLL', 'FATES_LAISHA_Z_CLLL', -'FATES_LAISUN_Z_CLLLPF', 'FATES_LAISHA_Z_CLLLPF', 'FATES_LAISUN_TOP_CL', -'FATES_LAISHA_TOP_CL', 'FATES_FABD_SUN_CLLLPF', 'FATES_FABD_SHA_CLLLPF', -'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_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', -'FATES_MORTALITY_CANOPY_SZAP', 'FATES_MORTALITY_USTORY_SZAP', -'FATES_NPLANT_SZAPPF', 'FATES_NPP_APPF', 'FATES_VEGC_APPF', 'FATES_GPP_SZPF', -'FATES_GPP_CANOPY_SZPF', 'FATES_AUTORESP_CANOPY_SZPF', 'FATES_GPP_USTORY_SZPF', -'FATES_AUTORESP_USTORY_SZPF', 'FATES_NPP_SZPF', 'FATES_LEAF_ALLOC_SZPF', -'FATES_SEED_ALLOC_SZPF', 'FATES_FROOT_ALLOC_SZPF', 'FATES_BGSAPWOOD_ALLOC_SZPF', -'FATES_BGSTRUCT_ALLOC_SZPF', 'FATES_AGSAPWOOD_ALLOC_SZPF', -'FATES_AGSTRUCT_ALLOC_SZPF', 'FATES_STORE_ALLOC_SZPF', 'FATES_DDBH_SZPF', -'FATES_GROWTHFLUX_SZPF', 'FATES_GROWTHFLUX_FUSION_SZPF', -'FATES_DDBH_CANOPY_SZPF', 'FATES_DDBH_USTORY_SZPF', 'FATES_BASALAREA_SZPF', -'FATES_VEGC_ABOVEGROUND_SZPF', 'FATES_NPLANT_SZPF', 'FATES_NPLANT_ACPF', -'FATES_MORTALITY_BACKGROUND_SZPF', 'FATES_MORTALITY_HYDRAULIC_SZPF', -'FATES_MORTALITY_CSTARV_SZPF', 'FATES_MORTALITY_IMPACT_SZPF', -'FATES_MORTALITY_FIRE_SZPF', 'FATES_MORTALITY_CROWNSCORCH_SZPF', -'FATES_MORTALITY_CAMBIALBURN_SZPF', 'FATES_MORTALITY_TERMINATION_SZPF', -'FATES_MORTALITY_LOGGING_SZPF', 'FATES_MORTALITY_FREEZING_SZPF', -'FATES_MORTALITY_SENESCENCE_SZPF', 'FATES_MORTALITY_AGESCEN_SZPF', -'FATES_MORTALITY_AGESCEN_ACPF', 'FATES_MORTALITY_CANOPY_SZPF', -'FATES_STOREC_CANOPY_SZPF', 'FATES_LEAFC_CANOPY_SZPF', -'FATES_NPLANT_CANOPY_SZPF', 'FATES_MORTALITY_USTORY_SZPF', -'FATES_STOREC_USTORY_SZPF', 'FATES_LEAFC_USTORY_SZPF', -'FATES_NPLANT_USTORY_SZPF', 'FATES_CWD_ABOVEGROUND_DC', -'FATES_CWD_BELOWGROUND_DC', 'FATES_CWD_ABOVEGROUND_IN_DC', -'FATES_CWD_BELOWGROUND_IN_DC', 'FATES_CWD_ABOVEGROUND_OUT_DC', -'FATES_CWD_BELOWGROUND_OUT_DC', 'FATES_AUTORESP_SZPF', 'FATES_GROWAR_SZPF', -'FATES_MAINTAR_SZPF', 'FATES_RDARK_SZPF', 'FATES_AGSAPMAINTAR_SZPF', -'FATES_BGSAPMAINTAR_SZPF', 'FATES_FROOTMAINTAR_SZPF', -'FATES_YESTCANLEV_CANOPY_SZ', 'FATES_YESTCANLEV_USTORY_SZ', -'FATES_VEGC_SZ', 'FATES_DEMOTION_RATE_SZ', 'FATES_PROMOTION_RATE_SZ', -'FATES_SAI_CANOPY_SZ', 'FATES_SAI_USTORY_SZ', 'FATES_NPP_CANOPY_SZ', -'FATES_NPP_USTORY_SZ', 'FATES_TRIMMING_CANOPY_SZ', 'FATES_TRIMMING_USTORY_SZ', -'FATES_CROWNAREA_CANOPY_SZ', 'FATES_CROWNAREA_USTORY_SZ', -'FATES_LEAFCTURN_CANOPY_SZ', 'FATES_FROOTCTURN_CANOPY_SZ', -'FATES_STORECTURN_CANOPY_SZ', 'FATES_STRUCTCTURN_CANOPY_SZ', -'FATES_SAPWOODCTURN_CANOPY_SZ', 'FATES_SEED_PROD_CANOPY_SZ', -'FATES_LEAF_ALLOC_CANOPY_SZ', 'FATES_FROOT_ALLOC_CANOPY_SZ', -'FATES_SAPWOOD_ALLOC_CANOPY_SZ', 'FATES_STRUCT_ALLOC_CANOPY_SZ', -'FATES_SEED_ALLOC_CANOPY_SZ', 'FATES_STORE_ALLOC_CANOPY_SZ', -'FATES_RDARK_CANOPY_SZ', 'FATES_LSTEMMAINTAR_CANOPY_SZ', -'FATES_CROOTMAINTAR_CANOPY_SZ', 'FATES_FROOTMAINTAR_CANOPY_SZ', -'FATES_GROWAR_CANOPY_SZ', 'FATES_MAINTAR_CANOPY_SZ', -'FATES_LEAFCTURN_USTORY_SZ', 'FATES_FROOTCTURN_USTORY_SZ', -'FATES_STORECTURN_USTORY_SZ', 'FATES_STRUCTCTURN_USTORY_SZ', -'FATES_SAPWOODCTURN_USTORY_SZ', 'FATES_SEED_PROD_USTORY_SZ', -'FATES_LEAF_ALLOC_USTORY_SZ', 'FATES_FROOT_ALLOC_USTORY_SZ', -'FATES_SAPWOOD_ALLOC_USTORY_SZ', 'FATES_STRUCT_ALLOC_USTORY_SZ', -'FATES_SEED_ALLOC_USTORY_SZ', 'FATES_STORE_ALLOC_USTORY_SZ', -'FATES_RDARK_USTORY_SZ', 'FATES_LSTEMMAINTAR_USTORY_SZ', -'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_DROUGHT_STATUS_PF', -'FATES_DAYSINCE_DROUGHTLEAFOFF_PF', 'FATES_DAYSINCE_DROUGHTLEAFON_PF', -'FATES_MEANLIQVOL_DROUGHTPHEN_PF', 'FATES_MEANSMP_DROUGHTPHEN_PF', -'FATES_ELONG_FACTOR_PF' +hist_fincl1 = 'FATES_TLONGTERM', +'FATES_TGROWTH','FATES_SEEDS_IN_GRIDCELL_PF','FATES_SEEDS_OUT_GRIDCELL_PF','FATES_NCL_AP', +'FATES_NPATCH_AP','FATES_VEGC_AP','FATES_SECONDAREA_ANTHRODIST_AP','FATES_SECONDAREA_DIST_AP', +'FATES_FUEL_AMOUNT_APFC','FATES_STOREC_TF_USTORY_SZPF','FATES_STOREC_TF_CANOPY_SZPF', +'FATES_CROWNAREA_CLLL','FATES_ABOVEGROUND_MORT_SZPF', +'FATES_ABOVEGROUND_PROD_SZPF','FATES_NPLANT_SZAP','FATES_NPLANT_CANOPY_SZAP', +'FATES_NPLANT_USTORY_SZAP','FATES_DDBH_CANOPY_SZAP','FATES_DDBH_USTORY_SZAP', +'FATES_MORTALITY_CANOPY_SZAP','FATES_MORTALITY_USTORY_SZAP','FATES_NPLANT_SZAPPF', +'FATES_NPP_APPF','FATES_VEGC_APPF','FATES_SCORCH_HEIGHT_APPF','FATES_GPP_SZPF', +'FATES_GPP_CANOPY_SZPF','FATES_AUTORESP_CANOPY_SZPF','FATES_GPP_USTORY_SZPF', +'FATES_AUTORESP_USTORY_SZPF','FATES_NPP_SZPF','FATES_LEAF_ALLOC_SZPF', +'FATES_SEED_ALLOC_SZPF','FATES_FROOT_ALLOC_SZPF','FATES_BGSAPWOOD_ALLOC_SZPF', +'FATES_BGSTRUCT_ALLOC_SZPF','FATES_AGSAPWOOD_ALLOC_SZPF','FATES_AGSTRUCT_ALLOC_SZPF', +'FATES_STORE_ALLOC_SZPF','FATES_DDBH_SZPF','FATES_GROWTHFLUX_SZPF','FATES_GROWTHFLUX_FUSION_SZPF', +'FATES_DDBH_CANOPY_SZPF','FATES_DDBH_USTORY_SZPF','FATES_BASALAREA_SZPF','FATES_VEGC_ABOVEGROUND_SZPF', +'FATES_NPLANT_SZPF','FATES_NPLANT_ACPF','FATES_MORTALITY_BACKGROUND_SZPF','FATES_MORTALITY_HYDRAULIC_SZPF', +'FATES_MORTALITY_CSTARV_SZPF','FATES_MORTALITY_IMPACT_SZPF','FATES_MORTALITY_FIRE_SZPF', +'FATES_MORTALITY_CROWNSCORCH_SZPF','FATES_MORTALITY_CAMBIALBURN_SZPF','FATES_MORTALITY_TERMINATION_SZPF', +'FATES_MORTALITY_LOGGING_SZPF','FATES_MORTALITY_FREEZING_SZPF','FATES_MORTALITY_SENESCENCE_SZPF', +'FATES_MORTALITY_AGESCEN_SZPF','FATES_MORTALITY_AGESCEN_ACPF','FATES_MORTALITY_CANOPY_SZPF', +'FATES_M3_MORTALITY_CANOPY_SZPF','FATES_M3_MORTALITY_USTORY_SZPF','FATES_C13DISC_SZPF', +'FATES_STOREC_CANOPY_SZPF','FATES_LEAFC_CANOPY_SZPF','FATES_LAI_CANOPY_SZPF','FATES_CROWNAREA_CANOPY_SZPF', +'FATES_CROWNAREA_USTORY_SZPF','FATES_NPLANT_CANOPY_SZPF','FATES_MORTALITY_USTORY_SZPF','FATES_STOREC_USTORY_SZPF', +'FATES_LEAFC_USTORY_SZPF','FATES_LAI_USTORY_SZPF','FATES_NPLANT_USTORY_SZPF','FATES_CWD_ABOVEGROUND_DC', +'FATES_CWD_BELOWGROUND_DC','FATES_CWD_ABOVEGROUND_IN_DC','FATES_CWD_BELOWGROUND_IN_DC', +'FATES_CWD_ABOVEGROUND_OUT_DC','FATES_CWD_BELOWGROUND_OUT_DC','FATES_YESTCANLEV_CANOPY_SZ', +'FATES_YESTCANLEV_USTORY_SZ','FATES_VEGC_SZ','FATES_DEMOTION_RATE_SZ','FATES_PROMOTION_RATE_SZ', +'FATES_SAI_CANOPY_SZ','FATES_M3_MORTALITY_CANOPY_SZ','FATES_M3_MORTALITY_USTORY_SZ','FATES_SAI_USTORY_SZ', +'FATES_NPP_CANOPY_SZ','FATES_NPP_USTORY_SZ','FATES_TRIMMING_CANOPY_SZ','FATES_TRIMMING_USTORY_SZ', +'FATES_CROWNAREA_CANOPY_SZ','FATES_CROWNAREA_USTORY_SZ','FATES_LEAFCTURN_CANOPY_SZ','FATES_FROOTCTURN_CANOPY_SZ', +'FATES_STORECTURN_CANOPY_SZ','FATES_STRUCTCTURN_CANOPY_SZ','FATES_SAPWOODCTURN_CANOPY_SZ','FATES_SEED_PROD_CANOPY_SZ', +'FATES_LEAF_ALLOC_CANOPY_SZ','FATES_FROOT_ALLOC_CANOPY_SZ','FATES_SAPWOOD_ALLOC_CANOPY_SZ','FATES_STRUCT_ALLOC_CANOPY_SZ', +'FATES_SEED_ALLOC_CANOPY_SZ','FATES_STORE_ALLOC_CANOPY_SZ','FATES_LEAFCTURN_USTORY_SZ','FATES_FROOTCTURN_USTORY_SZ', +'FATES_STORECTURN_USTORY_SZ','FATES_STRUCTCTURN_USTORY_SZ','FATES_SAPWOODCTURN_USTORY_SZ', +'FATES_SEED_PROD_USTORY_SZ','FATES_LEAF_ALLOC_USTORY_SZ','FATES_FROOT_ALLOC_USTORY_SZ','FATES_SAPWOOD_ALLOC_USTORY_SZ', +'FATES_STRUCT_ALLOC_USTORY_SZ','FATES_SEED_ALLOC_USTORY_SZ','FATES_STORE_ALLOC_USTORY_SZ','FATES_CROWNAREA_CANOPY_CD', +'FATES_CROWNAREA_USTORY_CD','FATES_NPLANT_CDPF','FATES_NPLANT_CANOPY_CDPF','FATES_NPLANT_USTORY_CDPF', +'FATES_M3_CDPF','FATES_M11_SZPF','FATES_M11_CDPF','FATES_MORTALITY_CDPF','FATES_M3_MORTALITY_CANOPY_CDPF', +'FATES_M3_MORTALITY_USTORY_CDPF','FATES_M11_MORTALITY_CANOPY_CDPF','FATES_M11_MORTALITY_USTORY_CDPF', +'FATES_MORTALITY_CANOPY_CDPF','FATES_MORTALITY_USTORY_CDPF','FATES_DDBH_CDPF','FATES_DDBH_CANOPY_CDPF', +'FATES_DDBH_USTORY_CDPF','FATES_VEGC_SZPF','FATES_LEAFC_SZPF','FATES_FROOTC_SZPF','FATES_SAPWOODC_SZPF', +'FATES_STOREC_SZPF','FATES_REPROC_SZPF','FATES_NPP_AP','FATES_GPP_AP','FATES_RDARK_USTORY_SZ', +'FATES_LSTEMMAINTAR_USTORY_SZ','FATES_CROOTMAINTAR_USTORY_SZ','FATES_FROOTMAINTAR_USTORY_SZ','FATES_GROWAR_USTORY_SZ', +'FATES_MAINTAR_USTORY_SZ','FATES_RDARK_CANOPY_SZ','FATES_CROOTMAINTAR_CANOPY_SZ','FATES_FROOTMAINTAR_CANOPY_SZ', +'FATES_GROWAR_CANOPY_SZ','FATES_MAINTAR_CANOPY_SZ','FATES_LSTEMMAINTAR_CANOPY_SZ','FATES_AUTORESP_SZPF', +'FATES_GROWAR_SZPF','FATES_MAINTAR_SZPF','FATES_RDARK_SZPF','FATES_AGSAPMAINTAR_SZPF','FATES_BGSAPMAINTAR_SZPF', +'FATES_FROOTMAINTAR_SZPF','FATES_PARSUN_CLLL','FATES_PARSHA_CLLL','FATES_PARSUN_CLLLPF','FATES_PARSHA_CLLLPF', +'FATES_PARSUN_CL','FATES_PARSHA_CL','FATES_LAISUN_CLLL','FATES_LAISHA_CLLL','FATES_LAISUN_CLLLPF', +'FATES_LAISHA_CLLLPF','FATES_PARPROF_DIR_CLLLPF','FATES_PARPROF_DIF_CLLLPF','FATES_LAISUN_CL','FATES_LAISHA_CL', +'FATES_PARPROF_DIR_CLLL','FATES_PARPROF_DIF_CLLL','FATES_NET_C_UPTAKE_CLLL','FATES_CROWNFRAC_CLLLPF', +'FATES_LBLAYER_COND_AP','FATES_STOMATAL_COND_AP' diff --git a/cime_config/testdefs/testmods_dirs/clm/Hillslope/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/Hillslope/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/Hillslope/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/Hillslope/shell_commands b/cime_config/testdefs/testmods_dirs/clm/Hillslope/shell_commands new file mode 100644 index 0000000000..6f3602d2e6 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/Hillslope/shell_commands @@ -0,0 +1,4 @@ +./xmlchange CLM_BLDNML_OPTS="-bgc sp" +DIN_LOC_ROOT=$(./xmlquery --value DIN_LOC_ROOT) +meshfile=$DIN_LOC_ROOT/lnd/clm2/testdata/ESMFmesh_10x15_synthetic_cosphill_1.0.nc +./xmlchange ATM_DOMAIN_MESH=${meshfile},LND_DOMAIN_MESH=${meshfile} diff --git a/cime_config/testdefs/testmods_dirs/clm/Hillslope/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/Hillslope/user_nl_clm new file mode 100644 index 0000000000..afdcf4d1fc --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/Hillslope/user_nl_clm @@ -0,0 +1,11 @@ +use_hillslope = .true. +use_hillslope_routing = .true. +downscale_hillslope_meteorology = .false. +hillslope_head_gradient_method = 'Darcy' +hillslope_transmissivity_method = 'LayerSum' +hillslope_pft_distribution_method = 'PftLowlandUpland' +hillslope_soil_profile_method = 'Uniform' + +fsurdat = '$DIN_LOC_ROOT/lnd/clm2/testdata/surfdata_10x15_78pfts_simyr2000_synthetic_cosphill_1.3.nc' + +use_ssre = .false. diff --git a/cime_config/testdefs/testmods_dirs/clm/HillslopeC/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/HillslopeC/include_user_mods new file mode 100644 index 0000000000..fa2e50a80d --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/HillslopeC/include_user_mods @@ -0,0 +1 @@ +../Hillslope diff --git a/cime_config/testdefs/testmods_dirs/clm/HillslopeC/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/HillslopeC/user_nl_clm new file mode 100644 index 0000000000..10450766d0 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/HillslopeC/user_nl_clm @@ -0,0 +1,7 @@ +! Various hillslope options not exercised by other testmods +use_hillslope_routing = .false. +downscale_hillslope_meteorology = .true. +hillslope_head_gradient_method = 'Kinematic' +hillslope_transmissivity_method = 'Uniform' +hillslope_pft_distribution_method = 'DominantPftUniform' +hillslope_soil_profile_method = 'SetLowlandUpland' diff --git a/cime_config/testdefs/testmods_dirs/clm/HillslopeD/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/HillslopeD/include_user_mods new file mode 100644 index 0000000000..fa2e50a80d --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/HillslopeD/include_user_mods @@ -0,0 +1 @@ +../Hillslope diff --git a/cime_config/testdefs/testmods_dirs/clm/HillslopeD/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/HillslopeD/user_nl_clm new file mode 100644 index 0000000000..04a2332df7 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/HillslopeD/user_nl_clm @@ -0,0 +1,3 @@ +! Various hillslope options not exercised by other testmods +hillslope_pft_distribution_method = 'DominantPftLowland' +hillslope_soil_profile_method = 'Linear' diff --git a/cime_config/testdefs/testmods_dirs/clm/HillslopeFromFile/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/HillslopeFromFile/include_user_mods new file mode 100644 index 0000000000..fa2e50a80d --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/HillslopeFromFile/include_user_mods @@ -0,0 +1 @@ +../Hillslope diff --git a/cime_config/testdefs/testmods_dirs/clm/HillslopeFromFile/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/HillslopeFromFile/user_nl_clm new file mode 100644 index 0000000000..7be761eccc --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/HillslopeFromFile/user_nl_clm @@ -0,0 +1,2 @@ +hillslope_pft_distribution_method = 'FromFile' +hillslope_soil_profile_method = 'FromFile' 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 c235d72df1..3c686f08b9 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 = '$DIN_LOC_ROOT/lnd/clm2/paramdata/ctsm51_ciso_cwd_hr_params.c240207b.nc' +paramfile = '$DIN_LOC_ROOT/lnd/clm2/paramdata/ctsm51_ciso_cwd_hr_params.c240208.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/oldhyd/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/oldhyd/user_nl_clm index 351bce0a82..5ef1fc660a 100644 --- a/cime_config/testdefs/testmods_dirs/clm/oldhyd/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/oldhyd/user_nl_clm @@ -1,4 +1,3 @@ snow_cover_fraction_method = 'NiuYang2007' h2osfcflag = 0 - origflag = 1 use_subgrid_fluxes = .false. diff --git a/doc/ChangeLog b/doc/ChangeLog index d26715f699..fbaa87f2dd 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,420 @@ =============================================================== +Tag name: ctsm5.1.dev174 +Originator(s): olyson (Keith Oleson,UCAR/TSS) +Date: Thu 14 Mar 2024 04:56:37 PM MDT +One-line Summary: Improve vegetation health at high latitudes + +Purpose and description of changes +---------------------------------- + +The corresponding changes: + +Remove snicar_snobc_intmix from EXPERIMENTAL endrun (allow it to be true) +Remove flg_snoage_scl in SNICAR such that xdrdt can have an effect (fixes #2298 ) +New parameter file and namelist values for clm5_1: + +- snow_thermal_cond_method = Sturm1997 (default for clm5_1) +- snicar_snobc_intmix = .true. (default for clm5_1) +- ctsm51_params.c240208.nc is the new CTSM parameter file (changes: froot_leaf(11:12)=1.2, FUN_fracfixers(11:12)=1, xdrdt=5, scvng_fct_mlt_sf=0.5, snw_rds_refrz=1500, fresh_snw_rds_max=400) +- New history fields for coupler history verification (default off) +- Add snow5d_thresh_for_onset to parameter file, set to 0.2 for clm51 and 0.1 (unchanged) for clm50 and clm45. + + +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 #2298 +Fixes LMWG_dev discussion #3 + +New bug discovered or introduced: +Relevant post appears in #2348 on 2024/3/14 + +Notes of particular relevance for users +--------------------------------------- +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + New namelist defaults including new parameter files: + ctsm51_params.c240208.nc + clm50_params.c240208.nc + clm45_params.c240208.nc + ctsm51_ciso_cwd_hr_params.c240208.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): + + derecho ----- OK + izumi ------- OK + + any other testing (give details below): + Keith Oleson replicated simulation in LMWG_dev issue #51 as bfb when + snow5d_thresh_for_onset on ctsm51_ciso_cwd_hr_params.c240208.nc was set + to original value of 0.1. + +Answer changes +-------------- + +Changes answers relative to baseline: YES + + Summarize any changes to answers, i.e., + - what code configurations: clm51 + - what platforms/compilers: all + - nature of change: new climate at high latitudes + + See LMWG_dev discussion #3 and simulations discussed therein, + including (though possibly not limited to) LMWG_dev issues #51, 52, 54, 57. + + slevis will add this tag with the label "SIGNIFICANT" to the + Answer-changing-tags wiki: + 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/2348 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev173 +Originator(s): rgknox (Ryan Knox,LAWRENCE BERKELEY NATIONAL LABORATORY) +Date: Wed 13 Mar 2024 04:46:37 PM MDT +One-line Summary: New FATES namelist variable, fates_history_dimlevel + +Purpose and description of changes +---------------------------------- +This set of changes introduces a new namelist setting that allows more control over fates history diagnostics. This setting, fates_history_dimlevel accepts two integers, comma-delimited, from 0-2. The first specifies the history output dimension level for high-frequency output (ie model timestep) and the second is for output at the dynamics timestep. A value of 0 indicates no history variables should be processed. A value of 1 indicates that only site-level mean values should be processed. A value of 2 indicates that all variables, including those that use an extra dimension should be processed. This is different from adding and excluding history variable names from the namelist, in that these settings not only omit variables from the output file, but they prevent their allocations and calculations all together. Processing history diagnostics in FATES takes a non-trivial amount of time. + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +These changes have no non-trivial impacts on any scientific configurations, this is a refactor. + +Bugs fixed +---------- + +No bugs fixed, feature addition only. + +Notes of particular relevance for users +--------------------------------------- + +Users should be aware of the new namelist setting fates_history_dimlevel. Omitting this setting will default to a level of "2" which enables all output and should maintain existing model behavior. + +Substantial timing or memory changes: The FATES model is somewhere on the order of 10% faster for level 0 and 1, versus 2, for large gridded runs with non satellite phenology, on derecho. + + +Notes of particular relevance for developers: +--------------------------------------------- + +None of note. + + +Testing summary: +---------------- + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - OK (64 fates tests differ) + + 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 ------- OK + + fates tests: (give name of baseline if different from CTSM tagname, normally fates baselines are fates--) + derecho ----- OK + izumi ------- OK + + +If the tag used for baseline comparisons was NOT the previous tag, note that here: (used ctsm5.1.dev172) + + +Answer changes +-------------- + +Answer changes for FATES tests were detected. All diffs were small enough to be consistent with order of operations changes, with the exception of some variables that were updated to have ignore values used for non-vegetated patches instead of zero. + + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev172 +Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) +Date: Tue 12 Mar 2024 11:59:48 PM MDT +One-line Summary: Merge b4b-dev + +Purpose and description of changes +---------------------------------- + +Update of externals to what's expected in cesm2_3_beta17. Some documentation updates including a rebuild +of the documentation and updating how images are handled with git lfs. Improvements to the documentation for +residue removal and tillage for prognostic crops. + +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 #2351 Update to cesm2_3_beta17 externals + Fixes #2380 ctsm_pylib on izumi can use python3.7.9 + Fixes #2331 py_env_create fails on izumi + Fixes #1910 modify_singlept_neon on izumi + Fixes #1658 malformed file on izumi + Fixes #2412 Check current directory isn't being removed in run_neon + +Notes of particular relevance for users +--------------------------------------- + +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): + + ccs_config config_machines.xml files were updated from v2 to v3 + + The timing and memory usage information was moved from drv.log to med.log for nuopc runs. + This allows the creating of the baselines cpl-mem.log and cpl-tput.log files. + + SMP_PRESENT removed and BUILD_THREADED added. + +Changes to tests or testing: + Bring back DEBUG testing with intel for mpi-serial cases. + + +Testing summary: regular +---------------- + [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 + + clm_pymods test suite on derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +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.): + manage externals + cismwrap_2_1_97 + = rtm1_0_79 + mosart1_0_49 + ccs_config_cesm0.0.92 + cime6.0.217_httpsbranch03 + cmeps0.14.50 + cdeps1.0.28 + share1.0.18 + +Pull Requests that document the changes (include PR ids): +(https://github.com/ESCOMP/ctsm/pull) + + #2396 Explain residue removal + #2385 Update externals + #2394 Fix tillage instructions and images + #2389 Minor docs updates + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev171 +Originator(s): olyson (Keith Oleson,UCAR/TSS) +Date: Mon 04 Mar 2024 10:33:55 AM MST +One-line Summary: Set initial t_soisno=272 for soils and 274K for urban road + +Purpose and description of changes +---------------------------------- + + Issue #2338 and PR #2355 explain: + Soil temperature initialization not implemented correctly in + ctsm5.1.dev058 and thus subsequent tags. + +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 #2338 + +Notes of particular relevance for developers: +--------------------------------------------- +Changes to tests or testing: + SMS_Lm3_D_Mmpi-serial.1x1_brazil.I2000Clm50FatesCruRsGs.izumi_intel.clm-FatesColdHydro + added to expected failures, issue #2373, to be revisited when #2384 is + resolved. + +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 + + Summarize any changes to answers, i.e., + - what code configurations: all + - what platforms/compilers: all + - nature of change: larger than roundoff but not climate-changing + + The original diagnostics for this change are here: + https://webext.cgd.ucar.edu/I2000/ctsm51c6_PPEn08ctsm51d023_2deg_GSWP3V1_Sparse400_cs_ts_tsoisno272_2000AD/lnd/ctsm51c6_PPEn08ctsm51d023_2deg_GSWP3V1_Sparse400_cs_ts_tsoisno272_2000AD.381_400-ctsm51c6_PPEn08ctsm51d023_2deg_GSWP3V1_Sparse400_cs_ts_2000AD.381_400/setsIndex.html + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2355 + +=============================================================== +=============================================================== +Tag name: ctsm5.1.dev170 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Wed Feb 28 11:01:43 MST 2024 +One-line Summary: Add hillslope hydrology + +Purpose and description of changes +---------------------------------- + +Changes include multiple soil columns per vegetated landunit, additional meteorological downscaling, new subsurface lateral flow equations, and a hillslope routing parameterization. + +Described in: +Swenson, S. C., Clark, M., Fan, Y., Lawrence, D. M., & Perket, J. (2019). Representing intra-hillslope lateral subsurface flow in the community land model. Journal of Advances in Modeling Earth Systems, 11, 4044–4065. https://doi.org/10.1029/2019MS001833 + + +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 developers: +--------------------------------------------- + +Changes to tests or testing: +* oldhyd test changes answers due to removal of origflag parameter +* Adds several hillslope-specific tests + + +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 +-------------- + + Summarize any changes to answers, i.e., + - what code configurations: all + - what platforms/compilers: all + - nature of change: roundoff + + 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. + * FSDS answers change due to rounding differences, since the history field now uses a column-level variable instead of a gridcell-level one. Note that this is JUST the history field that's affected, which is why there are no diffs in any other variable. (Confirmed using branch at https://github.com/samsrabin/CTSM/tree/hillslope-revert-fsds-diffs.) + * The origflag parameter (used to reproduce CLM4 behavior) was removed, so anything using that will break. This includes the oldhyd test. + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +* ESCOMP/CTSM#1715: Hillslope hydrology (https://github.com/ESCOMP/CTSM/pull/1715) +* ESCOMP/CTSM#2390: Hillslope merge (https://github.com/ESCOMP/CTSM/pull/2390) + +=============================================================== +=============================================================== Tag name: ctsm5.1.dev169 Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) Date: Thu 22 Feb 2024 09:42:57 AM MST diff --git a/doc/ChangeSum b/doc/ChangeSum index 18ae34626f..49afdeb03f 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,10 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.1.dev174 olyson 03/14/2024 Improve vegetation health at high latitudes + ctsm5.1.dev173 rgknox 03/13/2024 New FATES namelist variable: fates_history_dimlevel + ctsm5.1.dev172 erik 03/12/2024 Merge b4b-dev + ctsm5.1.dev171 olyson 03/01/2024 Set initial t_soisno=272 for soils and 274K for urban road + ctsm5.1.dev170 samrabin 02/28/2024 Add hillslope hydrology ctsm5.1.dev169 samrabin 02/22/2024 Merge b4b-dev ctsm5.1.dev168 slevis 02/16/2024 Remove a source of negative snocan in CanopyFluxesMod ctsm5.1.dev167 samrabin 02/08/2024 Delete _FillValue and history from parameter files diff --git a/doc/Makefile b/doc/Makefile index 1b8a86ad9a..49e9764b7a 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -17,6 +17,7 @@ help: # have configured this repository (via an .lfsconfig file at the top level) to NOT # automatically fetch any of the large files when cloning / fetching. fetch-images: + git lfs install git lfs pull --exclude="" --include="" .PHONY: help fetch-images Makefile diff --git a/doc/source/conf.py b/doc/source/conf.py index dcd0b2eae6..6c00f5a686 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -189,4 +189,4 @@ numfig_secnum_depth = 2 def setup(app): - app.add_stylesheet('css/custom.css') + app.add_css_file('css/custom.css') 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 42238c4273..2840d37176 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 @@ -41,6 +41,7 @@ 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 +- Crop residue removal .. _The crop model: @@ -509,9 +510,15 @@ where :math:`{C}_{leaf}`, :math:`{C}_{stem}`, and :math:`{C}_{froot}` is the car 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:`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. +Whereas live crop C and N in grain was formerly transferred to the litter pool upon harvest, CLM5 splits this between "food" and "seed" pools. In the former—more generally a "crop product" pool—C and N decay to the atmosphere over one year, similar to how the wood product pools work. The latter is used in the subsequent year to account for the C and N required for crop seeding. -In the equations below, subscript :math:`p` refers to either the leaf or live stem biomass pool. +Live leaf and stem biomass at harvest is transferred to biofuel, removed residue, and/or litter pools. + +For the biofuel crops Miscanthus and switchgrass, 70% of live leaf and stem biomass at harvest is transferred to the crop product pool as described for "food" harvest above. This value can be changed for these crops—or set to something other than the default zero for any other crop—with the parameter :math:`biofuel\_harvfrac` (0-1). + +50% of any remaining live leaf and stem biomass at harvest (after biofuel removal, if any) is removed to the crop product pool to represent off-field uses such as use for animal feed and bedding. This value can be changed with the parameter :math:`crop\_residue\_removal\_frac` (0–1). The default 50% is derived from :ref:`Smerald et al. 2023 `, who found a global average of 50% of residues left on the field. This includes residues burned in the field, meaning that our implementation implictly assumes the CLM crop burning representation will handle those residues appropriately. + +The following equations illustrate how this works. Subscript :math:`p` refers to either the leaf or live stem biomass pool. .. math:: :label: 25.9 @@ -553,64 +560,6 @@ with corresponding nitrogen fluxes: 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: - -.. table:: Plant functional type (PFT) parameters for harvested fraction of leaf/livestem for bioenergy production. - - +----------------------------------+----------------------------+ - | PFT | :math:`biofuel\_harvfrac` | - +==================================+============================+ - | NET Temperate | 0.00 | - +----------------------------------+----------------------------+ - | NET Boreal | 0.00 | - +----------------------------------+----------------------------+ - | NDT Boreal | 0.00 | - +----------------------------------+----------------------------+ - | BET Tropical | 0.00 | - +----------------------------------+----------------------------+ - | BET temperate | 0.00 | - +----------------------------------+----------------------------+ - | BDT tropical | 0.00 | - +----------------------------------+----------------------------+ - | BDT temperate | 0.00 | - +----------------------------------+----------------------------+ - | BDT boreal | 0.00 | - +----------------------------------+----------------------------+ - | BES temperate | 0.00 | - +----------------------------------+----------------------------+ - | BDS temperate | 0.00 | - +----------------------------------+----------------------------+ - | BDS boreal | 0.00 | - +----------------------------------+----------------------------+ - | C\ :sub:`3` arctic grass | 0.00 | - +----------------------------------+----------------------------+ - | C\ :sub:`3` grass | 0.00 | - +----------------------------------+----------------------------+ - | C\ :sub:`4` grass | 0.00 | - +----------------------------------+----------------------------+ - | Temperate Corn | 0.00 | - +----------------------------------+----------------------------+ - | Spring Wheat | 0.00 | - +----------------------------------+----------------------------+ - | Temperate Soybean | 0.00 | - +----------------------------------+----------------------------+ - | Cotton | 0.00 | - +----------------------------------+----------------------------+ - | Rice | 0.00 | - +----------------------------------+----------------------------+ - | Sugarcane | 0.00 | - +----------------------------------+----------------------------+ - | Tropical Corn | 0.00 | - +----------------------------------+----------------------------+ - | Tropical Soybean | 0.00 | - +----------------------------------+----------------------------+ - | Miscanthus | 0.70 | - +----------------------------------+----------------------------+ - | 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. 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:: 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 90be6e6ad0..ac45114a28 100644 --- a/doc/source/tech_note/Methane/CLM50_Tech_Note_Methane.rst +++ b/doc/source/tech_note/Methane/CLM50_Tech_Note_Methane.rst @@ -225,7 +225,7 @@ For gaseous diffusion, we adopted the temperature dependence of molecular free-a +==========================================================+==========================================================+========================================================+ | 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.00117\ *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: 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 b824f705bd..eafd44e8f4 100644 --- a/doc/source/tech_note/References/CLM50_Tech_Note_References.rst +++ b/doc/source/tech_note/References/CLM50_Tech_Note_References.rst @@ -1264,6 +1264,10 @@ Sitch, S et al. (2003). Evaluation of ecosystem dynamics, plant geography and te 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. +.. _Smeraldetal2023: + +Smerald, A., Rahimi, J., & Scheer, C., 2023. A global dataset for the production and usage of cereal residues in the period 1997–2021. Scientific Data, 10(1), 685. doi: 10.1038/s41597-023-02587-0 + .. _smith2001: Smith, B., I.C. Prentice, and M.T. Sykes, 2001. Representation of vegetation dynamics in the modelling of terrestrial ecosystems: comparing two contrasting approaches within European climate space. Global Ecology and Biogeography 10.6, pp. 621-637. 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 1665b9a00f..5bb4dc9e40 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 @@ -118,7 +118,7 @@ The deciduous phenology algorithms also specify the occurrence of litterfall dur 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:`Harvest to food and seed`) 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: 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 similarity index 100% rename from doc/source/users_guide/running-special-cases/running-the-prognostic-crop-model.rst rename to doc/source/users_guide/running-special-cases/Running-the-prognostic-crop-model.rst 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 similarity index 100% rename from doc/source/users_guide/running-special-cases/running-with-custom-crop-calendars.rst rename to doc/source/users_guide/running-special-cases/Running-with-custom-crop-calendars.rst 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 similarity index 100% rename from doc/source/users_guide/running-special-cases/running-with-irrigation.rst rename to doc/source/users_guide/running-special-cases/Running-with-irrigation.rst 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 index 77309aea07..8cfcaa680b 100644 --- a/doc/source/users_guide/running-special-cases/Running-with-tillage.rst +++ b/doc/source/users_guide/running-special-cases/Running-with-tillage.rst @@ -7,25 +7,25 @@ ===================== -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'``. +Cropland tillage (Sect. :numref:`decomp_mgmt_modifiers`) is set to ``'low'`` by default. This can be changed to a value of ``'off'`` (no tillage) or ``'high'`` (high-intensity tillage) for the ``tillage_mode`` namelist option. 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 -------------------------------------- +Example: Crop simulation with no tillage +---------------------------------------- :: - > cime/scripts/create_newcase -case IHistClm51BgcCrop_till -res f19_g17_gl4 -compset IHistClm51BgcCrop + > cime/scripts/create_newcase -case IHistClm51BgcCrop_notill -res f19_g17_gl4 -compset IHistClm51BgcCrop - > cd IHistClm51BgcCrop_till + > cd IHistClm51BgcCrop_notill > ./case.setup - # turn on tillage ('low' or 'high'; default 'off') - > echo "tillage_mode = 'high'" >> user_nl_clm + # turn off tillage + > echo "tillage_mode = 'off'" >> user_nl_clm Reverting fixes relative to original tillage implementation ----------------------------------------------------------- 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 similarity index 100% rename from doc/source/users_guide/running-special-cases/what-is-a-special-case.rst rename to doc/source/users_guide/running-special-cases/What-is-a-special-case.rst diff --git a/doc/source/users_guide/running-special-cases/index.rst b/doc/source/users_guide/running-special-cases/index.rst index 7ead8f1551..9173825d04 100644 --- a/doc/source/users_guide/running-special-cases/index.rst +++ b/doc/source/users_guide/running-special-cases/index.rst @@ -14,10 +14,11 @@ Running Special Cases .. toctree:: :maxdepth: 2 - what-is-a-special-case.rst - running-the-prognostic-crop-model.rst - running-with-irrigation.rst - running-with-custom-crop-calendars.rst + What-is-a-special-case.rst + Running-the-prognostic-crop-model.rst + Running-with-irrigation.rst + Running-with-custom-crop-calendars.rst + Running-with-tillage.rst Spinning-up-the-Satellite-Phenology-Model-CLMSP-spinup.rst Spinning-up-the-biogeochemistry-BGC-spinup.rst Running-with-excess-ground-ice.rst diff --git a/manage_externals/manic/repository_git.py b/manage_externals/manic/repository_git.py index adc666cc57..aab1a468a8 100644 --- a/manage_externals/manic/repository_git.py +++ b/manage_externals/manic/repository_git.py @@ -7,6 +7,7 @@ import copy import os +import sys from .global_constants import EMPTY_STR, LOCAL_PATH_INDICATOR from .global_constants import VERBOSITY_VERBOSE @@ -380,7 +381,6 @@ def _check_for_valid_ref(self, ref, remote_name, dirname): 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: msg = ('In repo "{0}": reference "{1}" does not appear to be a ' @@ -710,7 +710,10 @@ def _git_lsremote_branch(ref, remote_name, dirname): 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) + status, output = execute_subprocess(cmd, status_to_caller=True, output_to_caller=True) + if not status and not f"refs/heads/{ref}" in output: + # In this case the ref is contained in the branch name but is not the complete branch name + return -1 return status @staticmethod @@ -837,12 +840,19 @@ def _git_update_submodules(verbosity, dirname): """Run git submodule update for the side effect of updating this repo's submodules. """ + # due to https://vielmetti.typepad.com/logbook/2022/10/git-security-fixes-lead-to-fatal-transport-file-not-allowed-error-in-ci-systems-cve-2022-39253.html + # submodules from file doesn't work without overriding the protocol, this is done + # for testing submodule support but should not be done in practice + file_protocol = "" + if 'unittest' in sys.modules.keys(): + file_protocol = "-c protocol.file.allow=always" + # First, verify that we have a .gitmodules file if os.path.exists( os.path.join(dirname, ExternalsDescription.GIT_SUBMODULES_FILENAME)): - cmd = ('git -C {dirname} submodule update --init --recursive' - .format(dirname=dirname)).split() + cmd = ('git {file_protocol} -C {dirname} submodule update --init --recursive' + .format(file_protocol=file_protocol, 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 922855d34e..32a71184b4 100644 --- a/manage_externals/manic/repository_svn.py +++ b/manage_externals/manic/repository_svn.py @@ -42,6 +42,9 @@ def __init__(self, component_name, repo, ignore_ancestry=False): Parse repo (a XML element). """ Repository.__init__(self, component_name, repo) + if 'github.com' in self._url: + msg = "SVN access to github.com is no longer supported" + fatal_error(msg) self._ignore_ancestry = ignore_ancestry if self._url.endswith('/'): # there is already a '/' separator in the URL; no need to add another diff --git a/manage_externals/test/repos/README.md b/manage_externals/test/repos/README.md index 8a3502c35f..026b684ea3 100644 --- a/manage_externals/test/repos/README.md +++ b/manage_externals/test/repos/README.md @@ -1,6 +1,6 @@ -Git repositories for testing git-related behavior. For usage and terminology notes, see test/test_sys_checkout.py. +Git and svn repositories for testing git and svn-related behavior. For usage and terminology notes, see test/test_sys_checkout.py. -To list files and view file contents at HEAD: +For git repos: To list files and view file contents at HEAD: ``` cd git ls-tree --full-tree -r --name-only HEAD diff --git a/manage_externals/test/repos/simple-ext.svn/README.txt b/manage_externals/test/repos/simple-ext.svn/README.txt new file mode 100644 index 0000000000..9935818a1b --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/README.txt @@ -0,0 +1,5 @@ +This is a Subversion repository; use the 'svnadmin' and 'svnlook' +tools to examine it. Do not add, delete, or modify files here +unless you know how to avoid corrupting the repository. + +Visit http://subversion.apache.org/ for more information. diff --git a/manage_externals/test/repos/simple-ext.svn/conf/authz b/manage_externals/test/repos/simple-ext.svn/conf/authz new file mode 100644 index 0000000000..0b9a41074e --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/conf/authz @@ -0,0 +1,32 @@ +### This file is an example authorization file for svnserve. +### Its format is identical to that of mod_authz_svn authorization +### files. +### As shown below each section defines authorizations for the path and +### (optional) repository specified by the section name. +### The authorizations follow. An authorization line can refer to: +### - a single user, +### - a group of users defined in a special [groups] section, +### - an alias defined in a special [aliases] section, +### - all authenticated users, using the '$authenticated' token, +### - only anonymous users, using the '$anonymous' token, +### - anyone, using the '*' wildcard. +### +### A match can be inverted by prefixing the rule with '~'. Rules can +### grant read ('r') access, read-write ('rw') access, or no access +### (''). + +[aliases] +# joe = /C=XZ/ST=Dessert/L=Snake City/O=Snake Oil, Ltd./OU=Research Institute/CN=Joe Average + +[groups] +# harry_and_sally = harry,sally +# harry_sally_and_joe = harry,sally,&joe + +# [/foo/bar] +# harry = rw +# &joe = r +# * = + +# [repository:/baz/fuz] +# @harry_and_sally = rw +# * = r diff --git a/manage_externals/test/repos/simple-ext.svn/conf/hooks-env.tmpl b/manage_externals/test/repos/simple-ext.svn/conf/hooks-env.tmpl new file mode 100644 index 0000000000..ee965c316c --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/conf/hooks-env.tmpl @@ -0,0 +1,19 @@ +### This file is an example hook script environment configuration file. +### Hook scripts run in an empty environment by default. +### As shown below each section defines environment variables for a +### particular hook script. The [default] section defines environment +### variables for all hook scripts, unless overridden by a hook-specific +### section. + +### This example configures a UTF-8 locale for all hook scripts, so that +### special characters, such as umlauts, may be printed to stderr. +### If UTF-8 is used with a mod_dav_svn server, the SVNUseUTF8 option must +### also be set to 'yes' in httpd.conf. +### With svnserve, the LANG environment variable of the svnserve process +### must be set to the same value as given here. +[default] +LANG = en_US.UTF-8 + +### This sets the PATH environment variable for the pre-commit hook. +[pre-commit] +PATH = /usr/local/bin:/usr/bin:/usr/sbin diff --git a/manage_externals/test/repos/simple-ext.svn/conf/passwd b/manage_externals/test/repos/simple-ext.svn/conf/passwd new file mode 100644 index 0000000000..ecaa08dcec --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/conf/passwd @@ -0,0 +1,8 @@ +### This file is an example password file for svnserve. +### Its format is similar to that of svnserve.conf. As shown in the +### example below it contains one section labelled [users]. +### The name and password for each user follow, one account per line. + +[users] +# harry = harryssecret +# sally = sallyssecret diff --git a/manage_externals/test/repos/simple-ext.svn/conf/svnserve.conf b/manage_externals/test/repos/simple-ext.svn/conf/svnserve.conf new file mode 100644 index 0000000000..6cefc17b3e --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/conf/svnserve.conf @@ -0,0 +1,81 @@ +### This file controls the configuration of the svnserve daemon, if you +### use it to allow access to this repository. (If you only allow +### access through http: and/or file: URLs, then this file is +### irrelevant.) + +### Visit http://subversion.apache.org/ for more information. + +[general] +### The anon-access and auth-access options control access to the +### repository for unauthenticated (a.k.a. anonymous) users and +### authenticated users, respectively. +### Valid values are "write", "read", and "none". +### Setting the value to "none" prohibits both reading and writing; +### "read" allows read-only access, and "write" allows complete +### read/write access to the repository. +### The sample settings below are the defaults and specify that anonymous +### users have read-only access to the repository, while authenticated +### users have read and write access to the repository. +# anon-access = read +# auth-access = write +### The password-db option controls the location of the password +### database file. Unless you specify a path starting with a /, +### the file's location is relative to the directory containing +### this configuration file. +### If SASL is enabled (see below), this file will NOT be used. +### Uncomment the line below to use the default password file. +# password-db = passwd +### The authz-db option controls the location of the authorization +### rules for path-based access control. Unless you specify a path +### starting with a /, the file's location is relative to the +### directory containing this file. The specified path may be a +### repository relative URL (^/) or an absolute file:// URL to a text +### file in a Subversion repository. If you don't specify an authz-db, +### no path-based access control is done. +### Uncomment the line below to use the default authorization file. +# authz-db = authz +### The groups-db option controls the location of the file with the +### group definitions and allows maintaining groups separately from the +### authorization rules. The groups-db file is of the same format as the +### authz-db file and should contain a single [groups] section with the +### group definitions. If the option is enabled, the authz-db file cannot +### contain a [groups] section. Unless you specify a path starting with +### a /, the file's location is relative to the directory containing this +### file. The specified path may be a repository relative URL (^/) or an +### absolute file:// URL to a text file in a Subversion repository. +### This option is not being used by default. +# groups-db = groups +### This option specifies the authentication realm of the repository. +### If two repositories have the same authentication realm, they should +### have the same password database, and vice versa. The default realm +### is repository's uuid. +# realm = My First Repository +### The force-username-case option causes svnserve to case-normalize +### usernames before comparing them against the authorization rules in the +### authz-db file configured above. Valid values are "upper" (to upper- +### case the usernames), "lower" (to lowercase the usernames), and +### "none" (to compare usernames as-is without case conversion, which +### is the default behavior). +# force-username-case = none +### The hooks-env options specifies a path to the hook script environment +### configuration file. This option overrides the per-repository default +### and can be used to configure the hook script environment for multiple +### repositories in a single file, if an absolute path is specified. +### Unless you specify an absolute path, the file's location is relative +### to the directory containing this file. +# hooks-env = hooks-env + +[sasl] +### This option specifies whether you want to use the Cyrus SASL +### library for authentication. Default is false. +### Enabling this option requires svnserve to have been built with Cyrus +### SASL support; to check, run 'svnserve --version' and look for a line +### reading 'Cyrus SASL authentication is available.' +# use-sasl = true +### These options specify the desired strength of the security layer +### that you want SASL to provide. 0 means no encryption, 1 means +### integrity-checking only, values larger than 1 are correlated +### to the effective key length for encryption (e.g. 128 means 128-bit +### encryption). The values below are the defaults. +# min-encryption = 0 +# max-encryption = 256 diff --git a/manage_externals/test/repos/simple-ext.svn/db/current b/manage_externals/test/repos/simple-ext.svn/db/current new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/current @@ -0,0 +1 @@ +3 diff --git a/manage_externals/test/repos/simple-ext.svn/db/format b/manage_externals/test/repos/simple-ext.svn/db/format new file mode 100644 index 0000000000..5dd0c22198 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/format @@ -0,0 +1,3 @@ +8 +layout sharded 1000 +addressing logical diff --git a/manage_externals/test/repos/simple-ext.svn/db/fs-type b/manage_externals/test/repos/simple-ext.svn/db/fs-type new file mode 100644 index 0000000000..4fdd95313f --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/fs-type @@ -0,0 +1 @@ +fsfs diff --git a/manage_externals/test/repos/simple-ext.svn/db/fsfs.conf b/manage_externals/test/repos/simple-ext.svn/db/fsfs.conf new file mode 100644 index 0000000000..ac6877a727 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/fsfs.conf @@ -0,0 +1,200 @@ +### This file controls the configuration of the FSFS filesystem. + +[memcached-servers] +### These options name memcached servers used to cache internal FSFS +### data. See http://www.danga.com/memcached/ for more information on +### memcached. To use memcached with FSFS, run one or more memcached +### servers, and specify each of them as an option like so: +# first-server = 127.0.0.1:11211 +# remote-memcached = mymemcached.corp.example.com:11212 +### The option name is ignored; the value is of the form HOST:PORT. +### memcached servers can be shared between multiple repositories; +### however, if you do this, you *must* ensure that repositories have +### distinct UUIDs and paths, or else cached data from one repository +### might be used by another accidentally. Note also that memcached has +### no authentication for reads or writes, so you must ensure that your +### memcached servers are only accessible by trusted users. + +[caches] +### When a cache-related error occurs, normally Subversion ignores it +### and continues, logging an error if the server is appropriately +### configured (and ignoring it with file:// access). To make +### Subversion never ignore cache errors, uncomment this line. +# fail-stop = true + +[rep-sharing] +### To conserve space, the filesystem can optionally avoid storing +### duplicate representations. This comes at a slight cost in +### performance, as maintaining a database of shared representations can +### increase commit times. The space savings are dependent upon the size +### of the repository, the number of objects it contains and the amount of +### duplication between them, usually a function of the branching and +### merging process. +### +### The following parameter enables rep-sharing in the repository. It can +### be switched on and off at will, but for best space-saving results +### should be enabled consistently over the life of the repository. +### 'svnadmin verify' will check the rep-cache regardless of this setting. +### rep-sharing is enabled by default. +# enable-rep-sharing = true + +[deltification] +### To conserve space, the filesystem stores data as differences against +### existing representations. This comes at a slight cost in performance, +### as calculating differences can increase commit times. Reading data +### will also create higher CPU load and the data will be fragmented. +### Since deltification tends to save significant amounts of disk space, +### the overall I/O load can actually be lower. +### +### The options in this section allow for tuning the deltification +### strategy. Their effects on data size and server performance may vary +### from one repository to another. Versions prior to 1.8 will ignore +### this section. +### +### The following parameter enables deltification for directories. It can +### be switched on and off at will, but for best space-saving results +### should be enabled consistently over the lifetime of the repository. +### Repositories containing large directories will benefit greatly. +### In rarely accessed repositories, the I/O overhead may be significant +### as caches will most likely be low. +### directory deltification is enabled by default. +# enable-dir-deltification = true +### +### The following parameter enables deltification for properties on files +### and directories. Overall, this is a minor tuning option but can save +### some disk space if you merge frequently or frequently change node +### properties. You should not activate this if rep-sharing has been +### disabled because this may result in a net increase in repository size. +### property deltification is enabled by default. +# enable-props-deltification = true +### +### During commit, the server may need to walk the whole change history of +### of a given node to find a suitable deltification base. This linear +### process can impact commit times, svnadmin load and similar operations. +### This setting limits the depth of the deltification history. If the +### threshold has been reached, the node will be stored as fulltext and a +### new deltification history begins. +### Note, this is unrelated to svn log. +### Very large values rarely provide significant additional savings but +### can impact performance greatly - in particular if directory +### deltification has been activated. Very small values may be useful in +### repositories that are dominated by large, changing binaries. +### Should be a power of two minus 1. A value of 0 will effectively +### disable deltification. +### For 1.8, the default value is 1023; earlier versions have no limit. +# max-deltification-walk = 1023 +### +### The skip-delta scheme used by FSFS tends to repeatably store redundant +### delta information where a simple delta against the latest version is +### often smaller. By default, 1.8+ will therefore use skip deltas only +### after the linear chain of deltas has grown beyond the threshold +### specified by this setting. +### Values up to 64 can result in some reduction in repository size for +### the cost of quickly increasing I/O and CPU costs. Similarly, smaller +### numbers can reduce those costs at the cost of more disk space. For +### rarely read repositories or those containing larger binaries, this may +### present a better trade-off. +### Should be a power of two. A value of 1 or smaller will cause the +### exclusive use of skip-deltas (as in pre-1.8). +### For 1.8, the default value is 16; earlier versions use 1. +# max-linear-deltification = 16 +### +### After deltification, we compress the data to minimize on-disk size. +### This setting controls the compression algorithm, which will be used in +### future revisions. It can be used to either disable compression or to +### select between available algorithms (zlib, lz4). zlib is a general- +### purpose compression algorithm. lz4 is a fast compression algorithm +### which should be preferred for repositories with large and, possibly, +### incompressible files. Note that the compression ratio of lz4 is +### usually lower than the one provided by zlib, but using it can +### significantly speed up commits as well as reading the data. +### lz4 compression algorithm is supported, starting from format 8 +### repositories, available in Subversion 1.10 and higher. +### The syntax of this option is: +### compression = none | lz4 | zlib | zlib-1 ... zlib-9 +### Versions prior to Subversion 1.10 will ignore this option. +### The default value is 'lz4' if supported by the repository format and +### 'zlib' otherwise. 'zlib' is currently equivalent to 'zlib-5'. +# compression = lz4 +### +### DEPRECATED: The new 'compression' option deprecates previously used +### 'compression-level' option, which was used to configure zlib compression. +### For compatibility with previous versions of Subversion, this option can +### still be used (and it will result in zlib compression with the +### corresponding compression level). +### compression-level = 0 ... 9 (default is 5) + +[packed-revprops] +### This parameter controls the size (in kBytes) of packed revprop files. +### Revprops of consecutive revisions will be concatenated into a single +### file up to but not exceeding the threshold given here. However, each +### pack file may be much smaller and revprops of a single revision may be +### much larger than the limit set here. The threshold will be applied +### before optional compression takes place. +### Large values will reduce disk space usage at the expense of increased +### latency and CPU usage reading and changing individual revprops. +### Values smaller than 4 kByte will not improve latency any further and +### quickly render revprop packing ineffective. +### revprop-pack-size is 16 kBytes by default for non-compressed revprop +### pack files and 64 kBytes when compression has been enabled. +# revprop-pack-size = 16 +### +### To save disk space, packed revprop files may be compressed. Standard +### revprops tend to allow for very effective compression. Reading and +### even more so writing, become significantly more CPU intensive. +### Compressing packed revprops is disabled by default. +# compress-packed-revprops = false + +[io] +### Parameters in this section control the data access granularity in +### format 7 repositories and later. The defaults should translate into +### decent performance over a wide range of setups. +### +### When a specific piece of information needs to be read from disk, a +### data block is being read at once and its contents are being cached. +### If the repository is being stored on a RAID, the block size should be +### either 50% or 100% of RAID block size / granularity. Also, your file +### system blocks/clusters should be properly aligned and sized. In that +### setup, each access will hit only one disk (minimizes I/O load) but +### uses all the data provided by the disk in a single access. +### For SSD-based storage systems, slightly lower values around 16 kB +### may improve latency while still maximizing throughput. If block-read +### has not been enabled, this will be capped to 4 kBytes. +### Can be changed at any time but must be a power of 2. +### block-size is given in kBytes and with a default of 64 kBytes. +# block-size = 64 +### +### The log-to-phys index maps data item numbers to offsets within the +### rev or pack file. This index is organized in pages of a fixed maximum +### capacity. To access an item, the page table and the respective page +### must be read. +### This parameter only affects revisions with thousands of changed paths. +### If you have several extremely large revisions (~1 mio changes), think +### about increasing this setting. Reducing the value will rarely result +### in a net speedup. +### This is an expert setting. Must be a power of 2. +### l2p-page-size is 8192 entries by default. +# l2p-page-size = 8192 +### +### The phys-to-log index maps positions within the rev or pack file to +### to data items, i.e. describes what piece of information is being +### stored at any particular offset. The index describes the rev file +### in chunks (pages) and keeps a global list of all those pages. Large +### pages mean a shorter page table but a larger per-page description of +### data items in it. The latency sweetspot depends on the change size +### distribution but covers a relatively wide range. +### If the repository contains very large files, i.e. individual changes +### of tens of MB each, increasing the page size will shorten the index +### file at the expense of a slightly increased latency in sections with +### smaller changes. +### For source code repositories, this should be about 16x the block-size. +### Must be a power of 2. +### p2l-page-size is given in kBytes and with a default of 1024 kBytes. +# p2l-page-size = 1024 + +[debug] +### +### Whether to verify each new revision immediately before finalizing +### the commit. This is disabled by default except in maintainer-mode +### builds. +# verify-before-commit = false diff --git a/manage_externals/test/repos/simple-ext.svn/db/min-unpacked-rev b/manage_externals/test/repos/simple-ext.svn/db/min-unpacked-rev new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/min-unpacked-rev @@ -0,0 +1 @@ +0 diff --git a/manage_externals/test/repos/simple-ext.svn/db/rep-cache.db b/manage_externals/test/repos/simple-ext.svn/db/rep-cache.db new file mode 100644 index 0000000000..3193b2eaad Binary files /dev/null and b/manage_externals/test/repos/simple-ext.svn/db/rep-cache.db differ diff --git a/manage_externals/test/repos/simple-ext.svn/db/rep-cache.db-journal b/manage_externals/test/repos/simple-ext.svn/db/rep-cache.db-journal new file mode 100644 index 0000000000..e69de29bb2 diff --git a/manage_externals/test/repos/simple-ext.svn/db/revprops/0/0 b/manage_externals/test/repos/simple-ext.svn/db/revprops/0/0 new file mode 100644 index 0000000000..92768005d3 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/revprops/0/0 @@ -0,0 +1,5 @@ +K 8 +svn:date +V 27 +2023-11-16T20:11:46.318861Z +END diff --git a/manage_externals/test/repos/simple-ext.svn/db/revprops/0/1 b/manage_externals/test/repos/simple-ext.svn/db/revprops/0/1 new file mode 100644 index 0000000000..aa95a9de9f --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/revprops/0/1 @@ -0,0 +1,13 @@ +K 10 +svn:author +V 5 +sacks +K 8 +svn:date +V 27 +2023-11-16T20:15:56.917904Z +K 7 +svn:log +V 30 +Setting up directory structure +END diff --git a/manage_externals/test/repos/simple-ext.svn/db/revprops/0/2 b/manage_externals/test/repos/simple-ext.svn/db/revprops/0/2 new file mode 100644 index 0000000000..3d04d8909a --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/revprops/0/2 @@ -0,0 +1,13 @@ +K 10 +svn:author +V 5 +sacks +K 8 +svn:date +V 27 +2023-11-16T20:27:31.407916Z +K 7 +svn:log +V 10 +Add README +END diff --git a/manage_externals/test/repos/simple-ext.svn/db/revprops/0/3 b/manage_externals/test/repos/simple-ext.svn/db/revprops/0/3 new file mode 100644 index 0000000000..de20268415 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/revprops/0/3 @@ -0,0 +1,13 @@ +K 10 +svn:author +V 5 +sacks +K 8 +svn:date +V 27 +2023-11-16T21:14:43.366717Z +K 7 +svn:log +V 27 +Creating cesm2.0.beta07 tag +END diff --git a/manage_externals/test/repos/simple-ext.svn/db/revs/0/0 b/manage_externals/test/repos/simple-ext.svn/db/revs/0/0 new file mode 100644 index 0000000000..9a56c280c4 Binary files /dev/null and b/manage_externals/test/repos/simple-ext.svn/db/revs/0/0 differ diff --git a/manage_externals/test/repos/simple-ext.svn/db/revs/0/1 b/manage_externals/test/repos/simple-ext.svn/db/revs/0/1 new file mode 100644 index 0000000000..c6982eeab7 Binary files /dev/null and b/manage_externals/test/repos/simple-ext.svn/db/revs/0/1 differ diff --git a/manage_externals/test/repos/simple-ext.svn/db/revs/0/2 b/manage_externals/test/repos/simple-ext.svn/db/revs/0/2 new file mode 100644 index 0000000000..99a14cf4b7 Binary files /dev/null and b/manage_externals/test/repos/simple-ext.svn/db/revs/0/2 differ diff --git a/manage_externals/test/repos/simple-ext.svn/db/revs/0/3 b/manage_externals/test/repos/simple-ext.svn/db/revs/0/3 new file mode 100644 index 0000000000..f437a6d530 Binary files /dev/null and b/manage_externals/test/repos/simple-ext.svn/db/revs/0/3 differ diff --git a/manage_externals/test/repos/simple-ext.svn/db/txn-current b/manage_externals/test/repos/simple-ext.svn/db/txn-current new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/txn-current @@ -0,0 +1 @@ +3 diff --git a/manage_externals/test/repos/simple-ext.svn/db/txn-current-lock b/manage_externals/test/repos/simple-ext.svn/db/txn-current-lock new file mode 100644 index 0000000000..e69de29bb2 diff --git a/manage_externals/test/repos/simple-ext.svn/db/uuid b/manage_externals/test/repos/simple-ext.svn/db/uuid new file mode 100644 index 0000000000..0b16502652 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/db/uuid @@ -0,0 +1,2 @@ +1c80dd47-0c07-4207-8ee0-e60dd9d98853 +31d57ab1-759c-4129-a63d-898c774d96c9 diff --git a/manage_externals/test/repos/simple-ext.svn/db/write-lock b/manage_externals/test/repos/simple-ext.svn/db/write-lock new file mode 100644 index 0000000000..e69de29bb2 diff --git a/manage_externals/test/repos/simple-ext.svn/format b/manage_externals/test/repos/simple-ext.svn/format new file mode 100644 index 0000000000..7ed6ff82de --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/format @@ -0,0 +1 @@ +5 diff --git a/manage_externals/test/repos/simple-ext.svn/hooks/post-commit.tmpl b/manage_externals/test/repos/simple-ext.svn/hooks/post-commit.tmpl new file mode 100755 index 0000000000..988f041fa5 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/hooks/post-commit.tmpl @@ -0,0 +1,62 @@ +#!/bin/sh + +# POST-COMMIT HOOK +# +# The post-commit hook is invoked after a commit. Subversion runs +# this hook by invoking a program (script, executable, binary, etc.) +# named 'post-commit' (for which this file is a template) with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] REV (the number of the revision just committed) +# [3] TXN-NAME (the name of the transaction that has become REV) +# +# Because the commit has already completed and cannot be undone, +# the exit code of the hook program is ignored. The hook program +# can use the 'svnlook' utility to help it examine the +# newly-committed tree. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'post-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-commit.bat' or 'post-commit.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +REV="$2" +TXN_NAME="$3" + +mailer.py commit "$REPOS" "$REV" /path/to/mailer.conf diff --git a/manage_externals/test/repos/simple-ext.svn/hooks/post-lock.tmpl b/manage_externals/test/repos/simple-ext.svn/hooks/post-lock.tmpl new file mode 100755 index 0000000000..96f2165209 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/hooks/post-lock.tmpl @@ -0,0 +1,64 @@ +#!/bin/sh + +# POST-LOCK HOOK +# +# The post-lock hook is run after a path is locked. Subversion runs +# this hook by invoking a program (script, executable, binary, etc.) +# named 'post-lock' (for which this file is a template) with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the user who created the lock) +# +# The paths that were just locked are passed to the hook via STDIN. +# +# Because the locks have already been created and cannot be undone, +# the exit code of the hook program is ignored. The hook program +# can use the 'svnlook' utility to examine the paths in the repository +# but since the hook is invoked asynchronously the newly-created locks +# may no longer be present. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'post-lock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-lock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-lock.bat' or 'post-lock.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +USER="$2" + +# Send email to interested parties, let them know a lock was created: +mailer.py lock "$REPOS" "$USER" /path/to/mailer.conf diff --git a/manage_externals/test/repos/simple-ext.svn/hooks/post-revprop-change.tmpl b/manage_externals/test/repos/simple-ext.svn/hooks/post-revprop-change.tmpl new file mode 100755 index 0000000000..de1b914648 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/hooks/post-revprop-change.tmpl @@ -0,0 +1,69 @@ +#!/bin/sh + +# POST-REVPROP-CHANGE HOOK +# +# The post-revprop-change hook is invoked after a revision property +# has been added, modified or deleted. Subversion runs this hook by +# invoking a program (script, executable, binary, etc.) named +# 'post-revprop-change' (for which this file is a template), with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] REV (the revision that was tweaked) +# [3] USER (the username of the person tweaking the property) +# [4] PROPNAME (the property that was changed) +# [5] ACTION (the property was 'A'dded, 'M'odified, or 'D'eleted) +# +# [STDIN] PROPVAL ** the old property value is passed via STDIN. +# +# Because the propchange has already completed and cannot be undone, +# the exit code of the hook program is ignored. The hook program +# can use the 'svnlook' utility to help it examine the +# new property value. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'post-revprop-change' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-revprop-change' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-revprop-change.bat' or 'post-revprop-change.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +REV="$2" +USER="$3" +PROPNAME="$4" +ACTION="$5" + +mailer.py propchange2 "$REPOS" "$REV" "$USER" "$PROPNAME" "$ACTION" /path/to/mailer.conf diff --git a/manage_externals/test/repos/simple-ext.svn/hooks/post-unlock.tmpl b/manage_externals/test/repos/simple-ext.svn/hooks/post-unlock.tmpl new file mode 100755 index 0000000000..e33f793c25 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/hooks/post-unlock.tmpl @@ -0,0 +1,61 @@ +#!/bin/sh + +# POST-UNLOCK HOOK +# +# The post-unlock hook runs after a path is unlocked. Subversion runs +# this hook by invoking a program (script, executable, binary, etc.) +# named 'post-unlock' (for which this file is a template) with the +# following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the user who destroyed the lock) +# +# The paths that were just unlocked are passed to the hook via STDIN. +# +# Because the lock has already been destroyed and cannot be undone, +# the exit code of the hook program is ignored. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'post-unlock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'post-unlock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'post-unlock.bat' or 'post-unlock.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +USER="$2" + +# Send email to interested parties, let them know a lock was removed: +mailer.py unlock "$REPOS" "$USER" /path/to/mailer.conf diff --git a/manage_externals/test/repos/simple-ext.svn/hooks/pre-commit.tmpl b/manage_externals/test/repos/simple-ext.svn/hooks/pre-commit.tmpl new file mode 100755 index 0000000000..626e72300c --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/hooks/pre-commit.tmpl @@ -0,0 +1,91 @@ +#!/bin/sh + +# PRE-COMMIT HOOK +# +# The pre-commit hook is invoked before a Subversion txn is +# committed. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-commit' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] TXN-NAME (the name of the txn about to be committed) +# +# [STDIN] LOCK-TOKENS ** the lock tokens are passed via STDIN. +# +# If STDIN contains the line "LOCK-TOKENS:\n" (the "\n" denotes a +# single newline), the lines following it are the lock tokens for +# this commit. The end of the list is marked by a line containing +# only a newline character. +# +# Each lock token line consists of a URI-escaped path, followed +# by the separator character '|', followed by the lock token string, +# followed by a newline. +# +# If the hook program exits with success, the txn is committed; but +# if it exits with failure (non-zero), the txn is aborted, no commit +# takes place, and STDERR is returned to the client. The hook +# program can use the 'svnlook' utility to help it examine the txn. +# +# *** NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN, EXCEPT *** +# *** FOR REVISION PROPERTIES (like svn:log or svn:author). *** +# +# This is why we recommend using the read-only 'svnlook' utility. +# In the future, Subversion may enforce the rule that pre-commit +# hooks should not modify the versioned data in txns, or else come +# up with a mechanism to make it safe to do so (by informing the +# committing client of the changes). However, right now neither +# mechanism is implemented, so hook writers just have to be careful. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'pre-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-commit.bat' or 'pre-commit.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +TXN="$2" + +# Make sure that the log message contains some text. +SVNLOOK=/opt/homebrew/Cellar/subversion/1.14.2_1/bin/svnlook +$SVNLOOK log -t "$TXN" "$REPOS" | \ + grep "[a-zA-Z0-9]" > /dev/null || exit 1 + +# Check that the author of this commit has the rights to perform +# the commit on the files and directories being modified. +commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1 + +# All checks passed, so allow the commit. +exit 0 diff --git a/manage_externals/test/repos/simple-ext.svn/hooks/pre-lock.tmpl b/manage_externals/test/repos/simple-ext.svn/hooks/pre-lock.tmpl new file mode 100755 index 0000000000..148582a689 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/hooks/pre-lock.tmpl @@ -0,0 +1,95 @@ +#!/bin/sh + +# PRE-LOCK HOOK +# +# The pre-lock hook is invoked before an exclusive lock is +# created. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-lock' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] PATH (the path in the repository about to be locked) +# [3] USER (the user creating the lock) +# [4] COMMENT (the comment of the lock) +# [5] STEAL-LOCK (1 if the user is trying to steal the lock, else 0) +# +# If the hook program outputs anything on stdout, the output string will +# be used as the lock token for this lock operation. If you choose to use +# this feature, you must guarantee the tokens generated are unique across +# the repository each time. +# +# If the hook program exits with success, the lock is created; but +# if it exits with failure (non-zero), the lock action is aborted +# and STDERR is returned to the client. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'pre-lock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-lock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-lock.bat' or 'pre-lock.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +PATH="$2" +USER="$3" +COMMENT="$4" +STEAL="$5" + +# If a lock exists and is owned by a different person, don't allow it +# to be stolen (e.g., with 'svn lock --force ...'). + +# (Maybe this script could send email to the lock owner?) +SVNLOOK=/opt/homebrew/Cellar/subversion/1.14.2_1/bin/svnlook +GREP=/bin/grep +SED=/bin/sed + +LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \ + $GREP '^Owner: ' | $SED 's/Owner: //'` + +# If we get no result from svnlook, there's no lock, allow the lock to +# happen: +if [ "$LOCK_OWNER" = "" ]; then + exit 0 +fi + +# If the person locking matches the lock's owner, allow the lock to +# happen: +if [ "$LOCK_OWNER" = "$USER" ]; then + exit 0 +fi + +# Otherwise, we've got an owner mismatch, so return failure: +echo "Error: $PATH already locked by ${LOCK_OWNER}." 1>&2 +exit 1 diff --git a/manage_externals/test/repos/simple-ext.svn/hooks/pre-revprop-change.tmpl b/manage_externals/test/repos/simple-ext.svn/hooks/pre-revprop-change.tmpl new file mode 100755 index 0000000000..8b065d7c79 --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/hooks/pre-revprop-change.tmpl @@ -0,0 +1,79 @@ +#!/bin/sh + +# PRE-REVPROP-CHANGE HOOK +# +# The pre-revprop-change hook is invoked before a revision property +# is added, modified or deleted. Subversion runs this hook by invoking +# a program (script, executable, binary, etc.) named 'pre-revprop-change' +# (for which this file is a template), with the following ordered +# arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] REV (the revision being tweaked) +# [3] USER (the username of the person tweaking the property) +# [4] PROPNAME (the property being set on the revision) +# [5] ACTION (the property is being 'A'dded, 'M'odified, or 'D'eleted) +# +# [STDIN] PROPVAL ** the new property value is passed via STDIN. +# +# If the hook program exits with success, the propchange happens; but +# if it exits with failure (non-zero), the propchange doesn't happen. +# The hook program can use the 'svnlook' utility to examine the +# existing value of the revision property. +# +# WARNING: unlike other hooks, this hook MUST exist for revision +# properties to be changed. If the hook does not exist, Subversion +# will behave as if the hook were present, but failed. The reason +# for this is that revision properties are UNVERSIONED, meaning that +# a successful propchange is destructive; the old value is gone +# forever. We recommend the hook back up the old value somewhere. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'pre-revprop-change' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-revprop-change' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-revprop-change.bat' or 'pre-revprop-change.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +REV="$2" +USER="$3" +PROPNAME="$4" +ACTION="$5" + +if [ "$ACTION" = "M" -a "$PROPNAME" = "svn:log" ]; then exit 0; fi + +echo "Changing revision properties other than svn:log is prohibited" >&2 +exit 1 diff --git a/manage_externals/test/repos/simple-ext.svn/hooks/pre-unlock.tmpl b/manage_externals/test/repos/simple-ext.svn/hooks/pre-unlock.tmpl new file mode 100755 index 0000000000..9ba99d071b --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/hooks/pre-unlock.tmpl @@ -0,0 +1,87 @@ +#!/bin/sh + +# PRE-UNLOCK HOOK +# +# The pre-unlock hook is invoked before an exclusive lock is +# destroyed. Subversion runs this hook by invoking a program +# (script, executable, binary, etc.) named 'pre-unlock' (for which +# this file is a template), with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] PATH (the path in the repository about to be unlocked) +# [3] USER (the user destroying the lock) +# [4] TOKEN (the lock token to be destroyed) +# [5] BREAK-UNLOCK (1 if the user is breaking the lock, else 0) +# +# If the hook program exits with success, the lock is destroyed; but +# if it exits with failure (non-zero), the unlock action is aborted +# and STDERR is returned to the client. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'pre-unlock' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'pre-unlock' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'pre-unlock.bat' or 'pre-unlock.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +PATH="$2" +USER="$3" +TOKEN="$4" +BREAK="$5" + +# If a lock is owned by a different person, don't allow it be broken. +# (Maybe this script could send email to the lock owner?) + +SVNLOOK=/opt/homebrew/Cellar/subversion/1.14.2_1/bin/svnlook +GREP=/bin/grep +SED=/bin/sed + +LOCK_OWNER=`$SVNLOOK lock "$REPOS" "$PATH" | \ + $GREP '^Owner: ' | $SED 's/Owner: //'` + +# If we get no result from svnlook, there's no lock, return success: +if [ "$LOCK_OWNER" = "" ]; then + exit 0 +fi + +# If the person unlocking matches the lock's owner, return success: +if [ "$LOCK_OWNER" = "$USER" ]; then + exit 0 +fi + +# Otherwise, we've got an owner mismatch, so return failure: +echo "Error: $PATH locked by ${LOCK_OWNER}." 1>&2 +exit 1 diff --git a/manage_externals/test/repos/simple-ext.svn/hooks/start-commit.tmpl b/manage_externals/test/repos/simple-ext.svn/hooks/start-commit.tmpl new file mode 100755 index 0000000000..1395e8315a --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/hooks/start-commit.tmpl @@ -0,0 +1,81 @@ +#!/bin/sh + +# START-COMMIT HOOK +# +# The start-commit hook is invoked immediately after a Subversion txn is +# created and populated with initial revprops in the process of doing a +# commit. Subversion runs this hook by invoking a program (script, +# executable, binary, etc.) named 'start-commit' (for which this file +# is a template) with the following ordered arguments: +# +# [1] REPOS-PATH (the path to this repository) +# [2] USER (the authenticated user attempting to commit) +# [3] CAPABILITIES (a colon-separated list of capabilities reported +# by the client; see note below) +# [4] TXN-NAME (the name of the commit txn just created) +# +# Note: The CAPABILITIES parameter is new in Subversion 1.5, and 1.5 +# clients will typically report at least the "mergeinfo" capability. +# If there are other capabilities, then the list is colon-separated, +# e.g.: "mergeinfo:some-other-capability" (the order is undefined). +# +# The list is self-reported by the client. Therefore, you should not +# make security assumptions based on the capabilities list, nor should +# you assume that clients reliably report every capability they have. +# +# Note: The TXN-NAME parameter is new in Subversion 1.8. Prior to version +# 1.8, the start-commit hook was invoked before the commit txn was even +# created, so the ability to inspect the commit txn and its metadata from +# within the start-commit hook was not possible. +# +# If the hook program exits with success, the commit continues; but +# if it exits with failure (non-zero), the commit is stopped before +# a Subversion txn is created, and STDERR is returned to the client. +# +# The default working directory for the invocation is undefined, so +# the program should set one explicitly if it cares. +# +# On a Unix system, the normal procedure is to have 'start-commit' +# invoke other programs to do the real work, though it may do the +# work itself too. +# +# Note that 'start-commit' must be executable by the user(s) who will +# invoke it (typically the user httpd runs as), and that user must +# have filesystem-level permission to access the repository. +# +# On a Windows system, you should name the hook program +# 'start-commit.bat' or 'start-commit.exe', +# but the basic idea is the same. +# +# The hook program runs in an empty environment, unless the server is +# explicitly configured otherwise. For example, a common problem is for +# the PATH environment variable to not be set to its usual value, so +# that subprograms fail to launch unless invoked via absolute path. +# If you're having unexpected problems with a hook program, the +# culprit may be unusual (or missing) environment variables. +# +# CAUTION: +# For security reasons, you MUST always properly quote arguments when +# you use them, as those arguments could contain whitespace or other +# problematic characters. Additionally, you should delimit the list +# of options with "--" before passing the arguments, so malicious +# clients cannot bootleg unexpected options to the commands your +# script aims to execute. +# For similar reasons, you should also add a trailing @ to URLs which +# are passed to SVN commands accepting URLs with peg revisions. +# +# Here is an example hook script, for a Unix /bin/sh interpreter. +# For more examples and pre-written hooks, see those in +# the Subversion repository at +# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and +# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/ + + +REPOS="$1" +USER="$2" + +commit-allower.pl --repository "$REPOS" --user "$USER" || exit 1 +special-auth-check.py --user "$USER" --auth-level 3 || exit 1 + +# All checks passed, so allow the commit. +exit 0 diff --git a/manage_externals/test/repos/simple-ext.svn/locks/db-logs.lock b/manage_externals/test/repos/simple-ext.svn/locks/db-logs.lock new file mode 100644 index 0000000000..20dd6369be --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/locks/db-logs.lock @@ -0,0 +1,3 @@ +This file is not used by Subversion 1.3.x or later. +However, its existence is required for compatibility with +Subversion 1.2.x or earlier. diff --git a/manage_externals/test/repos/simple-ext.svn/locks/db.lock b/manage_externals/test/repos/simple-ext.svn/locks/db.lock new file mode 100644 index 0000000000..20dd6369be --- /dev/null +++ b/manage_externals/test/repos/simple-ext.svn/locks/db.lock @@ -0,0 +1,3 @@ +This file is not used by Subversion 1.3.x or later. +However, its existence is required for compatibility with +Subversion 1.2.x or earlier. diff --git a/manage_externals/test/test_sys_checkout.py b/manage_externals/test/test_sys_checkout.py old mode 100644 new mode 100755 index ab4f77e88f..664160dc99 --- a/manage_externals/test/test_sys_checkout.py +++ b/manage_externals/test/test_sys_checkout.py @@ -97,6 +97,7 @@ 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 +SVN_TEST_REPO = 'simple-ext.svn' # Subversion repository # Standard (arbitrary) external names for test configs TAG_SECTION = 'simp_tag' @@ -120,8 +121,6 @@ # Branch that exists in both the simple and simple-fork repos. REMOTE_BRANCH_FEATURE2 = 'feature2' -SVN_TEST_REPO = 'https://github.com/escomp/cesm' - # Disable too-many-public-methods error # pylint: disable=R0904 @@ -354,7 +353,7 @@ def create_section_reference_to_subexternal(self, name): self._config.set(name, ExternalsDescription.EXTERNALS, CFG_SUB_NAME) - def create_svn_external(self, name, tag='', branch=''): + def create_svn_external(self, name, url, tag='', branch=''): """Create a config section for an svn repository. """ @@ -365,7 +364,7 @@ def create_svn_external(self, name, tag='', branch=''): self._config.set(name, ExternalsDescription.PROTOCOL, ExternalsDescription.PROTOCOL_SVN) - self._config.set(name, ExternalsDescription.REPO_URL, SVN_TEST_REPO) + self._config.set(name, ExternalsDescription.REPO_URL, url) self._config.set(name, ExternalsDescription.REQUIRED, str(True)) @@ -1387,36 +1386,10 @@ def test_container_sparse(self): 'simple_subdir', 'subdir_file.txt')) - class TestSysCheckoutSVN(BaseTestSysCheckout): """Run systems level tests of checkout_externals accessing svn repositories - SVN tests - these tests use the svn repository interface. Since - they require an active network connection, they are significantly - slower than the git tests. But svn testing is critical. So try to - design the tests to only test svn repository functionality - (checkout, switch) and leave generic testing of functionality like - 'optional' to the fast git tests. - - Example timing as of 2017-11: - - * All other git and unit tests combined take between 4-5 seconds - - * Just checking if svn is available for a single test takes 2 seconds. - - * The single svn test typically takes between 10 and 25 seconds - (depending on the network)! - - NOTE(bja, 2017-11) To enable CI testing we can't use a real remote - repository that restricts access and it seems inappropriate to hit - a random open source repo. For now we are just hitting one of our - own github repos using the github svn server interface. This - should be "good enough" for basic checkout and swich - functionality. But if additional svn functionality is required, a - better solution will be necessary. I think eventually we want to - create a small local svn repository on the fly (doesn't require an - svn server or network connection!) and use it for testing. - + SVN tests - these tests use the svn repository interface. """ @staticmethod @@ -1427,6 +1400,9 @@ def _svn_branch_name(): def _svn_tag_name(): return './{0}/svn_tag'.format(EXTERNALS_PATH) + def _svn_test_repo_url(self): + return 'file://' + os.path.join(self._bare_root, SVN_TEST_REPO) + def _check_tag_branch_svn_tag_clean(self, tree): self._check_sync_clean(tree[self._external_path(TAG_SECTION)], ExternalStatus.STATUS_OK, @@ -1438,13 +1414,12 @@ def _check_tag_branch_svn_tag_clean(self, tree): ExternalStatus.STATUS_OK, ExternalStatus.STATUS_OK) - @staticmethod - def _have_svn_access(): + def _have_svn_access(self): """Check if we have svn access so we can enable tests that use svn. """ have_svn = False - cmd = ['svn', 'ls', SVN_TEST_REPO, ] + cmd = ['svn', 'ls', self._svn_test_repo_url(), ] try: execute_subprocess(cmd) have_svn = True @@ -1472,8 +1447,8 @@ def test_container_simple_svn(self): self._generator.create_section(SIMPLE_REPO, TAG_SECTION, tag='tag1') # 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.create_svn_external('svn_branch', self._svn_test_repo_url(), branch='trunk') + self._generator.create_svn_external('svn_tag', self._svn_test_repo_url(), tag='tags/cesm2.0.beta07') self._generator.write_config(cloned_repo_dir) @@ -1557,7 +1532,7 @@ def setUp(self): execute_subprocess(cmd) cmd = ['git', 'checkout', self._bare_branch_name] execute_subprocess(cmd) - cmd = ['git', 'submodule', 'add', fork_repo_dir] + cmd = ['git', '-c', 'protocol.file.allow=always','submodule', 'add', fork_repo_dir] execute_subprocess(cmd) cmd = ['git', 'commit', '-am', "'Added simple-ext-fork as a submodule'"] execute_subprocess(cmd) @@ -1571,7 +1546,7 @@ def setUp(self): execute_subprocess(cmd) cmd = ['git', 'checkout', self._config_branch_name] execute_subprocess(cmd) - cmd = ['git', 'submodule', 'add', '--name', SIMPLE_REPO, + cmd = ['git', '-c', 'protocol.file.allow=always', 'submodule', 'add', '--name', SIMPLE_REPO, simple_repo_dir, self._simple_ext_name] execute_subprocess(cmd) # Checkout feature2 diff --git a/py_env_create b/py_env_create index c323a374df..4b3612cfda 100755 --- a/py_env_create +++ b/py_env_create @@ -15,8 +15,8 @@ conda --help >& condahelp.txt error=$? if [ $error != 0 ]; then echo "conda is NOT in your path for the bash shell add it with modules or whatever is required on your system to get it in your path" - echo "on cheyenne/capser/etc use -- module load conda" - echo "on izumi/CGD systems use -- module load lang/python" + echo "on Derecho/capser/etc use -- module load conda" + echo "on izumi/CGD systems use -- module unload lang/python; module load lang/anaconda/23.11.0/base" echo "For notes on installing on a user system see: https://docs.conda.io/projects/conda/en/latest/user-guide/install/index.html" echo "Error code was $error" cat condahelp.txt @@ -29,11 +29,7 @@ ctsm_python=ctsm_pylib condadir="$dir/python" domain=`domainname` -if [[ $domain =~ cgd.* ]]; then - condafile="conda_env_ctsm_py_cgd.txt" -else - condafile="conda_env_ctsm_py.txt" -fi +condafile="conda_env_ctsm_py.txt" #---------------------------------------------------------------------- # Usage subroutine usage() { @@ -119,15 +115,6 @@ if [ $? != 0 ]; then echo "Trouble installing the $ctsm_python python environment" echo "There must be a problem in the $condadir/$condafile conda specification environment file" echo "Change the file and try again" - if [[ $domain =~ cgd.* ]]; then - pythonpath=`which python` - echo - echo "On CGD systems you may need to do the following..." - echo "Create a bin subdirectory and then link the python version into it..." - echo " mkdir $HOME/.conda/envs/$ctsm_python/bin/" - echo " ln -s $pythonpath $HOME/.conda/envs/$ctsm_python/bin/python3.7" - echo - fi exit -2 fi echo "Successfully installed the $ctsm_python python environment" diff --git a/python/README.md b/python/README.md index c40f55c6c7..d0024744e4 100644 --- a/python/README.md +++ b/python/README.md @@ -21,7 +21,6 @@ all the conda commands and do this for you. Conda requirements files: conda_env_ctsm_py.txt --------- Standard conda environment to use for most machines -conda_env_ctsm_py_cgd.txt ----- Standard conda environment for CGD machines conda_env_ctsm_py_latest.txt -- Test environment with latest versions that work ## Unit and system tests diff --git a/python/conda_env_ctsm_py.txt b/python/conda_env_ctsm_py.txt index e621081591..abccf74773 100644 --- a/python/conda_env_ctsm_py.txt +++ b/python/conda_env_ctsm_py.txt @@ -1,7 +1,5 @@ # -# 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... +# NOTE: On Derecho you may need to "module load conda" # # use the top level bash script: # ../py_env_create # Do this each time you update your CTSM Version @@ -18,7 +16,7 @@ scipy netcdf4 requests packaging -numpy=1.18.5 +numpy=1.19.5 xarray=0.17.0 xesmf numba=0.55.2 # Avoid 0.56 until numpy>=1.20. This is the minimum for xesmf diff --git a/python/conda_env_ctsm_py_cgd.txt b/python/conda_env_ctsm_py_cgd.txt deleted file mode 100644 index 3afcf4bba2..0000000000 --- a/python/conda_env_ctsm_py_cgd.txt +++ /dev/null @@ -1,24 +0,0 @@ -# NOTE: This version is just for cgd.ucar.edu systems, where conda limits python to version 3.7.0 -# See issue https://github.com/ESCOMP/CTSM/issues/1792 -# -# This should be coordinated with the main python environment file! -# -# use the top level bash 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_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.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/ctsm/lilac_build_ctsm.py b/python/ctsm/lilac_build_ctsm.py index b189cb56ea..d7b92517c5 100644 --- a/python/ctsm/lilac_build_ctsm.py +++ b/python/ctsm/lilac_build_ctsm.py @@ -153,6 +153,14 @@ def build_ctsm( existing_inputdata = existing_machine or inputdata_path is not None _create_build_dir(build_dir=build_dir, existing_inputdata=existing_inputdata) + # Some error checking + if inputdata_path is not None: + if not os.path.isdir(inputdata_path): + abort("Input inputdata_path directory does NOT exist = " + inputdata_path) + + if not os.path.isdir(build_dir): + abort("Input build_dir directory does NOT exist = " + build_dir) + if machine is None: assert os_type is not None, "with machine absent, os_type must be given" assert netcdf_path is not None, "with machine absent, netcdf_path must be given" @@ -176,6 +184,7 @@ def build_ctsm( extra_fflags=extra_fflags, extra_cflags=extra_cflags, ) + assert os.path.isdir(cime_path), "cime_path must be a directory" _create_case( cime_path=cime_path, @@ -627,6 +636,7 @@ def _fill_out_machine_files( "CIME_OUTPUT_ROOT": build_dir, "GMAKE": gmake, "GMAKE_J": gmake_j, + "MAX_TASKS_PER_NODE": max_mpitasks_per_node, "MAX_MPITASKS_PER_NODE": max_mpitasks_per_node, "ESMF_MKFILE_PATH": esmf_mkfile_path, }, diff --git a/python/ctsm/machine_defaults.py b/python/ctsm/machine_defaults.py index 0f3900c152..a17f063a4b 100644 --- a/python/ctsm/machine_defaults.py +++ b/python/ctsm/machine_defaults.py @@ -74,7 +74,7 @@ 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", + create_test_queue=CREATE_TEST_QUEUE_UNSPECIFIED, job_launcher_defaults={ JOB_LAUNCHER_QSUB: QsubDefaults( queue="main", diff --git a/python/ctsm/site_and_regional/neon_site.py b/python/ctsm/site_and_regional/neon_site.py index 31ae78f5ad..9ad690becb 100755 --- a/python/ctsm/site_and_regional/neon_site.py +++ b/python/ctsm/site_and_regional/neon_site.py @@ -18,6 +18,7 @@ # 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 ctsm.utils import abort from CIME import build from CIME.case import Case @@ -75,6 +76,9 @@ def build_base_case( if overwrite and os.path.isdir(case_path): print("Removing the existing case at: {}".format(case_path)) + if os.getcwd() == case_path: + abort("Trying to remove the directory tree that we are in") + shutil.rmtree(case_path) with Case(case_path, read_only=False) as case: @@ -211,6 +215,8 @@ def run_case( if os.path.isdir(case_root): if overwrite: print("---- removing the existing case -------") + if os.getcwd() == case_root: + abort("Trying to remove the directory tree that we are in") shutil.rmtree(case_root) elif rerun: with Case(case_root, read_only=False) as case: diff --git a/python/ctsm/test/joblauncher/test_unit_job_launcher_no_batch.py b/python/ctsm/test/joblauncher/test_unit_job_launcher_no_batch.py index 46b59f4e76..4e22e3c085 100755 --- a/python/ctsm/test/joblauncher/test_unit_job_launcher_no_batch.py +++ b/python/ctsm/test/joblauncher/test_unit_job_launcher_no_batch.py @@ -21,9 +21,11 @@ class TestJobLauncherNoBatch(unittest.TestCase): """Tests of job_launcher_no_batch""" def setUp(self): + self._previous_dir = os.getcwd() self._testdir = tempfile.mkdtemp() def tearDown(self): + os.chdir(self._previous_dir) shutil.rmtree(self._testdir, ignore_errors=True) def assertFileContentsEqual(self, expected, filepath, msg=None): diff --git a/python/ctsm/test/test_sys_fsurdat_modifier.py b/python/ctsm/test/test_sys_fsurdat_modifier.py index 1a5045c14d..0e11a204a8 100755 --- a/python/ctsm/test/test_sys_fsurdat_modifier.py +++ b/python/ctsm/test/test_sys_fsurdat_modifier.py @@ -38,6 +38,7 @@ def setUp(self): - modify_fsurdat.cfg - fsurdat_out.nc """ + self._previous_dir = os.getcwd() self._cfg_template_path = os.path.join( path_to_ctsm_root(), "tools/modify_input_files/modify_fsurdat_template.cfg" ) @@ -55,6 +56,7 @@ def tearDown(self): """ Remove temporary directory """ + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) def test_no_files_given_fail(self): diff --git a/python/ctsm/test/test_sys_lilac_build_ctsm.py b/python/ctsm/test/test_sys_lilac_build_ctsm.py index d773749bf7..a8af9b2eab 100755 --- a/python/ctsm/test/test_sys_lilac_build_ctsm.py +++ b/python/ctsm/test/test_sys_lilac_build_ctsm.py @@ -26,6 +26,7 @@ class TestSysBuildCtsm(unittest.TestCase): """System tests for lilac_build_ctsm""" def setUp(self): + self._previous_dir = os.getcwd() self._tempdir = tempfile.mkdtemp() self.assertTrue(os.path.isdir(self._tempdir)) @@ -42,6 +43,7 @@ def setUp(self): self._ncarhost = None def tearDown(self): + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) if self._ncarhost is not None: os.environ["NCAR_HOST"] = self._ncarhost diff --git a/python/ctsm/test/test_sys_modify_singlept_site_neon.py b/python/ctsm/test/test_sys_modify_singlept_site_neon.py index 74362be4cd..76a78c3db5 100755 --- a/python/ctsm/test/test_sys_modify_singlept_site_neon.py +++ b/python/ctsm/test/test_sys_modify_singlept_site_neon.py @@ -27,6 +27,7 @@ def setUp(self): Make /_tempdir for use by these tests. Check tempdir for history files """ + self._previous_dir = os.getcwd() self._tempdir = tempfile.mkdtemp() testinputs_path = os.path.join(path_to_ctsm_root(), "python/ctsm/test/testinputs") self._cfg_file_path = os.path.join( @@ -37,6 +38,7 @@ def tearDown(self): """ Remove temporary directory """ + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) def test_modify_site(self): diff --git a/python/ctsm/test/test_sys_regrid_ggcmi_shdates.py b/python/ctsm/test/test_sys_regrid_ggcmi_shdates.py index 6c2e230481..9209ebfe78 100755 --- a/python/ctsm/test/test_sys_regrid_ggcmi_shdates.py +++ b/python/ctsm/test/test_sys_regrid_ggcmi_shdates.py @@ -38,6 +38,7 @@ def setUp(self): self._testinputs_cc_path = testinputs_cc_path # Make /_tempdir for use by these tests. + self._previous_dir = os.getcwd() self._tempdir = tempfile.mkdtemp() # Obtain path for the directory being created in /_tempdir @@ -73,6 +74,7 @@ def tearDown(self): """ Remove temporary directory """ + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) def test_regrid_ggcmi_shdates(self): diff --git a/python/ctsm/test/test_unit_fsurdat_modifier.py b/python/ctsm/test/test_unit_fsurdat_modifier.py index 166924903b..bcb9099f61 100755 --- a/python/ctsm/test/test_unit_fsurdat_modifier.py +++ b/python/ctsm/test/test_unit_fsurdat_modifier.py @@ -46,6 +46,7 @@ def setUp(self): testinputs_path, "surfdata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc", ) + self._previous_dir = os.getcwd() self._tempdir = tempfile.mkdtemp() self._fsurdat_in = os.path.join( testinputs_path, @@ -80,6 +81,7 @@ def tearDown(self): """ Remove temporary directory """ + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) def test_subgrid_and_idealized_fails(self): diff --git a/python/ctsm/test/test_unit_modify_singlept_site_neon.py b/python/ctsm/test/test_unit_modify_singlept_site_neon.py index 3a9d7d424c..db1fc1966d 100755 --- a/python/ctsm/test/test_unit_modify_singlept_site_neon.py +++ b/python/ctsm/test/test_unit_modify_singlept_site_neon.py @@ -43,12 +43,14 @@ def setUp(self): Make /_tempdir for use by these tests. Check tempdir for history files """ + self._previous_dir = os.getcwd() self._tempdir = tempfile.mkdtemp() def tearDown(self): """ Remove temporary directory """ + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) def test_get_neon(self): diff --git a/python/ctsm/test/test_unit_neon_arg_parse.py b/python/ctsm/test/test_unit_neon_arg_parse.py index 7bae337709..4a5b0b9e6c 100755 --- a/python/ctsm/test/test_unit_neon_arg_parse.py +++ b/python/ctsm/test/test_unit_neon_arg_parse.py @@ -34,12 +34,14 @@ def setUp(self): """ Make /_tempdir for use by these tests. """ + self._previous_dir = os.getcwd() self._tempdir = tempfile.mkdtemp() def tearDown(self): """ Remove temporary directory """ + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) def test_function(self): diff --git a/python/ctsm/test/test_unit_neon_site.py b/python/ctsm/test/test_unit_neon_site.py index 4828718272..8ef6034f94 100755 --- a/python/ctsm/test/test_unit_neon_site.py +++ b/python/ctsm/test/test_unit_neon_site.py @@ -33,12 +33,14 @@ def setUp(self): """ Make /_tempdir for use by these tests. """ + self._previous_dir = os.getcwd() self._tempdir = tempfile.mkdtemp() def tearDown(self): """ Remove temporary directory """ + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) def test_modify_user_nl_transient(self): diff --git a/python/ctsm/test/test_unit_path_utils.py b/python/ctsm/test/test_unit_path_utils.py index 566c458acc..067809cbf6 100755 --- a/python/ctsm/test/test_unit_path_utils.py +++ b/python/ctsm/test/test_unit_path_utils.py @@ -22,9 +22,11 @@ class TestPathUtils(unittest.TestCase): """Tests of path_utils""" def setUp(self): + self._previous_dir = os.getcwd() self._testdir = tempfile.mkdtemp() def tearDown(self): + os.chdir(self._previous_dir) shutil.rmtree(self._testdir, ignore_errors=True) def _ctsm_path_in_cesm(self): diff --git a/python/ctsm/test/test_unit_run_neon.py b/python/ctsm/test/test_unit_run_neon.py index a35608e249..904db885a9 100755 --- a/python/ctsm/test/test_unit_run_neon.py +++ b/python/ctsm/test/test_unit_run_neon.py @@ -32,12 +32,14 @@ def setUp(self): """ Make /_tempdir for use by these tests. """ + self._previous_dir = os.getcwd() self._tempdir = tempfile.mkdtemp() def tearDown(self): """ Remove temporary directory """ + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) def test_check_neon_listing(self): diff --git a/python/ctsm/test/test_unit_utils.py b/python/ctsm/test/test_unit_utils.py index a78928abcc..85ba2515dd 100755 --- a/python/ctsm/test/test_unit_utils.py +++ b/python/ctsm/test/test_unit_utils.py @@ -21,9 +21,11 @@ class TestUtilsFillTemplateFile(unittest.TestCase): """Tests of utils: fill_template_file""" def setUp(self): + self._previous_dir = os.getcwd() self._testdir = tempfile.mkdtemp() def tearDown(self): + os.chdir(self._previous_dir) shutil.rmtree(self._testdir, ignore_errors=True) def test_fillTemplateFile_basic(self): diff --git a/python/ctsm/test/test_unit_utils_import_coord.py b/python/ctsm/test/test_unit_utils_import_coord.py index 6e339a913f..11ff3c1b54 100755 --- a/python/ctsm/test/test_unit_utils_import_coord.py +++ b/python/ctsm/test/test_unit_utils_import_coord.py @@ -39,6 +39,7 @@ class TestUtilsImportCoord(unittest.TestCase): def setUp(self): """Setup for trying out the methods""" + self._previous_dir = os.getcwd() testinputs_path = os.path.join(path_to_ctsm_root(), "python/ctsm/test/testinputs") self._testinputs_path = testinputs_path self._tempdir = tempfile.mkdtemp() @@ -55,6 +56,7 @@ def tearDown(self): """ Remove temporary directory """ + os.chdir(self._previous_dir) shutil.rmtree(self._tempdir, ignore_errors=True) def test_importcoord1d(self): diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 05041527a7..fffb19bc46 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -97,6 +97,7 @@ module CNPhenologyMod real(r8) :: soilpsi_off ! critical soil water potential for leaf offset real(r8) :: lwtop ! live wood turnover proportion (annual fraction) real(r8) :: phenology_soil_depth ! soil depth used for measuring states for phenology triggers + real(r8) :: snow5d_thresh_for_onset ! 5-day snow depth threshold for leaf onset end type params_type type(params_type) :: params_inst @@ -280,6 +281,7 @@ subroutine CNPhenologySetParams( ) params_inst%soilpsi_off = -0.8 ! MPa params_inst%lwtop = 0.7_r8 ! Fraction params_inst%phenology_soil_depth = 0.08_r8 ! m + params_inst%snow5d_thresh_for_onset = 0.2_r8 ! m end subroutine CNPhenologySetParams !----------------------------------------------------------------------- @@ -313,6 +315,7 @@ subroutine readParams ( ncid ) call readNcdioScalar(ncid, 'soilpsi_off', subname, params_inst%soilpsi_off) call readNcdioScalar(ncid, 'lwtop_ann', subname, params_inst%lwtop) call readNcdioScalar(ncid, 'phenology_soil_depth', subname, params_inst%phenology_soil_depth) + call readNcdioScalar(ncid, 'snow5d_thresh_for_onset', subname, params_inst%snow5d_thresh_for_onset) end subroutine readParams @@ -1136,7 +1139,6 @@ function SeasonalDecidOnset( onset_gdd, onset_gddflag, soilt, soila10, t_a5min, logical :: do_onset ! Flag if onset should happen (return value) ! ! !LOCAL VARIABLES: - real(r8), parameter :: snow5d_thresh_for_onset = 0.1_r8 ! 5-day snow depth threshold for leaf onset real(r8), parameter :: min_critical_daylength_onset = 39300._r8/2._r8 ! Minimum daylength for onset to happen ! NOTE above: The 39300/2(19650) value is what we've ! tested with, we are concerned that changing @@ -1192,7 +1194,8 @@ function SeasonalDecidOnset( onset_gdd, onset_gddflag, soilt, soila10, t_a5min, else if (season_decid_temperate == 0 .and. onset_gddflag == 1.0_r8 .and. & soila10 > SHR_CONST_TKFRZ .and. & t_a5min > SHR_CONST_TKFRZ .and. ws_flag==1.0_r8 .and. & - dayl>min_critical_daylength_onset .and. snow_5daymin_critical_daylength_onset .and. & + snow_5day atm2lnd_inst%forc_solad_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (visible only) + associate( & + forc_solai => atm2lnd_inst%forc_solai_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (visible only) + forc_solad => atm2lnd_inst%forc_solad_downscaled_col, & ! 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) forc_pbot => atm2lnd_inst%forc_pbot_downscaled_col , & ! Input: [real(r8) (:) ] downscaled surface pressure (Pa) forc_rain => wateratm2lndbulk_inst%forc_rain_downscaled_col , & ! Input: [real(r8) (:) ] downscaled rain rate [mm/s] - h2osoi_vol => waterstatebulk_inst%h2osoi_vol_col , & ! Input: [real(r8) (:,:) ] volumetric soil water (0<=h2osoi_vol<=watsat) snow_depth => waterdiagnosticbulk_inst%snow_depth_col , & ! Input: [real(r8) (:) ] snow height (m) @@ -324,7 +324,7 @@ subroutine depvel_compute( bounds, & spec_hum = forc_q(c) rain = forc_rain(c) sfc_temp = forc_t(c) - solar_flux = forc_solad(g,1) + solar_flux = forc_solad(c,1) lat = grc%latdeg(g) lon = grc%londeg(g) clmveg = patch%itype(pi) diff --git a/src/biogeochem/VOCEmissionMod.F90 b/src/biogeochem/VOCEmissionMod.F90 index f1865af3b7..a4bd9dc4d2 100644 --- a/src/biogeochem/VOCEmissionMod.F90 +++ b/src/biogeochem/VOCEmissionMod.F90 @@ -485,7 +485,7 @@ subroutine VOCEmission (bounds, num_soilp, filter_soilp, & !h2osoi_vol => waterstate_inst%h2osoi_vol_col , & ! Input: [real(r8) (:,:) ] volumetric soil water (m3/m3) !h2osoi_ice => waterstate_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice soil content (kg/m3) - forc_solad => atm2lnd_inst%forc_solad_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (visible only) + forc_solad => atm2lnd_inst%forc_solad_downscaled_col, & ! Input: [real(r8) (:,:) ] direct beam radiation (visible only) forc_solai => atm2lnd_inst%forc_solai_grc , & ! Input: [real(r8) (:,:) ] diffuse radiation (visible only) forc_pbot => atm2lnd_inst%forc_pbot_downscaled_col , & ! Input: [real(r8) (:) ] downscaled atmospheric pressure (Pa) forc_pco2 => atm2lnd_inst%forc_pco2_grc , & ! Input: [real(r8) (:) ] partial pressure co2 (Pa) @@ -557,7 +557,7 @@ subroutine VOCEmission (bounds, num_soilp, filter_soilp, & ! Calculate PAR: multiply w/m2 by 4.6 to get umol/m2/s for par (added 8/14/02) !------------------------ ! SUN: - par_sun = (forc_solad(g,1) + fsun(p) * forc_solai(g,1)) * 4.6_r8 + par_sun = (forc_solad(c,1) + fsun(p) * forc_solai(g,1)) * 4.6_r8 par24_sun = (forc_solad24(p) + fsun24(p) * forc_solai24(p)) * 4.6_r8 par240_sun = (forc_solad240(p) + fsun240(p) * forc_solai240(p)) * 4.6_r8 diff --git a/src/biogeophys/BalanceCheckMod.F90 b/src/biogeophys/BalanceCheckMod.F90 index ff72bcb307..b3efe6e525 100644 --- a/src/biogeophys/BalanceCheckMod.F90 +++ b/src/biogeophys/BalanceCheckMod.F90 @@ -35,6 +35,7 @@ module BalanceCheckMod use landunit_varcon , only : istdlak, istsoil,istcrop,istwet,istice use column_varcon , only : icol_roof, icol_sunwall, icol_shadewall use column_varcon , only : icol_road_perv, icol_road_imperv + use clm_varctl , only : use_hillslope_routing ! ! !PUBLIC TYPES: implicit none @@ -215,6 +216,7 @@ subroutine WaterGridcellBalanceSingle(bounds, & ! ! !USES: use subgridAveMod, only: c2g + use LandunitType , only : lun ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -231,8 +233,8 @@ subroutine WaterGridcellBalanceSingle(bounds, & character(len=5) , intent(in) :: flag ! specifies begwb or endwb ! ! !LOCAL VARIABLES: - integer :: g ! indices - integer :: begc, endc, begg, endg ! bounds + integer :: g, l ! indices + integer :: begc, endc, begl, endl, begg, endg ! bounds real(r8) :: wb_col(bounds%begc:bounds%endc) ! temporary column-level water mass real(r8) :: wb_grc(bounds%begg:bounds%endg) ! temporary grid cell-level water mass real(r8) :: qflx_liq_dynbal_left_to_dribble(bounds%begg:bounds%endg) ! grc liq dynamic land cover change conversion runoff flux @@ -250,6 +252,8 @@ subroutine WaterGridcellBalanceSingle(bounds, & begc = bounds%begc endc = bounds%endc + begl = bounds%begl + endl = bounds%endl begg = bounds%begg endg = bounds%endg @@ -266,6 +270,15 @@ subroutine WaterGridcellBalanceSingle(bounds, & call c2g(bounds, wb_col(begc:endc), wb_grc(begg:endg), & c2l_scale_type='urbanf', l2g_scale_type='unity') + ! add landunit level state variable, convert from (m3) to (kg m-2) + if (use_hillslope_routing) then + do l = begl, endl + g = lun%gridcell(l) + wb_grc(g) = wb_grc(g) + waterstate_inst%stream_water_volume_lun(l) & + *1e3_r8/(grc%area(g)*1.e6_r8) + enddo + endif + ! Call the beginning or ending version of the subroutine according ! to flag value if (flag == 'begwb') then @@ -500,8 +513,9 @@ subroutine BalanceCheck( bounds, & !----------------------------------------------------------------------- associate( & - forc_solad => atm2lnd_inst%forc_solad_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (vis=forc_sols , nir=forc_soll ) - forc_solai => atm2lnd_inst%forc_solai_grc , & ! Input: [real(r8) (:,:) ] diffuse radiation (vis=forc_solsd, nir=forc_solld) + forc_solad_col => atm2lnd_inst%forc_solad_downscaled_col , & ! Input: [real(r8) (:,:) ] direct beam radiation (vis=forc_sols , nir=forc_soll ) + forc_solad => atm2lnd_inst%forc_solad_not_downscaled_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (vis=forc_sols , nir=forc_soll ) + forc_solai => atm2lnd_inst%forc_solai_grc , & ! Input: [real(r8) (:,:) ] diffuse radiation (vis=forc_solsd, nir=forc_solld) forc_rain => wateratm2lnd_inst%forc_rain_downscaled_col , & ! Input: [real(r8) (:) ] column level rain rate [mm/s] forc_rain_grc => wateratm2lnd_inst%forc_rain_not_downscaled_grc, & ! Input: [real(r8) (:) ] grid cell-level rain rate [mm/s] forc_snow => wateratm2lnd_inst%forc_snow_downscaled_col , & ! Input: [real(r8) (:) ] column level snow rate [mm/s] @@ -546,6 +560,7 @@ subroutine BalanceCheck( bounds, & qflx_qrgwl_grc => waterlnd2atm_inst%qflx_rofliq_qgwl_grc , & ! Input: [real(r8) (:) ] grid cell-level qflx_surf at glaciers, wetlands, lakes qflx_drain_col => waterflux_inst%qflx_drain_col , & ! Input: [real(r8) (:) ] column level sub-surface runoff (mm H2O /s) qflx_drain_grc => waterlnd2atm_inst%qflx_rofliq_qsub_grc , & ! Input: [real(r8) (:) ] grid cell-level drainage (mm H20 /s) + qflx_streamflow_grc => waterlnd2atm_inst%qflx_rofliq_stream_grc, & ! Input: [real(r8) (:) ] streamflow [mm H2O/s] qflx_ice_runoff_col => waterlnd2atm_inst%qflx_ice_runoff_col , & ! Input: [real(r8) (:) ] column level solid runoff from snow capping and from excess ice in soil (mm H2O /s) qflx_ice_runoff_grc => waterlnd2atm_inst%qflx_rofice_grc , & ! Input: [real(r8) (:) ] grid cell-level solid runoff from snow capping and from excess ice in soil (mm H2O /s) qflx_sl_top_soil => waterflux_inst%qflx_sl_top_soil_col , & ! Input: [real(r8) (:) ] liquid water + ice from layer above soil to top soil layer or sent to qflx_qrgwl (mm H2O/s) @@ -725,6 +740,15 @@ subroutine BalanceCheck( bounds, & - qflx_snwcp_discarded_ice_grc(g)) * dtime end do + ! add landunit level flux variable, convert from (m3/s) to (kg m-2 s-1) + if (use_hillslope_routing) then + ! output water flux from streamflow (+) + do g = bounds%begg, bounds%endg + errh2o_grc(g) = errh2o_grc(g) & + + qflx_streamflow_grc(g) * dtime + enddo + endif + errh2o_max_val = maxval(abs(errh2o_grc(bounds%begg:bounds%endg))) ! BUG(rgk, 2021-04-13, ESCOMP/CTSM#1314) Temporarily bypassing gridcell-level check with use_fates_planthydro until issue 1314 is resolved @@ -883,8 +907,8 @@ subroutine BalanceCheck( bounds, & ! level because of interactions between columns and since a separate check is done ! in the urban radiation module if (.not. lun%urbpoi(l)) then - errsol(p) = fsa(p) + fsr(p) & - - (forc_solad(g,1) + forc_solad(g,2) + forc_solai(g,1) + forc_solai(g,2)) + errsol(p) = fsa(p) + fsr(p) & + - (forc_solad_col(c,1) + forc_solad_col(c,2) + forc_solai(g,1) + forc_solai(g,2)) else errsol(p) = spval end if diff --git a/src/biogeophys/CMakeLists.txt b/src/biogeophys/CMakeLists.txt index 3cf5e0eaf0..2ffc346670 100644 --- a/src/biogeophys/CMakeLists.txt +++ b/src/biogeophys/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND clm_sources CanopyStateType.F90 EnergyFluxType.F90 GlacierSurfaceMassBalanceMod.F90 + HillslopeHydrologyUtilsMod.F90 HumanIndexMod.F90 InfiltrationExcessRunoffMod.F90 IrrigationMod.F90 diff --git a/src/biogeophys/HillslopeHydrologyMod.F90 b/src/biogeophys/HillslopeHydrologyMod.F90 new file mode 100644 index 0000000000..b2866df679 --- /dev/null +++ b/src/biogeophys/HillslopeHydrologyMod.F90 @@ -0,0 +1,1148 @@ +module HillslopeHydrologyMod + + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! Read geomorphological parameters for hillslope columns + ! + ! !USES: +#include "shr_assert.h" + use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_log_mod , only : errMsg => shr_log_errMsg + use spmdMod , only : masterproc, iam + use abortutils , only : endrun + use clm_varctl , only : iulog + use clm_varctl , only : use_hillslope_routing + use decompMod , only : bounds_type + use clm_varcon , only : rpi + use HillslopeHydrologyUtilsMod, only : HillslopeSoilThicknessProfile_linear + + ! !PUBLIC TYPES: + implicit none + + private + save + + ! !PUBLIC MEMBER FUNCTIONS: + public hillslope_properties_init + public InitHillslope + public SetHillslopeSoilThickness + public HillslopeSoilThicknessProfile + public HillslopeSetLowlandUplandPfts + public HillslopeDominantLowlandPft + public HillslopePftFromFile + public HillslopeStreamOutflow + public HillslopeUpdateStreamWater + + integer, public :: pft_distribution_method ! Method for distributing pfts across hillslope columns + integer, public :: soil_profile_method ! Method for varying soil thickness across hillslope columns + + ! Streamflow methods + integer, public, parameter :: streamflow_manning = 0 + ! Pft distribution methods + integer, public, parameter :: pft_standard = 0 + integer, public, parameter :: pft_from_file = 1 + integer, public, parameter :: pft_uniform_dominant_pft = 2 + integer, public, parameter :: pft_lowland_dominant_pft = 3 + integer, public, parameter :: pft_lowland_upland = 4 + + ! PRIVATE + character(len=*), parameter, private :: sourcefile = & + __FILE__ + integer, private, parameter :: soil_profile_uniform = 0 + integer, private, parameter :: soil_profile_from_file = 1 + integer, private, parameter :: soil_profile_set_lowland_upland = 2 + integer, private, parameter :: soil_profile_linear = 3 + + !----------------------------------------------------------------------- + +contains + + !----------------------------------------------------------------------- + subroutine hillslope_properties_init(NLFilename) + ! + ! DESCRIPTION + ! read in hillslope hydrology veg/soil properties namelist variables + ! + ! !USES: + use abortutils , only : endrun + use fileutils , only : getavu, relavu + use spmdMod , only : mpicom, masterproc + use shr_mpi_mod , only : shr_mpi_bcast + use clm_varctl , only : iulog + use clm_nlUtilsMod , only : find_nlgroup_name + + ! !ARGUMENTS: + implicit none + character(len=*), intent(in) :: NLFilename ! Namelist filename + !---------------------------------------------------------------------- + integer :: nu_nml ! unit for namelist file + integer :: nml_error ! namelist i/o error flag + character(len=*), parameter :: nmlname = 'hillslope_properties_inparm' + character(*), parameter :: subName = "('read_hillslope_properties_namelist')" + ! Default values for namelist + character(len=50) :: hillslope_pft_distribution_method = 'Standard' ! pft distribution method string + character(len=50) :: hillslope_soil_profile_method = 'Uniform' ! soil thickness distribution method string + !----------------------------------------------------------------------- + +! MUST agree with name in namelist and read statement + namelist /hillslope_properties_inparm/ & + hillslope_pft_distribution_method, & + hillslope_soil_profile_method + + ! Read hillslope hydrology namelist + if (masterproc) then + nu_nml = getavu() + open( nu_nml, file=trim(NLFilename), status='old', iostat=nml_error ) + call find_nlgroup_name(nu_nml, 'hillslope_properties_inparm', status=nml_error) + if (nml_error == 0) then + read(nu_nml, nml=hillslope_properties_inparm,iostat=nml_error) + if (nml_error /= 0) then + call endrun(subname // ':: ERROR reading hillslope properties namelist') + end if + else + call endrun(subname // ':: ERROR reading hillslope properties namelist') + end if + close(nu_nml) + call relavu( nu_nml ) + + if ( trim(hillslope_pft_distribution_method) == 'Standard' ) then + pft_distribution_method = pft_standard + else if ( trim(hillslope_pft_distribution_method) == 'FromFile' ) then + pft_distribution_method = pft_from_file + else if ( trim(hillslope_pft_distribution_method) == 'DominantPftUniform') then + pft_distribution_method = pft_uniform_dominant_pft + else if ( trim(hillslope_pft_distribution_method) == 'DominantPftLowland') then + pft_distribution_method = pft_lowland_dominant_pft + else if ( trim(hillslope_pft_distribution_method) == 'PftLowlandUpland') then + pft_distribution_method = pft_lowland_upland + else + call endrun(msg="ERROR bad value for hillslope_pft_distribution_method in "//nmlname//"namelist"//errmsg(sourcefile, __LINE__)) + end if + + if ( trim(hillslope_soil_profile_method) == 'Uniform' ) then + soil_profile_method = soil_profile_uniform + else if ( trim(hillslope_soil_profile_method) == 'FromFile' ) then + soil_profile_method = soil_profile_from_file + else if ( trim(hillslope_soil_profile_method) == 'SetLowlandUpland' ) then + soil_profile_method = soil_profile_set_lowland_upland + else if ( trim(hillslope_soil_profile_method) == 'Linear') then + soil_profile_method = soil_profile_linear + else + call endrun(msg="ERROR bad value for hillslope_soil_profile_method in "//nmlname//"namelist"//errmsg(sourcefile, __LINE__)) + end if + + end if + + call shr_mpi_bcast(pft_distribution_method, mpicom) + call shr_mpi_bcast(soil_profile_method, mpicom) + + if (masterproc) then + + write(iulog,*) ' ' + write(iulog,*) 'hillslope_properties settings:' + write(iulog,*) ' hillslope_pft_distribution_method = ',hillslope_pft_distribution_method + write(iulog,*) ' hillslope_soil_profile_method = ',hillslope_soil_profile_method + + end if + + end subroutine hillslope_properties_init + + !----------------------------------------------------------------------- + subroutine check_aquifer_layer() + ! + ! !DESCRIPTION: + ! Check whether use_hillslope and use_aquifer_layer are both set + ! The use of use_hillslope is implied by the call to this function + ! in InitHillslope, but explicitly compare here for clarity. + ! + ! !USES: + use clm_varctl , only : use_hillslope + use SoilWaterMovementMod , only : use_aquifer_layer + if (use_hillslope .and. use_aquifer_layer()) then + write(iulog,*) ' ERROR: use_hillslope and use_aquifer_layer may not be used simultaneously' + call endrun(msg=' ERROR: use_hillslope and use_aquifer_layer cannot both be set to true' // & + errMsg(sourcefile, __LINE__)) + end if + + end subroutine check_aquifer_layer + + !----------------------------------------------------------------------- + + subroutine InitHillslope(bounds,fsurdat) + ! + ! !DESCRIPTION: + ! Initialize hillslope geomorphology from input dataset + ! + ! !USES: + use LandunitType , only : lun + use GridcellType , only : grc + use ColumnType , only : col + use clm_varctl , only : nhillslope, max_columns_hillslope + use spmdMod , only : masterproc + use fileutils , only : getfil + use clm_varcon , only : spval, ispval, grlnd + use landunit_varcon , only : istsoil + use subgridWeightsMod , only : compute_higher_order_weights + use ncdio_pio + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + character(len=*) , intent(in) :: fsurdat ! surface data file name + integer, pointer :: ihillslope_in(:,:) ! read in - integer + integer, pointer :: ncolumns_hillslope_in(:) ! read in number of columns + integer, allocatable :: ncolumns_hillslope(:) ! number of hillslope columns + integer, allocatable :: hill_ndx(:,:) ! hillslope index + integer, allocatable :: col_ndx(:,:) ! column index + integer, allocatable :: col_dndx(:,:) ! downhill column index + integer, allocatable :: hill_pftndx(:,:) ! hillslope pft index [] + integer, allocatable :: col_pftndx(:) ! hillslope column pft index [] + real(r8), pointer :: fhillslope_in(:,:) ! read in - float + real(r8), allocatable :: pct_hillslope(:,:) ! percent of landunit occupied by hillslope + real(r8), allocatable :: hill_slope(:,:) ! hillslope slope [m/m] + real(r8), allocatable :: hill_aspect(:,:) ! hillslope azimuth [radians] + real(r8), allocatable :: hill_area(:,:) ! hillslope area [m2] + real(r8), allocatable :: hill_dist(:,:) ! hillslope length [m] + real(r8), allocatable :: hill_width(:,:) ! hillslope width [m] + real(r8), allocatable :: hill_elev(:,:) ! hillslope height [m] + real(r8), allocatable :: hill_bedrock(:,:) ! hillslope bedrock depth [m] + real(r8), pointer :: fstream_in(:) ! read in - 1D - float + + type(file_desc_t) :: ncid ! netcdf id + logical :: readvar ! check whether variable on file + character(len=256) :: locfn ! local filename + integer :: ierr ! error code + integer :: c, l, g, i, j, ci, nh ! indices + + real(r8) :: ncol_per_hillslope(nhillslope) ! number of columns per hillslope + real(r8) :: hillslope_area(nhillslope) ! area of hillslope + real(r8) :: nhill_per_landunit(nhillslope) ! total number of each representative hillslope per landunit + + character(len=*), parameter :: subname = 'InitHillslope' + + !----------------------------------------------------------------------- + + ! consistency check + call check_aquifer_layer() + + ! Open surface dataset to read in data below + + call getfil (fsurdat, locfn, 0) + call ncd_pio_openfile (ncid, locfn, 0) + + allocate( & + ncolumns_hillslope(bounds%begl:bounds%endl), & + pct_hillslope(bounds%begl:bounds%endl,nhillslope), & + hill_ndx (bounds%begl:bounds%endl,max_columns_hillslope), & + col_ndx (bounds%begl:bounds%endl,max_columns_hillslope), & + col_dndx (bounds%begl:bounds%endl,max_columns_hillslope), & + hill_slope (bounds%begl:bounds%endl,max_columns_hillslope), & + hill_aspect (bounds%begl:bounds%endl,max_columns_hillslope), & + hill_area (bounds%begl:bounds%endl,max_columns_hillslope), & + hill_dist (bounds%begl:bounds%endl,max_columns_hillslope), & + hill_width (bounds%begl:bounds%endl,max_columns_hillslope), & + hill_elev (bounds%begl:bounds%endl,max_columns_hillslope), & + col_pftndx (bounds%begc:bounds%endc), & + stat=ierr) + + allocate(ncolumns_hillslope_in(bounds%begg:bounds%endg)) + + call ncd_io(ncid=ncid, varname='nhillcolumns', flag='read', data=ncolumns_hillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: nhillcolumns not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + ncolumns_hillslope(l) = ncolumns_hillslope_in(g) + ! vegetated landunits having nonzero hillslope columns and nonzero weight + if (lun%wtgcell(l) > 0._r8 .and. lun%itype(l) == istsoil .and. ncolumns_hillslope_in(g) > 0) then + do c = lun%coli(l), lun%colf(l) + col%is_hillslope_column(c) = .true. + enddo + end if + enddo + deallocate(ncolumns_hillslope_in) + + allocate(fhillslope_in(bounds%begg:bounds%endg,nhillslope)) + + call ncd_io(ncid=ncid, varname='pct_hillslope', flag='read', data=fhillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: pct_hillslope not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + pct_hillslope(l,:) = fhillslope_in(g,:) + enddo + deallocate(fhillslope_in) + + allocate(ihillslope_in(bounds%begg:bounds%endg,max_columns_hillslope)) + + call ncd_io(ncid=ncid, varname='hillslope_index', flag='read', data=ihillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_index not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + hill_ndx(l,:) = ihillslope_in(g,:) + enddo + + call ncd_io(ncid=ncid, varname='column_index', flag='read', data=ihillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: column_index not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + col_ndx(l,:) = ihillslope_in(g,:) + enddo + + call ncd_io(ncid=ncid, varname='downhill_column_index', flag='read', data=ihillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: downhill_column_index not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + col_dndx(l,:) = ihillslope_in(g,:) + enddo + deallocate(ihillslope_in) + + allocate(fhillslope_in(bounds%begg:bounds%endg,max_columns_hillslope)) + call ncd_io(ncid=ncid, varname='hillslope_slope', flag='read', data=fhillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_slope not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + hill_slope(l,:) = fhillslope_in(g,:) + enddo + + call ncd_io(ncid=ncid, varname='hillslope_aspect', flag='read', data=fhillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_aspect not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + hill_aspect(l,:) = fhillslope_in(g,:) + enddo + + call ncd_io(ncid=ncid, varname='hillslope_area', flag='read', data=fhillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_area not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + hill_area(l,:) = fhillslope_in(g,:) + enddo + call ncd_io(ncid=ncid, varname='hillslope_distance', flag='read', data=fhillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_length not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + hill_dist(l,:) = fhillslope_in(g,:) + enddo + + call ncd_io(ncid=ncid, varname='hillslope_width', flag='read', data=fhillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_width not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + hill_width(l,:) = fhillslope_in(g,:) + enddo + + call ncd_io(ncid=ncid, varname='hillslope_elevation', flag='read', data=fhillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_height not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + hill_elev(l,:) = fhillslope_in(g,:) + enddo + + deallocate(fhillslope_in) + + allocate(ihillslope_in(bounds%begg:bounds%endg,max_columns_hillslope)) + call ncd_io(ncid=ncid, varname='hillslope_pftndx', flag='read', data=ihillslope_in, dim1name=grlnd, readvar=readvar) + if (readvar) then + allocate(hill_pftndx (bounds%begl:bounds%endl,max_columns_hillslope), stat=ierr) + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + hill_pftndx(l,:) = ihillslope_in(g,:) + enddo + end if + + deallocate(ihillslope_in) + + if (use_hillslope_routing) then + allocate(fstream_in(bounds%begg:bounds%endg)) + + call ncd_io(ncid=ncid, varname='hillslope_stream_depth', flag='read', data=fstream_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_stream_depth not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + lun%stream_channel_depth(l) = fstream_in(g) + enddo + + call ncd_io(ncid=ncid, varname='hillslope_stream_width', flag='read', data=fstream_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_stream_width not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + lun%stream_channel_width(l) = fstream_in(g) + enddo + + call ncd_io(ncid=ncid, varname='hillslope_stream_slope', flag='read', data=fstream_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: hillslope_stream_slope not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + lun%stream_channel_slope(l) = fstream_in(g) + enddo + + deallocate(fstream_in) + end if + + ! Set hillslope hydrology column level variables + ! This needs to match how columns set up in subgridMod + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + if (lun%itype(l) == istsoil) then + + ! map external column index to internal column index + do c = lun%coli(l), lun%colf(l) + ! ci should span [1:nhillcolumns(l)] + ci = c-lun%coli(l)+1 + + if (col_dndx(l,ci) <= -999) then + ! lowermost column of hillslope has no downstream neighbor + col%cold(c) = ispval + else + ! relative separation should be the same + col%cold(c) = c + (col_dndx(l,ci) - col_ndx(l,ci)) + end if + enddo + + do c = lun%coli(l), lun%colf(l) + ci = c-lun%coli(l)+1 + + col%hillslope_ndx(c) = hill_ndx(l,ci) + + ! Find uphill neighbors (this may not actually be useful...) + col%colu(c) = ispval + do i = lun%coli(l), lun%colf(l) + if (c == col%cold(i)) then + col%colu(c) = i + end if + enddo + + ! distance of lower edge of column from hillslope bottom + col%hill_distance(c) = hill_dist(l,ci) + ! width of lower edge of column + col%hill_width(c) = hill_width(l,ci) + ! mean elevation of column relative to gridcell mean elevation + col%hill_elev(c) = hill_elev(l,ci) + ! mean along-hill slope of column + col%hill_slope(c) = hill_slope(l,ci) + ! area of column + col%hill_area(c) = hill_area(l,ci) + ! azimuth of column + col%hill_aspect(c) = hill_aspect(l,ci) + ! pft index of column + if ( allocated(hill_pftndx) ) then + col_pftndx(c) = hill_pftndx(l,ci) + end if + + enddo + + ! Calculate total hillslope area on landunit and + ! number of columns in each hillslope + ncol_per_hillslope(:)= 0._r8 + hillslope_area(:) = 0._r8 + do c = lun%coli(l), lun%colf(l) + nh = col%hillslope_ndx(c) + if (nh > 0) then + ncol_per_hillslope(nh) = ncol_per_hillslope(nh) + 1 + hillslope_area(nh) = hillslope_area(nh) + col%hill_area(c) + end if + enddo + + if (use_hillslope_routing) then + + ! Total area occupied by each hillslope (m2) is + ! grc%area(g)*1.e6*lun%wtgcell(l)*pct_hillslope(l,nh)*0.01 + ! Number of representative hillslopes per landunit + ! is the total area divided by individual area + ! include factor of 0.5 because a channel is shared by ~2 hillslopes + + lun%stream_channel_number(l) = 0._r8 + do nh = 1, nhillslope + if (hillslope_area(nh) > 0._r8) then + nhill_per_landunit(nh) = grc%area(g)*1.e6_r8*lun%wtgcell(l) & + *pct_hillslope(l,nh)*0.01/hillslope_area(nh) + + lun%stream_channel_number(l) = lun%stream_channel_number(l) & + + 0.5_r8 * nhill_per_landunit(nh) + end if + enddo + + ! Calculate steam channel length + ! Total length of stream banks is individual widths + ! times number of hillslopes per landunit + ! include factor of 0.5 because a channel is shared by ~2 hillslopes + lun%stream_channel_length(l) = 0._r8 + do c = lun%coli(l), lun%colf(l) + if (col%cold(c) == ispval) then + lun%stream_channel_length(l) = lun%stream_channel_length(l) & + + col%hill_width(c) * 0.5_r8 * nhill_per_landunit(col%hillslope_ndx(c)) + end if + enddo + end if + + ! if missing hillslope information on surface dataset, + ! call endrun + if (ncolumns_hillslope(l) > 0 .and. sum(hillslope_area) == 0._r8 .and. masterproc) then + write(iulog,*) 'Problem with input data: nhillcolumns is non-zero, but hillslope area is zero' + write(iulog,*) 'Check surface data for gridcell at (lon/lat): ', grc%londeg(g),grc%latdeg(g) + call endrun( 'ERROR:: sum of hillslope areas is zero.'//errmsg(sourcefile, __LINE__) ) + end if + + ! Recalculate column weights using input areas + ! The higher order weights will be updated in a subsequent reweight_wrapup call + do c = lun%coli(l), lun%colf(l) + nh = col%hillslope_ndx(c) + if (col%is_hillslope_column(c)) then + col%wtlunit(c) = (col%hill_area(c)/hillslope_area(nh)) & + * (pct_hillslope(l,nh)*0.01_r8) + end if + enddo + end if + enddo ! end of landunit loop + + deallocate(ncolumns_hillslope,pct_hillslope,hill_ndx,col_ndx,col_dndx, & + hill_slope,hill_area,hill_dist, & + hill_width,hill_elev,hill_aspect) + + ! Modify pft distributions + ! this may require modifying subgridMod/natveg_patch_exists + ! to ensure patch exists in every gridcell + if (pft_distribution_method == pft_from_file) then + call HillslopePftFromFile(bounds,col_pftndx) + else if (pft_distribution_method == pft_lowland_dominant_pft) then + ! Specify different pfts for uplands / lowlands + call HillslopeDominantLowlandPft(bounds) + else if (pft_distribution_method == pft_lowland_upland) then + ! example usage: + ! upland_ivt = 13 ! c3 non-arctic grass + ! lowland_ivt = 7 ! broadleaf deciduous tree + call HillslopeSetLowlandUplandPfts(bounds,lowland_ivt=7,upland_ivt=13) + else if (masterproc .and. .not. (pft_distribution_method == pft_standard .or. pft_distribution_method ==pft_uniform_dominant_pft)) then + call endrun( 'ERROR:: unrecognized hillslope_pft_distribution_method'//errmsg(sourcefile, __LINE__) ) + end if + + if ( allocated(hill_pftndx) ) then + deallocate(hill_pftndx) + deallocate(col_pftndx) + end if + + ! Update higher order weights and check that weights sum to 1 + call compute_higher_order_weights(bounds) + + call ncd_pio_closefile(ncid) + + end subroutine InitHillslope + + !----------------------------------------------------------------------- + + subroutine SetHillslopeSoilThickness(bounds,fsurdat,soil_depth_lowland_in,soil_depth_upland_in) + ! + ! !DESCRIPTION: + ! Set hillslope column nbedrock values + ! + ! !USES: + use LandunitType , only : lun + use GridcellType , only : grc + use ColumnType , only : col + use clm_varctl , only : nhillslope, max_columns_hillslope + use clm_varcon , only : zmin_bedrock, zisoi + use clm_varpar , only : nlevsoi + use spmdMod , only : masterproc + use fileutils , only : getfil + use clm_varcon , only : spval, ispval, grlnd + use ncdio_pio + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + character(len=*) , intent(in) :: fsurdat ! surface data file name + real(r8), intent(in), optional :: soil_depth_lowland_in + real(r8), intent(in), optional :: soil_depth_upland_in + real(r8), pointer :: fhillslope_in(:,:) ! read in - float + + type(file_desc_t) :: ncid ! netcdf id + logical :: readvar ! check whether variable on file + character(len=256) :: locfn ! local filename + integer :: ierr ! error code + integer :: c, l, g, j, ci ! indices + + real(r8) :: soil_depth_lowland + real(r8) :: soil_depth_upland + real(r8), parameter :: soil_depth_lowland_default = 8.0 + real(r8), parameter :: soil_depth_upland_default = 8.0 + character(len=*), parameter :: subname = 'SetHillslopeSoilThickness' + + !----------------------------------------------------------------------- + + if (soil_profile_method==soil_profile_from_file) then + + ! Open surface dataset to read in data below + call getfil (fsurdat, locfn, 0) + call ncd_pio_openfile (ncid, locfn, 0) + + allocate(fhillslope_in(bounds%begg:bounds%endg,max_columns_hillslope)) + call ncd_io(ncid=ncid, varname='hillslope_bedrock_depth', flag='read', data=fhillslope_in, dim1name=grlnd, readvar=readvar) + if (masterproc .and. .not. readvar) then + call endrun( 'ERROR:: soil_profile_method = "FromFile", but hillslope_bedrock not found on surface data set.'//errmsg(sourcefile, __LINE__) ) + end if + do l = bounds%begl,bounds%endl + g = lun%gridcell(l) + do c = lun%coli(l), lun%colf(l) + if (col%is_hillslope_column(c) .and. col%active(c)) then + ci = c-lun%coli(l)+1 + do j = 1,nlevsoi + if (zisoi(j-1) > zmin_bedrock) then + if (zisoi(j-1) < fhillslope_in(g,ci) & + .and. zisoi(j) >= fhillslope_in(g,ci)) then + col%nbedrock(c) = j + end if + end if + enddo + end if + enddo + enddo + deallocate(fhillslope_in) + call ncd_pio_closefile(ncid) + + else if (soil_profile_method==soil_profile_set_lowland_upland & + .or. soil_profile_method==soil_profile_linear) then + + if (present(soil_depth_lowland_in)) then + soil_depth_lowland = soil_depth_lowland_in + else + soil_depth_lowland = soil_depth_lowland_default + end if + + if (present(soil_depth_upland_in)) then + soil_depth_upland = soil_depth_upland_in + else + soil_depth_upland = soil_depth_upland_default + end if + + ! Modify hillslope soil thickness profile + call HillslopeSoilThicknessProfile(bounds,& + soil_profile_method=soil_profile_method,& + soil_depth_lowland_in=soil_depth_lowland,& + soil_depth_upland_in=soil_depth_upland) + + else if (soil_profile_method /= soil_profile_uniform .and. masterproc) then + call endrun( msg=' ERROR: unrecognized hillslope_soil_profile_method'//errMsg(sourcefile, __LINE__)) + + end if + + end subroutine SetHillslopeSoilThickness + + !----------------------------------------------------------------------- + subroutine HillslopeSoilThicknessProfile(bounds,& + soil_profile_method,soil_depth_lowland_in,soil_depth_upland_in) + ! + ! !DESCRIPTION: + ! Modify soil thickness across hillslope by changing + ! col%nbedrock + ! + ! !USES: + use LandunitType , only : lun + use GridcellType , only : grc + use ColumnType , only : col + use clm_varcon , only : zmin_bedrock, zisoi + use clm_varpar , only : nlevsoi + use spmdMod , only : masterproc + use fileutils , only : getfil + use clm_varcon , only : spval, ispval, grlnd + use ncdio_pio + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + integer, intent(in) :: soil_profile_method + real(r8), intent(in), optional :: soil_depth_lowland_in + real(r8), intent(in), optional :: soil_depth_upland_in + + integer :: c, l, g, i, j + real(r8) :: min_hill_dist, max_hill_dist + real(r8) :: m, b ! linear soil thickness slope/intercept + real(r8) :: soil_depth_col + real(r8) :: soil_depth_lowland + real(r8) :: soil_depth_upland + real(r8), parameter :: soil_depth_lowland_default = 8.0 + real(r8), parameter :: soil_depth_upland_default = 8.0 + + character(len=*), parameter :: subname = 'HillslopeSoilThicknessProfile' + + !----------------------------------------------------------------------- + + if (present(soil_depth_lowland_in)) then + soil_depth_lowland = soil_depth_lowland_in + else + soil_depth_lowland = soil_depth_lowland_default + end if + + if (present(soil_depth_upland_in)) then + soil_depth_upland = soil_depth_upland_in + else + soil_depth_upland = soil_depth_upland_default + end if + + ! Specify lowland/upland soil thicknesses separately + if (soil_profile_method == soil_profile_set_lowland_upland) then + do c = bounds%begc,bounds%endc + if (col%is_hillslope_column(c) .and. col%active(c)) then + if (col%cold(c) /= ispval) then + do j = 1,nlevsoi + if (zisoi(j-1) > zmin_bedrock) then + if (zisoi(j-1) < soil_depth_upland .and. zisoi(j) >= soil_depth_upland) then + col%nbedrock(c) = j + end if + end if + enddo + else + do j = 1,nlevsoi + if (zisoi(j-1) > zmin_bedrock) then + if (zisoi(j-1) < soil_depth_lowland .and. zisoi(j) >= soil_depth_lowland) then + col%nbedrock(c) = j + end if + end if + enddo + end if + end if + end do + ! Linear soil thickness profile + else if (soil_profile_method == soil_profile_linear) then + call HillslopeSoilThicknessProfile_linear(bounds, soil_depth_lowland, soil_depth_upland) + else if (masterproc) then + call endrun( 'ERROR:: invalid soil_profile_method.'//errmsg(sourcefile, __LINE__) ) + end if + + end subroutine HillslopeSoilThicknessProfile + + !------------------------------------------------------------------------ + subroutine HillslopeSetLowlandUplandPfts(bounds,lowland_ivt,upland_ivt) + ! + ! !DESCRIPTION: + ! Reassign patch type of each column based on whether a column + ! is identified as a lowland or an upland. + ! Assumes each column has a single pft. + ! In preparation for this reassignment of patch type, only the + ! first patch was given a non-zero weight in surfrd_hillslope + ! + ! !USES + use LandunitType , only : lun + use ColumnType , only : col + use clm_varcon , only : ispval + use clm_varpar , only : natpft_lb + use PatchType , only : patch + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + integer, intent(in) :: upland_ivt + integer, intent(in) :: lowland_ivt + ! + ! !LOCAL VARIABLES: + integer :: p,c ! indices + integer :: npatches_per_column + + !------------------------------------------------------------------------ + + do c = bounds%begc, bounds%endc + if (col%is_hillslope_column(c)) then + npatches_per_column = 0 + do p = col%patchi(c), col%patchf(c) + if (col%cold(c) == ispval) then + ! lowland + patch%itype(p) = lowland_ivt + else + ! upland + patch%itype(p) = upland_ivt + end if + ! update mxy as is done in initSubgridMod.add_patch + patch%mxy(p) = patch%itype(p) + (1 - natpft_lb) + + npatches_per_column = npatches_per_column + 1 + enddo + if ((npatches_per_column /= 1) .and. masterproc) then + call endrun( 'ERROR:: number of patches per hillslope column not equal to 1'//errmsg(sourcefile, __LINE__) ) + end if + end if + enddo + + end subroutine HillslopeSetLowlandUplandPfts + + !------------------------------------------------------------------------ + subroutine HillslopeDominantLowlandPft(bounds) + ! + ! !DESCRIPTION: + ! Reassign patch weights of each column based on each gridcell's + ! two most dominant pfts on the input dataset. + ! HillslopeTwoLargestPftIndices is called in surfrd_hillslope to + ! prepare the patch weights for this routine. + ! Assumes each column has a single pft. + ! Use largest weight for lowland, 2nd largest weight for uplands + ! + ! !USES + use LandunitType , only : lun + use ColumnType , only : col + use decompMod , only : get_clump_bounds, get_proc_clumps + use clm_varcon , only : ispval + use PatchType , only : patch + use pftconMod , only : pftcon, ndllf_evr_tmp_tree, nc3_nonarctic_grass, nc4_grass + use array_utils , only : find_k_max_indices + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + integer :: p,c ! indices + integer :: plow, phigh + integer :: max_index(1) + integer, allocatable :: max_indices(:) ! largest weight pft indices + real(r8) :: sum_wtcol, sum_wtlun, sum_wtgrc + + !------------------------------------------------------------------------ + + allocate(max_indices(2)) + do c = bounds%begc,bounds%endc + if (col%is_hillslope_column(c)) then + + ! if only one pft exists, find dominant pft index and set 2nd index to the same value + + if (size(patch%wtcol(col%patchi(c):col%patchf(c))) == 1) then + call find_k_max_indices(patch%wtcol(col%patchi(c):col%patchf(c)),1,1,max_index) + max_indices(1) = max_index(1) + (col%patchi(c) - 1) + max_indices(2) = max_indices(1) + else + call find_k_max_indices(patch%wtcol(col%patchi(c):col%patchf(c)),1,2,max_indices) + max_indices = max_indices + (col%patchi(c) - 1) + end if + + sum_wtcol = sum(patch%wtcol(col%patchi(c):col%patchf(c))) + sum_wtlun = sum(patch%wtlunit(col%patchi(c):col%patchf(c))) + sum_wtgrc = sum(patch%wtgcell(col%patchi(c):col%patchf(c))) + + patch%wtcol(col%patchi(c):col%patchf(c)) = 0._r8 + patch%wtlunit(col%patchi(c):col%patchf(c)) = 0._r8 + patch%wtgcell(col%patchi(c):col%patchf(c)) = 0._r8 + + ! Put the highest stature vegetation on the lowland column + ! non-tree and tree ; place tree on lowland + ! grass and shrub ; place shrub on lowland + ! bare soil and vegetation; place vegetation on lowland + if ((.not. pftcon%is_tree(patch%itype(max_indices(1))) .and. pftcon%is_tree(patch%itype(max_indices(2)))) & + .or. (pftcon%is_grass(patch%itype(max_indices(1))) .and. pftcon%is_shrub(patch%itype(max_indices(2)))) & + .or. (patch%itype(max_indices(1)) == 0)) then + plow = max_indices(2) + phigh = max_indices(1) + else + plow = max_indices(1) + phigh = max_indices(2) + end if + + ! Special cases (subjective) + + ! if NET/BDT assign BDT to lowland + if ((patch%itype(max_indices(1)) == ndllf_evr_tmp_tree) .and. pftcon%is_tree(patch%itype(max_indices(2)))) then + plow = max_indices(2) + phigh = max_indices(1) + end if + ! if C3/C4 assign C4 to lowland + if ((patch%itype(max_indices(1)) == nc4_grass) .and. (patch%itype(max_indices(2)) == nc3_nonarctic_grass)) then + plow = max_indices(1) + phigh = max_indices(2) + end if + if ((patch%itype(max_indices(1)) == nc3_nonarctic_grass) .and. (patch%itype(max_indices(2)) == nc4_grass)) then + plow = max_indices(2) + phigh = max_indices(1) + end if + + if (col%cold(c) == ispval) then + ! lowland column + patch%wtcol(plow) = sum_wtcol + patch%wtlunit(plow) = sum_wtlun + patch%wtgcell(plow) = sum_wtgrc + else + ! upland columns + patch%wtcol(phigh) = sum_wtcol + patch%wtlunit(phigh) = sum_wtlun + patch%wtgcell(phigh) = sum_wtgrc + end if + end if + enddo ! end loop c + deallocate(max_indices) + + end subroutine HillslopeDominantLowlandPft + + !------------------------------------------------------------------------ + subroutine HillslopePftFromFile(bounds,col_pftndx) + ! + ! !DESCRIPTION: + ! Reassign patch type using indices from surface data file + ! Assumes one patch per hillslope column + ! In preparation for this reassignment of patch type, only the + ! first patch was given a non-zero weight in surfrd_hillslope. + ! + ! !USES + use ColumnType , only : col + use PatchType , only : patch + use clm_varpar , only : natpft_lb + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + integer, intent(in) :: col_pftndx(:) + ! + ! !LOCAL VARIABLES: + integer :: p,c ! indices + integer :: npatches_per_column + + !------------------------------------------------------------------------ + + do c = bounds%begc, bounds%endc + if (col%is_hillslope_column(c)) then + ! In preparation for this re-weighting of patch type + ! only first patch was given a non-zero weight in surfrd_hillslope + npatches_per_column = 0 + do p = col%patchi(c), col%patchf(c) + patch%itype(p) = col_pftndx(c) + ! update mxy as is done in initSubgridMod.add_patch + patch%mxy(p) = patch%itype(p) + (1 - natpft_lb) + npatches_per_column = npatches_per_column + 1 + enddo + if ((npatches_per_column /= 1) .and. masterproc) then + call endrun( 'ERROR:: number of patches per hillslope column not equal to 1'//errmsg(sourcefile, __LINE__) ) + end if + end if + enddo + + end subroutine HillslopePftFromFile + + !----------------------------------------------------------------------- + subroutine HillslopeStreamOutflow(bounds, & + waterstatebulk_inst, waterfluxbulk_inst,streamflow_method) + ! + ! !DESCRIPTION: + ! Calculate discharge from stream channel + ! + ! !USES: + use LandunitType , only : lun + use GridcellType , only : grc + use ColumnType , only : col + use WaterFluxBulkType , only : waterfluxbulk_type + use WaterStateBulkType , only : waterstatebulk_type + use spmdMod , only : masterproc + use clm_varcon , only : spval, ispval, grlnd + use landunit_varcon , only : istsoil + use ncdio_pio + use clm_time_manager , only : get_step_size_real + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + integer, intent(in) :: streamflow_method + type(waterstatebulk_type), intent(inout) :: waterstatebulk_inst + type(waterfluxbulk_type), intent(inout) :: waterfluxbulk_inst + + integer :: c, l, g, i, j + integer :: nstep + real(r8) :: dtime ! land model time step (sec) + real(r8) :: cross_sectional_area ! cross sectional area of stream water (m2) + real(r8) :: stream_depth ! depth of stream water (m) + real(r8) :: hydraulic_radius ! cross sectional area divided by wetted perimeter (m) + real(r8) :: flow_velocity ! flow velocity (m/s) + real(r8) :: overbank_area ! area of water above bankfull (m2) + real(r8), parameter :: manning_roughness = 0.03_r8 ! manning roughness + real(r8), parameter :: manning_exponent = 0.667_r8 ! manning exponent + + integer, parameter :: overbank_method = 1 ! method to treat overbank stream storage; 1 = increase dynamic slope, 2 = increase flow area cross section, 3 = remove instantaneously + logical :: active_stream + character(len=*), parameter :: subname = 'HillslopeStreamOutflow' + + !----------------------------------------------------------------------- + associate( & + stream_water_volume => waterstatebulk_inst%stream_water_volume_lun , & ! Input: [real(r8) (:) ] stream water volume (m3) + volumetric_streamflow => waterfluxbulk_inst%volumetric_streamflow_lun & ! Input: [real(r8) (:) ] stream water discharge (m3/s) + ) + + ! Get time step + dtime = get_step_size_real() + + do l = bounds%begl,bounds%endl + volumetric_streamflow(l) = 0._r8 + + ! Check for vegetated landunits having initialized stream channel properties + active_stream = .false. + if (lun%itype(l) == istsoil .and. & + lun%stream_channel_length(l) > 0._r8 .and. & + lun%stream_channel_width(l) > 0._r8) then + active_stream = .true. + end if + + if (lun%active(l) .and. active_stream) then + ! Streamflow calculated from Manning equation + if (streamflow_method == streamflow_manning) then + cross_sectional_area = stream_water_volume(l) & + /lun%stream_channel_length(l) + stream_depth = cross_sectional_area & + /lun%stream_channel_width(l) + hydraulic_radius = cross_sectional_area & + /(lun%stream_channel_width(l) + 2*stream_depth) + + if (hydraulic_radius <= 0._r8) then + volumetric_streamflow(l) = 0._r8 + else + flow_velocity = (hydraulic_radius)**manning_exponent & + * sqrt(lun%stream_channel_slope(l)) & + / manning_roughness + ! overbank flow + if (stream_depth > lun%stream_channel_depth(l)) then + if (overbank_method == 1) then + ! try increasing dynamic slope + volumetric_streamflow(l) = cross_sectional_area * flow_velocity & + *(stream_depth/lun%stream_channel_depth(l)) + else if (overbank_method == 2) then + ! try increasing flow area cross section + overbank_area = (stream_depth -lun%stream_channel_depth(l)) * 30._r8 * lun%stream_channel_width(l) + volumetric_streamflow(l) = (cross_sectional_area + overbank_area) * flow_velocity + else if (overbank_method == 3) then + ! try removing all overbank flow instantly + volumetric_streamflow(l) = cross_sectional_area * flow_velocity & + + (stream_depth-lun%stream_channel_depth(l)) & + *lun%stream_channel_width(l)*lun%stream_channel_length(l)/dtime + else + call endrun( 'ERROR:: invalid overbank_method.'//errmsg(sourcefile, __LINE__) ) + end if + + else + volumetric_streamflow(l) = cross_sectional_area * flow_velocity + end if + + ! scale streamflow by number of channel reaches + volumetric_streamflow(l) = volumetric_streamflow(l) * lun%stream_channel_number(l) + + volumetric_streamflow(l) = max(0._r8,min(volumetric_streamflow(l),stream_water_volume(l)/dtime)) + end if + else + call endrun( 'ERROR:: invalid streamflow_method'//errmsg(sourcefile, __LINE__) ) + end if + end if ! end of istsoil + enddo ! end of loop over landunits + + end associate + + end subroutine HillslopeStreamOutflow + + !----------------------------------------------------------------------- + subroutine HillslopeUpdateStreamWater(bounds, waterstatebulk_inst, & + waterfluxbulk_inst,waterdiagnosticbulk_inst) + ! + ! !DESCRIPTION: + ! Calculate discharge from stream channel + ! + ! !USES: + use LandunitType , only : lun + use GridcellType , only : grc + use ColumnType , only : col + use WaterFluxBulkType , only : waterfluxbulk_type + use WaterStateBulkType , only : waterstatebulk_type + use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type + use spmdMod , only : masterproc + use clm_varcon , only : spval, ispval, grlnd + use landunit_varcon , only : istsoil + use clm_time_manager, only : get_step_size_real + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + type(waterstatebulk_type), intent(inout) :: waterstatebulk_inst + type(waterfluxbulk_type), intent(inout) :: waterfluxbulk_inst + type(waterdiagnosticbulk_type), intent(inout) :: waterdiagnosticbulk_inst + + integer :: c, l, g, i, j + real(r8) :: qflx_surf_vol ! volumetric surface runoff (m3/s) + real(r8) :: qflx_drain_perched_vol ! volumetric perched saturated drainage (m3/s) + real(r8) :: qflx_drain_vol ! volumetric saturated drainage (m3/s) + real(r8) :: dtime ! land model time step (sec) + logical :: active_stream + + character(len=*), parameter :: subname = 'HillslopeUpdateStreamWater' + + !----------------------------------------------------------------------- + associate( & + stream_water_volume => waterstatebulk_inst%stream_water_volume_lun, & ! Input/Output: [real(r8) (:) ] stream water volume (m3) + volumetric_streamflow => waterfluxbulk_inst%volumetric_streamflow_lun,& ! Input: [real(r8) (:) ] stream water discharge (m3/s) + qflx_drain => waterfluxbulk_inst%qflx_drain_col, & ! Input: [real(r8) (:) ] column level sub-surface runoff (mm H2O /s) + qflx_drain_perched => waterfluxbulk_inst%qflx_drain_perched_col, & ! Input: [real(r8) (:) ] column level sub-surface runoff (mm H2O /s) + qflx_surf => waterfluxbulk_inst%qflx_surf_col, & ! Input: [real(r8) (:) ] total surface runoff (mm H2O /s) + stream_water_depth => waterdiagnosticbulk_inst%stream_water_depth_lun & ! Output: [real(r8) (:) ] stream water depth (m) + ) + + ! Get time step + dtime = get_step_size_real() + + do l = bounds%begl,bounds%endl + + ! Check for vegetated landunits having initialized stream channel properties + active_stream = .false. + if (lun%itype(l) == istsoil .and. & + lun%stream_channel_length(l) > 0._r8 .and. & + lun%stream_channel_width(l) > 0._r8) then + active_stream = .true. + end if + + if (lun%active(l) .and. active_stream) then + g = lun%gridcell(l) + ! the drainage terms are 'net' quantities, so summing over + ! all columns in a hillslope is equivalent to the outflow + ! from the lowland column + do c = lun%coli(l), lun%colf(l) + if (col%is_hillslope_column(c) .and. col%active(c)) then + qflx_surf_vol = qflx_surf(c)*1.e-3_r8 & + *(grc%area(g)*1.e6_r8*col%wtgcell(c)) + qflx_drain_perched_vol = qflx_drain_perched(c)*1.e-3_r8 & + *(grc%area(g)*1.e6_r8*col%wtgcell(c)) + qflx_drain_vol = qflx_drain(c)*1.e-3_r8 & + *(grc%area(g)*1.e6_r8*col%wtgcell(c)) + + stream_water_volume(l) = stream_water_volume(l) & + + (qflx_drain_perched_vol & + + qflx_drain_vol + qflx_surf_vol) * dtime + end if + enddo + stream_water_volume(l) = stream_water_volume(l) & + - volumetric_streamflow(l) * dtime + + ! account for negative drainage (via searchforwater in soilhydrology) + if (stream_water_volume(l) < 0._r8) then + volumetric_streamflow(l) = volumetric_streamflow(l) + stream_water_volume(l)/dtime + stream_water_volume(l) = 0._r8 + end if + + stream_water_depth(l) = stream_water_volume(l) & + /lun%stream_channel_length(l) & + /lun%stream_channel_width(l) + + end if + enddo + + end associate + + end subroutine HillslopeUpdateStreamWater + +end module HillslopeHydrologyMod diff --git a/src/biogeophys/HillslopeHydrologyUtilsMod.F90 b/src/biogeophys/HillslopeHydrologyUtilsMod.F90 new file mode 100644 index 0000000000..299971055c --- /dev/null +++ b/src/biogeophys/HillslopeHydrologyUtilsMod.F90 @@ -0,0 +1,85 @@ +module HillslopeHydrologyUtilsMod + + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! Utilities used in HillslopeHydrologyMod + ! + ! !USES: +#include "shr_assert.h" + use decompMod , only : bounds_type + use shr_kind_mod , only : r8 => shr_kind_r8 + use shr_log_mod , only : errMsg => shr_log_errMsg + use spmdMod , only : masterproc, iam + use abortutils , only : endrun + use clm_varctl , only : iulog + + ! !PUBLIC TYPES: + implicit none + + private + save + + real(r8), parameter :: toosmall_distance_default = 1e-6 + + ! !PUBLIC MEMBER FUNCTIONS: + public HillslopeSoilThicknessProfile_linear + +contains + + !------------------------------------------------------------------------ + subroutine HillslopeSoilThicknessProfile_linear(bounds, soil_depth_lowland, soil_depth_upland, toosmall_distance_in) + ! + ! !DESCRIPTION: + ! Modify soil thickness across hillslope by changing + ! nbedrock according to the "Linear" method + ! + ! !USES: + use LandunitType , only : lun + use ColumnType , only : col + use clm_varpar , only : nlevsoi + use clm_varcon , only : zisoi + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + real(r8), intent(in) :: soil_depth_lowland, soil_depth_upland + real(r8), intent(in), optional :: toosmall_distance_in + ! + ! !LOCAL VARIABLES + real(r8) :: min_hill_dist, max_hill_dist + real(r8) :: toosmall_distance + real(r8) :: soil_depth_col + real(r8) :: m, b + integer :: c, j, l + + if (present(toosmall_distance_in)) then + toosmall_distance = toosmall_distance_in + else + toosmall_distance = toosmall_distance_default + end if + + do l = bounds%begl,bounds%endl + min_hill_dist = minval(col%hill_distance(lun%coli(l):lun%colf(l))) + max_hill_dist = maxval(col%hill_distance(lun%coli(l):lun%colf(l))) + + if (abs(max_hill_dist - min_hill_dist) > toosmall_distance) then + m = (soil_depth_lowland - soil_depth_upland)/ & + (max_hill_dist - min_hill_dist) + else + m = 0._r8 + end if + b = soil_depth_upland + + do c = lun%coli(l), lun%colf(l) + if (col%is_hillslope_column(c) .and. col%active(c)) then + soil_depth_col = m*(max_hill_dist - col%hill_distance(c)) + b + do j = 1,nlevsoi + if ((zisoi(j-1) < soil_depth_col) .and. (zisoi(j) >= soil_depth_col)) then + col%nbedrock(c) = j + exit + end if + enddo + end if + enddo + enddo + end subroutine HillslopeSoilThicknessProfile_linear +end module HillslopeHydrologyUtilsMod \ No newline at end of file diff --git a/src/biogeophys/HydrologyDrainageMod.F90 b/src/biogeophys/HydrologyDrainageMod.F90 index 31ffc817a0..ce5b78e3ff 100644 --- a/src/biogeophys/HydrologyDrainageMod.F90 +++ b/src/biogeophys/HydrologyDrainageMod.F90 @@ -40,7 +40,7 @@ subroutine HydrologyDrainage(bounds, & num_hydrologyc, filter_hydrologyc, & num_urbanc, filter_urbanc, & num_do_smb_c, filter_do_smb_c, & - atm2lnd_inst, glc2lnd_inst, temperature_inst, & + glc2lnd_inst, temperature_inst, & soilhydrology_inst, soilstate_inst, waterstatebulk_inst, & waterdiagnosticbulk_inst, waterbalancebulk_inst, waterfluxbulk_inst, & wateratm2lndbulk_inst, glacier_smb_inst) @@ -52,11 +52,12 @@ subroutine HydrologyDrainage(bounds, & use landunit_varcon , only : istwet, istsoil, istice, istcrop use column_varcon , only : icol_roof, icol_road_imperv, icol_road_perv, icol_sunwall, icol_shadewall use clm_varcon , only : denh2o, denice - use clm_varctl , only : use_vichydro + use clm_varctl , only : use_vichydro, use_hillslope, use_hillslope_routing use clm_varpar , only : nlevgrnd, nlevurb use clm_time_manager , only : get_step_size_real, get_nstep - use SoilHydrologyMod , only : CLMVICMap, Drainage, PerchedLateralFlow, LateralFlowPowerLaw + use SoilHydrologyMod , only : CLMVICMap, Drainage, PerchedLateralFlow, SubsurfaceLateralFlow use SoilWaterMovementMod , only : use_aquifer_layer + use HillslopeHydrologyMod, only : streamflow_manning, HillslopeStreamOutflow, HillslopeUpdateStreamWater ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -66,18 +67,18 @@ subroutine HydrologyDrainage(bounds, & integer , intent(in) :: filter_hydrologyc(:) ! column filter for soil points integer , intent(in) :: num_urbanc ! number of column urban points in column filter integer , intent(in) :: filter_urbanc(:) ! column filter for urban points - integer , intent(in) :: num_do_smb_c ! number of columns in which SMB is calculated, in column filter - integer , intent(in) :: filter_do_smb_c(:) ! column filter for bare landwhere SMB is calculated - type(atm2lnd_type) , intent(in) :: atm2lnd_inst + integer , intent(in) :: num_do_smb_c ! number of bareland columns in which SMB is calculated, in column filter + integer , intent(in) :: filter_do_smb_c(:) ! column filter for bare land SMB columns + type(glc2lnd_type) , intent(in) :: glc2lnd_inst type(temperature_type) , intent(in) :: temperature_inst type(soilhydrology_type) , intent(inout) :: soilhydrology_inst type(soilstate_type) , intent(inout) :: soilstate_inst type(waterstatebulk_type) , intent(inout) :: waterstatebulk_inst type(waterdiagnosticbulk_type) , intent(inout) :: waterdiagnosticbulk_inst - type(waterbalance_type) , intent(inout) :: waterbalancebulk_inst + type(waterbalance_type) , intent(inout) :: waterbalancebulk_inst type(waterfluxbulk_type) , intent(inout) :: waterfluxbulk_inst - type(wateratm2lndbulk_type) , intent(inout) :: wateratm2lndbulk_inst + type(wateratm2lndbulk_type) , intent(inout) :: wateratm2lndbulk_inst type(glacier_smb_type) , intent(in) :: glacier_smb_inst ! ! !LOCAL VARIABLES: @@ -112,6 +113,7 @@ subroutine HydrologyDrainage(bounds, & qflx_surf => waterfluxbulk_inst%qflx_surf_col , & ! surface runoff (mm H2O /s) qflx_infl => waterfluxbulk_inst%qflx_infl_col , & ! infiltration (mm H2O /s) qflx_qrgwl => waterfluxbulk_inst%qflx_qrgwl_col , & ! qflx_surf at glaciers, wetlands, lakes + qflx_latflow_out => waterfluxbulk_inst%qflx_latflow_out_col , & ! lateral subsurface flow qflx_runoff => waterfluxbulk_inst%qflx_runoff_col , & ! total runoff (qflx_drain+qflx_surf+qflx_qrgwl) (mm H2O /s) qflx_runoff_u => waterfluxbulk_inst%qflx_runoff_u_col , & ! Urban total runoff (qflx_drain+qflx_surf) (mm H2O /s) qflx_runoff_r => waterfluxbulk_inst%qflx_runoff_r_col , & ! Rural total runoff (qflx_drain+qflx_surf+qflx_qrgwl) (mm H2O /s) @@ -135,16 +137,26 @@ subroutine HydrologyDrainage(bounds, & else call PerchedLateralFlow(bounds, num_hydrologyc, filter_hydrologyc, & - num_urbanc, filter_urbanc,& - soilhydrology_inst, soilstate_inst, & - waterstatebulk_inst, waterfluxbulk_inst) - + soilhydrology_inst, soilstate_inst, & + waterstatebulk_inst, waterfluxbulk_inst, & + wateratm2lndbulk_inst) + call SubsurfaceLateralFlow(bounds, & + num_hydrologyc, filter_hydrologyc, & + num_urbanc, filter_urbanc,& + soilhydrology_inst, soilstate_inst, & + waterstatebulk_inst, waterfluxbulk_inst, & + wateratm2lndbulk_inst) + + if (use_hillslope_routing) then + call HillslopeStreamOutflow(bounds,& + waterstatebulk_inst, waterfluxbulk_inst, & + streamflow_method=streamflow_manning) + + call HillslopeUpdateStreamWater(bounds, & + waterstatebulk_inst, waterfluxbulk_inst, & + waterdiagnosticbulk_inst) + endif - call LateralFlowPowerLaw(bounds, num_hydrologyc, filter_hydrologyc, & - num_urbanc, filter_urbanc,& - soilhydrology_inst, soilstate_inst, & - waterstatebulk_inst, waterfluxbulk_inst) - endif do j = 1, nlevgrnd @@ -182,6 +194,7 @@ subroutine HydrologyDrainage(bounds, & if (lun%itype(l)==istwet .or. lun%itype(l)==istice) then + qflx_latflow_out(c) = 0._r8 qflx_drain(c) = 0._r8 qflx_drain_perched(c) = 0._r8 qflx_surf(c) = 0._r8 diff --git a/src/biogeophys/SaturatedExcessRunoffMod.F90 b/src/biogeophys/SaturatedExcessRunoffMod.F90 index 309d251460..5643a95394 100644 --- a/src/biogeophys/SaturatedExcessRunoffMod.F90 +++ b/src/biogeophys/SaturatedExcessRunoffMod.F90 @@ -233,10 +233,8 @@ subroutine SaturatedExcessRunoff (this, bounds, num_hydrologyc, filter_hydrology qflx_sat_excess_surf => waterfluxbulk_inst%qflx_sat_excess_surf_col, & ! Output: [real(r8) (:) ] surface runoff due to saturated surface (mm H2O /s) qflx_floodc => waterfluxbulk_inst%qflx_floodc_col , & ! Input: [real(r8) (:) ] column flux of flood water from RTM - qflx_rain_plus_snomelt => waterfluxbulk_inst%qflx_rain_plus_snomelt_col , & ! Input: [real(r8) (:) ] rain plus snow melt falling on the soil (mm/s) + qflx_rain_plus_snomelt => waterfluxbulk_inst%qflx_rain_plus_snomelt_col & ! Input: [real(r8) (:) ] rain plus snow melt falling on the soil (mm/s) - origflag => soilhydrology_inst%origflag , & ! Input: logical - fracice => soilhydrology_inst%fracice_col & ! Input: [real(r8) (:,:) ] fractional impermeability (-) ) ! ------------------------------------------------------------------------ @@ -275,29 +273,14 @@ subroutine SaturatedExcessRunoff (this, bounds, num_hydrologyc, filter_hydrology ! qflx_rain_plus_snomelt in control ! ------------------------------------------------------------------------ - if (origflag == 1) then - if (this%fsat_method == FSAT_METHOD_VIC) then - ! NOTE(wjs, 2017-07-12) I'm not sure if it's the VIC fsat method per se that - ! is incompatible with origflag, or some other aspect of VIC: The original - ! check was for origflag == 1 and use_vichydro, which also appears in error - ! checks elsewhere. - call endrun(msg="VICHYDRO is not available for origflag=1"//errmsg(sourcefile, __LINE__)) - end if - do fc = 1, num_hydrologyc - c = filter_hydrologyc(fc) - fcov(c) = (1._r8 - fracice(c,1)) * fsat(c) + fracice(c,1) - qflx_sat_excess_surf(c) = fcov(c) * qflx_rain_plus_snomelt(c) - end do - else - do fc = 1, num_hydrologyc - c = filter_hydrologyc(fc) - ! only send fast runoff directly to streams - qflx_sat_excess_surf(c) = fsat(c) * qflx_rain_plus_snomelt(c) - - ! Set fcov just to have it on the history file - fcov(c) = fsat(c) - end do - end if + do fc = 1, num_hydrologyc + c = filter_hydrologyc(fc) + ! only send fast runoff directly to streams + qflx_sat_excess_surf(c) = fsat(c) * qflx_rain_plus_snomelt(c) + + ! Set fcov just to have it on the history file + fcov(c) = fsat(c) + end do ! ------------------------------------------------------------------------ ! For urban columns, send flood water flux to runoff diff --git a/src/biogeophys/SnowSnicarMod.F90 b/src/biogeophys/SnowSnicarMod.F90 index 5bc1c61edb..f6d41bd6a0 100644 --- a/src/biogeophys/SnowSnicarMod.F90 +++ b/src/biogeophys/SnowSnicarMod.F90 @@ -81,9 +81,6 @@ module SnowSnicarMod real(r8), parameter :: tim_cns_dst_rmv = 2.2E-8_r8 ! time constant for removal of dust in snow on sea-ice ! [s-1] (50% mass removal/year) - ! scaling of the snow aging rate (tuning option): - logical :: flg_snoage_scl = .false. ! flag for scaling the snow aging rate by some arbitrary factor - ! snow and aerosol Mie parameters: ! (arrays declared here, but are set in iniTimeConst) ! (idx_Mie_snw_mx is number of snow radii with defined parameters (i.e. from 30um to 1500um)) @@ -1656,13 +1653,10 @@ subroutine SnowAge_grain(bounds, & dr = dr + dr_wet ! - !********** 3. SNOWAGE SCALING (TURNED OFF BY DEFAULT) ************* + !********** 3. SNOWAGE SCALING ************* ! ! Multiply rate of change of effective radius by some constant, xdrdt - if (flg_snoage_scl) then - dr = dr*params_inst%xdrdt - endif - + dr = dr*params_inst%xdrdt ! !********** 4. INCREMENT EFFECTIVE RADIUS, ACCOUNTING FOR: *********** diff --git a/src/biogeophys/SoilHydrologyMod.F90 b/src/biogeophys/SoilHydrologyMod.F90 index 4bc6a784de..5a4aa50f6e 100644 --- a/src/biogeophys/SoilHydrologyMod.F90 +++ b/src/biogeophys/SoilHydrologyMod.F90 @@ -10,6 +10,7 @@ module SoilHydrologyMod use abortutils , only : endrun use decompMod , only : bounds_type, subgrid_level_column use clm_varctl , only : iulog, use_vichydro + use clm_varcon , only : ispval use clm_varcon , only : denh2o, denice, rpi use clm_varcon , only : pondmx_urban use clm_varpar , only : nlevsoi, nlevgrnd, nlayer, nlayert @@ -31,7 +32,8 @@ module SoilHydrologyMod use TemperatureType , only : temperature_type use LandunitType , only : lun use ColumnType , only : column_type, col - use PatchType , only : patch + use PatchType , only : patch + ! ! !PUBLIC TYPES: implicit none @@ -51,7 +53,7 @@ module SoilHydrologyMod public :: PerchedWaterTable ! Calculate perched water table public :: PerchedLateralFlow ! Calculate lateral flow from perched saturated zone public :: ThetaBasedWaterTable ! Calculate water table from soil moisture state - public :: LateralFlowPowerLaw ! Calculate lateral flow based on power law drainage function + public :: SubsurfaceLateralFlow ! Calculate subsurface lateral flow from saturated zone public :: RenewCondensation ! Misc. corrections public :: CalcIrrigWithdrawals ! Calculate irrigation withdrawals from groundwater by layer public :: WithdrawGroundwaterIrrigation ! Remove groundwater irrigation from unconfined and confined aquifers @@ -63,17 +65,112 @@ module SoilHydrologyMod real(r8) :: perched_baseflow_scalar ! Scalar multiplier for perched base flow rate (kg/m2/s) real(r8) :: e_ice ! Soil ice impedance factor (unitless) end type params_type - type(params_type), private :: params_inst + type(params_type), public :: params_inst !----------------------------------------------------------------------- real(r8), private :: baseflow_scalar = 1.e-2_r8 real(r8), parameter :: tolerance = 1.e-12_r8 ! tolerance for checking whether sublimation is greater than ice in top soil layer + integer, private :: head_gradient_method ! Method for calculating hillslope saturated head gradient + integer, private :: transmissivity_method ! Method for calculating transmissivity of hillslope columns + + ! Head gradient methods + integer, parameter, private :: kinematic = 0 + integer, parameter, private :: darcy = 1 + ! Transmissivity methods + integer, parameter, private :: uniform_transmissivity = 0 + integer, parameter, private :: layersum = 1 + character(len=*), parameter, private :: sourcefile = & __FILE__ contains + !----------------------------------------------------------------------- + subroutine hillslope_hydrology_ReadNML(NLFilename) + ! + ! DESCRIPTION + ! read in hillslope hydrology namelist variables related to + ! subsurface lateral flow + ! + ! !USES: + use abortutils , only : endrun + use fileutils , only : getavu, relavu + use spmdMod , only : mpicom, masterproc + use shr_mpi_mod , only : shr_mpi_bcast + use clm_varctl , only : iulog + use clm_nlUtilsMod , only : find_nlgroup_name + + ! !ARGUMENTS: + implicit none + character(len=*), intent(in) :: NLFilename ! Namelist filename + !-------------------------------------------------------------------- + integer :: nu_nml ! unit for namelist file + integer :: nml_error ! namelist i/o error flag + character(len=*), parameter :: nmlname = 'hillslope_hydrology_inparm' + character(*), parameter :: subName = "('hillslope_hydrology_ReadNML')" + character(len=50) :: hillslope_head_gradient_method = 'Darcy' ! head gradient method string + character(len=50) :: hillslope_transmissivity_method = 'LayerSum' ! transmissivity method string + !----------------------------------------------------------------------- + +! MUST agree with name in namelist and read statement + namelist /hillslope_hydrology_inparm/ & + hillslope_head_gradient_method, & + hillslope_transmissivity_method + + ! Default values for namelist + head_gradient_method = darcy + transmissivity_method = layersum + + ! Read hillslope hydrology namelist + if (masterproc) then + nu_nml = getavu() + open( nu_nml, file=trim(NLFilename), status='old', iostat=nml_error ) + call find_nlgroup_name(nu_nml, 'hillslope_hydrology_inparm', status=nml_error) + if (nml_error == 0) then + read(nu_nml, nml=hillslope_hydrology_inparm,iostat=nml_error) + if (nml_error /= 0) then + call endrun(subname // ':: ERROR reading hillslope hydrology namelist') + end if + else + call endrun(subname // ':: ERROR reading hillslope hydrology namelist') + end if + close(nu_nml) + call relavu( nu_nml ) + + ! Convert namelist strings to numerical values + if ( trim(hillslope_head_gradient_method) == 'Kinematic' ) then + head_gradient_method = kinematic + else if ( trim(hillslope_head_gradient_method) == 'Darcy' ) then + head_gradient_method = darcy + else + call endrun(msg="ERROR bad value for hillslope_head_gradient_method in "//nmlname//"namelist"//errmsg(sourcefile, __LINE__)) + end if + + if ( trim(hillslope_transmissivity_method) == 'Uniform' ) then + transmissivity_method = uniform_transmissivity + else if ( trim(hillslope_transmissivity_method) == 'LayerSum') then + transmissivity_method = layersum + else + call endrun(msg="ERROR bad value for hillslope_transmissivity_method in "//nmlname//"namelist"//errmsg(sourcefile, __LINE__)) + end if + + endif + + call shr_mpi_bcast(head_gradient_method, mpicom) + call shr_mpi_bcast(transmissivity_method, mpicom) + + if (masterproc) then + + write(iulog,*) ' ' + write(iulog,*) 'hillslope_hydrology lateral flow settings:' + write(iulog,*) ' hillslope_head_gradient_method = ',hillslope_head_gradient_method + write(iulog,*) ' hillslope_transmissivity_method = ',hillslope_transmissivity_method + + endif + + end subroutine hillslope_hydrology_ReadNML + !----------------------------------------------------------------------- subroutine readParams( ncid ) ! @@ -157,6 +254,8 @@ subroutine soilHydReadNML( NLFilename ) end subroutine soilhydReadNML + + !----------------------------------------------------------------------- subroutine SetSoilWaterFractions(bounds, num_hydrologyc, filter_hydrologyc, & soilhydrology_inst, soilstate_inst, waterstatebulk_inst) @@ -193,10 +292,7 @@ subroutine SetSoilWaterFractions(bounds, num_hydrologyc, filter_hydrologyc, & 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) (:,:) ] - fracice => soilhydrology_inst%fracice_col & ! Output: [real(r8) (:,:) ] fractional impermeability (-) + icefrac => soilhydrology_inst%icefrac_col & ! Output: [real(r8) (:,:) ] ) do j = 1,nlevsoi @@ -210,15 +306,6 @@ subroutine SetSoilWaterFractions(bounds, num_hydrologyc, filter_hydrologyc, & 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)) - ! fracice is only used in code with origflag == 1. For this calculation, we use - ! the version of icefrac that was used in this original hydrology code. - if (h2osoi_ice(c,j) == 0._r8) then - ! Avoid possible divide by zero (in case h2osoi_liq(c,j) is also 0) - icefrac_orig = 0._r8 - else - icefrac_orig = min(1._r8,h2osoi_ice(c,j)/(h2osoi_ice(c,j)+h2osoi_liq(c,j))) - end if - fracice(c,j) = max(0._r8,exp(-3._r8*(1._r8-icefrac_orig))- exp(-3._r8))/(1.0_r8-exp(-3._r8)) end do end do @@ -601,7 +688,6 @@ subroutine WaterTable(bounds, num_hydrologyc, filter_hydrologyc, & real(r8) :: xs(bounds%begc:bounds%endc) ! water needed to bring soil moisture to watmin (mm) real(r8) :: dzmm(bounds%begc:bounds%endc,1:nlevsoi) ! layer thickness (mm) integer :: jwt(bounds%begc:bounds%endc) ! index of the soil layer right above the water table (-) - real(r8) :: rsub_bot(bounds%begc:bounds%endc) ! subsurface runoff - bottom drainage (mm/s) real(r8) :: rsub_top(bounds%begc:bounds%endc) ! subsurface runoff - topographic control (mm/s) real(r8) :: xsi(bounds%begc:bounds%endc) ! excess soil water above saturation at layer i (mm) real(r8) :: rous ! aquifer yield (-) @@ -610,7 +696,6 @@ subroutine WaterTable(bounds, num_hydrologyc, filter_hydrologyc, & real(r8) :: s_node ! soil wetness (-) real(r8) :: dzsum ! summation of dzmm of layers below water table (mm) real(r8) :: icefracsum ! summation of icefrac*dzmm of layers below water table (-) - real(r8) :: fracice_rsub(bounds%begc:bounds%endc) ! fractional impermeability of soil layers (-) real(r8) :: ka ! hydraulic conductivity of the aquifer (mm/s) real(r8) :: available_h2osoi_liq ! available soil liquid water in a layer real(r8) :: imped @@ -657,7 +742,6 @@ subroutine WaterTable(bounds, num_hydrologyc, filter_hydrologyc, & frost_table => soilhydrology_inst%frost_table_col , & ! Output: [real(r8) (:) ] frost table depth (m) wa => waterstatebulk_inst%wa_col , & ! Output: [real(r8) (:) ] water in the unconfined aquifer (mm) qcharge => soilhydrology_inst%qcharge_col , & ! Input: [real(r8) (:) ] aquifer recharge rate (mm/s) - origflag => soilhydrology_inst%origflag , & ! Input: logical qflx_drain => waterfluxbulk_inst%qflx_drain_col , & ! Output: [real(r8) (:) ] sub-surface runoff (mm H2O /s) qflx_drain_perched => waterfluxbulk_inst%qflx_drain_perched_col , & ! Output: [real(r8) (:) ] perched wt sub-surface runoff (mm H2O /s) @@ -792,8 +876,7 @@ subroutine WaterTable(bounds, num_hydrologyc, filter_hydrologyc, & !=================== water table above frost table ============================= ! if water table is above frost table, do not use topmodel baseflow formulation - if (zwt(c) < frost_table(c) .and. t_soisno(c,k_frz) <= tfrz & - .and. origflag == 0) then + if (zwt(c) < frost_table(c) .and. t_soisno(c,k_frz) <= tfrz) then else !=================== water table below frost table ============================= !-- compute possible perched water table *and* groundwater table afterwards @@ -865,7 +948,6 @@ subroutine Drainage(bounds, num_hydrologyc, filter_hydrologyc, num_urbanc, filte real(r8) :: xs(bounds%begc:bounds%endc) ! water needed to bring soil moisture to watmin (mm) real(r8) :: dzmm(bounds%begc:bounds%endc,1:nlevsoi) ! layer thickness (mm) integer :: jwt(bounds%begc:bounds%endc) ! index of the soil layer right above the water table (-) - real(r8) :: rsub_bot(bounds%begc:bounds%endc) ! subsurface runoff - bottom drainage (mm/s) real(r8) :: rsub_top(bounds%begc:bounds%endc) ! subsurface runoff - topographic control (mm/s) real(r8) :: fff(bounds%begc:bounds%endc) ! decay factor (m-1) real(r8) :: xsi(bounds%begc:bounds%endc) ! excess soil water above saturation at layer i (mm) @@ -880,7 +962,6 @@ subroutine Drainage(bounds, num_hydrologyc, filter_hydrologyc, num_urbanc, filte real(r8) :: s_node ! soil wetness (-) real(r8) :: dzsum ! summation of dzmm of layers below water table (mm) real(r8) :: icefracsum ! summation of icefrac*dzmm of layers below water table (-) - real(r8) :: fracice_rsub(bounds%begc:bounds%endc) ! fractional impermeability of soil layers (-) real(r8) :: ka ! hydraulic conductivity of the aquifer (mm/s) real(r8) :: dza ! fff*(zwt-z(jwt)) (-) real(r8) :: available_h2osoi_liq ! available soil liquid water in a layer @@ -943,7 +1024,6 @@ subroutine Drainage(bounds, num_hydrologyc, filter_hydrologyc, num_urbanc, filte wa => waterstatebulk_inst%wa_col , & ! Input: [real(r8) (:) ] water in the unconfined aquifer (mm) ice => soilhydrology_inst%ice_col , & ! Input: [real(r8) (:,:) ] soil layer moisture (mm) qcharge => soilhydrology_inst%qcharge_col , & ! Input: [real(r8) (:) ] aquifer recharge rate (mm/s) - origflag => soilhydrology_inst%origflag , & ! Input: logical h2osfcflag => soilhydrology_inst%h2osfcflag , & ! Input: integer qflx_snwcp_liq => waterfluxbulk_inst%qflx_snwcp_liq_col , & ! Output: [real(r8) (:) ] excess liquid h2o due to snow capping (outgoing) (mm H2O /s) [+] @@ -981,11 +1061,8 @@ subroutine Drainage(bounds, num_hydrologyc, filter_hydrologyc, num_urbanc, filte do fc = 1, num_hydrologyc c = filter_hydrologyc(fc) qflx_drain(c) = 0._r8 - rsub_bot(c) = 0._r8 qflx_rsub_sat(c) = 0._r8 rsub_top(c) = 0._r8 - fracice_rsub(c) = 0._r8 - end do ! The layer index of the first unsaturated layer, i.e., the layer right above @@ -1039,8 +1116,7 @@ subroutine Drainage(bounds, num_hydrologyc, filter_hydrologyc, num_urbanc, filte !=================== water table above frost table ============================= ! if water table is above frost table, do not use topmodel baseflow formulation - if (zwt(c) < frost_table(c) .and. t_soisno(c,k_frz) <= tfrz & - .and. origflag == 0) then + if (zwt(c) < frost_table(c) .and. t_soisno(c,k_frz) <= tfrz) then ! compute drainage from perched saturated region wtsub = 0._r8 q_perch = 0._r8 @@ -1130,9 +1206,6 @@ subroutine Drainage(bounds, num_hydrologyc, filter_hydrologyc, num_urbanc, filte qflx_drain_perched(c) = q_perch_max * q_perch & *(frost_table(c) - zwt_perched(c)) - ! no perched water table drainage if using original formulation - if(origflag == 1) qflx_drain_perched(c) = 0._r8 - ! remove drainage from perched saturated layers rsub_top_tot = - qflx_drain_perched(c) * dtime do k = k_perch+1, k_frz @@ -1168,25 +1241,15 @@ subroutine Drainage(bounds, num_hydrologyc, filter_hydrologyc, num_urbanc, filte icefracsum = icefracsum + icefrac(c,j) * dzmm(c,j) end do ! add ice impedance factor to baseflow - if(origflag == 1) then - if (use_vichydro) then - call endrun(msg="VICHYDRO is not available for origflag=1"//errmsg(sourcefile, __LINE__)) - else - fracice_rsub(c) = max(0._r8,exp(-3._r8*(1._r8-(icefracsum/dzsum))) & - - exp(-3._r8))/(1.0_r8-exp(-3._r8)) - imped=(1._r8 - fracice_rsub(c)) - rsub_top_max = 5.5e-3_r8 - end if + if (use_vichydro) then + imped=10._r8**(-params_inst%e_ice*min(1.0_r8,ice(c,nlayer)/max_moist(c,nlayer))) + dsmax_tmp(c) = Dsmax(c) * dtime/ secspday !mm/day->mm/dtime + rsub_top_max = dsmax_tmp(c) else - if (use_vichydro) then - imped=10._r8**(-params_inst%e_ice*min(1.0_r8,ice(c,nlayer)/max_moist(c,nlayer))) - dsmax_tmp(c) = Dsmax(c) * dtime/ secspday !mm/day->mm/dtime - rsub_top_max = dsmax_tmp(c) - else - imped=10._r8**(-params_inst%e_ice*(icefracsum/dzsum)) - rsub_top_max = 10._r8 * sin((rpi/180.) * col%topo_slope(c)) - end if - endif + imped=10._r8**(-params_inst%e_ice*(icefracsum/dzsum)) + rsub_top_max = 10._r8 * sin((rpi/180.) * col%topo_slope(c)) + end if + if (use_vichydro) then ! ARNO model for the bottom soil layer (based on bottom soil layer ! moisture from previous time step @@ -1525,7 +1588,7 @@ subroutine PerchedWaterTable(bounds, num_hydrologyc, filter_hydrologyc, & ! Calculate watertable, considering aquifer recharge but no drainage. ! ! !USES: - use clm_varcon , only : pondmx, tfrz, watmin,denice,denh2o + use clm_varcon , only : tfrz, denice, denh2o use column_varcon , only : icol_roof, icol_road_imperv ! ! !ARGUMENTS: @@ -1537,19 +1600,15 @@ subroutine PerchedWaterTable(bounds, num_hydrologyc, filter_hydrologyc, & type(soilhydrology_type) , intent(inout) :: soilhydrology_inst type(soilstate_type) , intent(in) :: soilstate_inst type(temperature_type) , intent(in) :: temperature_inst - type(waterstatebulk_type) , intent(inout) :: waterstatebulk_inst - type(waterfluxbulk_type) , intent(inout) :: waterfluxbulk_inst + type(waterstatebulk_type), intent(inout) :: waterstatebulk_inst + type(waterfluxbulk_type) , intent(inout) :: waterfluxbulk_inst ! ! !LOCAL VARIABLES: - integer :: c,j,fc,i ! indices - real(r8) :: s_y - integer :: k,k_frz,k_perch,k_zwt - real(r8) :: sat_lev - real(r8) :: s1 - real(r8) :: s2 - real(r8) :: m - real(r8) :: b - integer :: sat_flag + integer :: c,j,fc,i ! indices + integer :: k,k_frz,k_perch,k_zwt ! indices + real(r8) :: s1, s2 ! temporary moisture values + real(r8) :: m, b ! slope and intercept + real(r8), parameter :: sat_lev = 0.9 ! saturation value used to identify saturated layers !----------------------------------------------------------------------- associate( & @@ -1564,8 +1623,7 @@ subroutine PerchedWaterTable(bounds, num_hydrologyc, filter_hydrologyc, & watsat => soilstate_inst%watsat_col , & ! Input: [real(r8) (:,:) ] volumetric soil water at saturation (porosity) zwt => soilhydrology_inst%zwt_col , & ! Output: [real(r8) (:) ] water table depth (m) zwt_perched => soilhydrology_inst%zwt_perched_col , & ! Output: [real(r8) (:) ] perched water table depth (m) - frost_table => soilhydrology_inst%frost_table_col , & ! Output: [real(r8) (:) ] frost table depth (m) - origflag => soilhydrology_inst%origflag & ! Input: logical + frost_table => soilhydrology_inst%frost_table_col & ! Output: [real(r8) (:) ] frost table depth (m) ) ! calculate perched water table location @@ -1594,16 +1652,13 @@ subroutine PerchedWaterTable(bounds, num_hydrologyc, filter_hydrologyc, & !======= water table above frost table =================== ! if water table is above frost table, do nothing - if (zwt(c) < frost_table(c) .and. t_soisno(c,k_frz) <= tfrz & - .and. origflag == 0) then + if (zwt(c) < frost_table(c) .and. t_soisno(c,k_frz) <= tfrz) then else if (k_frz > 1) then !========== water table below frost table ============ ! locate perched water table from bottom up starting at ! frost table sat_lev is an arbitrary saturation level ! used to determine perched water table - sat_lev = 0.9 - k_perch = 1 do k=k_frz,1,-1 h2osoi_vol(c,k) = h2osoi_liq(c,k)/(dz(c,k)*denh2o) & @@ -1619,7 +1674,7 @@ subroutine PerchedWaterTable(bounds, num_hydrologyc, filter_hydrologyc, & ! and only compute perched water table if frozen if (t_soisno(c,k_frz) > tfrz) k_perch=k_frz - ! if perched water table exists + ! if perched water table exists above frost table, ! interpolate between k_perch and k_perch+1 to find ! perched water table height if (k_frz > k_perch) then @@ -1635,8 +1690,7 @@ subroutine PerchedWaterTable(bounds, num_hydrologyc, filter_hydrologyc, & b=z(c,k_perch+1)-m*s2 zwt_perched(c)=max(0._r8,m*sat_lev+b) endif - - endif !k_frz > k_perch + endif endif end do @@ -1644,48 +1698,53 @@ subroutine PerchedWaterTable(bounds, num_hydrologyc, filter_hydrologyc, & end subroutine PerchedWaterTable -!#4 +!#4 !----------------------------------------------------------------------- - subroutine PerchedLateralFlow(bounds, num_hydrologyc, filter_hydrologyc, & - num_urbanc, filter_urbanc, soilhydrology_inst, soilstate_inst, & - waterstatebulk_inst, waterfluxbulk_inst) + subroutine PerchedLateralFlow(bounds, num_hydrologyc, & + filter_hydrologyc, soilhydrology_inst, soilstate_inst, & + waterstatebulk_inst, waterfluxbulk_inst, wateratm2lndbulk_inst) ! ! !DESCRIPTION: ! Calculate subsurface drainage from perched saturated zone ! ! !USES: use clm_varcon , only : pondmx, tfrz, watmin,rpi, secspday, nlvic - use column_varcon , only : icol_roof, icol_road_imperv, icol_road_perv + use LandunitType , only : lun + use landunit_varcon , only : istsoil + use clm_varctl , only : use_hillslope_routing ! ! !ARGUMENTS: - type(bounds_type) , intent(in) :: bounds - integer , intent(in) :: num_hydrologyc ! number of column soil points in column filter - integer , intent(in) :: num_urbanc ! number of column urban points in column filter - integer , intent(in) :: filter_urbanc(:) ! column filter for urban points - integer , intent(in) :: filter_hydrologyc(:) ! column filter for soil points - type(soilstate_type) , intent(in) :: soilstate_inst - type(soilhydrology_type) , intent(inout) :: soilhydrology_inst - type(waterstatebulk_type) , intent(inout) :: waterstatebulk_inst - type(waterfluxbulk_type) , intent(inout) :: waterfluxbulk_inst + type(bounds_type) , intent(in) :: bounds + integer , intent(in) :: num_hydrologyc ! number of column soil points in column filter + integer , intent(in) :: filter_hydrologyc(:) ! column filter for soil points + type(soilstate_type) , intent(in) :: soilstate_inst + type(soilhydrology_type) , intent(inout) :: soilhydrology_inst + type(waterstatebulk_type) , intent(inout) :: waterstatebulk_inst + type(waterfluxbulk_type) , intent(inout) :: waterfluxbulk_inst + type(wateratm2lndbulk_type), intent(in) :: wateratm2lndbulk_inst ! ! !LOCAL VARIABLES: - character(len=32) :: subname = 'PerchedLateralFlow' ! subroutine name - integer :: c,j,fc,i ! indices - real(r8) :: dtime ! land model time step (sec) - real(r8) :: wtsub ! summation of hk*dzmm for layers below water table (mm**2/s) - real(r8) :: h2osoi_vol - real(r8) :: drainage_tot - real(r8) :: drainage_layer - real(r8) :: s_y - integer :: k - integer :: k_frost(bounds%begc:bounds%endc) - integer :: k_perch(bounds%begc:bounds%endc) - real(r8) :: sat_lev - real(r8) :: s1, s2, m, b - real(r8) :: q_perch - real(r8) :: q_perch_max - !----------------------------------------------------------------------- + character(len=32) :: subname = 'PerchedLateralFlowHillslope' ! subroutine name + integer :: c,fc,k,l,g ! indices + real(r8) :: dtime ! land model time step (sec) + real(r8) :: drainage_tot ! total amount of drainage to be removed from the column (mm/s) + real(r8) :: drainage_layer ! amount of drainage to be removed from current layer (mm/s) + real(r8) :: s_y ! specific yield (unitless) + integer :: k_frost(bounds%begc:bounds%endc) ! indices identifying frost table layer + integer :: k_perch(bounds%begc:bounds%endc) ! indices identifying perched water table layer + real(r8) :: wtsub ! temporary variable + real(r8) :: q_perch ! transmissivity (mm2/s) + real(r8) :: q_perch_max ! baseflow coefficient + real(r8) :: stream_water_depth ! depth of water in stream channel (m) + real(r8) :: stream_channel_depth ! depth of stream channel (m) + + real(r8) :: transmis ! transmissivity (m2/s) + real(r8) :: head_gradient ! head gradient (m/m) + real(r8), parameter :: k_anisotropic = 1._r8 ! anisotropy factor + integer :: c0, c_src, c_dst ! indices + real(r8) :: qflx_drain_perched_vol(bounds%begc:bounds%endc) ! volumetric lateral subsurface flow through active layer [m3/s] + real(r8) :: qflx_drain_perched_out(bounds%begc:bounds%endc) ! lateral subsurface flow through active layer [mm/s] associate( & nbedrock => col%nbedrock , & ! Input: [real(r8) (:,:) ] depth to bedrock (m) @@ -1700,7 +1759,11 @@ subroutine PerchedLateralFlow(bounds, num_hydrologyc, filter_hydrologyc, & frost_table => soilhydrology_inst%frost_table_col , & ! Input: [real(r8) (:) ] frost table depth (m) zwt => soilhydrology_inst%zwt_col , & ! Input: [real(r8) (:) ] water table depth (m) zwt_perched => soilhydrology_inst%zwt_perched_col , & ! Input: [real(r8) (:) ] perched water table depth (m) - + tdepth => wateratm2lndbulk_inst%tdepth_grc , & ! Input: [real(r8) (:) ] depth of water in tributary channels (m) + tdepth_bankfull => wateratm2lndbulk_inst%tdepthmax_grc , & ! Input: [real(r8) (:) ] bankfull depth of tributary channels (m) + stream_water_volume => waterstatebulk_inst%stream_water_volume_lun , & ! Input: [real(r8) (:) ] stream water volume (m3) + + qflx_drain_perched => waterfluxbulk_inst%qflx_drain_perched_col , & ! Output: [real(r8) (:) ] perched wt sub-surface runoff (mm H2O /s) h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Output: [real(r8) (:,:) ] liquid water (kg/m2) @@ -1716,14 +1779,14 @@ subroutine PerchedLateralFlow(bounds, num_hydrologyc, filter_hydrologyc, & c = filter_hydrologyc(fc) k_frost(c) = nbedrock(c) k_perch(c) = nbedrock(c) - do k = 1, nbedrock(c) + do k = 1,nbedrock(c) if (frost_table(c) >= zi(c,k-1) .and. frost_table(c) < zi(c,k)) then k_frost(c) = k exit endif enddo - do k = 1, nbedrock(c) + do k = 1,nbedrock(c) if (zwt_perched(c) >= zi(c,k-1) .and. zwt_perched(c) < zi(c,k)) then k_perch(c) = k exit @@ -1734,48 +1797,183 @@ subroutine PerchedLateralFlow(bounds, num_hydrologyc, filter_hydrologyc, & ! compute drainage from perched saturated region do fc = 1, num_hydrologyc c = filter_hydrologyc(fc) + l = col%landunit(c) + g = col%gridcell(c) + qflx_drain_perched(c) = 0._r8 + qflx_drain_perched_out(c) = 0._r8 + qflx_drain_perched_vol(c) = 0._r8 - qflx_drain_perched(c) = 0._r8 if (frost_table(c) > zwt_perched(c)) then + ! Hillslope columns + if (col%is_hillslope_column(c) .and. col%active(c)) then + + ! calculate head gradient + + if (head_gradient_method == kinematic) then + ! kinematic wave approximation + head_gradient = col%hill_slope(c) + else if (head_gradient_method == darcy) then + ! darcy's law + if (col%cold(c) /= ispval) then + head_gradient = (col%hill_elev(c)-zwt_perched(c)) & + - (col%hill_elev(col%cold(c))-zwt_perched(col%cold(c))) + head_gradient = head_gradient / (col%hill_distance(c) - col%hill_distance(col%cold(c))) + else + if (use_hillslope_routing) then + stream_water_depth = stream_water_volume(l) & + /lun%stream_channel_length(l)/lun%stream_channel_width(l) + stream_channel_depth = lun%stream_channel_depth(l) + else + stream_water_depth = tdepth(g) + stream_channel_depth = tdepth_bankfull(g) + endif + + ! flow between channel and lowest column + ! bankfull height is defined to be zero + head_gradient = (col%hill_elev(c)-zwt_perched(c)) & + ! ignore overbankfull storage + - max(min((stream_water_depth - stream_channel_depth),0._r8), & + (col%hill_elev(c)-frost_table(c))) + + head_gradient = head_gradient / (col%hill_distance(c)) + + ! head_gradient cannot be negative when channel is empty + if (stream_water_depth <= 0._r8) then + head_gradient = max(head_gradient, 0._r8) + endif + endif + else + call endrun(msg="head_gradient_method must be kinematic or darcy"//errmsg(sourcefile, __LINE__)) + endif - ! specify maximum drainage rate - q_perch_max = params_inst%perched_baseflow_scalar & - * sin(col%topo_slope(c) * (rpi/180._r8)) + ! Determine source and destination columns + if (head_gradient >= 0._r8) then + c_src = c + c_dst = col%cold(c) + else + c_src = col%cold(c) + c_dst = c + endif - wtsub = 0._r8 - q_perch = 0._r8 - do k = k_perch(c), k_frost(c)-1 - q_perch = q_perch + hksat(c,k)*dz(c,k) - wtsub = wtsub + dz(c,k) - end do - if (wtsub > 0._r8) q_perch = q_perch/wtsub + ! Calculate transmissivity of source column + transmis = 0._r8 + + if (transmissivity_method == layersum) then + if (head_gradient_method == kinematic) then + if(k_perch(c_src) < k_frost(c_src)) then + do k = k_perch(c_src), k_frost(c_src)-1 + if(k == k_perch(c_src)) then + transmis = transmis + 1.e-3_r8*hksat(c_src,k)*(zi(c_src,k) - zwt_perched(c_src)) + else + transmis = transmis + 1.e-3_r8*hksat(c_src,k)*dz(c_src,k) + endif + enddo + endif + else if (head_gradient_method == darcy) then + if(c_src == ispval) then + ! lowland, losing stream (c_src == ispval) + ! use hksat of c_dst for transmissivity + transmis = (1.e-3_r8*hksat(c,k_perch(c_dst)))*stream_water_depth + else + ! if k_perch equals k_frost, no perched saturated zone exists + if(k_perch(c_src) < k_frost(c_src)) then + do k = k_perch(c_src), k_frost(c_src)-1 + if(k == k_perch(c_src)) then + transmis = transmis + 1.e-3_r8*hksat(c_src,k)*(zi(c_src,k) - zwt_perched(c_src)) + else + if(c_dst == ispval) then + ! lowland, gaining stream + ! only include layers above stream channel bottom + if ((col%hill_elev(c_src)-z(c_src,k)) > (-stream_channel_depth)) then + + transmis = transmis + 1.e-3_r8*hksat(c_src,k)*dz(c_src,k) + endif + else + ! uplands + ! only include layers above dst water table elevation + if ((col%hill_elev(c_src)-z(c_src,k)) > (col%hill_elev(c_dst) - zwt_perched(c_dst))) then + + transmis = transmis + 1.e-3_r8*hksat(c_src,k)*dz(c_src,k) + endif + endif + endif + enddo + endif + endif + endif + else if (transmissivity_method == uniform_transmissivity) then + ! constant conductivity based on shallowest saturated layer hydraulic conductivity + transmis = (1.e-3_r8*hksat(c_src,k_perch(c_src))) & + *(zi(c_src,k_frost(c_src)) - zwt_perched(c_src) ) + endif - qflx_drain_perched(c) = q_perch_max * q_perch & - *(frost_table(c) - zwt_perched(c)) + ! adjust by 'anisotropy factor' + transmis = k_anisotropic*transmis + + qflx_drain_perched_vol(c) = transmis*col%hill_width(c)*head_gradient + qflx_drain_perched_out(c) = 1.e3_r8*(qflx_drain_perched_vol(c)/col%hill_area(c)) + + else + ! Non-hillslope columns + ! specify maximum drainage rate + q_perch_max = params_inst%perched_baseflow_scalar & + * sin(col%topo_slope(c) * (rpi/180._r8)) + + wtsub = 0._r8 + q_perch = 0._r8 + ! this should be consistent with hillslope and k_perch=k_frost means no + ! saturated zone; should probably change q_perch to tranmis and change + ! units and q_perch_max + do k = k_perch(c), k_frost(c)-1 + q_perch = q_perch + hksat(c,k)*dz(c,k) + wtsub = wtsub + dz(c,k) + end do + if (wtsub > 0._r8) q_perch = q_perch/wtsub + + qflx_drain_perched_out(c) = q_perch_max * q_perch & + *(frost_table(c) - zwt_perched(c)) + endif endif + enddo + ! compute net drainage from perched saturated region + do fc = 1, num_hydrologyc + c = filter_hydrologyc(fc) + ! drainage-out + qflx_drain_perched(c) = qflx_drain_perched(c) + qflx_drain_perched_out(c) + if (col%is_hillslope_column(c) .and. col%active(c)) then + ! drainage-in + if (col%cold(c) /= ispval) then + qflx_drain_perched(col%cold(c)) = & + qflx_drain_perched(col%cold(c)) - & + 1.e3_r8*(qflx_drain_perched_vol(c))/col%hill_area(col%cold(c)) + endif + endif + enddo + ! remove drainage from soil moisture storage do fc = 1, num_hydrologyc c = filter_hydrologyc(fc) ! remove drainage from perched saturated layers - drainage_tot = qflx_drain_perched(c) * dtime - + drainage_tot = qflx_drain_perched(c) * dtime + ! ignore frozen layer (k_frost) do k = k_perch(c), k_frost(c)-1 + s_y = watsat(c,k) & * ( 1. - (1.+1.e3*zwt_perched(c)/sucsat(c,k))**(-1./bsw(c,k))) s_y=max(s_y,params_inst%aq_sp_yield_min) - - if (k == k_perch(c)) then + if (k==k_perch(c)) then drainage_layer=min(drainage_tot,(s_y*(zi(c,k) - zwt_perched(c))*1.e3)) else drainage_layer=min(drainage_tot,(s_y*(dz(c,k))*1.e3)) endif - + drainage_layer=max(drainage_layer,0._r8) drainage_tot = drainage_tot - drainage_layer h2osoi_liq(c,k) = h2osoi_liq(c,k) - drainage_layer + enddo ! if drainage_tot is greater than available water @@ -1886,17 +2084,24 @@ end subroutine ThetaBasedWaterTable !#6 !----------------------------------------------------------------------- - subroutine LateralFlowPowerLaw(bounds, num_hydrologyc, filter_hydrologyc, & + subroutine SubsurfaceLateralFlow(bounds, & + num_hydrologyc, filter_hydrologyc, & num_urbanc, filter_urbanc,soilhydrology_inst, soilstate_inst, & - waterstatebulk_inst, waterfluxbulk_inst) + waterstatebulk_inst, waterfluxbulk_inst, wateratm2lndbulk_inst) ! ! !DESCRIPTION: ! Calculate subsurface drainage ! ! !USES: - use clm_varcon , only : pondmx, watmin,rpi, secspday, nlvic - use column_varcon , only : icol_roof, icol_road_imperv, icol_road_perv - use GridcellType , only : grc + use clm_time_manager , only : get_step_size + use clm_varpar , only : nlevsoi, nlevgrnd, nlayer, nlayert + use clm_varctl , only : nhillslope + use clm_varcon , only : pondmx, watmin,rpi, secspday + use column_varcon , only : icol_road_perv + use abortutils , only : endrun + use GridcellType , only : grc + use landunit_varcon , only : istsoil, istcrop + use clm_varctl , only : use_hillslope_routing ! ! !ARGUMENTS: @@ -1906,47 +2111,46 @@ subroutine LateralFlowPowerLaw(bounds, num_hydrologyc, filter_hydrologyc, & integer , intent(in) :: filter_urbanc(:) ! column filter for urban points integer , intent(in) :: filter_hydrologyc(:) ! column filter for soil points type(soilstate_type) , intent(in) :: soilstate_inst + type(wateratm2lndbulk_type) , intent(in) :: wateratm2lndbulk_inst type(soilhydrology_type) , intent(inout) :: soilhydrology_inst - type(waterstatebulk_type) , intent(inout) :: waterstatebulk_inst - type(waterfluxbulk_type) , intent(inout) :: waterfluxbulk_inst + type(waterstatebulk_type), intent(inout) :: waterstatebulk_inst + type(waterfluxbulk_type) , intent(inout) :: waterfluxbulk_inst + ! ! !LOCAL VARIABLES: - character(len=32) :: subname = 'Drainage' ! subroutine name - integer :: c,j,fc,i ! indices + character(len=32) :: subname = 'SubsurfaceLateralFlow' ! subroutine name + integer :: c,j,fc,i,l,g ! indices real(r8) :: dtime ! land model time step (sec) real(r8) :: xs(bounds%begc:bounds%endc) ! water needed to bring soil moisture to watmin (mm) real(r8) :: dzmm(bounds%begc:bounds%endc,1:nlevsoi) ! layer thickness (mm) integer :: jwt(bounds%begc:bounds%endc) ! index of the soil layer right above the water table (-) - real(r8) :: rsub_bot(bounds%begc:bounds%endc) ! subsurface runoff - bottom drainage (mm/s) - real(r8) :: rsub_top(bounds%begc:bounds%endc) ! subsurface runoff - topographic control (mm/s) + real(r8) :: drainage(bounds%begc:bounds%endc) ! subsurface drainage (mm/s) real(r8) :: xsi(bounds%begc:bounds%endc) ! excess soil water above saturation at layer i (mm) - real(r8) :: xsia(bounds%begc:bounds%endc) ! available pore space at layer i (mm) real(r8) :: xs1(bounds%begc:bounds%endc) ! excess soil water above saturation at layer 1 (mm) - real(r8) :: smpfz(1:nlevsoi) ! matric potential of layer right above water table (mm) - real(r8) :: wtsub ! summation of hk*dzmm for layers below water table (mm**2/s) real(r8) :: dzsum ! summation of dzmm of layers below water table (mm) real(r8) :: icefracsum ! summation of icefrac*dzmm of layers below water table (-) - real(r8) :: fracice_rsub(bounds%begc:bounds%endc) ! fractional impermeability of soil layers (-) + real(r8) :: ice_imped_col(bounds%begc:bounds%endc) ! column average hydraulic conductivity reduction due to presence of soil ice (-) + real(r8) :: ice_imped(bounds%begc:bounds%endc,1:nlevsoi) ! hydraulic conductivity reduction due to presence of soil ice (-) real(r8) :: available_h2osoi_liq ! available soil liquid water in a layer - real(r8) :: h2osoi_vol - real(r8) :: imped - real(r8) :: rsub_top_tot - real(r8) :: rsub_top_layer - real(r8) :: theta_unsat - real(r8) :: f_unsat - real(r8) :: s_y - integer :: k - real(r8) :: s1 - real(r8) :: s2 - real(r8) :: m - real(r8) :: b - real(r8) :: vol_ice - real(r8) :: dsmax_tmp(bounds%begc:bounds%endc) ! temporary variable for ARNO subsurface runoff calculation - real(r8) :: rsub_tmp ! temporary variable for ARNO subsurface runoff calculation - real(r8) :: frac ! temporary variable for ARNO subsurface runoff calculation - real(r8) :: rel_moist ! relative moisture, temporary variable - real(r8) :: wtsub_vic ! summation of hk*dzmm for layers in the third VIC layer - integer :: g + real(r8) :: h2osoi_vol ! volumetric water content (mm3/mm3) + real(r8) :: drainage_tot ! total drainage to be removed from column (mm) + real(r8) :: drainage_layer ! drainage to be removed from current layer (mm) + real(r8) :: s_y ! specific yield (unitless) + real(r8) :: vol_ice ! volumetric ice content (mm3/mm3) + logical, parameter :: no_lateral_flow = .false. ! flag for testing + real(r8) :: transmis ! transmissivity (m2/s) + real(r8) :: head_gradient ! hydraulic head gradient (m/m) + real(r8) :: stream_water_depth ! depth of water in stream channel (m) + real(r8) :: stream_channel_depth ! depth of stream channel (m) + real(r8) :: available_stream_water ! stream water (m3) + real(r8), parameter :: n_baseflow = 1 ! drainage power law exponent + real(r8), parameter :: k_anisotropic = 1._r8 ! anisotropy scalar + real(r8) :: qflx_latflow_out_vol(bounds%begc:bounds%endc) ! volumetric lateral flow (m3/s) + real(r8) :: qflx_net_latflow(bounds%begc:bounds%endc) ! net lateral flow in column (mm/s) + real(r8) :: qflx_latflow_avg(bounds%begc:bounds%endc) ! average lateral flow (mm/s) + real(r8) :: larea ! area of hillslope in landunit + integer :: c0, c_src, c_dst ! indices + !----------------------------------------------------------------------- associate( & @@ -1962,28 +2166,21 @@ subroutine LateralFlowPowerLaw(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 , & ! Input: [real(r8) (:,:) ] effective porosity = porosity - vol_ice hk_l => soilstate_inst%hk_l_col , & ! Input: [real(r8) (:,:) ] hydraulic conductivity (mm/s) + qflx_latflow_out => waterfluxbulk_inst%qflx_latflow_out_col, & ! Output: [real(r8) (:) ] lateral saturated outflow (mm/s) + qflx_latflow_in => waterfluxbulk_inst%qflx_latflow_in_col, & ! Output: [real(r8) (:) ] lateral saturated inflow (mm/s) + volumetric_discharge => waterfluxbulk_inst%volumetric_discharge_col , & ! Output: [real(r8) (:) ] discharge from column (m3/s) + + tdepth => wateratm2lndbulk_inst%tdepth_grc , & ! Input: [real(r8) (:) ] depth of water in tributary channels (m) + tdepth_bankfull => wateratm2lndbulk_inst%tdepthmax_grc , & ! Input: [real(r8) (:) ] bankfull depth of tributary channels (m) depth => soilhydrology_inst%depth_col , & ! Input: [real(r8) (:,:) ] VIC soil depth - c_param => soilhydrology_inst%c_param_col , & ! Input: [real(r8) (:) ] baseflow exponent (Qb) - Dsmax => soilhydrology_inst%dsmax_col , & ! Input: [real(r8) (:) ] max. velocity of baseflow (mm/day) - max_moist => soilhydrology_inst%max_moist_col , & ! Input: [real(r8) (:,:) ] maximum soil moisture (ice + liq) - moist => soilhydrology_inst%moist_col , & ! Input: [real(r8) (:,:) ] soil layer moisture (mm) - Ds => soilhydrology_inst%ds_col , & ! Input: [real(r8) (:) ] fracton of Dsmax where non-linear baseflow begins - Wsvic => soilhydrology_inst%Wsvic_col , & ! Input: [real(r8) (:) ] fraction of maximum soil moisutre where non-liear base flow occurs icefrac => soilhydrology_inst%icefrac_col , & ! Output: [real(r8) (:,:) ] fraction of ice in layer frost_table => soilhydrology_inst%frost_table_col , & ! Input: [real(r8) (:) ] frost table depth (m) zwt => soilhydrology_inst%zwt_col , & ! Input: [real(r8) (:) ] water table depth (m) - wa => waterstatebulk_inst%wa_col , & ! Input: [real(r8) (:) ] water in the unconfined aquifer (mm) - ice => soilhydrology_inst%ice_col , & ! Input: [real(r8) (:,:) ] soil layer moisture (mm) - qcharge => soilhydrology_inst%qcharge_col , & ! Input: [real(r8) (:) ] aquifer recharge rate (mm/s) - origflag => soilhydrology_inst%origflag , & ! Input: logical - h2osfcflag => soilhydrology_inst%h2osfcflag , & ! Input: integer + stream_water_volume => waterstatebulk_inst%stream_water_volume_lun, & ! Input: [real(r8) (:) ] stream water volume (m3) qflx_snwcp_liq => waterfluxbulk_inst%qflx_snwcp_liq_col , & ! Output: [real(r8) (:) ] excess rainfall due to snow capping (mm H2O /s) [+] qflx_ice_runoff_xs => waterfluxbulk_inst%qflx_ice_runoff_xs_col , & ! Output: [real(r8) (:) ] solid runoff from excess ice in soil (mm H2O /s) [+] - qflx_liqdew_to_top_layer => waterfluxbulk_inst%qflx_liqdew_to_top_layer_col , & ! Output: [real(r8) (:) ] rate of liquid water deposited on top soil or snow layer (dew) (mm H2O /s) [+] - qflx_soliddew_to_top_layer => waterfluxbulk_inst%qflx_soliddew_to_top_layer_col , & ! Output: [real(r8) (:) ] rate of solid water deposited on top soil or snow layer (frost) (mm H2O /s) [+] - qflx_solidevap_from_top_layer => waterfluxbulk_inst%qflx_solidevap_from_top_layer_col, & ! Output: [real(r8) (:) ] rate of ice evaporated from top soil or snow layer (sublimation) (mm H2O /s) [+] qflx_drain => waterfluxbulk_inst%qflx_drain_col , & ! Output: [real(r8) (:) ] sub-surface runoff (mm H2O /s) qflx_qrgwl => waterfluxbulk_inst%qflx_qrgwl_col , & ! Output: [real(r8) (:) ] qflx_surf at glaciers, wetlands, lakes (mm H2O /s) qflx_rsub_sat => waterfluxbulk_inst%qflx_rsub_sat_col , & ! Output: [real(r8) (:) ] soil saturation excess [mm h2o/s] @@ -2003,7 +2200,8 @@ subroutine LateralFlowPowerLaw(bounds, num_hydrologyc, filter_hydrologyc, & dzmm(c,j) = dz(c,j)*1.e3_r8 vol_ice = min(watsat(c,j), h2osoi_ice(c,j)/(dz(c,j)*denice)) - icefrac(c,j) = min(1._r8,vol_ice/watsat(c,j)) + icefrac(c,j) = min(1._r8,vol_ice/watsat(c,j)) + ice_imped(c,j)=10._r8**(-params_inst%e_ice*icefrac(c,j)) end do end do @@ -2012,80 +2210,298 @@ subroutine LateralFlowPowerLaw(bounds, num_hydrologyc, filter_hydrologyc, & do fc = 1, num_hydrologyc c = filter_hydrologyc(fc) qflx_drain(c) = 0._r8 - rsub_bot(c) = 0._r8 qflx_rsub_sat(c) = 0._r8 - rsub_top(c) = 0._r8 - fracice_rsub(c) = 0._r8 - end do - - ! The layer index of the first unsaturated layer, - ! i.e., the layer right above the water table - - do fc = 1, num_hydrologyc - c = filter_hydrologyc(fc) - jwt(c) = nlevsoi - ! allow jwt to equal zero when zwt is in top layer - do j = 1,nlevsoi - if(zwt(c) <= zi(c,j)) then - jwt(c) = j-1 - exit - end if - enddo - end do - - !-- Topographic runoff ------------------------- - do fc = 1, num_hydrologyc - c = filter_hydrologyc(fc) - - dzsum = 0._r8 - icefracsum = 0._r8 - do j = max(jwt(c),1), nlevsoi - dzsum = dzsum + dzmm(c,j) - icefracsum = icefracsum + icefrac(c,j) * dzmm(c,j) - end do - imped=10._r8**(-params_inst%e_ice*(icefracsum/dzsum)) - !@@ - ! baseflow is power law expression relative to bedrock layer - if(zwt(c) <= zi(c,nbedrock(c))) then - rsub_top(c) = imped * baseflow_scalar * tan(rpi/180._r8*col%topo_slope(c))* & - (zi(c,nbedrock(c)) - zwt(c))**(params_inst%n_baseflow) - else - rsub_top(c) = 0._r8 - endif - - !-- Now remove water via rsub_top - rsub_top_tot = - rsub_top(c)* dtime - - !should never be positive... but include for completeness - if(rsub_top_tot > 0.) then !rising water table - - call endrun(subgrid_index=c, subgrid_level=subgrid_level_column, & - msg="RSUB_TOP IS POSITIVE in Drainage!"//errmsg(sourcefile, __LINE__)) - + drainage(c) = 0._r8 + qflx_latflow_in(c) = 0._r8 + qflx_latflow_out(c) = 0._r8 + qflx_net_latflow(c) = 0._r8 + volumetric_discharge(c) = 0._r8 + qflx_latflow_out_vol(c) = 0._r8 + end do + + ! The layer index of the first unsaturated layer, + ! i.e., the layer right above the water table + + do fc = 1, num_hydrologyc + c = filter_hydrologyc(fc) + jwt(c) = nlevsoi + ! allow jwt to equal zero when zwt is in top layer + do j = 1,nlevsoi + if(zwt(c) <= zi(c,j)) then + jwt(c) = j-1 + exit + end if + enddo + end do + + ! Calculate ice impedance factor (after jwt calculated) + do fc = 1, num_hydrologyc + c = filter_hydrologyc(fc) + dzsum = 0._r8 + icefracsum = 0._r8 + do j = max(jwt(c),1), nlevsoi + dzsum = dzsum + dzmm(c,j) + icefracsum = icefracsum + icefrac(c,j) * dzmm(c,j) + end do + ice_imped_col(c)=10._r8**(-params_inst%e_ice*(icefracsum/dzsum)) + enddo + + do fc = 1, num_hydrologyc + c = filter_hydrologyc(fc) + l = col%landunit(c) + g = col%gridcell(c) + ! Hillslope columns + if (col%is_hillslope_column(c) .and. col%active(c)) then + + ! method for calculating head gradient + if (head_gradient_method == kinematic) then + head_gradient = col%hill_slope(c) + else if (head_gradient_method == darcy) then + if (col%cold(c) /= ispval) then + head_gradient = (col%hill_elev(c)-zwt(c)) & + - (col%hill_elev(col%cold(c))-zwt(col%cold(c))) + head_gradient = head_gradient / (col%hill_distance(c) - col%hill_distance(col%cold(c))) + else + if (use_hillslope_routing) then + stream_water_depth = stream_water_volume(l) & + /lun%stream_channel_length(l)/lun%stream_channel_width(l) + stream_channel_depth = lun%stream_channel_depth(l) + else + stream_water_depth = tdepth(g) + stream_channel_depth = tdepth_bankfull(g) + endif + + ! flow between channel and lowest column + ! bankfull height is defined to be zero + head_gradient = (col%hill_elev(c)-zwt(c)) & + ! ignore overbankfull storage + - min((stream_water_depth - stream_channel_depth),0._r8) + + head_gradient = head_gradient / (col%hill_distance(c)) + ! head_gradient cannot be negative when channel is empty + if (stream_water_depth <= 0._r8) then + head_gradient = max(head_gradient, 0._r8) + endif + ! add vertical drainage for losing streams + ! (this could be a separate term from lateral flow...) + if (head_gradient < 0._r8) then + ! head_gradient = head_gradient - 1._r8 + ! adjust lateral gradient w/ k_anisotropic + head_gradient = head_gradient - 1._r8/k_anisotropic + endif + endif + else + call endrun(msg="head_gradient_method must be kinematic or darcy"//errmsg(sourcefile, __LINE__)) + end if + + !scs: in cases of bad data, where hand differences in + ! adjacent bins are very large, cap maximum head_gradient + ! should a warning be used instead? + head_gradient = min(max(head_gradient,-2._r8),2._r8) + + ! Determine source and destination columns + if (head_gradient >= 0._r8) then + c_src = c + c_dst = col%cold(c) + else + c_src = col%cold(c) + c_dst = c + endif + + ! Calculate transmissivity of source column + transmis = 0._r8 + if(c_src /= ispval) then + ! transmissivity non-zero only when saturated conditions exist + if(zwt(c_src) <= zi(c_src,nbedrock(c_src))) then + ! sum of layer transmissivities + if (transmissivity_method == layersum) then + do j = jwt(c_src)+1, nbedrock(c_src) + if(j == jwt(c_src)+1) then + transmis = transmis + 1.e-3_r8*ice_imped(c_src,j)*hksat(c_src,j)*(zi(c_src,j) - zwt(c_src)) + else + if(c_dst == ispval) then + ! lowland, gaining stream + ! only include layers above stream channel bottom + if ((col%hill_elev(c_src)-z(c_src,j)) > (-stream_channel_depth)) then + + transmis = transmis + 1.e-3_r8*ice_imped(c_src,j)*hksat(c_src,j)*dz(c_src,j) + endif + else + ! uplands + if ((col%hill_elev(c_src)-z(c_src,j)) > (col%hill_elev(c_dst) - zwt(c_dst))) then + transmis = transmis + 1.e-3_r8*ice_imped(c_src,j)*hksat(c_src,j)*dz(c_src,j) + endif + endif + endif + end do + ! constant conductivity based on shallowest saturated layer hk + else if (transmissivity_method == uniform_transmissivity) then + transmis = (1.e-3_r8*ice_imped(c_src,jwt(c_src)+1)*hksat(c_src,jwt(c_src)+1)) & + *(zi(c_src,nbedrock(c_src)) - zwt(c_src) ) + else + call endrun(msg="transmissivity_method must be LayerSum or Uniform"//errmsg(sourcefile, __LINE__)) + endif + endif + else + ! transmissivity of losing stream (c_src == ispval) + transmis = (1.e-3_r8*ice_imped(c,jwt(c)+1)*hksat(c,jwt(c)+1))*stream_water_depth + endif + ! adjust transmissivity by 'anisotropy factor' + transmis = k_anisotropic*transmis + + ! the qflx_latflow_out_vol calculations use the + ! transmissivity to determine whether saturated flow + ! conditions exist, b/c gradients will be nonzero + ! even when no saturated layers are present + ! qflx_latflow_out_vol(c) = ice_imped(c)*transmis*col%hill_width(c)*head_gradient + ! include ice impedance in transmissivity + qflx_latflow_out_vol(c) = transmis*col%hill_width(c)*head_gradient + + ! When head gradient is negative (losing stream channel), + ! limit outflow by available stream channel water + if (use_hillslope_routing .and. (qflx_latflow_out_vol(c) < 0._r8)) then + available_stream_water = stream_water_volume(l)/lun%stream_channel_number(l)/nhillslope + if(abs(qflx_latflow_out_vol(c))*dtime > available_stream_water) then + qflx_latflow_out_vol(c) = -available_stream_water/dtime + endif + endif + + ! volumetric_discharge from lowest column is qflx_latflow_out_vol + ! scaled by total area of column in gridcell divided by column area + if (col%cold(c) == ispval) then + volumetric_discharge(c) = qflx_latflow_out_vol(c) & + *(grc%area(g)*1.e6_r8*col%wtgcell(c)/col%hill_area(c)) + endif + + ! convert volumetric flow to equivalent flux + qflx_latflow_out(c) = 1.e3_r8*qflx_latflow_out_vol(c)/col%hill_area(c) + + ! hilltop column has no inflow + if (col%colu(c) == ispval) then + qflx_latflow_in(c) = 0._r8 + endif + + ! current outflow is inflow to downhill column normalized by downhill area + if (col%cold(c) /= ispval) then + qflx_latflow_in(col%cold(c)) = qflx_latflow_in(col%cold(c)) + & + 1.e3_r8*qflx_latflow_out_vol(c)/col%hill_area(col%cold(c)) + endif + + else + ! Non-hillslope columns + ! baseflow is power law expression relative to bedrock layer + if(zwt(c) <= zi(c,nbedrock(c))) then + qflx_latflow_out(c) = ice_imped_col(c) * baseflow_scalar & + * tan(rpi/180._r8*col%topo_slope(c))* & + (zi(c,nbedrock(c)) - zwt(c))**(params_inst%n_baseflow) + endif + ! convert flux to volumetric flow + qflx_latflow_out_vol(c) = 1.e-3_r8*qflx_latflow_out(c)*(grc%area(g)*1.e6_r8*col%wtgcell(c)) + volumetric_discharge(c) = qflx_latflow_out_vol(c) + endif + enddo + + ! recalculate average flux for no-lateral flow case + if(no_lateral_flow) then + if (head_gradient_method /= kinematic) then + call endrun(msg="head_gradient_method must be kinematic for no_lateral_flow = .true.! "//errmsg(sourcefile, __LINE__)) + endif + do fc = 1, num_hydrologyc + c = filter_hydrologyc(fc) + if (col%is_hillslope_column(c) .and. col%active(c)) then + l = col%landunit(c) + !need to sum all columns w/ same hillslope id for each column + qflx_latflow_avg(c) = 0._r8 + larea = 0._r8 + do c0 = lun%coli(l), lun%colf(l) + if(col%hillslope_ndx(c0) == col%hillslope_ndx(c)) then + qflx_latflow_avg(c) = qflx_latflow_avg(c) + qflx_latflow_out_vol(c0) + larea = larea + col%hill_area(c0) + endif + enddo + qflx_latflow_avg(c) = 1.e3_r8*qflx_latflow_avg(c)/larea + else + qflx_latflow_avg(c) = qflx_latflow_out(c) + endif + enddo + endif + + !-- Topographic runoff ------------------------- + do fc = 1, num_hydrologyc + c = filter_hydrologyc(fc) + + ! net lateral flow (positive out) + qflx_net_latflow(c) = qflx_latflow_out(c) - qflx_latflow_in(c) + if(no_lateral_flow) then + qflx_net_latflow(c) = qflx_latflow_avg(c) + endif + + !@@ + ! baseflow + if(zwt(c) <= zi(c,nbedrock(c))) then + ! apply net lateral flow here + drainage(c) = qflx_net_latflow(c) + else + drainage(c) = 0._r8 + endif + + !-- Now remove water via drainage + drainage_tot = - drainage(c) * dtime + + if(drainage_tot > 0.) then !rising water table + do j = jwt(c)+1,1,-1 + + ! ensure water is not added to frozen layers + if (zi(c,j) < frost_table(c)) then + ! analytical expression for specific yield + s_y = watsat(c,j) & + * ( 1. - (1.+1.e3*zwt(c)/sucsat(c,j))**(-1./bsw(c,j))) + s_y=max(s_y,params_inst%aq_sp_yield_min) + + drainage_layer=min(drainage_tot,(s_y*dz(c,j)*1.e3)) + + drainage_layer=max(drainage_layer,0._r8) + h2osoi_liq(c,j) = h2osoi_liq(c,j) + drainage_layer + + drainage_tot = drainage_tot - drainage_layer + + if (drainage_tot <= 0.) then + zwt(c) = zwt(c) - drainage_layer/s_y/1000._r8 + exit + else + zwt(c) = zi(c,j-1) + endif + endif + + enddo + + !-- remove residual drainage -------------------------------- + h2osfc(c) = h2osfc(c) + drainage_tot + else ! deepening water table do j = jwt(c)+1, nbedrock(c) - ! use analytical expression for specific yield + ! analytical expression for specific yield s_y = watsat(c,j) & * ( 1. - (1.+1.e3*zwt(c)/sucsat(c,j))**(-1./bsw(c,j))) - s_y=max(s_y, params_inst%aq_sp_yield_min) - rsub_top_layer=max(rsub_top_tot,-(s_y*(zi(c,j) - zwt(c))*1.e3)) - rsub_top_layer=min(rsub_top_layer,0._r8) - h2osoi_liq(c,j) = h2osoi_liq(c,j) + rsub_top_layer - - rsub_top_tot = rsub_top_tot - rsub_top_layer + s_y=max(s_y,params_inst%aq_sp_yield_min) + + drainage_layer=max(drainage_tot,-(s_y*(zi(c,j) - zwt(c))*1.e3)) + drainage_layer=min(drainage_layer,0._r8) + h2osoi_liq(c,j) = h2osoi_liq(c,j) + drainage_layer - if (rsub_top_tot >= 0.) then - zwt(c) = zwt(c) - rsub_top_layer/s_y/1000._r8 + drainage_tot = drainage_tot - drainage_layer + if (drainage_tot >= 0.) then + zwt(c) = zwt(c) - drainage_layer/s_y/1000._r8 exit else zwt(c) = zi(c,j) endif enddo - !-- remove residual rsub_top -------------------------------- + !-- remove residual drainage ----------------------- ! make sure no extra water removed from soil column - rsub_top(c) = rsub_top(c) + rsub_top_tot/dtime + drainage(c) = drainage(c) + drainage_tot/dtime endif zwt(c) = max(0.0_r8,zwt(c)) @@ -2100,7 +2516,7 @@ subroutine LateralFlowPowerLaw(bounds, num_hydrologyc, filter_hydrologyc, & c = filter_hydrologyc(fc) xsi(c) = max(h2osoi_liq(c,j)-eff_porosity(c,j)*dzmm(c,j),0._r8) h2osoi_liq(c,j) = min(eff_porosity(c,j)*dzmm(c,j), h2osoi_liq(c,j)) - h2osoi_liq(c,j-1) = h2osoi_liq(c,j-1) + xsi(c) + h2osoi_liq(c,j-1) = h2osoi_liq(c,j-1) + xsi(c) end do end do @@ -2173,16 +2589,16 @@ subroutine LateralFlowPowerLaw(bounds, num_hydrologyc, filter_hydrologyc, & ! Instead of removing water from aquifer where it eventually ! shows up as excess drainage to the ocean, take it back out of ! drainage - qflx_rsub_sat(c) = qflx_rsub_sat(c) - xs(c)/dtime + qflx_rsub_sat(c) = qflx_rsub_sat(c) - xs(c)/dtime end do + do fc = 1, num_hydrologyc c = filter_hydrologyc(fc) ! Sub-surface runoff and drainage - - qflx_drain(c) = qflx_rsub_sat(c) + rsub_top(c) + qflx_drain(c) = qflx_rsub_sat(c) + drainage(c) ! Set imbalance for snow capping @@ -2190,6 +2606,7 @@ subroutine LateralFlowPowerLaw(bounds, num_hydrologyc, filter_hydrologyc, & end do + ! No drainage for urban columns (except for pervious road as computed above) do fc = 1, num_urbanc @@ -2203,7 +2620,7 @@ subroutine LateralFlowPowerLaw(bounds, num_hydrologyc, filter_hydrologyc, & end associate - end subroutine LateralFlowPowerLaw + end subroutine SubsurfaceLateralFlow !#7 !----------------------------------------------------------------------- diff --git a/src/biogeophys/SoilHydrologyType.F90 b/src/biogeophys/SoilHydrologyType.F90 index 4dfca06811..07ad2ca45b 100644 --- a/src/biogeophys/SoilHydrologyType.F90 +++ b/src/biogeophys/SoilHydrologyType.F90 @@ -19,8 +19,6 @@ Module SoilHydrologyType type, public :: soilhydrology_type integer :: h2osfcflag ! true => surface water is active (namelist) - integer :: origflag ! used to control soil hydrology properties (namelist) - real(r8), pointer :: num_substeps_col (:) ! col adaptive timestep counter ! NON-VIC real(r8), pointer :: frost_table_col (:) ! col frost table depth @@ -28,7 +26,6 @@ Module SoilHydrologyType real(r8), pointer :: zwts_col (:) ! col water table depth, the shallower of the two water depths real(r8), pointer :: zwt_perched_col (:) ! col perched water table depth real(r8), pointer :: qcharge_col (:) ! col aquifer recharge rate (mm/s) - real(r8), pointer :: fracice_col (:,:) ! col fractional impermeability (-) real(r8), pointer :: icefrac_col (:,:) ! col fraction of ice real(r8), pointer :: h2osfc_thresh_col (:) ! col level at which h2osfc "percolates" (time constant) real(r8), pointer :: xs_urban_col (:) ! col excess soil water above urban ponding limit @@ -121,7 +118,6 @@ subroutine InitAllocate(this, bounds) allocate(this%zwts_col (begc:endc)) ; this%zwts_col (:) = nan allocate(this%qcharge_col (begc:endc)) ; this%qcharge_col (:) = nan - allocate(this%fracice_col (begc:endc,nlevgrnd)) ; this%fracice_col (:,:) = nan allocate(this%icefrac_col (begc:endc,nlevgrnd)) ; this%icefrac_col (:,:) = nan allocate(this%h2osfc_thresh_col (begc:endc)) ; this%h2osfc_thresh_col (:) = nan allocate(this%xs_urban_col (begc:endc)) ; this%xs_urban_col (:) = nan @@ -340,16 +336,14 @@ subroutine ReadNL( this, NLFilename ) ! !LOCAL VARIABLES: integer :: ierr ! error code integer :: unitn ! unit for namelist file - integer :: origflag=0 !use to control soil hydraulic properties integer :: h2osfcflag=1 !If surface water is active or not character(len=32) :: subname = 'SoilHydrology_readnl' ! subroutine name !----------------------------------------------------------------------- - namelist / clm_soilhydrology_inparm / h2osfcflag, origflag + namelist / clm_soilhydrology_inparm / h2osfcflag ! preset values - origflag = 0 h2osfcflag = 1 if ( masterproc )then @@ -371,10 +365,8 @@ subroutine ReadNL( this, NLFilename ) end if call shr_mpi_bcast(h2osfcflag, mpicom) - call shr_mpi_bcast(origflag, mpicom) this%h2osfcflag = h2osfcflag - this%origflag = origflag end subroutine ReadNL diff --git a/src/biogeophys/SoilWaterMovementMod.F90 b/src/biogeophys/SoilWaterMovementMod.F90 index b1487e2779..85bcf42c5e 100644 --- a/src/biogeophys/SoilWaterMovementMod.F90 +++ b/src/biogeophys/SoilWaterMovementMod.F90 @@ -575,10 +575,8 @@ subroutine soilwater_zengdecker2009(bounds, num_hydrologyc, filter_hydrologyc, & zi => col%zi , & ! Input: [real(r8) (:,:) ] interface level below a "z" level (m) dz => col%dz , & ! Input: [real(r8) (:,:) ] layer thickness (m) - origflag => soilhydrology_inst%origflag , & ! Input: constant qcharge => soilhydrology_inst%qcharge_col , & ! Input: [real(r8) (:) ] aquifer recharge rate (mm/s) zwt => soilhydrology_inst%zwt_col , & ! Input: [real(r8) (:) ] water table depth (m) - fracice => soilhydrology_inst%fracice_col , & ! Input: [real(r8) (:,:) ] fractional impermeability (-) icefrac => soilhydrology_inst%icefrac_col , & ! Input: [real(r8) (:,:) ] fraction of ice hkdepth => soilhydrology_inst%hkdepth_col , & ! Input: [real(r8) (:) ] decay factor (m) @@ -720,22 +718,13 @@ subroutine soilwater_zengdecker2009(bounds, num_hydrologyc, filter_hydrologyc, & c = filter_hydrologyc(fc) ! compute hydraulic conductivity based on liquid water content only - if (origflag == 1) then - s1 = 0.5_r8*(h2osoi_vol(c,j) + h2osoi_vol(c,min(nlevsoi, j+1))) / & - (0.5_r8*(watsat(c,j)+watsat(c,min(nlevsoi, j+1)))) - else - s1 = 0.5_r8*(vwc_liq(c,j) + vwc_liq(c,min(nlevsoi, j+1))) / & - (0.5_r8*(watsat(c,j)+watsat(c,min(nlevsoi, j+1)))) - endif + s1 = 0.5_r8*(vwc_liq(c,j) + vwc_liq(c,min(nlevsoi, j+1))) / & + (0.5_r8*(watsat(c,j)+watsat(c,min(nlevsoi, j+1)))) s1 = min(1._r8, s1) s2 = hksat(c,j)*s1**(2._r8*bsw(c,j)+2._r8) - ! replace fracice with impedance factor, as in zhao 97,99 - if (origflag == 1) then - imped(c,j)=(1._r8-0.5_r8*(fracice(c,j)+fracice(c,min(nlevsoi, j+1)))) - else - imped(c,j)=10._r8**(-params_inst%e_ice*(0.5_r8*(icefrac(c,j)+icefrac(c,min(nlevsoi, j+1))))) - endif + imped(c,j)=10._r8**(-params_inst%e_ice*(0.5_r8*(icefrac(c,j)+icefrac(c,min(nlevsoi, j+1))))) + hk(c,j) = imped(c,j)*s1*s2 dhkdw(c,j) = imped(c,j)*(2._r8*bsw(c,j)+3._r8)*s2* & (1._r8/(watsat(c,j)+watsat(c,min(nlevsoi, j+1)))) @@ -751,11 +740,7 @@ subroutine soilwater_zengdecker2009(bounds, num_hydrologyc, filter_hydrologyc, & ! compute matric potential and derivative based on liquid water content only - if (origflag == 1) then - s_node = max(h2osoi_vol(c,j)/watsat(c,j), 0.01_r8) - else - s_node = max(vwc_liq(c,j)/watsat(c,j), 0.01_r8) - endif + s_node = max(vwc_liq(c,j)/watsat(c,j), 0.01_r8) s_node = min(1.0_r8, s_node) !call soil_water_retention_curve%soil_suction(sucsat(c,j), s_node, bsw(c,j), smp(c,j), dsmpds) @@ -765,11 +750,7 @@ subroutine soilwater_zengdecker2009(bounds, num_hydrologyc, filter_hydrologyc, & !do not turn on the line below, which will cause bit to bit error, jyt, 2014 Mar 6 !dsmpdw(c,j) = dsmpds/watsat(c,j) - if (origflag == 1) then - dsmpdw(c,j) = -bsw(c,j)*smp(c,j)/(s_node*watsat(c,j)) - else - dsmpdw(c,j) = -bsw(c,j)*smp(c,j)/vwc_liq(c,j) - endif + dsmpdw(c,j) = -bsw(c,j)*smp(c,j)/vwc_liq(c,j) smp_l(c,j) = smp(c,j) hk_l(c,j) = hk(c,j) @@ -861,11 +842,7 @@ subroutine soilwater_zengdecker2009(bounds, num_hydrologyc, filter_hydrologyc, & else ! water table is below soil column ! compute aquifer soil moisture as average of layer 10 and saturation - if(origflag == 1) then - s_node = max(0.5*(1.0_r8+h2osoi_vol(c,j)/watsat(c,j)), 0.01_r8) - else - s_node = max(0.5*((vwc_zwt(c)+vwc_liq(c,j))/watsat(c,j)), 0.01_r8) - endif + s_node = max(0.5*((vwc_zwt(c)+vwc_liq(c,j))/watsat(c,j)), 0.01_r8) s_node = min(1.0_r8, s_node) ! compute smp for aquifer layer @@ -940,7 +917,7 @@ subroutine soilwater_zengdecker2009(bounds, num_hydrologyc, filter_hydrologyc, & s_node = max(h2osoi_vol(c,jwt(c)+1)/watsat(c,jwt(c)+1), 0.01_r8) s1 = min(1._r8, s_node) - !scs: this is the expression for unsaturated hk + !this is the expression for unsaturated hk ka = imped(c,jwt(c)+1)*hksat(c,jwt(c)+1) & *s1**(2._r8*bsw(c,jwt(c)+1)+3._r8) @@ -953,12 +930,12 @@ subroutine soilwater_zengdecker2009(bounds, num_hydrologyc, filter_hydrologyc, & smp1 = max(smpmin(c), smp(c,max(1,jwt(c)))) wh = smp1 - zq(c,max(1,jwt(c))) - !scs: original formulation + !original formulation if(jwt(c) == 0) then qcharge(c) = -ka * (wh_zwt-wh) /((zwt(c)+1.e-3)*1000._r8) else ! qcharge(c) = -ka * (wh_zwt-wh)/((zwt(c)-z(c,jwt(c)))*1000._r8) - !scs: 1/2, assuming flux is at zwt interface, saturation deeper than zwt + !1/2, assuming flux is at zwt interface, saturation deeper than zwt qcharge(c) = -ka * (wh_zwt-wh)/((zwt(c)-z(c,jwt(c)))*1000._r8*2.0) endif @@ -1164,6 +1141,7 @@ subroutine soilwater_moisture_form(bounds, num_hydrologyc, & real(r8) :: vLiqRes(bounds%begc:bounds%endc,1:nlevsoi) ! residual for the volumetric liquid water content (v/v) real(r8) :: dwat_temp + real(r8) :: over_saturation !----------------------------------------------------------------------- associate(& @@ -1177,6 +1155,7 @@ subroutine soilwater_moisture_form(bounds, num_hydrologyc, & qcharge => soilhydrology_inst%qcharge_col , & ! Input: [real(r8) (:) ] aquifer recharge rate (mm/s) zwt => soilhydrology_inst%zwt_col , & ! Input: [real(r8) (:) ] water table depth (m) + watsat => soilstate_inst%watsat_col , & ! Input: [real(r8) (:,:) ] volumetric soil water at saturation (porosity) smp_l => soilstate_inst%smp_l_col , & ! Input: [real(r8) (:,:) ] soil matrix potential [mm] hk_l => soilstate_inst%hk_l_col , & ! Input: [real(r8) (:,:) ] hydraulic conductivity (mm/s) h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Input: [real(r8) (:,:) ] ice water (kg/m2) @@ -1413,10 +1392,10 @@ subroutine soilwater_moisture_form(bounds, num_hydrologyc, & end do ! substep loop -! save number of adaptive substeps used during time step + ! save number of adaptive substeps used during time step nsubsteps(c) = nsubstep -! check for negative moisture values + ! check for negative moisture values do j = 2, nlayers if(h2osoi_liq(c,j) < -1e-6_r8) then write(*,*) 'layer, h2osoi_liq: ', c,j,h2osoi_liq(c,j) @@ -1494,7 +1473,7 @@ subroutine compute_hydraulic_properties(c, nlayers, & character(len=32) :: subname = 'calculate_hydraulic_properties' ! subroutine name !----------------------------------------------------------------------- -!scs: originally, associate statements selected sections rather than +! originally, associate statements selected sections rather than ! entire arrays, but due to pgi bug, removed array section selections ! using array sections allowed consistent 1d indexing throughout associate(& @@ -1621,7 +1600,7 @@ subroutine compute_moisture_fluxes_and_derivs(c, nlayers, & real(r8) :: num, den ! used in calculating qin, qout real(r8) :: dhkds1, dhkds2 !temporary variable real(r8),parameter :: m_to_mm = 1.e3_r8 !convert meters to mm -!scs: temporarily use local variables for the following + ! temporarily use local variables for the following real(r8) :: vwc_liq_ub ! liquid volumetric water content at upper boundary real(r8) :: vwc_liq_lb ! liquid volumetric water content at lower boundary character(len=32) :: subname = 'calculate_moisture_fluxes_and_derivs' ! subroutine name @@ -1704,12 +1683,11 @@ subroutine compute_moisture_fluxes_and_derivs(c, nlayers, & dhkds1 = 0.5_r8 * dhkdw(j) / watsat(c,j) ! derivative w.r.t. volumetric liquid water in the upper layer dhkds2 = 0.5_r8 * dhkdw(j) / watsat(c,j+1) ! derivative w.r.t. volumetric liquid water in the lower layer -!scs: this is how zd is done + ! this is how zd is done if (zdflag == 1) then dhkds1 = dhkdw(j)/(watsat(c,j)+watsat(c,min(nlevsoi, j+1))) dhkds2 = dhkds1 endif -!scs ! compute flux at the bottom of the j-th layer ! NOTE: hk(j) is hydraulic conductivity at the bottom of the j-th @@ -1739,12 +1717,11 @@ subroutine compute_moisture_fluxes_and_derivs(c, nlayers, & ! layer interface w.r.t relative saturation at the interface dhkds1 = 0.5_r8 * dhkdw(j) / watsat(c,j) ! derivative w.r.t. volumetric liquid water in the upper layer dhkds2 = 0.5_r8 * dhkdw(j) / watsat(c,j+1) ! derivative w.r.t. volumetric liquid water in the lower layer -!scs: this is how zd is done + ! this is how zd is done if (zdflag == 1) then dhkds1 = dhkdw(j)/(watsat(c,j)+watsat(c,min(nlevsoi, j+1))) dhkds2 = dhkds1 endif -!scs ! compute flux at the bottom of the j-th layer ! NOTE: hk(j) is hydraulic conductivity at the bottom of the j-th layer @@ -1801,12 +1778,12 @@ subroutine compute_moisture_fluxes_and_derivs(c, nlayers, & ! condition when the water table is a long way below the soil column dhkds1 = dhkdw(j) / watsat(c,j) -!scs: this is how zd is done + ! this is how zd is done if (zdflag == 1) then dhkds1 = dhkdw(j)/(watsat(c,j)+watsat(c,min(nlevsoi, j+1))) dhkds2 = dhkds1 endif -!scs + ! compute flux num = -smp(j) ! NOTE: assume saturation at water table depth (smp=0) den = m_to_mm * (zwt(c) - z(c,j)) @@ -1824,7 +1801,7 @@ subroutine compute_moisture_fluxes_and_derivs(c, nlayers, & ! compute the relative saturation at the lower boundary s1 = vwc_liq_lb / watsat(c,j) -!scs: mc's original expression s1 = (vwc_liq_lb - watres(c,j)) / (watsat(c,j) - watres(c,j)) + ! mc's original expression s1 = (vwc_liq_lb - watres(c,j)) / (watsat(c,j) - watres(c,j)) s1 = min(s1, 1._r8) s1 = max(0.01_r8, s1) diff --git a/src/biogeophys/SurfaceAlbedoMod.F90 b/src/biogeophys/SurfaceAlbedoMod.F90 index d23320d5e7..6628f0fa4d 100644 --- a/src/biogeophys/SurfaceAlbedoMod.F90 +++ b/src/biogeophys/SurfaceAlbedoMod.F90 @@ -261,6 +261,8 @@ subroutine SurfaceAlbedo(bounds,nc, & use abortutils , only : endrun use clm_varctl , only : use_subgrid_fluxes, use_snicar_frc, use_fates use CLMFatesInterfaceMod, only : hlm_fates_interface_type + use landunit_varcon , only : istsoil + use clm_varctl , only : downscale_hillslope_meteorology ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds ! bounds @@ -305,7 +307,6 @@ subroutine SurfaceAlbedo(bounds,nc, & real(r8) :: ws (bounds%begp:bounds%endp) ! fraction of LAI+SAI that is SAI real(r8) :: blai(bounds%begp:bounds%endp) ! lai buried by snow: tlai - elai real(r8) :: bsai(bounds%begp:bounds%endp) ! sai buried by snow: tsai - esai - real(r8) :: coszen_gcell (bounds%begg:bounds%endg) ! cosine solar zenith angle for next time step (grc) real(r8) :: coszen_patch (bounds%begp:bounds%endp) ! cosine solar zenith angle for next time step (patch) real(r8) :: rho(bounds%begp:bounds%endp,numrad) ! leaf/stem refl weighted by fraction LAI and SAI real(r8) :: tau(bounds%begp:bounds%endp,numrad) ! leaf/stem tran weighted by fraction LAI and SAI @@ -334,6 +335,7 @@ subroutine SurfaceAlbedo(bounds,nc, & real(r8) :: mss_cnc_aer_in_fdb (bounds%begc:bounds%endc,-nlevsno+1:0,sno_nbr_aer) ! mass concentration of all aerosol species for feedback calculation (col,lyr,aer) [kg kg-1] real(r8), parameter :: mpe = 1.e-06_r8 ! prevents overflow for division by zero integer , parameter :: nband =numrad ! number of solar radiation waveband classes + real(r8) :: zenith_angle !----------------------------------------------------------------------- associate(& @@ -369,6 +371,8 @@ subroutine SurfaceAlbedo(bounds,nc, & vcmaxcintsha => surfalb_inst%vcmaxcintsha_patch , & ! Output: [real(r8) (:) ] leaf to canopy scaling coefficient, shaded leaf vcmax ncan => surfalb_inst%ncan_patch , & ! Output: [integer (:) ] number of canopy layers nrad => surfalb_inst%nrad_patch , & ! Output: [integer (:) ] number of canopy layers, above snow for radiative transfer + azsun_grc => surfalb_inst%azsun_grc , & ! Output: [real(r8) (:) ] cosine of solar zenith angle + coszen_grc => surfalb_inst%coszen_grc , & ! Output: [real(r8) (:) ] cosine of solar zenith angle coszen_col => surfalb_inst%coszen_col , & ! Output: [real(r8) (:) ] cosine of solar zenith angle albgrd => surfalb_inst%albgrd_col , & ! Output: [real(r8) (:,:) ] ground albedo (direct) albgri => surfalb_inst%albgri_col , & ! Output: [real(r8) (:,:) ] ground albedo (diffuse) @@ -426,16 +430,29 @@ subroutine SurfaceAlbedo(bounds,nc, & ! Cosine solar zenith angle for next time step do g = bounds%begg,bounds%endg - coszen_gcell(g) = shr_orb_cosz (nextsw_cday, grc%lat(g), grc%lon(g), declinp1) + coszen_grc(g) = shr_orb_cosz (nextsw_cday, grc%lat(g), grc%lon(g), declinp1) end do + do c = bounds%begc,bounds%endc g = col%gridcell(c) - coszen_col(c) = coszen_gcell(g) + if (col%is_hillslope_column(c) .and. downscale_hillslope_meteorology) then + ! calculate local incidence angle based on column slope and aspect + zenith_angle = acos(coszen_grc(g)) + + azsun_grc(g) = shr_orb_azimuth(nextsw_cday, grc%lat(g), grc%lon(g), declinp1, zenith_angle) + ! hill_slope is [m/m], convert to radians + coszen_col(c) = shr_orb_cosinc(zenith_angle,azsun_grc(g),atan(col%hill_slope(c)),col%hill_aspect(c)) + + if(coszen_grc(g) > 0._r8 .and. coszen_col(c) < 0._r8) coszen_col(c) = 0._r8 + + else + coszen_col(c) = coszen_grc(g) + endif end do do fp = 1,num_nourbanp p = filter_nourbanp(fp) - g = patch%gridcell(p) - coszen_patch(p) = coszen_gcell(g) + c = patch%column(p) + coszen_patch(p) = coszen_col(c) end do ! Initialize output because solar radiation only done if coszen > 0 diff --git a/src/biogeophys/SurfaceAlbedoType.F90 b/src/biogeophys/SurfaceAlbedoType.F90 index a8b645b84a..ddb57d88f7 100644 --- a/src/biogeophys/SurfaceAlbedoType.F90 +++ b/src/biogeophys/SurfaceAlbedoType.F90 @@ -16,6 +16,8 @@ module SurfaceAlbedoType ! !PUBLIC DATA MEMBERS: type, public :: surfalb_type + real(r8), pointer :: azsun_grc (:) ! azimuth angle of sun + real(r8), pointer :: coszen_grc (:) ! gridcell cosine of solar zenith angle real(r8), pointer :: coszen_col (:) ! col cosine of solar zenith angle real(r8), pointer :: albd_patch (:,:) ! patch surface albedo (direct) (numrad) real(r8), pointer :: albi_patch (:,:) ! patch surface albedo (diffuse) (numrad) @@ -123,11 +125,15 @@ subroutine InitAllocate(this, bounds) ! !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 + allocate(this%azsun_grc (begg:endg)) ; this%azsun_grc (:) = nan + allocate(this%coszen_grc (begg:endg)) ; this%coszen_grc (:) = nan allocate(this%coszen_col (begc:endc)) ; this%coszen_col (:) = nan allocate(this%albgrd_col (begc:endc,numrad)) ; this%albgrd_col (:,:) = nan allocate(this%albgri_col (begc:endc,numrad)) ; this%albgri_col (:,:) = nan @@ -210,15 +216,27 @@ subroutine InitHistory(this, bounds) ! !LOCAL VARIABLES: integer :: begp, endp integer :: begc, endc + integer :: begg, endg character(len=cs) :: defaultoutput !--------------------------------------------------------------------- begp = bounds%begp; endp = bounds%endp begc = bounds%begc; endc = bounds%endc + begg = bounds%begg; endg = bounds%endg + + this%azsun_grc(begg:endg) = spval + call hist_addfld1d (fname='AZSUN', units='radians', & + avgflag='A', long_name='cosine of solar zenith angle', & + ptr_lnd=this%azsun_grc, default='inactive') + + this%coszen_grc(begg:endg) = spval + call hist_addfld1d (fname='COSZEN_GRC', units='none', & + avgflag='A', long_name='cosine of solar zenith angle', & + ptr_lnd=this%coszen_grc, default='inactive') this%coszen_col(begc:endc) = spval call hist_addfld1d (fname='COSZEN', units='none', & - avgflag='A', long_name='cosine of solar zenith angle', & + avgflag='A', long_name='cosine of solar zenith angle (downscaled if downscaling is activated)', & ptr_col=this%coszen_col, default='inactive') this%albgrd_col(begc:endc,:) = spval @@ -418,6 +436,11 @@ subroutine Restart(this, bounds, ncid, flag, & begp = bounds%begp; endp = bounds%endp begc = bounds%begc; endc = bounds%endc + call restartvar(ncid=ncid, flag=flag, varname='coszen_grc', xtype=ncd_double, & + dim1name='gridcell', & + long_name='cosine of solar zenith angle', units='unitless', & + interpinic_flag='interp', readvar=readvar, data=this%coszen_grc) + call restartvar(ncid=ncid, flag=flag, varname='coszen', xtype=ncd_double, & dim1name='column', & long_name='cosine of solar zenith angle', units='unitless', & diff --git a/src/biogeophys/SurfaceRadiationMod.F90 b/src/biogeophys/SurfaceRadiationMod.F90 index 03557c6476..5de3ba6e09 100644 --- a/src/biogeophys/SurfaceRadiationMod.F90 +++ b/src/biogeophys/SurfaceRadiationMod.F90 @@ -383,6 +383,7 @@ subroutine CanopySunShadeFracs(filter_nourbanp, num_nourbanp, & ! local variables integer :: fp ! non-urban filter patch index integer :: p ! patch index + integer :: c ! column index integer :: g ! gridcell index integer :: iv ! canopy layer index integer,parameter :: ipar = 1 ! The band index for PAR @@ -390,7 +391,7 @@ subroutine CanopySunShadeFracs(filter_nourbanp, num_nourbanp, & associate( tlai_z => surfalb_inst%tlai_z_patch, & ! tlai increment for canopy layer fsun_z => surfalb_inst%fsun_z_patch, & ! sunlit fraction of canopy layer elai => canopystate_inst%elai_patch, & ! one-sided leaf area index - forc_solad => atm2lnd_inst%forc_solad_grc, & ! direct beam radiation (W/m**2) + forc_solad_col => atm2lnd_inst%forc_solad_downscaled_col, & ! direct beam radiation, column (W/m**2) forc_solai => atm2lnd_inst%forc_solai_grc, & ! diffuse radiation (W/m**2) fabd_sun_z => surfalb_inst%fabd_sun_z_patch, & ! absorbed sunlit leaf direct PAR fabd_sha_z => surfalb_inst%fabd_sha_z_patch, & ! absorbed shaded leaf direct PAR @@ -440,10 +441,11 @@ subroutine CanopySunShadeFracs(filter_nourbanp, num_nourbanp, & ! are canopy integrated so that layer values equal big leaf values. g = patch%gridcell(p) + c = patch%column(p) do iv = 1, nrad(p) - parsun_z(p,iv) = forc_solad(g,ipar)*fabd_sun_z(p,iv) + forc_solai(g,ipar)*fabi_sun_z(p,iv) - parsha_z(p,iv) = forc_solad(g,ipar)*fabd_sha_z(p,iv) + forc_solai(g,ipar)*fabi_sha_z(p,iv) + parsun_z(p,iv) = forc_solad_col(c,ipar)*fabd_sun_z(p,iv) + forc_solai(g,ipar)*fabi_sun_z(p,iv) + parsha_z(p,iv) = forc_solad_col(c,ipar)*fabd_sha_z(p,iv) + forc_solai(g,ipar)*fabi_sha_z(p,iv) end do end do ! end of fp = 1,num_nourbanp loop @@ -533,7 +535,7 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & associate( & snl => col%snl , & ! Input: [integer (:) ] negative number of snow layers [nbr] - forc_solad => atm2lnd_inst%forc_solad_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (W/m**2) + forc_solad_col => atm2lnd_inst%forc_solad_downscaled_col , & ! Input: [real(r8) (:,:) ] direct beam radiation, column (W/m**2) forc_solai => atm2lnd_inst%forc_solai_grc , & ! Input: [real(r8) (:,:) ] diffuse radiation (W/m**2) snow_depth => waterdiagnosticbulk_inst%snow_depth_col , & ! Input: [real(r8) (:) ] snow height (m) @@ -682,7 +684,7 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & ! Absorbed by canopy - cad(p,ib) = forc_solad(g,ib)*fabd(p,ib) + cad(p,ib) = forc_solad_col(c,ib)*fabd(p,ib) cai(p,ib) = forc_solai(g,ib)*fabi(p,ib) sabv(p) = sabv(p) + cad(p,ib) + cai(p,ib) fsa(p) = fsa(p) + cad(p,ib) + cai(p,ib) @@ -695,8 +697,8 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & ! Transmitted = solar fluxes incident on ground - trd(p,ib) = forc_solad(g,ib)*ftdd(p,ib) - tri(p,ib) = forc_solad(g,ib)*ftid(p,ib) + forc_solai(g,ib)*ftii(p,ib) + trd(p,ib) = forc_solad_col(c,ib)*ftdd(p,ib) + tri(p,ib) = forc_solad_col(c,ib)*ftid(p,ib) + forc_solai(g,ib)*ftii(p,ib) ! Solar radiation absorbed by ground surface ! calculate absorbed solar by soil/snow separately absrad = trd(p,ib)*(1._r8-albsod(c,ib)) + tri(p,ib)*(1._r8-albsoi(c,ib)) @@ -887,29 +889,30 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & do fp = 1,num_nourbanp p = filter_nourbanp(fp) g = patch%gridcell(p) + c = patch%column(p) ! NDVI and reflected solar radiation - rvis = albd(p,1)*forc_solad(g,1) + albi(p,1)*forc_solai(g,1) - rnir = albd(p,2)*forc_solad(g,2) + albi(p,2)*forc_solai(g,2) + rvis = albd(p,1)*forc_solad_col(c,1) + albi(p,1)*forc_solai(g,1) + rnir = albd(p,2)*forc_solad_col(c,2) + albi(p,2)*forc_solai(g,2) fsr(p) = rvis + rnir if (use_SSRE) then - rvisSF = albdSF(p,1)*forc_solad(g,1) + albiSF(p,1)*forc_solai(g,1) - rnirSF = albdSF(p,2)*forc_solad(g,2) + albiSF(p,2)*forc_solai(g,2) + rvisSF = albdSF(p,1)*forc_solad_col(c,1) + albiSF(p,1)*forc_solai(g,1) + rnirSF = albdSF(p,2)*forc_solad_col(c,2) + albiSF(p,2)*forc_solai(g,2) fsrSF(p) = rvisSF + rnirSF ssre_fsr(p) = fsr(p)-fsrSF(p) end if - fsds_vis_d(p) = forc_solad(g,1) - fsds_nir_d(p) = forc_solad(g,2) + fsds_vis_d(p) = forc_solad_col(c,1) + fsds_nir_d(p) = forc_solad_col(c,2) fsds_vis_i(p) = forc_solai(g,1) fsds_nir_i(p) = forc_solai(g,2) - fsr_vis_d(p) = albd(p,1)*forc_solad(g,1) - fsr_nir_d(p) = albd(p,2)*forc_solad(g,2) + fsr_vis_d(p) = albd(p,1)*forc_solad_col(c,1) + fsr_nir_d(p) = albd(p,2)*forc_solad_col(c,2) fsr_vis_i(p) = albi(p,1)*forc_solai(g,1) fsr_nir_i(p) = albi(p,2)*forc_solai(g,2) if (use_SSRE) then - fsrSF_vis_d(p) = albdSF(p,1)*forc_solad(g,1) - fsrSF_nir_d(p) = albdSF(p,2)*forc_solad(g,2) + fsrSF_vis_d(p) = albdSF(p,1)*forc_solad_col(c,1) + fsrSF_nir_d(p) = albdSF(p,2)*forc_solad_col(c,2) fsrSF_vis_i(p) = albiSF(p,1)*forc_solai(g,1) fsrSF_nir_i(p) = albiSF(p,2)*forc_solai(g,2) @@ -919,10 +922,10 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & ssre_fsr_nir_i(p) = fsrSF_nir_i(p)-fsr_nir_i(p) end if if ( is_near_local_noon( grc%londeg(g), deltasec=nint(dtime)/2 ) )then - fsds_vis_d_ln(p) = forc_solad(g,1) - fsds_nir_d_ln(p) = forc_solad(g,2) - fsr_vis_d_ln(p) = albd(p,1)*forc_solad(g,1) - fsr_nir_d_ln(p) = albd(p,2)*forc_solad(g,2) + fsds_vis_d_ln(p) = forc_solad_col(c,1) + fsds_nir_d_ln(p) = forc_solad_col(c,2) + fsr_vis_d_ln(p) = albd(p,1)*forc_solad_col(c,1) + fsr_nir_d_ln(p) = albd(p,2)*forc_solad_col(c,2) fsds_vis_i_ln(p) = forc_solai(g,1) parveg_ln(p) = parveg(p) else @@ -935,8 +938,8 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & end if if (use_SSRE) then if ( is_near_local_noon( grc%londeg(g), deltasec=nint(dtime)/2 ) )then - fsrSF_vis_d_ln(p) = albdSF(p,1)*forc_solad(g,1) - fsrSF_nir_d_ln(p) = albdSF(p,2)*forc_solad(g,2) + fsrSF_vis_d_ln(p) = albdSF(p,1)*forc_solad_col(c,1) + fsrSF_nir_d_ln(p) = albdSF(p,2)*forc_solad_col(c,2) else fsrSF_vis_d_ln(p) = spval fsrSF_nir_d_ln(p) = spval @@ -946,8 +949,8 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & ! (OPTIONAL) c = patch%column(p) if (snl(c) < 0) then - fsds_sno_vd(p) = forc_solad(g,1) - fsds_sno_nd(p) = forc_solad(g,2) + fsds_sno_vd(p) = forc_solad_col(c,1) + fsds_sno_nd(p) = forc_solad_col(c,2) fsds_sno_vi(p) = forc_solai(g,1) fsds_sno_ni(p) = forc_solai(g,2) @@ -972,6 +975,7 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & do fp = 1,num_urbanp p = filter_urbanp(fp) g = patch%gridcell(p) + c = patch%column(p) if(elai(p)==0.0_r8.and.fabd(p,1)>0._r8)then if ( local_debug ) write(iulog,*) 'absorption without LAI',elai(p),tlai(p),fabd(p,1),p @@ -979,15 +983,15 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & ! Solar incident - fsds_vis_d(p) = forc_solad(g,1) - fsds_nir_d(p) = forc_solad(g,2) + fsds_vis_d(p) = forc_solad_col(c,1) + fsds_nir_d(p) = forc_solad_col(c,2) fsds_vis_i(p) = forc_solai(g,1) fsds_nir_i(p) = forc_solai(g,2) ! Determine local noon incident solar if ( is_near_local_noon( grc%londeg(g), deltasec=nint(dtime)/2 ) )then - fsds_vis_d_ln(p) = forc_solad(g,1) - fsds_nir_d_ln(p) = forc_solad(g,2) + fsds_vis_d_ln(p) = forc_solad_col(c,1) + fsds_nir_d_ln(p) = forc_solad_col(c,2) fsds_vis_i_ln(p) = forc_solai(g,1) parveg_ln(p) = 0._r8 else @@ -1000,8 +1004,8 @@ subroutine SurfaceRadiation(bounds, num_nourbanp, filter_nourbanp, & ! Solar reflected ! per unit ground area (roof, road) and per unit wall area (sunwall, shadewall) - fsr_vis_d(p) = albd(p,1) * forc_solad(g,1) - fsr_nir_d(p) = albd(p,2) * forc_solad(g,2) + fsr_vis_d(p) = albd(p,1) * forc_solad_col(c,1) + fsr_nir_d(p) = albd(p,2) * forc_solad_col(c,2) fsr_vis_i(p) = albi(p,1) * forc_solai(g,1) fsr_nir_i(p) = albi(p,2) * forc_solai(g,2) diff --git a/src/biogeophys/SurfaceWaterMod.F90 b/src/biogeophys/SurfaceWaterMod.F90 index b293dd792c..562c64cc18 100644 --- a/src/biogeophys/SurfaceWaterMod.F90 +++ b/src/biogeophys/SurfaceWaterMod.F90 @@ -456,6 +456,7 @@ subroutine QflxH2osfcSurf(bounds, num_hydrologyc, filter_hydrologyc, & real(r8) :: dtime ! land model time step (sec) real(r8) :: frac_infclust ! fraction of submerged area that is connected real(r8) :: k_wet ! linear reservoir coefficient for h2osfc + real(r8),parameter :: min_hill_slope = 1e-3_r8! minimum value of hillslope for outflow character(len=*), parameter :: subname = 'QflxH2osfcSurf' !----------------------------------------------------------------------- @@ -483,6 +484,10 @@ subroutine QflxH2osfcSurf(bounds, num_hydrologyc, filter_hydrologyc, & if(h2osfc(c) > h2osfc_thresh(c) .and. h2osfcflag/=0) then ! spatially variable k_wet k_wet=1.0e-4_r8 * sin((rpi/180._r8) * topo_slope(c)) + if (col%is_hillslope_column(c)) then + ! require a minimum value to ensure non-zero outflow + k_wet = 1e-4_r8 * max(col%hill_slope(c),min_hill_slope) + endif qflx_h2osfc_surf(c) = k_wet * frac_infclust * (h2osfc(c) - h2osfc_thresh(c)) qflx_h2osfc_surf(c)=min(qflx_h2osfc_surf(c),(h2osfc(c) - h2osfc_thresh(c))/dtime) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 21445caaae..ab310650c8 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -732,7 +732,7 @@ subroutine InitCold(this, bounds, & end if else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - this%t_soisno_col(c,1:nlevgrnd) = 272._r8 + this%t_soisno_col(c,1:nlevgrnd) = 274._r8 else if (col%itype(c) == icol_sunwall .or. col%itype(c) == icol_shadewall & .or. col%itype(c) == icol_roof) then ! Set sunwall, shadewall, roof to fairly high temperature to avoid initialization @@ -741,7 +741,7 @@ subroutine InitCold(this, bounds, & end if end if else - this%t_soisno_col(c,1:nlevgrnd) = 274._r8 + this%t_soisno_col(c,1:nlevgrnd) = 272._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 diff --git a/src/biogeophys/UrbanRadiationMod.F90 b/src/biogeophys/UrbanRadiationMod.F90 index 0b6412f2d2..ccb3f196b7 100644 --- a/src/biogeophys/UrbanRadiationMod.F90 +++ b/src/biogeophys/UrbanRadiationMod.F90 @@ -117,9 +117,9 @@ subroutine UrbanRadiation (bounds , & canyon_hwr => lun%canyon_hwr , & ! Input: [real(r8) (:) ] ratio of building height to street width wtroad_perv => lun%wtroad_perv , & ! Input: [real(r8) (:) ] weight of pervious road wrt total road - forc_solad => atm2lnd_inst%forc_solad_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (vis=forc_sols , nir=forc_soll ) (W/m**2) + forc_solad => atm2lnd_inst%forc_solad_not_downscaled_grc , & ! Input: [real(r8) (:,:) ] direct beam radiation (vis=forc_sols , nir=forc_soll ) (W/m**2) forc_solai => atm2lnd_inst%forc_solai_grc , & ! Input: [real(r8) (:,:) ] diffuse beam radiation (vis=forc_sols , nir=forc_soll ) (W/m**2) - forc_solar => atm2lnd_inst%forc_solar_grc , & ! Input: [real(r8) (:) ] incident solar radiation (W/m**2) + forc_solar => atm2lnd_inst%forc_solar_not_downscaled_grc , & ! Input: [real(r8) (:) ] incident solar radiation (W/m**2) forc_lwrad => atm2lnd_inst%forc_lwrad_not_downscaled_grc , & ! Input: [real(r8) (:) ] downward infrared (longwave) radiation (W/m**2) frac_sno => waterdiagnosticbulk_inst%frac_sno_col , & ! Input: [real(r8) (:) ] fraction of ground covered by snow (0 to 1) diff --git a/src/biogeophys/WaterDiagnosticBulkType.F90 b/src/biogeophys/WaterDiagnosticBulkType.F90 index 057062777f..dd556a2df6 100644 --- a/src/biogeophys/WaterDiagnosticBulkType.F90 +++ b/src/biogeophys/WaterDiagnosticBulkType.F90 @@ -16,7 +16,7 @@ module WaterDiagnosticBulkType use shr_log_mod , only : errMsg => shr_log_errMsg use decompMod , only : bounds_type use abortutils , only : endrun - use clm_varctl , only : use_cn, iulog, use_luna + use clm_varctl , only : use_cn, iulog, use_luna, use_hillslope use clm_varpar , only : nlevgrnd, nlevsno, nlevcan, nlevsoi use clm_varcon , only : spval use LandunitType , only : lun @@ -83,6 +83,9 @@ module WaterDiagnosticBulkType real(r8), pointer :: qflx_prec_intr_patch (:) ! patch interception of precipitation (mm H2O/s) real(r8), pointer :: qflx_prec_grnd_col (:) ! col water onto ground including canopy runoff (mm H2O/s) + ! Hillslope stream variables + real(r8), pointer :: stream_water_depth_lun (:) ! landunit depth of water in the streams (m) + contains ! Public interfaces @@ -231,6 +234,7 @@ subroutine InitBulkAllocate(this, bounds) allocate(this%fdry_patch (begp:endp)) ; this%fdry_patch (:) = nan allocate(this%qflx_prec_intr_patch (begp:endp)) ; this%qflx_prec_intr_patch (:) = nan allocate(this%qflx_prec_grnd_col (begc:endc)) ; this%qflx_prec_grnd_col (:) = nan + allocate(this%stream_water_depth_lun (begl:endl)) ; this%stream_water_depth_lun (:) = nan end subroutine InitBulkAllocate @@ -252,12 +256,14 @@ subroutine InitBulkHistory(this, bounds) ! !LOCAL VARIABLES: integer :: begp, endp integer :: begc, endc + integer :: begl, endl integer :: begg, endg real(r8), pointer :: data2dptr(:,:), data1dptr(:) ! temp. pointers for slicing larger arrays !------------------------------------------------------------------------ begp = bounds%begp; endp= bounds%endp begc = bounds%begc; endc= bounds%endc + begl = bounds%begl; endl= bounds%endl begg = bounds%begg; endg= bounds%endg this%h2osno_total_col(begc:endc) = spval @@ -580,6 +586,14 @@ subroutine InitBulkHistory(this, bounds) long_name=this%info%lname('interception'), & ptr_patch=this%qflx_prec_intr_patch, set_lake=0._r8) + if (use_hillslope) then + this%stream_water_depth_lun(begl:endl) = spval + call hist_addfld1d (fname=this%info%fname('STREAM_WATER_DEPTH'), & + units='m', avgflag='A', & + long_name=this%info%lname('depth of water in stream channel (hillslope hydrology only)'), & + ptr_lunit=this%stream_water_depth_lun, l2g_scale_type='natveg', default='inactive') + endif + end subroutine InitBulkHistory !----------------------------------------------------------------------- diff --git a/src/biogeophys/WaterFluxType.F90 b/src/biogeophys/WaterFluxType.F90 index f7c55d44e1..23980a21c9 100644 --- a/src/biogeophys/WaterFluxType.F90 +++ b/src/biogeophys/WaterFluxType.F90 @@ -10,7 +10,7 @@ module WaterFluxType use clm_varpar , only : nlevsno, nlevsoi use clm_varcon , only : spval use decompMod , only : bounds_type - use decompMod , only : subgrid_level_patch, subgrid_level_column, subgrid_level_gridcell + use decompMod , only : subgrid_level_patch, subgrid_level_column, subgrid_level_landunit, subgrid_level_gridcell use LandunitType , only : lun use ColumnType , only : col use AnnualFluxDribbler, only : annual_flux_dribbler_type, annual_flux_dribbler_gridcell @@ -26,7 +26,7 @@ module WaterFluxType class(water_info_base_type), pointer :: info - ! water fluxes are in units or mm/s + ! water fluxes are in units of mm/s real(r8), pointer :: qflx_through_snow_patch (:) ! patch canopy throughfall of snow (mm H2O/s) real(r8), pointer :: qflx_through_liq_patch (:) ! patch canopy throughfal of liquid (rain+irrigation) (mm H2O/s) @@ -72,6 +72,10 @@ module WaterFluxType real(r8), pointer :: qflx_infl_col (:) ! col infiltration (mm H2O /s) real(r8), pointer :: qflx_surf_col (:) ! col total surface runoff (mm H2O /s) real(r8), pointer :: qflx_drain_col (:) ! col sub-surface runoff (mm H2O /s) + real(r8), pointer :: qflx_latflow_in_col (:) ! col hillslope lateral flow input (mm/s) + real(r8), pointer :: qflx_latflow_out_col (:) ! col hillslope lateral flow output (mm/s) + real(r8), pointer :: volumetric_discharge_col (:) ! col hillslope discharge (m3/s) + real(r8), pointer :: volumetric_streamflow_lun(:) ! lun stream discharge (m3/s) real(r8), pointer :: qflx_drain_perched_col (:) ! col sub-surface runoff from perched wt (mm H2O /s) real(r8), pointer :: qflx_top_soil_col (:) ! col net water input into soil from top (mm/s) real(r8), pointer :: qflx_floodc_col (:) ! col flood water flux at column level @@ -278,6 +282,18 @@ subroutine InitAllocate(this, bounds, tracer_vars) call AllocateVar1d(var = this%qflx_drain_perched_col, name = 'qflx_drain_perched_col', & container = tracer_vars, & bounds = bounds, subgrid_level = subgrid_level_column) + call AllocateVar1d(var = this%qflx_latflow_in_col, name = 'qflx_latflow_in_col', & + container = tracer_vars, & + bounds = bounds, subgrid_level = subgrid_level_column) + call AllocateVar1d(var = this%qflx_latflow_out_col, name = 'qflx_latflow_out_col', & + container = tracer_vars, & + bounds = bounds, subgrid_level = subgrid_level_column) + call AllocateVar1d(var = this%volumetric_discharge_col, name = 'volumetric_discharge_col', & + container = tracer_vars, & + bounds = bounds, subgrid_level = subgrid_level_column) + call AllocateVar1d(var = this%volumetric_streamflow_lun, name = 'volumetric_streamflow_lun', & + container = tracer_vars, & + bounds = bounds, subgrid_level = subgrid_level_landunit) call AllocateVar1d(var = this%qflx_top_soil_col, name = 'qflx_top_soil_col', & container = tracer_vars, & bounds = bounds, subgrid_level = subgrid_level_column) @@ -386,6 +402,8 @@ subroutine InitHistory(this, bounds) ! ! !USES: use histFileMod , only : hist_addfld1d, hist_addfld2d, no_snow_normal + use clm_varctl , only : use_hillslope, use_hillslope_routing + ! ! !ARGUMENTS: class(waterflux_type), intent(in) :: this @@ -394,12 +412,14 @@ subroutine InitHistory(this, bounds) ! !LOCAL VARIABLES: integer :: begp, endp integer :: begc, endc + integer :: begl, endl integer :: begg, endg real(r8), pointer :: data2dptr(:,:), data1dptr(:) ! temp. pointers for slicing larger arrays !------------------------------------------------------------------------ begp = bounds%begp; endp= bounds%endp begc = bounds%begc; endc= bounds%endc + begl = bounds%begl; endl= bounds%endl begg = bounds%begg; endg= bounds%endg this%qflx_through_liq_patch(begp:endp) = spval @@ -483,6 +503,37 @@ subroutine InitHistory(this, bounds) long_name=this%info%lname('sub-surface drainage'), & ptr_col=this%qflx_drain_col, c2l_scale_type='urbanf') + if (use_hillslope) then + this%qflx_latflow_out_col(begc:endc) = spval + call hist_addfld1d ( & + fname=this%info%fname('QLATFLOWOUT'), & + units='mm/s', & + avgflag='A', & + long_name=this%info%lname('hillcol lateral outflow'), & + l2g_scale_type='natveg', c2l_scale_type='urbanf', & + ptr_col=this%qflx_latflow_out_col) + + this%volumetric_discharge_col(begc:endc) = spval + call hist_addfld1d ( & + fname=this%info%fname('VOLUMETRIC_DISCHARGE'), & + units='m3/s', & + avgflag='A', & + long_name=this%info%lname('hillslope discharge from column'), & + l2g_scale_type='natveg', c2l_scale_type='urbanf', & + ptr_col=this%volumetric_discharge_col,default='inactive') + + if (use_hillslope_routing) then + this%volumetric_streamflow_lun(begl:endl) = spval + call hist_addfld1d ( & + fname=this%info%fname('VOLUMETRIC_STREAMFLOW'), & + units='m3/s', & + avgflag='A', & + long_name=this%info%lname('volumetric streamflow from hillslope'), & + l2g_scale_type='natveg', & + ptr_lunit=this%volumetric_streamflow_lun) + endif + endif + this%qflx_drain_perched_col(begc:endc) = spval call hist_addfld1d ( & fname=this%info%fname('QDRAI_PERCH'), & @@ -810,6 +861,8 @@ subroutine InitCold(this, bounds) ! ! !USES: use landunit_varcon, only : istsoil, istcrop + use clm_varctl , only : use_hillslope_routing + ! ! !ARGUMENTS: class(waterflux_type), intent(in) :: this @@ -861,9 +914,19 @@ subroutine InitCold(this, bounds) if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then this%qflx_drain_col(c) = 0._r8 this%qflx_surf_col(c) = 0._r8 + this%qflx_latflow_in_col(c) = 0._r8 + this%qflx_latflow_out_col(c) = 0._r8 + this%volumetric_discharge_col(c) = 0._r8 end if end do - + if (use_hillslope_routing) then + do l = bounds%begl, bounds%endl + if (lun%itype(l) == istsoil .or. lun%itype(l) == istcrop) then + this%volumetric_streamflow_lun(l) = 0._r8 + end if + end do + endif + end subroutine InitCold !------------------------------------------------------------------------ diff --git a/src/biogeophys/WaterStateType.F90 b/src/biogeophys/WaterStateType.F90 index cdbefa2a04..390e9e8691 100644 --- a/src/biogeophys/WaterStateType.F90 +++ b/src/biogeophys/WaterStateType.F90 @@ -12,10 +12,10 @@ module WaterStateType use shr_log_mod , only : errMsg => shr_log_errMsg use abortutils , only : endrun use decompMod , only : bounds_type - use decompMod , only : subgrid_level_patch, subgrid_level_column, subgrid_level_gridcell + use decompMod , only : subgrid_level_patch, subgrid_level_column, subgrid_level_landunit, subgrid_level_gridcell use clm_varctl , only : use_bedrock, use_excess_ice, iulog use spmdMod , only : masterproc - use clm_varctl , only : use_fates + use clm_varctl , only : use_fates, use_hillslope use clm_varpar , only : nlevgrnd, nlevsoi, nlevurb, nlevmaxurbgrnd, nlevsno use clm_varcon , only : spval use LandunitType , only : lun @@ -58,6 +58,9 @@ module WaterStateType type(excessicestream_type), private :: exicestream ! stream type for excess ice initialization NUOPC only + ! Hillslope stream variables + real(r8), pointer :: stream_water_volume_lun(:) ! landunit volume of water in the streams (m3) + contains procedure, public :: Init @@ -158,6 +161,9 @@ 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) + call AllocateVar1d(var = this%stream_water_volume_lun, name = 'stream_water_volume_lun', & + container = tracer_vars, & + bounds = bounds, subgrid_level = subgrid_level_landunit) !excess ice vars call AllocateVar2d(var = this%excess_ice_col, name = 'excess_ice_col', & container = tracer_vars, & @@ -178,6 +184,7 @@ subroutine InitHistory(this, bounds, use_aquifer_layer) ! !USES: use histFileMod , only : hist_addfld1d, hist_addfld2d, no_snow_normal use clm_varctl , only : use_soil_moisture_streams + use GridcellType , only : grc ! ! !ARGUMENTS: class(waterstate_type), intent(in) :: this @@ -187,12 +194,14 @@ subroutine InitHistory(this, bounds, use_aquifer_layer) ! !LOCAL VARIABLES: integer :: begp, endp integer :: begc, endc + integer :: begl, endl integer :: begg, endg real(r8), pointer :: data2dptr(:,:), data1dptr(:) ! temp. pointers for slicing larger arrays !------------------------------------------------------------------------ begp = bounds%begp; endp= bounds%endp begc = bounds%begc; endc= bounds%endc + begl = bounds%begl; endl= bounds%endl begg = bounds%begg; endg= bounds%endg data2dptr => this%h2osoi_liq_col(:,-nlevsno+1:0) @@ -284,6 +293,14 @@ subroutine InitHistory(this, bounds, use_aquifer_layer) ptr_col=this%wa_col, l2g_scale_type='veg') end if + if (use_hillslope) then + this%stream_water_volume_lun(begl:endl) = spval + call hist_addfld1d (fname=this%info%fname('STREAM_WATER_VOLUME'), units='m3', & + avgflag='A', & + long_name=this%info%lname('volume of water in stream channel (hillslope hydrology only)'), & + ptr_lunit=this%stream_water_volume_lun, l2g_scale_type='natveg', default='inactive') + end if + ! Add excess ice fields to history if (use_excess_ice) then @@ -345,7 +362,7 @@ subroutine InitCold(this, bounds, & this%h2osfc_col(bounds%begc:bounds%endc) = 0._r8 this%snocan_patch(bounds%begp:bounds%endp) = 0._r8 this%liqcan_patch(bounds%begp:bounds%endp) = 0._r8 - + this%stream_water_volume_lun(bounds%begl:bounds%endl) = 0._r8 !-------------------------------------------- ! Set soil water @@ -709,6 +726,13 @@ subroutine Restart(this, bounds, ncid, flag, & units='kg/m2', & interpinic_flag='interp', readvar=readvar, data=this%dynbal_baseline_ice_col) + call restartvar(ncid=ncid, flag=flag, & + varname=this%info%fname('STREAM_WATER_VOLUME'), & + xtype=ncd_double, & + dim1name='landunit', & + long_name=this%info%lname('water in stream channel'), & + units='m3', & + interpinic_flag='interp', readvar=readvar, data=this%stream_water_volume_lun) ! Restart excess ice vars if (.not. use_excess_ice) then ! no need to even define the restart vars diff --git a/src/biogeophys/Wateratm2lndBulkType.F90 b/src/biogeophys/Wateratm2lndBulkType.F90 index 03ee7522f3..4aacbe11c2 100644 --- a/src/biogeophys/Wateratm2lndBulkType.F90 +++ b/src/biogeophys/Wateratm2lndBulkType.F90 @@ -30,6 +30,8 @@ module Wateratm2lndBulkType real(r8), pointer :: volrmch_grc (:) ! rof volr main channel (m3) real(r8), pointer :: volr_grc (:) ! rof volr total volume (m3) + real(r8), pointer :: tdepth_grc (:) ! rof tributary water depth (m) + real(r8), pointer :: tdepthmax_grc (:) ! rof tributary bankfull water depth (m) real(r8), pointer :: forc_rh_grc (:) ! atmospheric relative humidity (%) real(r8) , pointer :: prec365_col (:) ! col 365-day running mean of tot. precipitation (see comment in UpdateAccVars regarding why this is col-level despite other prec accumulators being patch-level) real(r8) , pointer :: prec60_patch (:) ! patch 60-day running mean of tot. precipitation (mm/s) @@ -117,6 +119,8 @@ subroutine InitBulkAllocate(this, bounds) begc = bounds%begc; endc= bounds%endc begg = bounds%begg; endg= bounds%endg + allocate(this%tdepth_grc (begg:endg)) ; this%tdepth_grc (:) = ival + allocate(this%tdepthmax_grc (begg:endg)) ; this%tdepthmax_grc (:) = ival allocate(this%volr_grc (begg:endg)) ; this%volr_grc (:) = ival allocate(this%volrmch_grc (begg:endg)) ; this%volrmch_grc (:) = ival allocate(this%forc_rh_grc (begg:endg)) ; this%forc_rh_grc (:) = ival @@ -154,6 +158,15 @@ subroutine InitBulkHistory(this, bounds) begp = bounds%begp; endp= bounds%endp begg = bounds%begg; endg= bounds%endg + this%tdepth_grc(begg:endg) = spval + call hist_addfld1d (fname='TDEPTH', units='m', & + avgflag='A', long_name='tributary water depth', & + ptr_lnd=this%tdepth_grc, default = 'inactive') + + this%tdepthmax_grc(begg:endg) = spval + call hist_addfld1d (fname='TDEPTHMAX', units='m', & + avgflag='A', long_name='tributary bankfull water depth', & + ptr_lnd=this%tdepthmax_grc, default = 'inactive') this%volr_grc(begg:endg) = spval call hist_addfld1d (fname=this%info%fname('VOLR'), units='m3', & @@ -462,6 +475,8 @@ subroutine Clean(this) ! rof->lnd deallocate(this%forc_flood_grc) + deallocate(this%tdepth_grc) + deallocate(this%tdepthmax_grc) deallocate(this%volr_grc) deallocate(this%volrmch_grc) diff --git a/src/biogeophys/Wateratm2lndType.F90 b/src/biogeophys/Wateratm2lndType.F90 index 44fe39e58d..18e92c78f7 100644 --- a/src/biogeophys/Wateratm2lndType.F90 +++ b/src/biogeophys/Wateratm2lndType.F90 @@ -171,6 +171,11 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name=this%info%lname('atmospheric specific humidity (downscaled to columns in glacier regions)'), & ptr_col=this%forc_q_downscaled_col, default='inactive') + this%forc_q_not_downscaled_grc(begg:endg) = spval + call hist_addfld1d (fname=this%info%fname('QBOT_NOT_DOWNSCALED'), units='kg/kg', & + avgflag='A', long_name=this%info%lname('atmospheric specific humidity (pre-downscaling)'), & + ptr_lnd=this%forc_q_not_downscaled_grc, default='inactive') + this%forc_flood_grc(begg:endg) = spval call hist_addfld1d (fname=this%info%fname('QFLOOD'), units='mm/s', & avgflag='A', long_name=this%info%lname('runoff from river flooding'), & diff --git a/src/biogeophys/Waterlnd2atmType.F90 b/src/biogeophys/Waterlnd2atmType.F90 index 54972e9b00..80214bebbb 100644 --- a/src/biogeophys/Waterlnd2atmType.F90 +++ b/src/biogeophys/Waterlnd2atmType.F90 @@ -32,6 +32,7 @@ module Waterlnd2atmType real(r8), pointer :: qflx_rofliq_qsub_grc (:) ! rof liq -- subsurface runoff component real(r8), pointer :: qflx_rofliq_qgwl_grc (:) ! rof liq -- glacier, wetland and lakes water balance residual component real(r8), pointer :: qflx_rofliq_drain_perched_grc (:) ! rof liq -- perched water table runoff component + real(r8), pointer :: qflx_rofliq_stream_grc (:) ! rof liq -- stream channel runoff component real(r8), pointer :: qflx_ice_runoff_col(:) ! rof ice forcing, col level real(r8), pointer :: qflx_rofice_grc (:) ! rof ice forcing, grc level real(r8), pointer :: qflx_liq_from_ice_col(:) ! liquid runoff from converted ice runoff @@ -120,6 +121,10 @@ subroutine InitAllocate(this, bounds, tracer_vars) container = tracer_vars, & bounds = bounds, subgrid_level = subgrid_level_gridcell, & ival=ival) + call AllocateVar1d(var = this%qflx_rofliq_stream_grc, name = 'qflx_rofliq_stream_grc', & + container = tracer_vars, & + bounds = bounds, subgrid_level = subgrid_level_gridcell, & + ival=ival) call AllocateVar1d(var = this%qflx_ice_runoff_col, name = 'qflx_ice_runoff_col', & container = tracer_vars, & bounds = bounds, subgrid_level = subgrid_level_column, & diff --git a/src/biogeophys/test/CMakeLists.txt b/src/biogeophys/test/CMakeLists.txt index 49f80533de..5c15858210 100644 --- a/src/biogeophys/test/CMakeLists.txt +++ b/src/biogeophys/test/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(Daylength_test) add_subdirectory(Irrigation_test) add_subdirectory(HumanStress_test) +add_subdirectory(HillslopeHydrology_test) add_subdirectory(SnowHydrology_test) add_subdirectory(Photosynthesis_test) add_subdirectory(Balance_test) diff --git a/src/biogeophys/test/HillslopeHydrology_test/CMakeLists.txt b/src/biogeophys/test/HillslopeHydrology_test/CMakeLists.txt new file mode 100644 index 0000000000..f40baf96ed --- /dev/null +++ b/src/biogeophys/test/HillslopeHydrology_test/CMakeLists.txt @@ -0,0 +1,6 @@ +set (pfunit_sources + test_hillslopehydrologyUtils.pf) + +add_pfunit_ctest(HillslopeHydrologyUtils + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf_wrf_timemgr) diff --git a/src/biogeophys/test/HillslopeHydrology_test/test_hillslopehydrologyUtils.pf b/src/biogeophys/test/HillslopeHydrology_test/test_hillslopehydrologyUtils.pf new file mode 100644 index 0000000000..63db42cffd --- /dev/null +++ b/src/biogeophys/test/HillslopeHydrology_test/test_hillslopehydrologyUtils.pf @@ -0,0 +1,249 @@ +module test_hillslopehydrologyUtils + + ! Tests of the HillslopeHydrologyUtils module + + use funit + use unittestSubgridMod + use ColumnType , only : col + use LandunitType , only : lun + use landunit_varcon , only : istwet + use decompMod , only : bounds_type + use clm_varpar , only : nlevsoi, nlevgrnd + use shr_kind_mod , only : r8 => shr_kind_r8 + use HillslopeHydrologyUtilsMod, only : HillslopeSoilThicknessProfile_linear + + implicit none + + ! From clm_instInit + real(r8), parameter :: soil_depth_lowland = 8.5_r8 + real(r8), parameter :: soil_depth_upland = 2._r8 + + integer, parameter :: nbedrock_dummy_value = 9999 + + @TestCase + type, extends(TestCase) :: TestInit + contains + procedure :: setUp + procedure :: tearDown + end type TestInit + +contains + + subroutine setUp(this) + ! Set up variables needed for tests: various subgrid type variables, along with + ! bounds. + ! + class(TestInit), intent(inout) :: this + integer :: g, l, c + + ! Set up subgrid structure + ! The weights (of both landunits and columns) and column types in the following are + ! arbitrary, since they are not important for these tests + + call unittest_subgrid_setup_start() + + ! Set up gridcell with one landunit and two columns + call unittest_add_gridcell() + call unittest_add_landunit(my_gi=gi, ltype=istwet, wtgcell=0.25_r8) + call unittest_add_column(my_li=li, ctype=1, wtlunit=0.5_r8) + call unittest_add_column(my_li=li, ctype=1, wtlunit=0.5_r8) + + call unittest_subgrid_setup_end() + + ! These will be enabled by specific tests + col%active(begc:endc) = .false. + col%is_hillslope_column(begc:endc) = .false. + + ! Set up hill_distance + l = bounds%begl + do c = lun%coli(l), lun%colf(l) + col%hill_distance(c) = real(c, kind=r8) + end do + + + end subroutine setUp + + subroutine tearDown(this) + ! clean up stuff set up in setup() + use clm_varcon, only: clm_varcon_clean + class(TestInit), intent(inout) :: this + + call unittest_subgrid_teardown() + call clm_varcon_clean() + + end subroutine tearDown + + ! Set up ground/soil structure + subroutine ground_a(bounds) + use clm_varcon, only: clm_varcon_init, zisoi + type(bounds_type), intent(in) :: bounds + real(r8), allocatable :: my_zisoi(:) + + nlevsoi = 5 + allocate(my_zisoi(1:nlevsoi)) + my_zisoi = [0.01_r8, 0.02_r8, 2._r8, 4._r8, 6._r8] + nlevgrnd = size(my_zisoi) + call clm_varcon_init( is_simple_buildtemp = .true.) + zisoi(0) = 0._r8 + zisoi(1:nlevgrnd) = my_zisoi(:) + col%nbedrock(bounds%begc:bounds%endc) = nbedrock_dummy_value + + deallocate(my_zisoi) + end subroutine ground_a + + ! Set up ground/soil structure + subroutine ground_b(bounds) + use clm_varcon, only: clm_varcon_init, zisoi + type(bounds_type), intent(in) :: bounds + real(r8), allocatable :: my_zisoi(:) + + nlevsoi = 3 + allocate(my_zisoi(1:nlevsoi)) + my_zisoi = [0.01_r8, 0.02_r8, 1._r8] + nlevgrnd = size(my_zisoi) + call clm_varcon_init( is_simple_buildtemp = .true.) + zisoi(0) = 0._r8 + zisoi(1:nlevgrnd) = my_zisoi(:) + col%nbedrock(bounds%begc:bounds%endc) = nbedrock_dummy_value + + deallocate(my_zisoi) + end subroutine ground_b + + @Test + subroutine test_HillslopeSoilThicknessProfile_linear(this) + class(TestInit), intent(inout) :: this + integer, allocatable :: nbedrock_expected(:) + integer :: l, c + + l = bounds%begl + + call ground_a(bounds) + col%active(bounds%begc:bounds%endc) = .true. + col%is_hillslope_column(bounds%begc:bounds%endc) = .true. + + ! Get expected values + ! Column 1 soil_depth_col = 8.5 + ! Column 2 soil_depth_col = 2.0 + allocate(nbedrock_expected(bounds%begc:bounds%endc)) + nbedrock_expected(lun%coli(l)) = nbedrock_dummy_value + nbedrock_expected(lun%coli(l) + 1) = 3 + + call HillslopeSoilThicknessProfile_linear(bounds, soil_depth_lowland, soil_depth_upland) + + @assertEqual(nbedrock_expected(lun%coli(l):lun%colf(l)), col%nbedrock(lun%coli(l):lun%colf(l))) + + deallocate(nbedrock_expected) + + end subroutine test_HillslopeSoilThicknessProfile_linear + + @Test + subroutine test_HillslopeSoilThicknessProfile_linear_tooshallow(this) + class(TestInit), intent(inout) :: this + integer, allocatable :: nbedrock_expected(:) + integer :: l, c + + l = bounds%begl + + call ground_b(bounds) + col%active(bounds%begc:bounds%endc) = .true. + col%is_hillslope_column(bounds%begc:bounds%endc) = .true. + + ! Get expected values + ! Column 1 soil_depth_col = 8.5 + ! Column 2 soil_depth_col = 2.0; still too deep for ground_b() + allocate(nbedrock_expected(bounds%begc:bounds%endc)) + nbedrock_expected(lun%coli(l)) = nbedrock_dummy_value + nbedrock_expected(lun%coli(l) + 1) = nbedrock_dummy_value + + call HillslopeSoilThicknessProfile_linear(bounds, soil_depth_lowland, soil_depth_upland) + + @assertEqual(nbedrock_expected(lun%coli(l):lun%colf(l)), col%nbedrock(lun%coli(l):lun%colf(l))) + + deallocate(nbedrock_expected) + + end subroutine test_HillslopeSoilThicknessProfile_linear_tooshallow + + @Test + subroutine test_HillslopeSoilThicknessProfile_linear_noslope(this) + class(TestInit), intent(inout) :: this + integer, allocatable :: nbedrock_expected(:) + integer :: l, c + real(r8) :: toosmall_distance + + l = bounds%begl + + call ground_a(bounds) + col%active(bounds%begc:bounds%endc) = .true. + col%is_hillslope_column(bounds%begc:bounds%endc) = .true. + + ! Get expected values, setting toosmall_distance to something high enough that the (abs(max_hill_dist - min_hill_dist) > toosmall_distance) conditional will fail, causing m = 0.0 + toosmall_distance = 100._r8 + ! Column 1 soil_depth_col = 2.0 + ! Column 2 soil_depth_col = 2.0 + allocate(nbedrock_expected(bounds%begc:bounds%endc)) + nbedrock_expected(lun%coli(l)) = 3 + nbedrock_expected(lun%coli(l) + 1) = 3 + + call HillslopeSoilThicknessProfile_linear(bounds, soil_depth_lowland, soil_depth_upland, toosmall_distance_in=toosmall_distance) + + @assertEqual(nbedrock_expected(lun%coli(l):lun%colf(l)), col%nbedrock(lun%coli(l):lun%colf(l))) + + deallocate(nbedrock_expected) + + end subroutine test_HillslopeSoilThicknessProfile_linear_noslope + + @Test + subroutine test_HillslopeSoilThicknessProfile_linear_inactive(this) + class(TestInit), intent(inout) :: this + integer, allocatable :: nbedrock_expected(:) + integer :: l, c + + l = bounds%begl + + call ground_a(bounds) + col%active(bounds%begc:bounds%endc) = .false. + col%is_hillslope_column(bounds%begc:bounds%endc) = .true. + + ! Get expected values + ! Column 1 soil_depth_col = 8.5 + ! Column 2 soil_depth_col = 2.0, but not active + allocate(nbedrock_expected(bounds%begc:bounds%endc)) + nbedrock_expected(lun%coli(l)) = nbedrock_dummy_value + nbedrock_expected(lun%coli(l) + 1) = nbedrock_dummy_value + + call HillslopeSoilThicknessProfile_linear(bounds, soil_depth_lowland, soil_depth_upland) + + @assertEqual(nbedrock_expected(lun%coli(l):lun%colf(l)), col%nbedrock(lun%coli(l):lun%colf(l))) + + deallocate(nbedrock_expected) + + end subroutine test_HillslopeSoilThicknessProfile_linear_inactive + + @Test + subroutine test_HillslopeSoilThicknessProfile_linear_nohillslope(this) + class(TestInit), intent(inout) :: this + integer, allocatable :: nbedrock_expected(:) + integer :: l, c + + l = bounds%begl + + call ground_a(bounds) + col%active(bounds%begc:bounds%endc) = .true. + col%is_hillslope_column(bounds%begc:bounds%endc) = .false. + + ! Get expected values + ! Column 1 soil_depth_col = 8.5 + ! Column 2 soil_depth_col = 2.0, but not is_hillslope_column + allocate(nbedrock_expected(bounds%begc:bounds%endc)) + nbedrock_expected(lun%coli(l)) = nbedrock_dummy_value + nbedrock_expected(lun%coli(l) + 1) = nbedrock_dummy_value + + call HillslopeSoilThicknessProfile_linear(bounds, soil_depth_lowland, soil_depth_upland) + + @assertEqual(nbedrock_expected(lun%coli(l):lun%colf(l)), col%nbedrock(lun%coli(l):lun%colf(l))) + + deallocate(nbedrock_expected) + + end subroutine test_HillslopeSoilThicknessProfile_linear_nohillslope + +end module test_hillslopehydrologyUtils diff --git a/src/cpl/lilac/lnd_import_export.F90 b/src/cpl/lilac/lnd_import_export.F90 index 281666c3e7..bab24ed37f 100644 --- a/src/cpl/lilac/lnd_import_export.F90 +++ b/src/cpl/lilac/lnd_import_export.F90 @@ -154,11 +154,11 @@ subroutine import_fields( importState, bounds, first_call, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return call state_getimport(importState, 'c2l_fb_atm', 'Faxa_swvdr', bounds, & - output=atm2lnd_inst%forc_solad_grc(:,1), rc=rc) + output=atm2lnd_inst%forc_solad_not_downscaled_grc(:,1), rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return call state_getimport(importState, 'c2l_fb_atm', 'Faxa_swndr', bounds, & - output=atm2lnd_inst%forc_solad_grc(:,2), rc=rc) + output=atm2lnd_inst%forc_solad_not_downscaled_grc(:,2), rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return call state_getimport(importState, 'c2l_fb_atm', 'Faxa_swvdf', bounds, & diff --git a/src/cpl/mct/lnd_import_export.F90 b/src/cpl/mct/lnd_import_export.F90 index 3f7e67af68..537abd49d9 100644 --- a/src/cpl/mct/lnd_import_export.F90 +++ b/src/cpl/mct/lnd_import_export.F90 @@ -10,6 +10,7 @@ module lnd_import_export use Waterlnd2atmBulkType , only: waterlnd2atmbulk_type use Wateratm2lndBulkType , only: wateratm2lndbulk_type use clm_cpl_indices + use GridcellType , only : grc ! implicit none !=============================================================================== @@ -96,8 +97,8 @@ subroutine lnd_import( bounds, x2l, glc_present, atm2lnd_inst, glc2lnd_inst, wat atm2lnd_inst%forc_topo_grc(g) = x2l(index_x2l_Sa_topo,i) ! Atm surface height (m) atm2lnd_inst%forc_u_grc(g) = x2l(index_x2l_Sa_u,i) ! forc_uxy Atm state m/s atm2lnd_inst%forc_v_grc(g) = x2l(index_x2l_Sa_v,i) ! forc_vxy Atm state m/s - atm2lnd_inst%forc_solad_grc(g,2) = x2l(index_x2l_Faxa_swndr,i) ! forc_sollxy Atm flux W/m^2 - atm2lnd_inst%forc_solad_grc(g,1) = x2l(index_x2l_Faxa_swvdr,i) ! forc_solsxy Atm flux W/m^2 + atm2lnd_inst%forc_solad_not_downscaled_grc(g,2) = x2l(index_x2l_Faxa_swndr,i) ! forc_sollxy Atm flux W/m^2 + atm2lnd_inst%forc_solad_not_downscaled_grc(g,1) = x2l(index_x2l_Faxa_swvdr,i) ! forc_solsxy Atm flux W/m^2 atm2lnd_inst%forc_solai_grc(g,2) = x2l(index_x2l_Faxa_swndf,i) ! forc_solldxy Atm flux W/m^2 atm2lnd_inst%forc_solai_grc(g,1) = x2l(index_x2l_Faxa_swvdf,i) ! forc_solsdxy Atm flux W/m^2 diff --git a/src/cpl/nuopc/lnd_import_export.F90 b/src/cpl/nuopc/lnd_import_export.F90 index 5ed5ff76d1..11cc807640 100644 --- a/src/cpl/nuopc/lnd_import_export.F90 +++ b/src/cpl/nuopc/lnd_import_export.F90 @@ -9,7 +9,7 @@ module lnd_import_export use NUOPC_Model , only : NUOPC_ModelGet 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 clm_varctl , only : iulog + use clm_varctl , only : iulog, use_hillslope_routing use clm_time_manager , only : get_nstep use decompmod , only : bounds_type, get_proc_bounds use lnd2atmType , only : lnd2atm_type @@ -99,6 +99,8 @@ module lnd_import_export character(*), parameter :: Flrr_flood = 'Flrr_flood' character(*), parameter :: Flrr_volr = 'Flrr_volr' character(*), parameter :: Flrr_volrmch = 'Flrr_volrmch' + character(*), parameter :: Sr_tdepth = 'Sr_tdepth' + character(*), parameter :: Sr_tdepth_max = 'Sr_tdepth_max' character(*), parameter :: Sg_ice_covered_elev = 'Sg_ice_covered_elev' character(*), parameter :: Sg_topo_elev = 'Sg_topo_elev' character(*), parameter :: Flgg_hflx_elev = 'Flgg_hflx_elev' @@ -388,6 +390,8 @@ subroutine advertise_fields(gcomp, flds_scalar_name, glc_present, cism_evolve, r call fldlist_add(fldsToLnd_num, fldsToLnd, Flrr_flood ) call fldlist_add(fldsToLnd_num, fldsToLnd, Flrr_volr ) call fldlist_add(fldsToLnd_num, fldsToLnd, Flrr_volrmch ) + call fldlist_add(fldsToLnd_num, fldsToLnd, Sr_tdepth ) + call fldlist_add(fldsToLnd_num, fldsToLnd, Sr_tdepth_max ) end if if (glc_present) then @@ -549,9 +553,9 @@ subroutine import_fields( gcomp, bounds, glc_present, rof_prognostic, & if (ChkErr(rc,__LINE__,u_FILE_u)) return call state_getimport_1d(importState, Faxa_lwdn , atm2lnd_inst%forc_lwrad_not_downscaled_grc(begg:), rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return - call state_getimport_1d(importState, Faxa_swvdr, atm2lnd_inst%forc_solad_grc(begg:,1), rc=rc) + call state_getimport_1d(importState, Faxa_swvdr, atm2lnd_inst%forc_solad_not_downscaled_grc(begg:,1), rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return - call state_getimport_1d(importState, Faxa_swndr, atm2lnd_inst%forc_solad_grc(begg:,2), rc=rc) + call state_getimport_1d(importState, Faxa_swndr, atm2lnd_inst%forc_solad_not_downscaled_grc(begg:,2), rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return call state_getimport_1d(importState, Faxa_swvdf, atm2lnd_inst%forc_solai_grc(begg:,1), rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return @@ -608,6 +612,20 @@ subroutine import_fields( gcomp, bounds, glc_present, rof_prognostic, & wateratm2lndbulk_inst%volrmch_grc(:) = 0._r8 end if + if (fldchk(importState, Sr_tdepth)) then + call state_getimport_1d(importState, Sr_tdepth, wateratm2lndbulk_inst%tdepth_grc(begg:), rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + else + wateratm2lndbulk_inst%tdepth_grc(:) = 0._r8 + end if + + if (fldchk(importState, Sr_tdepth_max)) then + call state_getimport_1d(importState, Sr_tdepth_max, wateratm2lndbulk_inst%tdepthmax_grc(begg:), rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + else + wateratm2lndbulk_inst%tdepthmax_grc(:) = 0._r8 + end if + !-------------------------- ! Derived quantities for required fields ! and corresponding error checks @@ -891,6 +909,10 @@ subroutine export_fields( gcomp, bounds, glc_present, rof_prognostic, & do g = begg, endg data1d(g) = waterlnd2atmbulk_inst%qflx_rofliq_qsub_grc(g) + & waterlnd2atmbulk_inst%qflx_rofliq_drain_perched_grc(g) + if (use_hillslope_routing) then + data1d(g) = data1d(g) + & + waterlnd2atmbulk_inst%qflx_rofliq_stream_grc(g) + endif end do call state_setexport_1d(exportState, Flrl_rofsub, data1d(begg:), init_spval=.true., rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return diff --git a/src/cpl/utils/lnd_import_export_utils.F90 b/src/cpl/utils/lnd_import_export_utils.F90 index 4b7941da5b..1b40cb0e6c 100644 --- a/src/cpl/utils/lnd_import_export_utils.F90 +++ b/src/cpl/utils/lnd_import_export_utils.F90 @@ -76,8 +76,11 @@ subroutine derive_quantities( bounds, atm2lnd_inst, wateratm2lndbulk_inst, & atm2lnd_inst%forc_wind_grc(g) = sqrt(atm2lnd_inst%forc_u_grc(g)**2 + atm2lnd_inst%forc_v_grc(g)**2) - atm2lnd_inst%forc_solar_grc(g) = atm2lnd_inst%forc_solad_grc(g,1) + atm2lnd_inst%forc_solai_grc(g,1) + & - atm2lnd_inst%forc_solad_grc(g,2) + atm2lnd_inst%forc_solai_grc(g,2) + atm2lnd_inst%forc_solar_not_downscaled_grc(g) = & + atm2lnd_inst%forc_solad_not_downscaled_grc(g,1) & + + atm2lnd_inst%forc_solai_grc(g,1) & + + atm2lnd_inst%forc_solad_not_downscaled_grc(g,2) & + + atm2lnd_inst%forc_solai_grc(g,2) wateratm2lndbulk_inst%forc_rain_not_downscaled_grc(g) = forc_rainc(g) + forc_rainl(g) wateratm2lndbulk_inst%forc_snow_not_downscaled_grc(g) = forc_snowc(g) + forc_snowl(g) @@ -118,8 +121,8 @@ subroutine check_for_errors( bounds, atm2lnd_inst, wateratm2lndbulk_inst ) call shr_sys_abort( subname//& ' ERROR: Longwave down sent from the atmosphere model is negative or zero' ) end if - if ( (atm2lnd_inst%forc_solad_grc(g,1) < 0.0_r8) .or. & - (atm2lnd_inst%forc_solad_grc(g,2) < 0.0_r8) .or. & + if ( (atm2lnd_inst%forc_solad_not_downscaled_grc(g,1) < 0.0_r8) .or. & + (atm2lnd_inst%forc_solad_not_downscaled_grc(g,2) < 0.0_r8) .or. & (atm2lnd_inst%forc_solai_grc(g,1) < 0.0_r8) .or. & (atm2lnd_inst%forc_solai_grc(g,2) < 0.0_r8) ) then call shr_sys_abort( subname//& @@ -141,6 +144,7 @@ end subroutine check_for_errors !============================================================================= subroutine check_for_nans(array, fname, begg, direction) + use GridcellType , only : grc ! input/output variables real(r8) , intent(in) :: array(:) @@ -159,7 +163,7 @@ subroutine check_for_nans(array, fname, begg, direction) write(iulog,*) 'Which are NaNs = ', isnan(array) do i = 1, size(array) if (isnan(array(i))) then - write(iulog,*) "NaN found in field ", trim(fname), ' at gridcell index ',begg+i-1 + write(iulog,*) "NaN found in field ", trim(fname), ' at gridcell index/lon/lat: ',begg+i-1,grc%londeg(begg+i-1),grc%latdeg(begg+i-1) end if end do call shr_sys_abort(' ERROR: One or more of the CTSM cap '//direction//' fields are NaN ' ) diff --git a/src/main/ColumnType.F90 b/src/main/ColumnType.F90 index 5f57b3ed23..ab7ee8e261 100644 --- a/src/main/ColumnType.F90 +++ b/src/main/ColumnType.F90 @@ -68,8 +68,20 @@ module ColumnType real(r8), pointer :: z_lake (:,:) ! layer depth for lake (m) real(r8), pointer :: lakedepth (:) ! variable lake depth (m) integer , pointer :: nbedrock (:) ! variable depth to bedrock index + ! hillslope hydrology variables + integer, pointer :: col_ndx (:) ! column index of column (hillslope hydrology) + integer, pointer :: colu (:) ! column index of uphill column (hillslope hydrology) + integer, pointer :: cold (:) ! column index of downhill column (hillslope hydrology) + integer, pointer :: hillslope_ndx (:) ! hillslope identifier + real(r8), pointer :: hill_elev (:) ! mean elevation of column relative to stream channel (m) + real(r8), pointer :: hill_slope (:) ! mean along-hill slope (m/m) + real(r8), pointer :: hill_area (:) ! mean surface area (m2) + real(r8), pointer :: hill_width (:) ! across-hill width of bottom boundary of column (m) + real(r8), pointer :: hill_distance (:) ! along-hill distance of column from bottom of hillslope (m) + real(r8), pointer :: hill_aspect (:) ! azimuth angle of column wrt to north, positive to east (radians) ! other column characteristics + logical , pointer :: is_hillslope_column(:) ! true if this column is a hillslope element logical , pointer :: hydrologically_active(:) ! true if this column is a hydrologically active type logical , pointer :: urbpoi (:) ! true=>urban point @@ -130,13 +142,22 @@ subroutine Init(this, begc, endc) allocate(this%lakedepth (begc:endc)) ; this%lakedepth (:) = spval allocate(this%dz_lake (begc:endc,nlevlak)) ; this%dz_lake (:,:) = nan allocate(this%z_lake (begc:endc,nlevlak)) ; this%z_lake (:,:) = nan - + allocate(this%col_ndx (begc:endc)) ; this%col_ndx(:) = ispval + allocate(this%colu (begc:endc)) ; this%colu (:) = ispval + allocate(this%cold (begc:endc)) ; this%cold (:) = ispval + allocate(this%hillslope_ndx(begc:endc)) ; this%hillslope_ndx (:) = ispval + allocate(this%hill_elev(begc:endc)) ; this%hill_elev (:) = spval + allocate(this%hill_slope(begc:endc)) ; this%hill_slope (:) = spval + allocate(this%hill_area(begc:endc)) ; this%hill_area (:) = spval + allocate(this%hill_width(begc:endc)) ; this%hill_width (:) = spval + allocate(this%hill_distance(begc:endc)) ; this%hill_distance (:) = spval + allocate(this%hill_aspect(begc:endc)) ; this%hill_aspect (:) = spval allocate(this%nbedrock (begc:endc)) ; this%nbedrock (:) = ispval allocate(this%levgrnd_class(begc:endc,nlevmaxurbgrnd)) ; this%levgrnd_class(:,:) = ispval allocate(this%micro_sigma (begc:endc)) ; this%micro_sigma (:) = nan allocate(this%topo_slope (begc:endc)) ; this%topo_slope (:) = nan allocate(this%topo_std (begc:endc)) ; this%topo_std (:) = nan - + allocate(this%is_hillslope_column(begc:endc)) ; this%is_hillslope_column(:) = .false. allocate(this%hydrologically_active(begc:endc)) ; this%hydrologically_active(:) = .false. allocate(this%urbpoi (begc:endc)) ; this%urbpoi (:) = .false. @@ -174,9 +195,19 @@ subroutine Clean(this) deallocate(this%topo_std ) deallocate(this%nbedrock ) deallocate(this%levgrnd_class) + deallocate(this%is_hillslope_column) deallocate(this%hydrologically_active) - deallocate(this%urbpoi) - + deallocate(this%col_ndx ) + deallocate(this%colu ) + deallocate(this%cold ) + deallocate(this%hillslope_ndx) + deallocate(this%hill_elev ) + deallocate(this%hill_slope ) + deallocate(this%hill_area ) + deallocate(this%hill_width ) + deallocate(this%hill_distance) + deallocate(this%hill_aspect ) + deallocate(this%urbpoi ) end subroutine Clean !----------------------------------------------------------------------- diff --git a/src/main/LandunitType.F90 b/src/main/LandunitType.F90 index 22770d2334..3a5c68c4f3 100644 --- a/src/main/LandunitType.F90 +++ b/src/main/LandunitType.F90 @@ -32,6 +32,7 @@ module LandunitType integer , pointer :: coli (:) ! beginning column index per landunit integer , pointer :: colf (:) ! ending column index for each landunit integer , pointer :: ncolumns (:) ! number of columns for each landunit + integer , pointer :: nhillslopes (:) ! number of hillslopes for each landunit integer , pointer :: patchi (:) ! beginning patch index for each landunit integer , pointer :: patchf (:) ! ending patch index for each landunit integer , pointer :: npatches (:) ! number of patches for each landunit @@ -52,6 +53,13 @@ module LandunitType real(r8), pointer :: z_0_town (:) ! urban landunit momentum roughness length (m) real(r8), pointer :: z_d_town (:) ! urban landunit displacement height (m) + ! hillslope variables + real(r8), pointer :: stream_channel_depth (:) ! stream channel bankfull depth (m) + real(r8), pointer :: stream_channel_width (:) ! stream channel bankfull width (m) + real(r8), pointer :: stream_channel_length (:) ! stream channel length (m) + real(r8), pointer :: stream_channel_slope (:) ! stream channel slope (m/m) + real(r8), pointer :: stream_channel_number (:) ! number of channels in landunit + contains procedure, public :: Init ! Allocate and initialize @@ -82,6 +90,7 @@ subroutine Init(this, begl, endl) allocate(this%coli (begl:endl)); this%coli (:) = ispval allocate(this%colf (begl:endl)); this%colf (:) = ispval allocate(this%ncolumns (begl:endl)); this%ncolumns (:) = ispval + allocate(this%nhillslopes (begl:endl)); this%nhillslopes(:) = ispval allocate(this%patchi (begl:endl)); this%patchi (:) = ispval allocate(this%patchf (begl:endl)); this%patchf (:) = ispval allocate(this%npatches (begl:endl)); this%npatches (:) = ispval @@ -102,6 +111,13 @@ subroutine Init(this, begl, endl) allocate(this%z_0_town (begl:endl)); this%z_0_town (:) = nan allocate(this%z_d_town (begl:endl)); this%z_d_town (:) = nan + ! Hillslope variables initialized in HillslopeHydrologyMod + allocate(this%stream_channel_depth(begl:endl)); this%stream_channel_depth (:) = nan + allocate(this%stream_channel_width(begl:endl)); this%stream_channel_width (:) = nan + allocate(this%stream_channel_length(begl:endl)); this%stream_channel_length (:) = nan + allocate(this%stream_channel_slope(begl:endl)); this%stream_channel_slope (:) = nan + allocate(this%stream_channel_number(begl:endl)); this%stream_channel_number (:) = nan + end subroutine Init !------------------------------------------------------------------------ @@ -119,6 +135,7 @@ subroutine Clean(this) deallocate(this%coli ) deallocate(this%colf ) deallocate(this%ncolumns ) + deallocate(this%nhillslopes ) deallocate(this%patchi ) deallocate(this%patchf ) deallocate(this%npatches ) @@ -134,7 +151,11 @@ subroutine Clean(this) deallocate(this%wtlunit_roof ) deallocate(this%z_0_town ) deallocate(this%z_d_town ) - + deallocate(this%stream_channel_depth) + deallocate(this%stream_channel_width) + deallocate(this%stream_channel_length) + deallocate(this%stream_channel_slope) + deallocate(this%stream_channel_number) end subroutine Clean end module LandunitType diff --git a/src/main/TopoMod.F90 b/src/main/TopoMod.F90 index e14762cc21..b081c77482 100644 --- a/src/main/TopoMod.F90 +++ b/src/main/TopoMod.F90 @@ -13,8 +13,9 @@ module TopoMod use LandunitType , only : lun use glc2lndMod , only : glc2lnd_type use glcBehaviorMod , only : glc_behavior_type - use landunit_varcon, only : istice + use landunit_varcon, only : istice, istsoil use filterColMod , only : filter_col_type, col_filter_from_logical_array_active_only + use clm_varctl , only : use_hillslope, downscale_hillslope_meteorology ! ! !PUBLIC TYPES: implicit none @@ -139,8 +140,14 @@ subroutine InitCold(this, bounds) ! For other landunits, arbitrarily initialize topo_col to 0 m; for landunits ! where this matters, this will get overwritten in the run loop by values sent ! from CISM - this%topo_col(c) = 0._r8 - this%needs_downscaling_col(c) = .false. + if (col%is_hillslope_column(c) .and. downscale_hillslope_meteorology) then + this%topo_col(c) = col%hill_elev(c) + this%needs_downscaling_col(c) = .true. + else + this%topo_col(c) = 0._r8 + this%needs_downscaling_col(c) = .false. + endif + end if end do @@ -218,7 +225,9 @@ subroutine UpdateTopo(this, bounds, num_icec, filter_icec, & ! ! !LOCAL VARIABLES: integer :: begc, endc - integer :: c, g + integer :: c, l, g + real(r8), allocatable :: mean_hillslope_elevation(:) + real(r8):: mhe_norm character(len=*), parameter :: subname = 'UpdateTopo' !----------------------------------------------------------------------- @@ -240,18 +249,48 @@ subroutine UpdateTopo(this, bounds, num_icec, filter_icec, & this%topo_col(begc:endc), & this%needs_downscaling_col(begc:endc)) - ! For any point that isn't downscaled, set its topo value to the atmosphere's - ! topographic height. This shouldn't matter, but is useful if topo_col is written to - ! the history file. - ! + ! calculate area-weighted mean hillslope elevation on each landunit + if (use_hillslope) then + allocate(mean_hillslope_elevation(bounds%begl:bounds%endl)) + mean_hillslope_elevation(:) = 0._r8 + do l = bounds%begl, bounds%endl + mhe_norm = 0._r8 + do c = lun%coli(l), lun%colf(l) + if (col%is_hillslope_column(c)) then + mean_hillslope_elevation(l) = mean_hillslope_elevation(l) & + + col%hill_elev(c)*col%hill_area(c) + mhe_norm = mhe_norm + col%hill_area(c) + endif + enddo + if (mhe_norm > 0) then + mean_hillslope_elevation(l) = mean_hillslope_elevation(l)/mhe_norm + endif + enddo + endif + ! This could operate over a filter like 'allc' in order to just operate over active ! points, but I'm not sure that would speed things up much, and would require passing ! in this additional filter. + do c = bounds%begc, bounds%endc if (.not. this%needs_downscaling_col(c)) then + ! For any point that isn't already set to be downscaled, set its topo value to the + ! atmosphere's topographic height. This is important for the hillslope block + ! below. For non-hillslope columns, this shouldn't matter, but is useful if + ! topo_col is written to the history file. g = col%gridcell(c) this%topo_col(c) = atm_topo(g) end if + ! If needs_downscaling_col was already set, then that implies + ! that topo_col was previously set by update_glc2lnd_topo. + ! In that case, topo_col should be used as a starting point, + ! rather than the atmosphere's topo value. + if (col%is_hillslope_column(c) .and. downscale_hillslope_meteorology) then + l = col%landunit(c) + this%topo_col(c) = this%topo_col(c) & + + (col%hill_elev(c) - mean_hillslope_elevation(l)) + this%needs_downscaling_col(c) = .true. + endif end do call glc_behavior%update_glc_classes(bounds, this%topo_col(begc:endc)) diff --git a/src/main/atm2lndMod.F90 b/src/main/atm2lndMod.F90 index 11e05f1496..5da4ff6333 100644 --- a/src/main/atm2lndMod.F90 +++ b/src/main/atm2lndMod.F90 @@ -18,12 +18,14 @@ module atm2lndMod use decompMod , only : bounds_type, subgrid_level_gridcell, subgrid_level_column use atm2lndType , only : atm2lnd_type use TopoMod , only : topo_type + use SurfaceAlbedoType, only : surfalb_type use filterColMod , only : filter_col_type use LandunitType , only : lun use ColumnType , only : col use landunit_varcon, only : istice use WaterType , only : water_type use Wateratm2lndBulkType, only : wateratm2lndbulk_type + ! ! !PUBLIC TYPES: implicit none @@ -46,6 +48,9 @@ module atm2lndMod private :: build_normalization ! Compute normalization factors so that downscaled fields are conservative private :: check_downscale_consistency ! Check consistency of downscaling + private :: downscale_hillslope_solar ! Downscale incoming direct solar radiation based on local slope and aspect. + private :: downscale_hillslope_precipitation ! Downscale precipitation based on local topographic height. + character(len=*), parameter, private :: sourcefile = & __FILE__ !----------------------------------------------------------------------- @@ -91,7 +96,7 @@ end subroutine set_atm2lnd_water_tracers !----------------------------------------------------------------------- subroutine downscale_forcings(bounds, & - topo_inst, atm2lnd_inst, wateratm2lndbulk_inst, eflx_sh_precip_conversion) + topo_inst, atm2lnd_inst, surfalb_inst, wateratm2lndbulk_inst, eflx_sh_precip_conversion) ! ! !DESCRIPTION: ! Downscale atmospheric forcing fields from gridcell to column. @@ -111,12 +116,14 @@ subroutine downscale_forcings(bounds, & ! ! !USES: use clm_varcon , only : rair, cpair, grav + use clm_varctl , only : use_hillslope,downscale_hillslope_meteorology use QsatMod , only : Qsat ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds class(topo_type) , intent(in) :: topo_inst type(atm2lnd_type) , intent(inout) :: atm2lnd_inst + class(surfalb_type) , intent(in) :: surfalb_inst type(wateratm2lndbulk_type) , intent(inout) :: wateratm2lndbulk_inst real(r8) , intent(out) :: eflx_sh_precip_conversion(bounds%begc:) ! sensible heat flux from precipitation conversion (W/m**2) [+ to atm] ! @@ -143,6 +150,8 @@ subroutine downscale_forcings(bounds, & ! Gridcell-level metadata: forc_topo_g => atm2lnd_inst%forc_topo_grc , & ! Input: [real(r8) (:)] atmospheric surface height (m) + forc_rain_g => wateratm2lndbulk_inst%forc_rain_not_downscaled_grc , & ! Input: [real(r8) (:)] rain rate [mm/s] + forc_snow_g => wateratm2lndbulk_inst%forc_snow_not_downscaled_grc , & ! Input: [real(r8) (:)] snow rate [mm/s] ! Column-level metadata: topo_c => topo_inst%topo_col , & ! Input: [real(r8) (:)] column surface height (m) @@ -153,13 +162,19 @@ subroutine downscale_forcings(bounds, & forc_q_g => wateratm2lndbulk_inst%forc_q_not_downscaled_grc , & ! Input: [real(r8) (:)] atmospheric specific humidity (kg/kg) forc_pbot_g => atm2lnd_inst%forc_pbot_not_downscaled_grc , & ! Input: [real(r8) (:)] atmospheric pressure (Pa) forc_rho_g => atm2lnd_inst%forc_rho_not_downscaled_grc , & ! Input: [real(r8) (:)] atmospheric density (kg/m**3) - + forc_solad_g => atm2lnd_inst%forc_solad_not_downscaled_grc , & ! Input: [real(r8) (:)] gridcell direct incoming solar radiation + forc_solar_g => atm2lnd_inst%forc_solar_not_downscaled_grc, & ! Input: [real(r8) (:)] gridcell direct incoming solar radiation + ! Column-level downscaled fields: + forc_rain_c => wateratm2lndbulk_inst%forc_rain_downscaled_col , & ! Output: [real(r8) (:)] rain rate [mm/s] + forc_snow_c => wateratm2lndbulk_inst%forc_snow_downscaled_col , & ! Output: [real(r8) (:)] snow rate [mm/s] + forc_q_c => wateratm2lndbulk_inst%forc_q_downscaled_col , & ! Output: [real(r8) (:)] atmospheric specific humidity (kg/kg) forc_t_c => atm2lnd_inst%forc_t_downscaled_col , & ! Output: [real(r8) (:)] atmospheric temperature (Kelvin) forc_th_c => atm2lnd_inst%forc_th_downscaled_col , & ! Output: [real(r8) (:)] atmospheric potential temperature (Kelvin) - forc_q_c => wateratm2lndbulk_inst%forc_q_downscaled_col , & ! Output: [real(r8) (:)] atmospheric specific humidity (kg/kg) forc_pbot_c => atm2lnd_inst%forc_pbot_downscaled_col , & ! Output: [real(r8) (:)] atmospheric pressure (Pa) - forc_rho_c => atm2lnd_inst%forc_rho_downscaled_col & ! Output: [real(r8) (:)] atmospheric density (kg/m**3) + forc_rho_c => atm2lnd_inst%forc_rho_downscaled_col , & ! Output: [real(r8) (:)] atmospheric density (kg/m**3) + forc_solad_c => atm2lnd_inst%forc_solad_downscaled_col , & ! Output: [real(r8) (:)] column direct incoming solar radiation + forc_solar_c => atm2lnd_inst%forc_solar_downscaled_col & ! Output: [real(r8) (:)] column total incoming solar radiation ) ! Initialize column forcing (needs to be done for ALL active columns) @@ -167,11 +182,15 @@ subroutine downscale_forcings(bounds, & if (col%active(c)) then g = col%gridcell(c) + forc_rain_c(c) = forc_rain_g(g) + forc_snow_c(c) = forc_snow_g(g) forc_t_c(c) = forc_t_g(g) forc_th_c(c) = forc_th_g(g) forc_q_c(c) = forc_q_g(g) forc_pbot_c(c) = forc_pbot_g(g) forc_rho_c(c) = forc_rho_g(g) + forc_solar_c(c) = forc_solar_g(g) + forc_solad_c(c,1:numrad) = forc_solad_g(g,1:numrad) end if end do @@ -247,6 +266,12 @@ subroutine downscale_forcings(bounds, & end do + ! adjust hillslope precpitation before repartitioning rain/snow + if (use_hillslope .and. downscale_hillslope_meteorology) then + call downscale_hillslope_solar(bounds, atm2lnd_inst, surfalb_inst) + call downscale_hillslope_precipitation(bounds, topo_inst, atm2lnd_inst, wateratm2lndbulk_inst) + endif + call partition_precip(bounds, atm2lnd_inst, wateratm2lndbulk_inst, & eflx_sh_precip_conversion(bounds%begc:bounds%endc)) @@ -312,10 +337,6 @@ subroutine partition_precip(bounds, atm2lnd_inst, wateratm2lndbulk_inst, eflx_sh SHR_ASSERT_ALL_FL((ubound(eflx_sh_precip_conversion) == (/bounds%endc/)), sourcefile, __LINE__) associate(& - ! Gridcell-level non-downscaled fields: - forc_rain_g => wateratm2lndbulk_inst%forc_rain_not_downscaled_grc , & ! Input: [real(r8) (:)] rain rate [mm/s] - forc_snow_g => wateratm2lndbulk_inst%forc_snow_not_downscaled_grc , & ! Input: [real(r8) (:)] snow rate [mm/s] - ! Column-level downscaled fields: forc_t_c => atm2lnd_inst%forc_t_downscaled_col , & ! Input: [real(r8) (:)] atmospheric temperature (Kelvin) forc_rain_c => wateratm2lndbulk_inst%forc_rain_downscaled_col , & ! Output: [real(r8) (:)] rain rate [mm/s] @@ -328,8 +349,6 @@ subroutine partition_precip(bounds, atm2lnd_inst, wateratm2lndbulk_inst, eflx_sh do c = bounds%begc,bounds%endc if (col%active(c)) then g = col%gridcell(c) - forc_rain_c(c) = forc_rain_g(g) - forc_snow_c(c) = forc_snow_g(g) rain_to_snow_conversion_c(c) = 0._r8 snow_to_rain_conversion_c(c) = 0._r8 eflx_sh_precip_conversion(c) = 0._r8 @@ -719,4 +738,250 @@ subroutine check_downscale_consistency(bounds, atm2lnd_inst, wateratm2lndbulk_in end subroutine check_downscale_consistency + subroutine downscale_hillslope_solar(bounds, atm2lnd_inst, surfalb_inst) + ! + ! !DESCRIPTION: + ! Downscale incoming direct solar radiation based on local slope and aspect. + ! + ! This is currently applied over columns + ! + ! USES + use clm_varpar , only : numrad + + ! !ARGUMENTS: + type(bounds_type) , intent(in) :: bounds + type(surfalb_type) , intent(in) :: surfalb_inst + type(atm2lnd_type) , intent(inout) :: atm2lnd_inst + ! + ! !LOCAL VARIABLES: + integer :: c,l,g,n ! indices + real(r8) :: norm(numrad) + real(r8) :: sum_solar(bounds%begg:bounds%endg,numrad) + real(r8) :: sum_wtgcell(bounds%begg:bounds%endg) + real(r8) :: illum_frac(bounds%begg:bounds%endg) + real(r8), parameter :: illumination_threshold = 0.05 + logical :: checkConservation = .true. + + character(len=*), parameter :: subname = 'downscale_hillslope_solar' + !----------------------------------------------------------------------- + + associate(& + ! Gridcell-level fields: + forc_solai_grc => atm2lnd_inst%forc_solai_grc , & ! Input: [real(r8) (:)] gridcell indirect incoming solar radiation + forc_solad_grc => atm2lnd_inst%forc_solad_not_downscaled_grc , & ! Input: [real(r8) (:)] gridcell direct incoming solar radiation + coszen_grc => surfalb_inst%coszen_grc , & ! Input: [real(r8) (:)] cosine of solar zenith angle + + ! Column-level fields: + coszen_col => surfalb_inst%coszen_col , & ! Input: [real(r8) (:)] cosine of solar zenith angle + forc_solar_col => atm2lnd_inst%forc_solar_downscaled_col , & ! Output: [real(r8) (:)] column total incoming solar radiation + forc_solad_col => atm2lnd_inst%forc_solad_downscaled_col & ! Output: [real(r8) (:)] column direct incoming solar radiation + ) + + ! Initialize column forcing + sum_solar(bounds%begg:bounds%endg,1:numrad) = 0._r8 + sum_wtgcell(bounds%begg:bounds%endg) = 0._r8 + illum_frac(bounds%begg:bounds%endg) = 0._r8 + do c = bounds%begc,bounds%endc + if (col%is_hillslope_column(c) .and. col%active(c)) then + g = col%gridcell(c) + if (coszen_grc(g) > 0._r8) then + forc_solad_col(c,1:numrad) = forc_solad_grc(g,1:numrad)*(coszen_col(c)/coszen_grc(g)) + if (coszen_col(c) > 0._r8) then + illum_frac(g) = illum_frac(g) + col%wtgcell(c) + endif + endif + + sum_solar(g,1:numrad) = sum_solar(g,1:numrad) + col%wtgcell(c)*forc_solad_col(c,1:numrad) + sum_wtgcell(g) = sum_wtgcell(g) + col%wtgcell(c) + end if + end do + + ! Calculate illuminated fraction of gridcell + do g = bounds%begg,bounds%endg + if (sum_wtgcell(g) > 0._r8) then + illum_frac(g) = illum_frac(g)/sum_wtgcell(g) + endif + enddo + + ! Normalize column level solar + do c = bounds%begc,bounds%endc + if (col%is_hillslope_column(c) .and. col%active(c)) then + g = col%gridcell(c) + do n = 1,numrad + ! absorbed energy is solar flux x area landunit (sum_wtgcell) + if(sum_solar(g,n) > 0._r8 .and. illum_frac(g) > illumination_threshold) then + norm(n) = sum_wtgcell(g)*forc_solad_grc(g,n)/sum_solar(g,n) + forc_solad_col(c,n) = forc_solad_col(c,n)*norm(n) + else + forc_solad_col(c,n) = forc_solad_grc(g,n) + endif + enddo + forc_solar_col(c) = sum(forc_solad_col(c,1:numrad))+sum(forc_solai_grc(g,1:numrad)) + end if + + end do + + ! check conservation + if(checkConservation) then + sum_solar(bounds%begg:bounds%endg,1:numrad) = 0._r8 + sum_wtgcell(bounds%begg:bounds%endg) = 0._r8 + ! Calculate normalization (area-weighted solar flux) + do c = bounds%begc,bounds%endc + if (col%is_hillslope_column(c) .and. col%active(c)) then + g = col%gridcell(c) + do n = 1,numrad + sum_solar(g,n) = sum_solar(g,n) + col%wtgcell(c)*forc_solad_col(c,n) + enddo + sum_wtgcell(g) = sum_wtgcell(g) + col%wtgcell(c) + end if + end do + do g = bounds%begg,bounds%endg + do n = 1,numrad + if(abs(sum_solar(g,n) - sum_wtgcell(g)*forc_solad_grc(g,n)) > 1.e-6) then + write(iulog,*) 'downscaled solar not conserved', g, n, sum_solar(g,n), sum_wtgcell(g)*forc_solad_grc(g,n) + call endrun(subgrid_index=g, subgrid_level=subgrid_level_gridcell, & + msg=' ERROR: Energy conservation error downscaling solar'//& + errMsg(sourcefile, __LINE__)) + endif + enddo + enddo + endif + + + end associate + + end subroutine downscale_hillslope_solar + + !----------------------------------------------------------------------- + subroutine downscale_hillslope_precipitation(bounds, & + topo_inst, atm2lnd_inst, wateratm2lndbulk_inst) + ! + ! !DESCRIPTION: + ! Downscale precipitation from gridcell to column. + ! + ! Downscaling is done based on the difference between each CLM column's elevation and + ! the atmosphere's surface elevation (which is the elevation at which the atmospheric + ! forcings are valid). + ! + ! !USES: + use clm_varcon , only : rair, cpair, grav + ! + ! !ARGUMENTS: + type(bounds_type) , intent(in) :: bounds + class(topo_type) , intent(in) :: topo_inst + type(atm2lnd_type) , intent(in) :: atm2lnd_inst + type(wateratm2lndbulk_type) , intent(inout) :: wateratm2lndbulk_inst + ! + ! !LOCAL VARIABLES: + integer :: g, l, c, fc ! indices + + ! temporaries for topo downscaling + real(r8) :: precip_anom, topo_anom + real(r8) :: norm_rain(bounds%begg:bounds%endg) + real(r8) :: norm_snow(bounds%begg:bounds%endg) + real(r8) :: sum_wt(bounds%begg:bounds%endg) + real(r8), parameter :: rain_scalar = 1.5e-3_r8 ! (1/m) + real(r8), parameter :: snow_scalar = 1.5e-3_r8 ! (1/m) + logical :: checkConservation = .true. + character(len=*), parameter :: subname = 'downscale_hillslope_precipitation' + !----------------------------------------------------------------------- + + associate(& + ! Gridcell-level metadata: + forc_topo_g => atm2lnd_inst%forc_topo_grc , & ! Input: [real(r8) (:)] atmospheric surface height (m) + forc_rain_g => wateratm2lndbulk_inst%forc_rain_not_downscaled_grc , & ! Input: [real(r8) (:)] rain rate [mm/s] + forc_snow_g => wateratm2lndbulk_inst%forc_snow_not_downscaled_grc , & ! Input: [real(r8) (:)] snow rate [mm/s] + ! Column-level metadata: + topo_c => topo_inst%topo_col , & ! Input: [real(r8) (:)] column surface height (m) + + ! Column-level downscaled fields: + forc_rain_c => wateratm2lndbulk_inst%forc_rain_downscaled_col , & ! Output: [real(r8) (:)] rain rate [mm/s] + forc_snow_c => wateratm2lndbulk_inst%forc_snow_downscaled_col & ! Output: [real(r8) (:)] snow rate [mm/s] + ) + + ! Redistribute precipitation based on departure + ! of column elevation from mean elevation + + do c = bounds%begc,bounds%endc + g = col%gridcell(c) + if (col%is_hillslope_column(c) .and. col%active(c)) then + + ! spatially uniform normalization, but separate rain/snow + topo_anom = max(-1._r8,(topo_c(c) - forc_topo_g(g))*rain_scalar) ! rain + precip_anom = forc_rain_g(g) * topo_anom + forc_rain_c(c) = forc_rain_c(c) + precip_anom + + topo_anom = max(-1._r8,(topo_c(c) - forc_topo_g(g))*snow_scalar) ! snow + precip_anom = forc_snow_g(g) * topo_anom + forc_snow_c(c) = forc_snow_c(c) + precip_anom + + end if + end do + + ! Initialize arrays of total landunit precipitation + norm_rain(bounds%begg:bounds%endg) = 0._r8 + norm_snow(bounds%begg:bounds%endg) = 0._r8 + sum_wt(bounds%begg:bounds%endg) = 0._r8 + ! Calculate normalization (area-weighted average precipitation) + do c = bounds%begc,bounds%endc + g = col%gridcell(c) + if (col%is_hillslope_column(c) .and. col%active(c)) then + norm_rain(g) = norm_rain(g) + col%wtgcell(c)*forc_rain_c(c) + norm_snow(g) = norm_snow(g) + col%wtgcell(c)*forc_snow_c(c) + sum_wt(g) = sum_wt(g) + col%wtgcell(c) + end if + end do + do g = bounds%begg,bounds%endg + if(sum_wt(g) > 0._r8) then + norm_rain(g) = norm_rain(g) / sum_wt(g) + norm_snow(g) = norm_snow(g) / sum_wt(g) + endif + enddo + + ! Normalize column precipitation to conserve gridcell average + do c = bounds%begc,bounds%endc + g = col%gridcell(c) + if (col%is_hillslope_column(c) .and. col%active(c)) then + if (norm_rain(g) > 0._r8) then + forc_rain_c(c) = forc_rain_c(c) * forc_rain_g(g) / norm_rain(g) + else + forc_rain_c(c) = forc_rain_g(g) + endif + if (norm_snow(g) > 0._r8) then + forc_snow_c(c) = forc_snow_c(c) * forc_snow_g(g) / norm_snow(g) + else + forc_snow_c(c) = forc_snow_g(g) + endif + end if + end do + + ! check conservation + if(checkConservation) then + norm_rain(bounds%begg:bounds%endg) = 0._r8 + norm_snow(bounds%begg:bounds%endg) = 0._r8 + sum_wt(bounds%begg:bounds%endg) = 0._r8 + ! Calculate normalization (area-weighted average precipitation) + do c = bounds%begc,bounds%endc + g = col%gridcell(c) + if (col%is_hillslope_column(c) .and. col%active(c)) then + norm_rain(g) = norm_rain(g) + col%wtgcell(c)*forc_rain_c(c) + norm_snow(g) = norm_snow(g) + col%wtgcell(c)*forc_snow_c(c) + sum_wt(g) = sum_wt(g) + col%wtgcell(c) + end if + end do + do g = bounds%begg,bounds%endg + if(abs(norm_rain(g) - sum_wt(g)*forc_rain_g(g)) > 1.e-6) then + write(iulog,*) 'rain not conserved', g, norm_rain(g), sum_wt(g)*forc_rain_g(g) + endif + if(abs(norm_snow(g) - sum_wt(g)*forc_snow_g(g)) > 1.e-6) then + write(iulog,*) 'snow not conserved', g, norm_snow(g), sum_wt(g)*forc_snow_g(g) + endif + enddo + endif + + end associate + + end subroutine downscale_hillslope_precipitation + + end module atm2lndMod diff --git a/src/main/atm2lndType.F90 b/src/main/atm2lndType.F90 index 53013caf24..298ca4a41d 100644 --- a/src/main/atm2lndType.F90 +++ b/src/main/atm2lndType.F90 @@ -80,9 +80,10 @@ module atm2lndType real(r8), pointer :: forc_vp_grc (:) => null() ! atmospheric vapor pressure (Pa) real(r8), pointer :: forc_pco2_grc (:) => null() ! CO2 partial pressure (Pa) real(r8), pointer :: forc_pco2_240_patch (:) => null() ! 10-day mean CO2 partial pressure (Pa) - real(r8), pointer :: forc_solad_grc (:,:) => null() ! direct beam radiation (numrad) (vis=forc_sols , nir=forc_soll ) + real(r8), pointer :: forc_solad_not_downscaled_grc (:,:) => null() ! direct beam radiation (numrad) (vis=forc_sols , nir=forc_soll ) real(r8), pointer :: forc_solai_grc (:,:) => null() ! diffuse radiation (numrad) (vis=forc_solsd, nir=forc_solld) - real(r8), pointer :: forc_solar_grc (:) => null() ! incident solar radiation + real(r8), pointer :: forc_solar_not_downscaled_grc (:) => null() ! incident solar radiation + real(r8), pointer :: forc_solar_downscaled_col (:) => null() ! incident solar radiation real(r8), pointer :: forc_ndep_grc (:) => null() ! nitrogen deposition rate (gN/m2/s) real(r8), pointer :: forc_pc13o2_grc (:) => null() ! C13O2 partial pressure (Pa) real(r8), pointer :: forc_po2_grc (:) => null() ! O2 partial pressure (Pa) @@ -104,7 +105,7 @@ module atm2lndType real(r8), pointer :: forc_pbot_downscaled_col (:) => null() ! downscaled atm pressure (Pa) real(r8), pointer :: forc_rho_downscaled_col (:) => null() ! downscaled atm density (kg/m**3) real(r8), pointer :: forc_lwrad_downscaled_col (:) => null() ! downscaled atm downwrd IR longwave radiation (W/m**2) - + real(r8), pointer :: forc_solad_downscaled_col (:,:) => null() ! direct beam radiation (numrad) (vis=forc_sols , nir=forc_soll ) ! time averaged quantities real(r8) , pointer :: fsd24_patch (:) => null() ! patch 24hr average of direct beam radiation @@ -475,9 +476,9 @@ subroutine InitAllocate(this, bounds) allocate(this%forc_hgt_q_grc (begg:endg)) ; this%forc_hgt_q_grc (:) = ival allocate(this%forc_vp_grc (begg:endg)) ; this%forc_vp_grc (:) = ival allocate(this%forc_pco2_grc (begg:endg)) ; this%forc_pco2_grc (:) = ival - allocate(this%forc_solad_grc (begg:endg,numrad)) ; this%forc_solad_grc (:,:) = ival + allocate(this%forc_solad_not_downscaled_grc (begg:endg,numrad)) ; this%forc_solad_not_downscaled_grc (:,:) = ival allocate(this%forc_solai_grc (begg:endg,numrad)) ; this%forc_solai_grc (:,:) = ival - allocate(this%forc_solar_grc (begg:endg)) ; this%forc_solar_grc (:) = ival + allocate(this%forc_solar_not_downscaled_grc (begg:endg)) ; this%forc_solar_not_downscaled_grc (:) = ival allocate(this%forc_ndep_grc (begg:endg)) ; this%forc_ndep_grc (:) = ival allocate(this%forc_pc13o2_grc (begg:endg)) ; this%forc_pc13o2_grc (:) = ival allocate(this%forc_po2_grc (begg:endg)) ; this%forc_po2_grc (:) = ival @@ -503,6 +504,8 @@ subroutine InitAllocate(this, bounds) allocate(this%forc_th_downscaled_col (begc:endc)) ; this%forc_th_downscaled_col (:) = ival allocate(this%forc_rho_downscaled_col (begc:endc)) ; this%forc_rho_downscaled_col (:) = ival allocate(this%forc_lwrad_downscaled_col (begc:endc)) ; this%forc_lwrad_downscaled_col (:) = ival + allocate(this%forc_solad_downscaled_col (begc:endc,numrad)) ; this%forc_solad_downscaled_col (:,:) = ival + allocate(this%forc_solar_downscaled_col (begc:endc)) ; this%forc_solar_downscaled_col (:) = ival allocate(this%fsd24_patch (begp:endp)) ; this%fsd24_patch (:) = nan allocate(this%fsd240_patch (begp:endp)) ; this%fsd240_patch (:) = nan @@ -530,6 +533,7 @@ subroutine InitHistory(this, bounds) integer :: begg, endg integer :: begc, endc integer :: begp, endp + real(r8), pointer :: data1dptr(:) ! temp. pointer for slicing larger arrays !--------------------------------------------------------------------- begg = bounds%begg; endg= bounds%endg @@ -545,6 +549,16 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='atmospheric wind velocity magnitude', & ptr_gcell=this%forc_wind_grc, default = 'inactive') + this%forc_u_grc(begg:endg) = spval + call hist_addfld1d (fname='UWIND', units='m/s', & + avgflag='A', long_name='atmospheric U wind velocity magnitude', & + ptr_lnd=this%forc_u_grc, default = 'inactive') + + this%forc_v_grc(begg:endg) = spval + call hist_addfld1d (fname='VWIND', units='m/s', & + avgflag='A', long_name='atmospheric V wind velocity magnitude', & + ptr_lnd=this%forc_v_grc, default = 'inactive') + this%forc_hgt_grc(begg:endg) = spval call hist_addfld1d (fname='ZBOT', units='m', & avgflag='A', long_name='atmospheric reference height', & @@ -555,24 +569,25 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='atmospheric surface height', & ptr_lnd=this%forc_topo_grc) + this%forc_solar_not_downscaled_grc(begg:endg) = spval + call hist_addfld1d (fname='FSDS_from_atm', units='W/m^2', & + avgflag='A', long_name='atmospheric incident solar radiation received from atmosphere (pre-downscaling)', & + ptr_lnd=this%forc_solar_not_downscaled_grc) + + this%forc_o3_grc(begg:endg) = spval call hist_addfld1d (fname='ATM_O3', units='mol/mol', & avgflag='A', long_name='atmospheric ozone partial pressure', & ptr_lnd=this%forc_o3_grc, default = 'inactive') - this%forc_solar_grc(begg:endg) = spval - call hist_addfld1d (fname='FSDS', units='W/m^2', & - avgflag='A', long_name='atmospheric incident solar radiation', & - ptr_lnd=this%forc_solar_grc) - this%forc_pco2_grc(begg:endg) = spval call hist_addfld1d (fname='PCO2', units='Pa', & avgflag='A', long_name='atmospheric partial pressure of CO2', & ptr_lnd=this%forc_pco2_grc) - this%forc_solar_grc(begg:endg) = spval + this%forc_solar_not_downscaled_grc(begg:endg) = spval call hist_addfld1d (fname='SWdown', units='W/m^2', & avgflag='A', long_name='atmospheric incident solar radiation', & - ptr_gcell=this%forc_solar_grc, default='inactive') + ptr_gcell=this%forc_solar_not_downscaled_grc, default='inactive') if (use_lch4) then this%forc_pch4_grc(begg:endg) = spval @@ -586,41 +601,136 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='atmospheric air temperature received from atmosphere (pre-downscaling)', & ptr_gcell=this%forc_t_not_downscaled_grc, default='inactive') + this%forc_solar_downscaled_col(begc:endc) = spval + call hist_addfld1d (fname='FSDS', units='W/m^2', & + avgflag='A', long_name='atmospheric incident solar radiation (downscaled for glacier and hillslope columns)', & + ptr_col=this%forc_solar_downscaled_col) + this%forc_t_downscaled_col(begc:endc) = spval call hist_addfld1d (fname='TBOT', units='K', & - avgflag='A', long_name='atmospheric air temperature (downscaled to columns in glacier regions)', & + avgflag='A', long_name='atmospheric air temperature (downscaled for glacier and hillslope columns)', & ptr_col=this%forc_t_downscaled_col) call hist_addfld1d (fname='Tair', units='K', & - avgflag='A', long_name='atmospheric air temperature (downscaled to columns in glacier regions)', & + avgflag='A', long_name='atmospheric air temperature (downscaled for glacier and hillslope columns)', & ptr_col=this%forc_t_downscaled_col, default='inactive') this%forc_pbot_downscaled_col(begc:endc) = spval call hist_addfld1d (fname='PBOT', units='Pa', & - avgflag='A', long_name='atmospheric pressure at surface (downscaled to columns in glacier regions)', & + avgflag='A', long_name='atmospheric pressure at surface (downscaled for glacier and hillslope columns)', & ptr_col=this%forc_pbot_downscaled_col) call hist_addfld1d (fname='PSurf', units='Pa', & - avgflag='A', long_name='atmospheric pressure at surface (downscaled to columns in glacier regions)', & + avgflag='A', long_name='atmospheric pressure at surface (downscaled for glacier and hillslope columns)', & ptr_col=this%forc_pbot_downscaled_col, default='inactive') + this%forc_pbot_not_downscaled_grc(begg:endg) = spval + call hist_addfld1d (fname='PBOT_NOT_DOWNSCALED', units='Pa', & + avgflag='A', long_name='atmospheric pressure at surface (pre-downscaling)', & + ptr_gcell=this%forc_pbot_not_downscaled_grc, default = 'inactive') + this%forc_lwrad_downscaled_col(begc:endc) = spval call hist_addfld1d (fname='FLDS', units='W/m^2', & - avgflag='A', long_name='atmospheric longwave radiation (downscaled to columns in glacier regions)', & + avgflag='A', long_name='atmospheric longwave radiation (downscaled for glacier and hillslope columns)', & ptr_col=this%forc_lwrad_downscaled_col) call hist_addfld1d (fname='LWdown', units='W/m^2', & - avgflag='A', long_name='atmospheric longwave radiation (downscaled to columns in glacier regions)', & + avgflag='A', long_name='atmospheric longwave radiation (downscaled for glacier and hillslope columns)', & ptr_col=this%forc_lwrad_downscaled_col, default='inactive') + this%forc_lwrad_not_downscaled_grc(begg:endg) = spval + call hist_addfld1d (fname='FLDS_NOT_DOWNSCALED', units='W/m^2', & + avgflag='A', long_name='atmospheric longwave radiation (pre-downscaling)', & + ptr_gcell=this%forc_lwrad_not_downscaled_grc, default = 'inactive') + call hist_addfld1d (fname='FLDS_ICE', units='W/m^2', & avgflag='A', & - long_name='atmospheric longwave radiation (downscaled to columns in glacier regions) (ice landunits only)', & + long_name='atmospheric longwave radiation (downscaled for glacier and hillslope columns) (ice landunits only)', & ptr_col=this%forc_lwrad_downscaled_col, l2g_scale_type='ice', & default='inactive') this%forc_th_downscaled_col(begc:endc) = spval call hist_addfld1d (fname='THBOT', units='K', & - avgflag='A', long_name='atmospheric air potential temperature (downscaled to columns in glacier regions)', & + avgflag='A', long_name='atmospheric air potential temperature (downscaled for glacier and hillslope columns)', & ptr_col=this%forc_th_downscaled_col) + this%forc_th_not_downscaled_grc(begg:endg) = spval + call hist_addfld1d (fname='Thair_from_atm', units='K', & + avgflag='A', long_name='atmospheric air potential temperature (pre-downscaling)', & + ptr_gcell=this%forc_th_not_downscaled_grc, default = 'inactive') + + this%forc_rho_not_downscaled_grc(begg:endg) = spval + call hist_addfld1d (fname='Rho_from_atm', units='kg/m^3', & + avgflag='A', long_name='atmospheric density (pre-downscaling)', & + ptr_gcell=this%forc_rho_not_downscaled_grc, default = 'inactive') + + this%forc_aer_grc(begg:endg,:) = spval + data1dptr => this%forc_aer_grc(begg:endg,1) + call hist_addfld1d (fname='BCPHIDRY', units='kg/m^2/s', & + avgflag='A', long_name='black carbon deposition (phidry) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,2) + call hist_addfld1d (fname='BCPHODRY', units='kg/m^2/s', & + avgflag='A', long_name='black carbon deposition (phodry) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,3) + call hist_addfld1d (fname='BCPHIWET', units='kg/m^2/s', & + avgflag='A', long_name='black carbon deposition (phiwet) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,4) + call hist_addfld1d (fname='OCPHIDRY', units='kg/m^2/s', & + avgflag='A', long_name='organic carbon deposition (phidry) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,5) + call hist_addfld1d (fname='OCPHODRY', units='kg/m^2/s', & + avgflag='A', long_name='black carbon deposition (phodry) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,6) + call hist_addfld1d (fname='OCPHIWET', units='kg/m^2/s', & + avgflag='A', long_name='organic carbon deposition (phiwet) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,7) + call hist_addfld1d (fname='DSTWET1', units='kg/m^2/s', & + avgflag='A', long_name='dust deposition (wet1) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,8) + call hist_addfld1d (fname='DSTDRY1', units='kg/m^2/s', & + avgflag='A', long_name='dust deposition (dry1) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,9) + call hist_addfld1d (fname='DSTWET2', units='kg/m^2/s', & + avgflag='A', long_name='dust deposition (wet2) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,10) + call hist_addfld1d (fname='DSTDRY2', units='kg/m^2/s', & + avgflag='A', long_name='dust deposition (dry2) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,11) + call hist_addfld1d (fname='DSTWET3', units='kg/m^2/s', & + avgflag='A', long_name='dust deposition (wet3) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,12) + call hist_addfld1d (fname='DSTDRY3', units='kg/m^2/s', & + avgflag='A', long_name='dust deposition (dry3) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,13) + call hist_addfld1d (fname='DSTWET4', units='kg/m^2/s', & + avgflag='A', long_name='dust deposition (wet4) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') + + data1dptr => this%forc_aer_grc(begg:endg,14) + call hist_addfld1d (fname='DSTDRY4', units='kg/m^2/s', & + avgflag='A', long_name='dust deposition (dry4) from atmosphere', & + ptr_gcell=data1dptr, default = 'inactive') ! Time averaged quantities this%fsi24_patch(begp:endp) = spval @@ -858,7 +968,7 @@ subroutine UpdateAccVars (this, bounds) ! Accumulate and extract forc_solad24 & forc_solad240 do p = begp,endp g = patch%gridcell(p) - rbufslp(p) = this%forc_solad_grc(g,1) + rbufslp(p) = this%forc_solad_not_downscaled_grc(g,1) end do call update_accum_field ('FSD240', rbufslp , nstep) call extract_accum_field ('FSD240', this%fsd240_patch , nstep) @@ -997,9 +1107,9 @@ subroutine Clean(this) deallocate(this%forc_hgt_q_grc) deallocate(this%forc_vp_grc) deallocate(this%forc_pco2_grc) - deallocate(this%forc_solad_grc) + deallocate(this%forc_solad_not_downscaled_grc) deallocate(this%forc_solai_grc) - deallocate(this%forc_solar_grc) + deallocate(this%forc_solar_not_downscaled_grc) deallocate(this%forc_ndep_grc) deallocate(this%forc_pc13o2_grc) deallocate(this%forc_po2_grc) @@ -1020,6 +1130,8 @@ subroutine Clean(this) deallocate(this%forc_th_downscaled_col) deallocate(this%forc_rho_downscaled_col) deallocate(this%forc_lwrad_downscaled_col) + deallocate(this%forc_solad_downscaled_col) + deallocate(this%forc_solar_downscaled_col) deallocate(this%fsd24_patch) deallocate(this%fsd240_patch) diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index 33e9412ba9..00a98e61b4 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -511,7 +511,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro atm_topo = atm2lnd_inst%forc_topo_grc(bounds_clump%begg:bounds_clump%endg)) call downscale_forcings(bounds_clump, & - topo_inst, atm2lnd_inst, water_inst%wateratm2lndbulk_inst, & + topo_inst, atm2lnd_inst, surfalb_inst, water_inst%wateratm2lndbulk_inst, & eflx_sh_precip_conversion = energyflux_inst%eflx_sh_precip_conversion_col(bounds_clump%begc:bounds_clump%endc)) call set_atm2lnd_water_tracers(bounds_clump, & @@ -1092,7 +1092,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro filter(nc)%num_hydrologyc, filter(nc)%hydrologyc, & filter(nc)%num_urbanc, filter(nc)%urbanc, & filter(nc)%num_do_smb_c, filter(nc)%do_smb_c, & - atm2lnd_inst, glc2lnd_inst, temperature_inst, & + glc2lnd_inst, temperature_inst, & soilhydrology_inst, soilstate_inst, water_inst%waterstatebulk_inst, & water_inst%waterdiagnosticbulk_inst, water_inst%waterbalancebulk_inst, & water_inst%waterfluxbulk_inst, water_inst%wateratm2lndbulk_inst, & diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index 3354c1e7d0..ab59ea5447 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -14,10 +14,10 @@ module clm_initializeMod 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 clm_varctl , only : use_lch4, use_cn, use_cndv, use_c13, use_c14, nhillslope 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 + use clm_instur , only : irrig_method, wt_glc_mec, topo_glc_mec, haslake, ncolumns_hillslope, pct_urban_max use perf_mod , only : t_startf, t_stopf use readParamsMod , only : readParameters use ncdio_pio , only : file_desc_t @@ -64,7 +64,8 @@ subroutine initialize1(dtime) use UrbanParamsType , only: IsSimpleBuildTemp use dynSubgridControlMod , only: dynSubgridControl_init use SoilBiogeochemDecompCascadeConType , only : decomp_cascade_par_init - use CropReprPoolsMod , only: crop_repr_pools_init + use CropReprPoolsMod , only: crop_repr_pools_init + use HillslopeHydrologyMod, only: hillslope_properties_init ! ! !ARGUMENTS integer, intent(in) :: dtime ! model time step (seconds) @@ -114,6 +115,7 @@ subroutine initialize1(dtime) if (masterproc) call control_print() call dynSubgridControl_init(NLFilename) call crop_repr_pools_init() + call hillslope_properties_init(NLFilename) call t_stopf('clm_init1') @@ -135,6 +137,7 @@ subroutine initialize2(ni,nj) use clm_varctl , only : finidat, finidat_interp_source, finidat_interp_dest, fsurdat 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_varctl , only : use_hillslope 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 @@ -176,9 +179,10 @@ subroutine initialize2(ni,nj) use NutrientCompetitionFactoryMod , only : create_nutrient_competition_method use FATESFireFactoryMod , only : scalar_lightning use dynFATESLandUseChangeMod , only : dynFatesLandUseInit + use HillslopeHydrologyMod , only : InitHillslope ! ! !ARGUMENTS - integer, intent(in) :: ni, nj ! global grid sizes + integer, intent(in) :: ni, nj ! global grid sizes ! ! !LOCAL VARIABLES: integer :: c,g,i,j,k,l,n,p ! indices @@ -236,6 +240,9 @@ subroutine initialize2(ni,nj) allocate (wt_glc_mec (begg:endg, maxpatch_glc )) allocate (topo_glc_mec (begg:endg, maxpatch_glc )) allocate (haslake (begg:endg )) + if (use_hillslope) then + allocate (ncolumns_hillslope (begg:endg )) + endif allocate (pct_urban_max(begg:endg, numurbl )) allocate (wt_nat_patch (begg:endg, surfpft_lb:surfpft_ub )) @@ -293,6 +300,11 @@ subroutine initialize2(ni,nj) ! Set global seg maps for gridcells, landlunits, columns and patches call decompInit_glcp(ni, nj, glc_behavior) + if (use_hillslope) then + ! Initialize hillslope properties + call InitHillslope(bounds_proc, fsurdat) + endif + ! Set filters call allocFilters() @@ -318,6 +330,7 @@ subroutine initialize2(ni,nj) ! end of the run for error checking, pct_urban_max is kept through the end of the run ! for reweighting in subgridWeights. deallocate (wt_lunit, wt_cft, wt_glc_mec, haslake) + if (use_hillslope) deallocate (ncolumns_hillslope) ! Determine processor bounds and clumps for this processor call get_proc_bounds(bounds_proc) diff --git a/src/main/clm_instMod.F90 b/src/main/clm_instMod.F90 index 428992402b..b9d74c418a 100644 --- a/src/main/clm_instMod.F90 +++ b/src/main/clm_instMod.F90 @@ -200,6 +200,9 @@ subroutine clm_instInit(bounds) use SoilWaterRetentionCurveFactoryMod , only : create_soil_water_retention_curve use decompMod , only : get_proc_bounds use BalanceCheckMod , only : GetBalanceCheckSkipSteps + use clm_varctl , only : use_hillslope + use HillslopeHydrologyMod , only : SetHillslopeSoilThickness + use initVerticalMod , only : setSoilLayerClass ! ! !ARGUMENTS type(bounds_type), intent(in) :: bounds ! processor bounds @@ -268,6 +271,14 @@ subroutine clm_instInit(bounds) urbanparams_inst%thick_wall(begl:endl), & urbanparams_inst%thick_roof(begl:endl)) + ! Set hillslope column bedrock values + if (use_hillslope) then + call SetHillslopeSoilThickness(bounds,fsurdat, & + soil_depth_lowland_in=8.5_r8,& + soil_depth_upland_in =2.0_r8) + call setSoilLayerClass(bounds) + endif + !----------------------------------------------- ! Set cold-start values for snow levels, snow layers and snow interfaces !----------------------------------------------- diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index e2ad7156ef..389ed50f88 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -152,6 +152,12 @@ module clm_varctl ! true => separate crop landunit is not created by default logical, public :: create_crop_landunit = .false. + ! number of hillslopes per landunit + integer, public :: nhillslope = 0 + + ! maximum number of hillslope columns per landunit + integer, public :: max_columns_hillslope = 1 + ! do not irrigate by default logical, public :: irrigate = .false. @@ -318,6 +324,20 @@ module clm_varctl 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 + + ! FATES history dimension level + ! fates can produce history at either the daily timescale (dynamics) + ! and the model step timescale. It can also generate output on the extra dimension + ! Performing this output can be expensive, so we allow different history dimension + ! levels. + ! The first index is output at the model timescale + ! The second index is output at the dynamics (daily) timescale + ! 0 - no output + ! 1 - include only column level means (3D) + ! 2 - include output that includes the 4th dimension + + integer, dimension(2), public :: fates_history_dimlevel = (/2,2/) + 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 @@ -383,7 +403,15 @@ module clm_varctl integer, public :: soil_layerstruct_userdefined_nlevsoi = iundef !---------------------------------------------------------- - !excess ice physics switch + ! hillslope hydrology switch + !---------------------------------------------------------- + + logical, public :: use_hillslope = .false. ! true => use multi-column hillslope hydrology + logical, public :: downscale_hillslope_meteorology = .false. ! true => downscale meteorological forcing in hillslope model + logical, public :: use_hillslope_routing = .false. ! true => use surface water routing in hillslope hydrology + + !---------------------------------------------------------- + ! excess ice physics switch !---------------------------------------------------------- logical, public :: use_excess_ice = .false. ! true. => use excess ice physics diff --git a/src/main/clm_varsur.F90 b/src/main/clm_varsur.F90 index d360941d23..c49c8bb052 100644 --- a/src/main/clm_varsur.F90 +++ b/src/main/clm_varsur.F90 @@ -45,13 +45,17 @@ module clm_instur ! subgrid glacier_mec sfc elevation real(r8), pointer :: topo_glc_mec(:,:) - + ! whether we have lake to initialise in each grid cell logical , pointer :: haslake(:) + + ! subgrid hillslope hydrology constituents + integer, pointer :: ncolumns_hillslope(:) ! whether we have urban to initialize in each grid cell ! (second dimension goes 1:numurbl) real(r8), pointer :: pct_urban_max(:,:) + !----------------------------------------------------------------------- end module clm_instur diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index d8a0eb6fea..cc56cb8d59 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -45,7 +45,7 @@ module controlMod use SoilBiogeochemLittVertTranspMod , only: som_adv_flux, max_depth_cryoturb use SoilBiogeochemVerticalProfileMod , only: surfprof_exp use SoilBiogeochemNitrifDenitrifMod , only: no_frozen_nitrif_denitrif - use SoilHydrologyMod , only: soilHydReadNML + use SoilHydrologyMod , only: soilHydReadNML, hillslope_hydrology_ReadNML use CNFireFactoryMod , only: CNFireReadNML use CanopyFluxesMod , only: CanopyFluxesReadNML use shr_drydep_mod , only: n_drydep @@ -241,7 +241,8 @@ subroutine control_init(dtime) fates_inventory_ctrl_filename, & fates_parteh_mode, & fates_seeddisp_cadence, & - use_fates_tree_damage + use_fates_tree_damage, & + fates_history_dimlevel ! Ozone vegetation stress method namelist / clm_inparm / o3_veg_stress_method @@ -263,6 +264,11 @@ subroutine control_init(dtime) namelist /clm_inparm/ use_biomass_heat_storage + namelist /clm_inparm/ use_hillslope + + namelist /clm_inparm/ downscale_hillslope_meteorology + + namelist /clm_inparm/ use_hillslope_routing namelist /clm_inparm/ use_hydrstress @@ -496,7 +502,7 @@ subroutine control_init(dtime) 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 @@ -580,8 +586,10 @@ subroutine control_init(dtime) end if call soilHydReadNML( NLFilename ) - - if( use_cn ) then + if ( use_hillslope ) then + call hillslope_hydrology_ReadNML( NLFilename ) + endif + if ( use_cn ) then call CNFireReadNML( NLFilename ) call CNPrecisionControlReadNML( NLFilename ) call CNNDynamicsReadNML ( NLFilename ) @@ -635,7 +643,7 @@ subroutine control_init(dtime) snicar_solarspec /= 'mid_latitude_winter' .or. & snicar_dust_optics /= 'sahara' .or. & snicar_numrad_snw /= 5 .or. & - snicar_snobc_intmix .or. snicar_snodst_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'//& @@ -796,6 +804,7 @@ subroutine control_spmd() 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) + call mpi_bcast (fates_history_dimlevel, 2, MPI_INTEGER, 0, mpicom, ier) ! flexibleCN nitrogen model call mpi_bcast (use_flexibleCN, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -822,6 +831,11 @@ subroutine control_spmd() call mpi_bcast (use_biomass_heat_storage, 1, MPI_LOGICAL, 0, mpicom, ier) + call mpi_bcast (use_hillslope, 1, MPI_LOGICAL, 0, mpicom, ier) + + call mpi_bcast (downscale_hillslope_meteorology, 1, MPI_LOGICAL, 0, mpicom, ier) + + call mpi_bcast (use_hillslope_routing, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_hydrstress, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -1072,6 +1086,7 @@ subroutine control_print () write(iulog,'(a,d20.10)') ' Max snow depth (mm) =', h2osno_max write(iulog,'(a,i8)') ' glc number of elevation classes =', maxpatch_glc + if (glc_do_dynglacier) then write(iulog,*) ' glc CLM glacier areas and topography WILL evolve dynamically' else @@ -1104,6 +1119,9 @@ subroutine control_print () end if write(iulog,*) ' land-ice albedos (unitless 0-1) = ', albice + write(iulog,*) ' hillslope hydrology = ', use_hillslope + write(iulog,*) ' downscale hillslope meteorology = ', downscale_hillslope_meteorology + write(iulog,*) ' hillslope routing = ', use_hillslope_routing write(iulog,*) ' pre-defined soil layer structure = ', soil_layerstruct_predefined write(iulog,*) ' user-defined soil layer structure = ', soil_layerstruct_userdefined write(iulog,*) ' user-defined number of soil layers = ', soil_layerstruct_userdefined_nlevsoi diff --git a/src/main/histFileMod.F90 b/src/main/histFileMod.F90 index fb1a25db37..863bf6e987 100644 --- a/src/main/histFileMod.F90 +++ b/src/main/histFileMod.F90 @@ -16,7 +16,7 @@ module histFileMod use clm_varctl , only : iulog, use_fates, compname, use_cn, use_crop use clm_varcon , only : spval, ispval use clm_varcon , only : grlnd, nameg, namel, namec, namep - use decompMod , only : get_proc_bounds, get_proc_global, bounds_type, get_global_index_array + use decompMod , only : get_proc_bounds, get_proc_global, bounds_type, get_global_index, get_global_index_array use decompMod , only : subgrid_level_gridcell, subgrid_level_landunit, subgrid_level_column use GridcellType , only : grc use LandunitType , only : lun @@ -180,7 +180,7 @@ module histFileMod private :: hist_set_snow_field_2d ! Set values in history field dimensioned by levsno private :: list_index ! Find index of field in exclude list private :: set_hist_filename ! Determine history dataset filenames - private :: getname ! Retrieve name portion of input "inname" + public :: getname ! Retrieve name portion of input "inname" (PUBLIC FOR FATES) private :: getflag ! Retrieve flag 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 @@ -2329,6 +2329,7 @@ subroutine htape_create (t, histrest) use landunit_varcon , only : max_lunit use clm_varctl , only : caseid, ctitle, fsurdat, finidat, paramfile use clm_varctl , only : version, hostname, username, conventions, source + use clm_varctl , only : use_hillslope,nhillslope,max_columns_hillslope use domainMod , only : ldomain use fileutils , only : get_filename ! @@ -2466,6 +2467,10 @@ subroutine htape_create (t, histrest) call ncd_defdim(lnfid, 'ltype', max_lunit, dimid) call ncd_defdim(lnfid, 'nlevcan',nlevcan, dimid) call ncd_defdim(lnfid, 'nvegwcs',nvegwcs, dimid) + if (use_hillslope) then + call ncd_defdim(lnfid, 'nhillslope',nhillslope, dimid) + call ncd_defdim(lnfid, 'max_columns_hillslope',max_columns_hillslope, dimid) + endif call ncd_defdim(lnfid, 'mxsowings' , mxsowings , dimid) call ncd_defdim(lnfid, 'mxharvests' , mxharvests , dimid) call htape_add_ltype_metadata(lnfid) @@ -2487,7 +2492,6 @@ subroutine htape_create (t, histrest) call ncd_defdim(lnfid, 'scale_type_string_length', scale_type_strlen, dimid) call ncd_defdim( lnfid, 'levdcmp', nlevdecomp_full, dimid) - if(use_fates)then call ncd_defdim(lnfid, 'fates_levscag', nlevsclass * nlevage, dimid) call ncd_defdim(lnfid, 'fates_levscagpf', nlevsclass * nlevage * numpft_fates, dimid) @@ -2730,6 +2734,7 @@ subroutine htape_timeconst3D(t, & 'lake', & ! ZLAKE 'lake' & ! DZLAKE ] + !----------------------------------------------------------------------- SHR_ASSERT_ALL_FL((ubound(watsat_col) == (/bounds%endc, nlevmaxurbgrnd/)), sourcefile, __LINE__) @@ -3024,7 +3029,8 @@ subroutine htape_timeconst(t, mode) ! ! !USES: use clm_varpar , only : nlevsoi - use clm_varcon , only : zsoi, zlak, secspday, isecspday, isecsphr, isecspmin + use clm_varctl , only : use_hillslope + use clm_varcon , only : zsoi, zlak, secspday, isecspday, isecsphr, isecspmin, ispval use domainMod , only : ldomain, lon1d, lat1d use clm_time_manager, only : get_nstep, get_curr_date, get_curr_time use clm_time_manager, only : get_ref_date, get_calendar, NO_LEAP_C, GREGORIAN_C @@ -3079,7 +3085,7 @@ subroutine htape_timeconst(t, mode) ! integer :: sec_hist_nhtfrq ! hist_nhtfrq converted to seconds ! !LOCAL VARIABLES: - integer :: vid,n,i,j,m ! indices + integer :: vid,n,i,j,m,c ! indices integer :: nstep ! current step integer :: mcsec ! seconds of current date integer :: mdcur ! current day @@ -3105,6 +3111,9 @@ subroutine htape_timeconst(t, mode) real(r8), pointer :: histo(:,:) ! temporary integer :: status real(r8) :: zsoi_1d(1) + type(bounds_type) :: bounds + integer :: ier ! error status + integer, pointer :: icarr(:) ! temporary character(len=*),parameter :: subname = 'htape_timeconst' !----------------------------------------------------------------------- @@ -3112,6 +3121,9 @@ subroutine htape_timeconst(t, mode) !*** Time constant grid variables only on first time-sample of file *** !------------------------------------------------------------------------------- + call get_proc_bounds(bounds) + + if (tape(t)%ntimes == 1) then if (mode == 'define') then call ncd_defvar(varname='levgrnd', xtype=tape(t)%ncprec, & @@ -3126,6 +3138,36 @@ subroutine htape_timeconst(t, mode) call ncd_defvar(varname='levdcmp', xtype=tape(t)%ncprec, dim1name='levdcmp', & long_name='coordinate levels for soil decomposition variables', units='m', ncid=nfid(t)) + if (use_hillslope .and. .not.tape(t)%dov2xy)then + call ncd_defvar(varname='hillslope_distance', xtype=ncd_double, & + dim1name=namec, long_name='hillslope column distance', & + units='m', ncid=nfid(t)) + call ncd_defvar(varname='hillslope_width', xtype=ncd_double, & + dim1name=namec, long_name='hillslope column width', & + units='m', ncid=nfid(t)) + call ncd_defvar(varname='hillslope_area', xtype=ncd_double, & + dim1name=namec, long_name='hillslope column area', & + units='m', ncid=nfid(t)) + call ncd_defvar(varname='hillslope_elev', xtype=ncd_double, & + dim1name=namec, long_name='hillslope column elevation', & + units='m', ncid=nfid(t)) + call ncd_defvar(varname='hillslope_slope', xtype=ncd_double, & + dim1name=namec, long_name='hillslope column slope', & + units='m', ncid=nfid(t)) + call ncd_defvar(varname='hillslope_aspect', xtype=ncd_double, & + dim1name=namec, long_name='hillslope column aspect', & + units='m', ncid=nfid(t)) + call ncd_defvar(varname='hillslope_index', xtype=ncd_int, & + dim1name=namec, long_name='hillslope index', & + ncid=nfid(t)) + call ncd_defvar(varname='hillslope_cold', xtype=ncd_int, & + dim1name=namec, long_name='hillslope downhill column index', & + ncid=nfid(t)) + call ncd_defvar(varname='hillslope_colu', xtype=ncd_int, & + dim1name=namec, long_name='hillslope uphill column index', & + ncid=nfid(t)) + end if + if(use_fates)then call ncd_defvar(varname='fates_levscls', xtype=tape(t)%ncprec, dim1name='fates_levscls', & @@ -3214,6 +3256,44 @@ subroutine htape_timeconst(t, mode) zsoi_1d(1) = 1._r8 call ncd_io(varname='levdcmp', data=zsoi_1d, ncid=nfid(t), flag='write') end if + + if (use_hillslope .and. .not.tape(t)%dov2xy) then + call ncd_io(varname='hillslope_distance' , data=col%hill_distance, dim1name=namec, ncid=nfid(t), flag='write') + call ncd_io(varname='hillslope_width' , data=col%hill_width, dim1name=namec, ncid=nfid(t), flag='write') + call ncd_io(varname='hillslope_area' , data=col%hill_area, dim1name=namec, ncid=nfid(t), flag='write') + call ncd_io(varname='hillslope_elev' , data=col%hill_elev, dim1name=namec, ncid=nfid(t), flag='write') + call ncd_io(varname='hillslope_slope' , data=col%hill_slope, dim1name=namec, ncid=nfid(t), flag='write') + call ncd_io(varname='hillslope_aspect' , data=col%hill_aspect, dim1name=namec, ncid=nfid(t), flag='write') + call ncd_io(varname='hillslope_index' , data=col%hillslope_ndx, dim1name=namec, ncid=nfid(t), flag='write') + + ! write global indices rather than local indices + allocate(icarr(bounds%begc:bounds%endc),stat=ier) + if (ier /= 0) then + call endrun(msg=' allocation error of icarr'//errMsg(sourcefile, __LINE__)) + end if + + do c = bounds%begc,bounds%endc + if (col%cold(c) /= ispval) then + icarr(c)= get_global_index(subgrid_index=col%cold(c), subgrid_level=subgrid_level_column) + else + icarr(c)= col%cold(c) + endif + enddo + + call ncd_io(varname='hillslope_cold' , data=icarr, dim1name=namec, ncid=nfid(t), flag='write') + + do c = bounds%begc,bounds%endc + if (col%colu(c) /= ispval) then + icarr(c)= get_global_index(subgrid_index=col%colu(c), subgrid_level=subgrid_level_column) + else + icarr(c)= col%colu(c) + endif + enddo + + call ncd_io(varname='hillslope_colu' , data=icarr, dim1name=namec, ncid=nfid(t), flag='write') + deallocate(icarr) + endif + if(use_fates)then call ncd_io(varname='fates_scmap_levscag',data=fates_hdim_scmap_levscag, ncid=nfid(t), flag='write') call ncd_io(varname='fates_agmap_levscag',data=fates_hdim_agmap_levscag, ncid=nfid(t), flag='write') @@ -3765,6 +3845,9 @@ subroutine hfields_1dinfo(t, mode) call ncd_defvar(varname='cols1d_active', xtype=ncd_log, dim1name=namec, & long_name='true => do computations on this column', ifill_value=0, ncid=ncid) + call ncd_defvar(varname='cols1d_nbedrock', xtype=ncd_int, dim1name=namec, & + long_name='column bedrock depth index', ifill_value=ispval, ncid=ncid) + ! Define patch info call ncd_defvar(varname='pfts1d_lon', xtype=ncd_double, dim1name=namep, & @@ -3912,6 +3995,7 @@ subroutine hfields_1dinfo(t, mode) call ncd_io(varname='cols1d_itype_lunit', data=icarr , dim1name=namec, ncid=ncid, flag='write') call ncd_io(varname='cols1d_active' , data=col%active , dim1name=namec, ncid=ncid, flag='write') + call ncd_io(varname='cols1d_nbedrock', data=col%nbedrock , dim1name=namec, ncid=ncid, flag='write') ! Write patch info @@ -4107,7 +4191,7 @@ subroutine hist_htapes_wrapup( rstwr, nlend, bounds, & call htape_timeconst(t, mode='define') ! Define 3D time-constant field variables on first history tapes - if ( do_3Dtconst) then + if ( do_3Dtconst .and. t == 1) then call htape_timeconst3D(t, & bounds, watsat_col, sucsat_col, bsw_col, hksat_col, & cellsand_col, cellclay_col, mode='define') @@ -4127,7 +4211,7 @@ subroutine hist_htapes_wrapup( rstwr, nlend, bounds, & call htape_timeconst(t, mode='write') ! Write 3D time constant history variables to first history tapes - if ( do_3Dtconst .and. tape(t)%ntimes == 1 )then + if ( do_3Dtconst .and. t == 1 .and. tape(t)%ntimes == 1 )then call htape_timeconst3D(t, & bounds, watsat_col, sucsat_col, bsw_col, hksat_col, & cellsand_col, cellclay_col, mode='write') @@ -4581,7 +4665,6 @@ subroutine hist_restart_ncd (bounds, ncid, flag, rdate) start(1)=1 - ! ! Add history namelist data to each history restart tape ! diff --git a/src/main/initGridCellsMod.F90 b/src/main/initGridCellsMod.F90 index 99303c32da..44bc9361b2 100644 --- a/src/main/initGridCellsMod.F90 +++ b/src/main/initGridCellsMod.F90 @@ -216,7 +216,7 @@ subroutine set_landunit_veg_compete (ltype, gi, li, ci, pi) integer , intent(inout) :: pi ! patch index ! ! !LOCAL VARIABLES: - integer :: m ! index + integer :: m, ci2 ! index integer :: npatches ! number of patches in landunit integer :: ncols integer :: nlunits @@ -224,6 +224,7 @@ subroutine set_landunit_veg_compete (ltype, gi, li, ci, pi) integer :: ncols_added ! number of columns actually added integer :: nlunits_added ! number of landunits actually added real(r8) :: wtlunit2gcell ! landunit weight in gridcell + real(r8) :: wtcol2lunit ! column weight in landunit real(r8) :: p_wt ! patch weight (0-1) !------------------------------------------------------------------------ @@ -240,31 +241,37 @@ subroutine set_landunit_veg_compete (ltype, gi, li, ci, pi) if (nlunits > 0) then call add_landunit(li=li, gi=gi, ltype=ltype, wtgcell=wtlunit2gcell) nlunits_added = nlunits_added + 1 - - ! Assume one column on the landunit - call add_column(ci=ci, li=li, ctype=1, wtlunit=1.0_r8) - ncols_added = ncols_added + 1 - - ! For FATES: the total number of patches may not match what is in the surface - ! file, and therefor the weighting can't be used. The weightings in - ! wt_nat_patch may be meaningful (like with fixed biogeography), but they - ! they need a mapping table to connect to the allocated patches (in fates) - ! so the wt_nat_patch array is not applicable to these area weights - ! A subsequent call, via the clmfates interface will update these weights - ! by using said mapping table - - do m = natpft_lb,natpft_ub - if (natveg_patch_exists(gi, m)) then - if(use_fates .and. .not.use_fates_sp)then - p_wt = 1.0_r8/real(natpft_size,r8) - else - p_wt = wt_nat_patch(gi,m) + + ! Potentially create multiple columns (e.g., for hillslope hydrology), but each + ! with the same PFT breakdown. + ! + ! Set column weight arbitrarily for now. If we have multiple columns because we're + ! using hillslope hydrology, then col%wtlunit will be modified in InitHillslope. + wtcol2lunit = 1.0_r8/real(ncols,r8) + do ci2 = 1,ncols + call add_column(ci=ci, li=li, ctype=1, wtlunit=wtcol2lunit) + ncols_added = ncols_added + 1 + + ! For FATES: the total number of patches may not match what is in the surface + ! file, and therefor the weighting can't be used. The weightings in + ! wt_nat_patch may be meaningful (like with fixed biogeography), but they + ! they need a mapping table to connect to the allocated patches (in fates) + ! so the wt_nat_patch array is not applicable to these area weights + ! A subsequent call, via the clmfates interface will update these weights + ! by using said mapping table + + do m = natpft_lb,natpft_ub + if (natveg_patch_exists(gi, m)) then + if(use_fates .and. .not.use_fates_sp)then + p_wt = 1.0_r8/real(natpft_size,r8) + else + p_wt = wt_nat_patch(gi,m) + end if + call add_patch(pi=pi, ci=ci, ptype=m, wtcol=p_wt) + npatches_added = npatches_added + 1 end if - call add_patch(pi=pi, ci=ci, ptype=m, wtcol=p_wt) - npatches_added = npatches_added + 1 - end if + end do end do - end if SHR_ASSERT_FL(nlunits_added == nlunits, sourcefile, __LINE__) diff --git a/src/main/initVerticalMod.F90 b/src/main/initVerticalMod.F90 index 1bf79706f9..e88c4e1a18 100644 --- a/src/main/initVerticalMod.F90 +++ b/src/main/initVerticalMod.F90 @@ -40,7 +40,8 @@ module initVerticalMod public :: initVertical public :: find_soil_layer_containing_depth public :: readParams - + public :: setSoilLayerClass + ! !PRIVATE MEMBER FUNCTIONS: private :: hasBedrock ! true if the given column type includes bedrock layers type, private :: params_type @@ -80,9 +81,75 @@ subroutine readParams( ncid ) end subroutine readParams + !------------------------------------------------------------------------ + subroutine setSoilLayerClass(bounds) + + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + ! + ! LOCAL VARAIBLES: + integer :: c,l,j ! indices + + ! Possible values for levgrnd_class. The important thing is that, for a given column, + ! layers that are fundamentally different (e.g., soil vs bedrock) have different + ! values. This information is used in the vertical interpolation in init_interp. + ! + ! IMPORTANT: These values should not be changed lightly. e.g., try to avoid changing + ! the values assigned to LEVGRND_CLASS_STANDARD, LEVGRND_CLASS_DEEP_BEDROCK, etc. The + ! problem with changing these is that init_interp expects that layers with a value of + ! (e.g.) 1 on the source file correspond to layers with a value of 1 on the + ! destination file. So if you change the values of these constants, you either need to + ! adequately inform users of this change, or build in some translation mechanism in + ! init_interp (such as via adding more metadata to the restart file on the meaning of + ! these different values). + ! + ! The distinction between "shallow" and "deep" bedrock is not made explicitly + ! elsewhere. But, since these classes have somewhat different behavior, they are + ! distinguished explicitly here. + integer, parameter :: LEVGRND_CLASS_STANDARD = 1 + integer, parameter :: LEVGRND_CLASS_DEEP_BEDROCK = 2 + integer, parameter :: LEVGRND_CLASS_SHALLOW_BEDROCK = 3 + + character(len=*), parameter :: subname = 'setSoilLayerClass' + + ! ------------------------------------------------------------------------ + ! Set classes of layers + ! ------------------------------------------------------------------------ + + do c = bounds%begc, bounds%endc + l = col%landunit(c) + if (hasBedrock(col_itype=col%itype(c), lun_itype=lun%itype(l))) then + ! NOTE(wjs, 2015-10-17) We are assuming that points with bedrock have both + ! "shallow" and "deep" bedrock. Currently, this is not true for lake columns: + ! lakes do not distinguish between "shallow" bedrock and "normal" soil. + ! However, that was just due to an oversight that is supposed to be corrected + ! soon; so to keep things simple we assume that any point with bedrock + ! potentially has both shallow and deep bedrock. + col%levgrnd_class(c, 1:col%nbedrock(c)) = LEVGRND_CLASS_STANDARD + if (col%nbedrock(c) < nlevsoi) then + col%levgrnd_class(c, (col%nbedrock(c) + 1) : nlevsoi) = LEVGRND_CLASS_SHALLOW_BEDROCK + end if + col%levgrnd_class(c, (nlevsoi + 1) : nlevmaxurbgrnd) = LEVGRND_CLASS_DEEP_BEDROCK + else + col%levgrnd_class(c, 1:nlevmaxurbgrnd) = LEVGRND_CLASS_STANDARD + end if + end do + + do j = 1, nlevmaxurbgrnd + do c = bounds%begc, bounds%endc + if (col%z(c,j) == spval) then + col%levgrnd_class(c,j) = ispval + end if + end do + end do + + end subroutine setSoilLayerClass + !------------------------------------------------------------------------ subroutine initVertical(bounds, glc_behavior, thick_wall, thick_roof) - use clm_varcon, only : zmin_bedrock + use clm_varcon , only : zmin_bedrock + ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -91,7 +158,7 @@ subroutine initVertical(bounds, glc_behavior, thick_wall, thick_roof) real(r8) , intent(in) :: thick_roof(bounds%begl:) ! ! LOCAL VARAIBLES: - integer :: c,l,g,i,j,lev ! indices + integer :: c,l,g,i,j,lev ! indices type(file_desc_t) :: ncid ! netcdf id logical :: readvar integer :: dimid ! dimension id @@ -115,27 +182,6 @@ subroutine initVertical(bounds, glc_behavior, thick_wall, thick_roof) integer :: begc, endc integer :: begl, endl integer :: jmin_bedrock - - ! Possible values for levgrnd_class. The important thing is that, for a given column, - ! layers that are fundamentally different (e.g., soil vs bedrock) have different - ! values. This information is used in the vertical interpolation in init_interp. - ! - ! IMPORTANT: These values should not be changed lightly. e.g., try to avoid changing - ! the values assigned to LEVGRND_CLASS_STANDARD, LEVGRND_CLASS_DEEP_BEDROCK, etc. The - ! problem with changing these is that init_interp expects that layers with a value of - ! (e.g.) 1 on the source file correspond to layers with a value of 1 on the - ! destination file. So if you change the values of these constants, you either need to - ! adequately inform users of this change, or build in some translation mechanism in - ! init_interp (such as via adding more metadata to the restart file on the meaning of - ! these different values). - ! - ! The distinction between "shallow" and "deep" bedrock is not made explicitly - ! elsewhere. But, since these classes have somewhat different behavior, they are - ! distinguished explicitly here. - integer, parameter :: LEVGRND_CLASS_STANDARD = 1 - integer, parameter :: LEVGRND_CLASS_DEEP_BEDROCK = 2 - integer, parameter :: LEVGRND_CLASS_SHALLOW_BEDROCK = 3 - character(len=*), parameter :: subname = 'initVertical' !------------------------------------------------------------------------ @@ -224,7 +270,7 @@ subroutine initVertical(bounds, glc_behavior, thick_wall, thick_roof) dzsoi(j) = soil_layerstruct_userdefined(j) end do else if (soil_layerstruct_predefined == '49SL_10m') then - !scs: 10 meter soil column, nlevsoi set to 49 in clm_varpar + ! 10 meter soil column, nlevsoi set to 49 in clm_varpar do j = 1, 10 dzsoi(j) = 1.e-2_r8 ! 10-mm layers enddo @@ -639,36 +685,11 @@ subroutine initVertical(bounds, glc_behavior, thick_wall, thick_roof) end if end do - ! ------------------------------------------------------------------------ + ! ---------------------------------------------- ! Set classes of layers - ! ------------------------------------------------------------------------ + ! ---------------------------------------------- - do c = bounds%begc, bounds%endc - l = col%landunit(c) - if (hasBedrock(col_itype=col%itype(c), lun_itype=lun%itype(l))) then - ! NOTE(wjs, 2015-10-17) We are assuming that points with bedrock have both - ! "shallow" and "deep" bedrock. Currently, this is not true for lake columns: - ! lakes do not distinguish between "shallow" bedrock and "normal" soil. - ! However, that was just due to an oversight that is supposed to be corrected - ! soon; so to keep things simple we assume that any point with bedrock - ! potentially has both shallow and deep bedrock. - col%levgrnd_class(c, 1:col%nbedrock(c)) = LEVGRND_CLASS_STANDARD - if (col%nbedrock(c) < nlevsoi) then - col%levgrnd_class(c, (col%nbedrock(c) + 1) : nlevsoi) = LEVGRND_CLASS_SHALLOW_BEDROCK - end if - col%levgrnd_class(c, (nlevsoi + 1) : nlevmaxurbgrnd) = LEVGRND_CLASS_DEEP_BEDROCK - else - col%levgrnd_class(c, 1:nlevmaxurbgrnd) = LEVGRND_CLASS_STANDARD - end if - end do - - do j = 1, nlevmaxurbgrnd - do c = bounds%begc, bounds%endc - if (col%z(c,j) == spval) then - col%levgrnd_class(c,j) = ispval - end if - end do - end do + call setSoilLayerClass(bounds) !----------------------------------------------- ! Read in topographic index and slope @@ -707,7 +728,13 @@ subroutine initVertical(bounds, glc_behavior, thick_wall, thick_roof) do c = begc,endc ! microtopographic parameter, units are meters (try smooth function of slope) slope0 = params_inst%slopemax**(1._r8/params_inst%slopebeta) - col%micro_sigma(c) = (col%topo_slope(c) + slope0)**(params_inst%slopebeta) + + if (col%is_hillslope_column(c)) then + col%micro_sigma(c) = (atan(col%hill_slope(c)) + slope0)**(params_inst%slopebeta) + else + col%micro_sigma(c) = (col%topo_slope(c) + slope0)**(params_inst%slopebeta) + endif + end do call ncd_pio_closefile(ncid) diff --git a/src/main/lnd2atmMod.F90 b/src/main/lnd2atmMod.F90 index 27769a69de..1cda0cff91 100644 --- a/src/main/lnd2atmMod.F90 +++ b/src/main/lnd2atmMod.F90 @@ -15,7 +15,7 @@ module lnd2atmMod use clm_varctl , only : iulog, use_lch4 use shr_drydep_mod , only : n_drydep use decompMod , only : bounds_type - use subgridAveMod , only : p2g, c2g + use subgridAveMod , only : p2g, c2g, l2g use filterColMod , only : filter_col_type, col_filter_from_logical_array use lnd2atmType , only : lnd2atm_type use atm2lndType , only : atm2lnd_type @@ -159,6 +159,7 @@ subroutine lnd2atm(bounds, & ! ! !USES: use ch4varcon , only : ch4offline + use clm_varctl , only : use_hillslope_routing ! ! !ARGUMENTS: type(bounds_type) , intent(in) :: bounds @@ -179,8 +180,11 @@ subroutine lnd2atm(bounds, & real(r8) , intent(in) :: net_carbon_exchange_grc( bounds%begg: ) ! net carbon exchange between land and atmosphere, positive for source (gC/m2/s) ! ! !LOCAL VARIABLES: - integer :: c, g ! indices + integer :: c, l, g ! indices real(r8) :: eflx_sh_ice_to_liq_grc(bounds%begg:bounds%endg) ! sensible heat flux generated from the ice to liquid conversion, averaged to gridcell + real(r8), allocatable :: qflx_surf_col_to_rof(:) ! surface runoff that is sent directly to rof + real(r8), allocatable :: qflx_drain_col_to_rof(:) ! drainagec that is sent directly to rof + real(r8), allocatable :: qflx_drain_perched_col_to_rof(:) ! perched drainage that is sent directly to rof real(r8), parameter :: amC = 12.0_r8 ! Atomic mass number for Carbon real(r8), parameter :: amO = 16.0_r8 ! Atomic mass number for Oxygen real(r8), parameter :: amCO2 = amC + 2.0_r8*amO ! Atomic mass number for CO2 @@ -336,15 +340,80 @@ subroutine lnd2atm(bounds, & ! lnd -> rof !---------------------------------------------------- - call c2g( bounds, & - water_inst%waterfluxbulk_inst%qflx_surf_col (bounds%begc:bounds%endc), & - water_inst%waterlnd2atmbulk_inst%qflx_rofliq_qsur_grc (bounds%begg:bounds%endg), & - c2l_scale_type= 'urbanf', l2g_scale_type='unity' ) + if (use_hillslope_routing) then + ! streamflow is volume/time, so sum over landunits (do not weight) + water_inst%waterlnd2atmbulk_inst%qflx_rofliq_stream_grc(bounds%begg:bounds%endg) = 0._r8 + do l = bounds%begl, bounds%endl + if(lun%active(l)) then + g = lun%gridcell(l) + water_inst%waterlnd2atmbulk_inst%qflx_rofliq_stream_grc(g) = & + water_inst%waterlnd2atmbulk_inst%qflx_rofliq_stream_grc(g) & + + water_inst%waterfluxbulk_inst%volumetric_streamflow_lun(l) & + *1e3_r8/(grc%area(g)*1.e6_r8) + endif + enddo + + ! If hillslope routing is used, exclude inputs to stream channel from gridcell averages to avoid double counting + allocate( & + qflx_surf_col_to_rof(bounds%begc:bounds%endc), & + qflx_drain_col_to_rof(bounds%begc:bounds%endc), & + qflx_drain_perched_col_to_rof(bounds%begc:bounds%endc)) + + qflx_surf_col_to_rof(bounds%begc:bounds%endc) = 0._r8 + qflx_drain_col_to_rof(bounds%begc:bounds%endc) = 0._r8 + qflx_drain_perched_col_to_rof(bounds%begc:bounds%endc) = 0._r8 + + do c = bounds%begc, bounds%endc + ! Exclude hillslope columns from gridcell average + ! hillslope runoff is sent to stream rather than directly + ! to rof, and is accounted for in qflx_rofliq_stream_grc + if (col%active(c) .and. .not. col%is_hillslope_column(c)) then + qflx_surf_col_to_rof(c) = qflx_surf_col_to_rof(c) & + + water_inst%waterfluxbulk_inst%qflx_surf_col(c) + qflx_drain_col_to_rof(c) = qflx_drain_col_to_rof(c) & + + water_inst%waterfluxbulk_inst%qflx_drain_col(c) + qflx_drain_perched_col_to_rof(c) = & + qflx_drain_perched_col_to_rof(c) & + + water_inst%waterfluxbulk_inst%qflx_drain_perched_col(c) + endif + enddo + + call c2g( bounds, & + qflx_surf_col_to_rof (bounds%begc:bounds%endc), & + water_inst%waterlnd2atmbulk_inst%qflx_rofliq_qsur_grc (bounds%begg:bounds%endg), & + c2l_scale_type= 'urbanf', l2g_scale_type='unity') + + call c2g( bounds, & + qflx_drain_col_to_rof (bounds%begc:bounds%endc), & + water_inst%waterlnd2atmbulk_inst%qflx_rofliq_qsub_grc (bounds%begg:bounds%endg), & + c2l_scale_type= 'urbanf', l2g_scale_type='unity') + + call c2g( bounds, & + qflx_drain_perched_col_to_rof (bounds%begc:bounds%endc), & + water_inst%waterlnd2atmbulk_inst%qflx_rofliq_drain_perched_grc(bounds%begg:bounds%endg), & + c2l_scale_type= 'urbanf', l2g_scale_type='unity') + + deallocate(qflx_surf_col_to_rof,qflx_drain_col_to_rof, & + qflx_drain_perched_col_to_rof) + + else + + call c2g( bounds, & + water_inst%waterfluxbulk_inst%qflx_surf_col (bounds%begc:bounds%endc), & + water_inst%waterlnd2atmbulk_inst%qflx_rofliq_qsur_grc (bounds%begg:bounds%endg), & + c2l_scale_type= 'urbanf', l2g_scale_type='unity' ) + + call c2g( bounds, & + water_inst%waterfluxbulk_inst%qflx_drain_col (bounds%begc:bounds%endc), & + water_inst%waterlnd2atmbulk_inst%qflx_rofliq_qsub_grc (bounds%begg:bounds%endg), & + c2l_scale_type= 'urbanf', l2g_scale_type='unity' ) + + call c2g( bounds, & + water_inst%waterfluxbulk_inst%qflx_drain_perched_col (bounds%begc:bounds%endc), & + water_inst%waterlnd2atmbulk_inst%qflx_rofliq_drain_perched_grc(bounds%begg:bounds%endg), & + c2l_scale_type= 'urbanf', l2g_scale_type='unity' ) - call c2g( bounds, & - water_inst%waterfluxbulk_inst%qflx_drain_col (bounds%begc:bounds%endc), & - water_inst%waterlnd2atmbulk_inst%qflx_rofliq_qsub_grc (bounds%begg:bounds%endg), & - c2l_scale_type= 'urbanf', l2g_scale_type='unity' ) + endif do c = bounds%begc, bounds%endc if (col%active(c)) then @@ -383,12 +452,6 @@ subroutine lnd2atm(bounds, & water_inst%waterfluxbulk_inst%qflx_liq_dynbal_grc(g) enddo - call c2g( bounds, & - water_inst%waterfluxbulk_inst%qflx_drain_perched_col (bounds%begc:bounds%endc), & - water_inst%waterlnd2atmbulk_inst%qflx_rofliq_drain_perched_grc(bounds%begg:bounds%endg), & - c2l_scale_type= 'urbanf', l2g_scale_type='unity' ) - - call c2g( bounds, & water_inst%waterfluxbulk_inst%qflx_sfc_irrig_col (bounds%begc:bounds%endc), & water_inst%waterlnd2atmbulk_inst%qirrig_grc(bounds%begg:bounds%endg), & diff --git a/src/main/lnd2glcMod.F90 b/src/main/lnd2glcMod.F90 index 34f50266ad..27fa7639d7 100644 --- a/src/main/lnd2glcMod.F90 +++ b/src/main/lnd2glcMod.F90 @@ -20,7 +20,7 @@ module lnd2glcMod use decompMod , only : get_proc_bounds, bounds_type, subgrid_level_column use domainMod , only : ldomain use clm_varpar , only : maxpatch_glc - use clm_varctl , only : iulog + use clm_varctl , only : iulog, use_hillslope use clm_varcon , only : spval, tfrz use column_varcon , only : col_itype_to_ice_class use landunit_varcon , only : istice, istsoil @@ -204,7 +204,16 @@ subroutine update_lnd2glc(this, bounds, num_do_smb_c, filter_do_smb_c, & ! Make sure we haven't already assigned the coupling fields for this point ! (this could happen, for example, if there were multiple columns in the ! istsoil landunit, which we aren't prepared to handle) - if (fields_assigned(g,n)) then + ! + ! BUG(wjs, 2022-07-17, ESCOMP/CTSM#204) We have a known bug in the handling of bare + ! land fluxes when we potentially have multiple vegetated columns in a grid cell. + ! The most common configuration where this is the case is when use_hillslope is + ! true. In order to allow hillslope hydrology runs to work for now, we are + ! bypassing this error check when use_hillslope is true - under the assumption + ! that, for now, people aren't going to be interested in SMB in a run with + ! hillslope hydrology. Once we resolve ESCOMP/CTSM#204, we should remove the '.and. + ! .not. use_hillslope' part of this conditional. + if (fields_assigned(g,n) .and. .not. use_hillslope) then write(iulog,*) subname//' ERROR: attempt to assign coupling fields twice for the same index.' write(iulog,*) 'One possible cause is having multiple columns in the istsoil landunit,' write(iulog,*) 'which this routine cannot handle.' diff --git a/src/main/subgridAveMod.F90 b/src/main/subgridAveMod.F90 index c5ce4a4a98..68431582ce 100644 --- a/src/main/subgridAveMod.F90 +++ b/src/main/subgridAveMod.F90 @@ -100,6 +100,70 @@ module subgridAveMod contains + !----------------------------------------------------------------------- + subroutine set_c2l_scale (bounds, c2l_scale_type, scale_c2l) + ! + ! !DESCRIPTION: + ! Set scale_c2l for different c2l_scale_type values + ! + ! !ARGUMENTS: + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: c2l_scale_type ! scale factor type for averaging (see note at top of module) + real(r8), intent(out) :: scale_c2l(bounds%begc:bounds%endc) ! scale factor for column->landunit mapping + + ! + ! !LOCAL VARIABLES: + integer :: c,l ! indices + !------------------------------------------------------------------------ + + ! Enforce expected array sizes + SHR_ASSERT_ALL_FL((ubound(scale_c2l) == (/bounds%endc/)), sourcefile, __LINE__) + + if (c2l_scale_type == 'unity') then + do c = bounds%begc,bounds%endc + scale_c2l(c) = 1.0_r8 + end do + else if (c2l_scale_type == 'urbanf') then + do c = bounds%begc,bounds%endc + l = col%landunit(c) + if (lun%urbpoi(l)) then + if (col%itype(c) == icol_sunwall) then + scale_c2l(c) = 3.0 * lun%canyon_hwr(l) + else if (col%itype(c) == icol_shadewall) then + scale_c2l(c) = 3.0 * lun%canyon_hwr(l) + else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then + scale_c2l(c) = 3.0_r8 + else if (col%itype(c) == icol_roof) then + scale_c2l(c) = 1.0_r8 + end if + else + scale_c2l(c) = 1.0_r8 + end if + end do + else if (c2l_scale_type == 'urbans') then + do c = bounds%begc,bounds%endc + l = col%landunit(c) + if (lun%urbpoi(l)) then + if (col%itype(c) == icol_sunwall) then + scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) + else if (col%itype(c) == icol_shadewall) then + scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) + else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then + scale_c2l(c) = 3.0 / (2.*lun%canyon_hwr(l) + 1.) + else if (col%itype(c) == icol_roof) then + scale_c2l(c) = 1.0_r8 + end if + else + scale_c2l(c) = 1.0_r8 + end if + end do + else + write(iulog,*)'set_c2l_scale: scale type ',c2l_scale_type,' not supported' + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + + end subroutine set_c2l_scale + !----------------------------------------------------------------------- subroutine p2c_1d (bounds, parr, carr, p2c_scale_type) ! @@ -310,48 +374,7 @@ subroutine p2l_1d (bounds, parr, larr, p2c_scale_type, c2l_scale_type) SHR_ASSERT_ALL_FL((ubound(parr) == (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(larr) == (/bounds%endl/)), sourcefile, __LINE__) - if (c2l_scale_type == 'unity') then - do c = bounds%begc,bounds%endc - scale_c2l(c) = 1.0_r8 - end do - else if (c2l_scale_type == 'urbanf') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0_r8 - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else if (c2l_scale_type == 'urbans') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0 / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else - write(iulog,*)'p2l_1d error: scale type ',c2l_scale_type,' not supported' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + call set_c2l_scale (bounds, c2l_scale_type, scale_c2l) if (p2c_scale_type == 'unity') then do p = bounds%begp,bounds%endp @@ -418,48 +441,7 @@ subroutine p2l_2d(bounds, num2d, parr, larr, p2c_scale_type, c2l_scale_type) SHR_ASSERT_ALL_FL((ubound(parr) == (/bounds%endp, num2d/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(larr) == (/bounds%endl, num2d/)), sourcefile, __LINE__) - if (c2l_scale_type == 'unity') then - do c = bounds%begc,bounds%endc - scale_c2l(c) = 1.0_r8 - end do - else if (c2l_scale_type == 'urbanf') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0_r8 - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else if (c2l_scale_type == 'urbans') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0 / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else - write(iulog,*)'p2l_2d error: scale type ',c2l_scale_type,' not supported' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + call set_c2l_scale (bounds, c2l_scale_type, scale_c2l) if (p2c_scale_type == 'unity') then do p = bounds%begp,bounds%endp @@ -532,48 +514,7 @@ subroutine p2g_1d(bounds, parr, garr, p2c_scale_type, c2l_scale_type, l2g_scale_ call build_scale_l2g(bounds, l2g_scale_type, & scale_l2g(bounds%begl:bounds%endl)) - if (c2l_scale_type == 'unity') then - do c = bounds%begc,bounds%endc - scale_c2l(c) = 1.0_r8 - end do - else if (c2l_scale_type == 'urbanf') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0_r8 - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else if (c2l_scale_type == 'urbans') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0 / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else - write(iulog,*)'p2g_1d error: scale type ',c2l_scale_type,' not supported' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + call set_c2l_scale (bounds, c2l_scale_type, scale_c2l) if (p2c_scale_type == 'unity') then do p = bounds%begp,bounds%endp @@ -648,48 +589,7 @@ subroutine p2g_2d(bounds, num2d, parr, garr, p2c_scale_type, c2l_scale_type, l2g call build_scale_l2g(bounds, l2g_scale_type, & scale_l2g(bounds%begl:bounds%endl)) - if (c2l_scale_type == 'unity') then - do c = bounds%begc,bounds%endc - scale_c2l(c) = 1.0_r8 - end do - else if (c2l_scale_type == 'urbanf') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0_r8 - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else if (c2l_scale_type == 'urbans') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0 / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else - write(iulog,*)'p2g_2d error: scale type ',c2l_scale_type,' not supported' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + call set_c2l_scale (bounds, c2l_scale_type, scale_c2l) if (p2c_scale_type == 'unity') then do p = bounds%begp,bounds%endp @@ -770,48 +670,7 @@ subroutine c2l_1d (bounds, carr, larr, c2l_scale_type, include_inactive) l_include_inactive = .false. end if - if (c2l_scale_type == 'unity') then - do c = bounds%begc,bounds%endc - scale_c2l(c) = 1.0_r8 - end do - else if (c2l_scale_type == 'urbanf') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0_r8 - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else if (c2l_scale_type == 'urbans') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0 / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else - write(iulog,*)'c2l_1d error: scale type ',c2l_scale_type,' not supported' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + call set_c2l_scale (bounds, c2l_scale_type, scale_c2l) larr(bounds%begl : bounds%endl) = spval sumwt(bounds%begl : bounds%endl) = 0._r8 @@ -866,48 +725,7 @@ subroutine c2l_2d (bounds, num2d, carr, larr, c2l_scale_type) SHR_ASSERT_ALL_FL((ubound(carr) == (/bounds%endc, num2d/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(larr) == (/bounds%endl, num2d/)), sourcefile, __LINE__) - if (c2l_scale_type == 'unity') then - do c = bounds%begc,bounds%endc - scale_c2l(c) = 1.0_r8 - end do - else if (c2l_scale_type == 'urbanf') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0_r8 - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else if (c2l_scale_type == 'urbans') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0 / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else - write(iulog,*)'c2l_2d error: scale type ',c2l_scale_type,' not supported' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + call set_c2l_scale (bounds, c2l_scale_type, scale_c2l) larr(bounds%begl : bounds%endl, :) = spval do j = 1,num2d @@ -968,48 +786,7 @@ subroutine c2g_1d(bounds, carr, garr, c2l_scale_type, l2g_scale_type) call build_scale_l2g(bounds, l2g_scale_type, & scale_l2g(bounds%begl:bounds%endl)) - if (c2l_scale_type == 'unity') then - do c = bounds%begc,bounds%endc - scale_c2l(c) = 1.0_r8 - end do - else if (c2l_scale_type == 'urbanf') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0_r8 - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else if (c2l_scale_type == 'urbans') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0 / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else - write(iulog,*)'c2l_1d error: scale type ',c2l_scale_type,' not supported' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + call set_c2l_scale (bounds, c2l_scale_type, scale_c2l) garr(bounds%begg : bounds%endg) = spval sumwt(bounds%begg : bounds%endg) = 0._r8 @@ -1070,48 +847,7 @@ subroutine c2g_2d(bounds, num2d, carr, garr, c2l_scale_type, l2g_scale_type) call build_scale_l2g(bounds, l2g_scale_type, & scale_l2g(bounds%begl:bounds%endl)) - if (c2l_scale_type == 'unity') then - do c = bounds%begc,bounds%endc - scale_c2l(c) = 1.0_r8 - end do - else if (c2l_scale_type == 'urbanf') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = 3.0 * lun%canyon_hwr(l) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0_r8 - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else if (c2l_scale_type == 'urbans') then - do c = bounds%begc,bounds%endc - l = col%landunit(c) - if (lun%urbpoi(l)) then - if (col%itype(c) == icol_sunwall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_shadewall) then - scale_c2l(c) = (3.0 * lun%canyon_hwr(l)) / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_road_perv .or. col%itype(c) == icol_road_imperv) then - scale_c2l(c) = 3.0 / (2.*lun%canyon_hwr(l) + 1.) - else if (col%itype(c) == icol_roof) then - scale_c2l(c) = 1.0_r8 - end if - else - scale_c2l(c) = 1.0_r8 - end if - end do - else - write(iulog,*)'c2g_2d error: scale type ',c2l_scale_type,' not supported' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + call set_c2l_scale (bounds, c2l_scale_type, scale_c2l) garr(bounds%begg : bounds%endg,:) = spval do j = 1,num2d diff --git a/src/main/subgridMod.F90 b/src/main/subgridMod.F90 index 7020f42be5..4118ac73ed 100644 --- a/src/main/subgridMod.F90 +++ b/src/main/subgridMod.F90 @@ -75,6 +75,8 @@ subroutine subgrid_get_gcellinfo (gi, glc_behavior, & ! atm_topo is arbitrary for the sake of getting these counts. We don't have a true ! atm_topo value at the point of this call, so use 0. real(r8), parameter :: atm_topo = 0._r8 + + !------------------------------------------------------------------------------ npatches = 0 @@ -85,6 +87,11 @@ subroutine subgrid_get_gcellinfo (gi, glc_behavior, & call subgrid_get_info_natveg(gi, npatches_temp, ncols_temp, nlunits_temp) call accumulate_counters() + ! call this after natveg call because we allocate space for + ! FATES cohorts based on the number of naturally vegetated columns + ! and nothing else + call subgrid_get_info_cohort(gi, ncols_temp, ncohorts) + call subgrid_get_info_urban_tbd(gi, npatches_temp, ncols_temp, nlunits_temp) call accumulate_counters() @@ -107,8 +114,6 @@ subroutine subgrid_get_gcellinfo (gi, glc_behavior, & call subgrid_get_info_crop(gi, npatches_temp, ncols_temp, nlunits_temp) call accumulate_counters() - call subgrid_get_info_cohort(gi,ncohorts) - contains subroutine accumulate_counters ! Accumulate running sums of patches, columns and landunits. @@ -131,6 +136,8 @@ subroutine subgrid_get_info_natveg(gi, npatches, ncols, nlunits) ! ! !USES use clm_varpar, only : natpft_lb, natpft_ub + use clm_instur, only : ncolumns_hillslope + use clm_varctl, only : use_hillslope ! ! !ARGUMENTS: integer, intent(in) :: gi ! grid cell index @@ -154,9 +161,16 @@ subroutine subgrid_get_info_natveg(gi, npatches, ncols, nlunits) end do if (npatches > 0) then - ! Assume that the vegetated landunit has one column - ncols = 1 nlunits = 1 + if (use_hillslope) then + ! ensure ncols is > 0 + ncols = max(ncolumns_hillslope(gi),1) + else + ncols = 1 + endif + ! Assume that each PFT present in the grid cell is present in every column + npatches = ncols*npatches + else ! As noted in natveg_patch_exists, we expect a naturally vegetated landunit in ! every grid cell. This means that npatches should be at least 1 in every grid @@ -220,7 +234,7 @@ end function natveg_patch_exists ! ----------------------------------------------------------------------------- - subroutine subgrid_get_info_cohort(gi, ncohorts) + subroutine subgrid_get_info_cohort(gi, ncols, ncohorts) ! ! !DESCRIPTION: ! Obtain cohort counts per each gridcell. @@ -230,6 +244,7 @@ subroutine subgrid_get_info_cohort(gi, ncohorts) ! ! !ARGUMENTS: integer, intent(in) :: gi ! grid cell index + integer, intent(in) :: ncols ! number of nat veg columns in this grid cell integer, intent(out) :: ncohorts ! number of cohorts in this grid cell ! ! !LOCAL VARIABLES: @@ -248,11 +263,10 @@ subroutine subgrid_get_info_cohort(gi, ncohorts) ! restart vector will just be a little sparse. ! ------------------------------------------------------------------------- - ncohorts = fates_maxElementsPerSite + ncohorts = ncols*fates_maxElementsPerSite end subroutine subgrid_get_info_cohort - !----------------------------------------------------------------------- subroutine subgrid_get_info_urban_tbd(gi, npatches, ncols, nlunits) ! diff --git a/src/main/surfrdMod.F90 b/src/main/surfrdMod.F90 index 23e96e7c1a..03a0082e97 100644 --- a/src/main/surfrdMod.F90 +++ b/src/main/surfrdMod.F90 @@ -721,7 +721,7 @@ subroutine surfrd_veg_all(begg, endg, ncid, ns, actual_numcft) ! Determine weight arrays for non-dynamic landuse mode ! ! !USES: - use clm_varctl , only : create_crop_landunit, use_fates, n_dom_pfts + use clm_varctl , only : create_crop_landunit, use_fates, n_dom_pfts, use_hillslope 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 @@ -815,7 +815,12 @@ subroutine surfrd_veg_all(begg, endg, ncid, ns, actual_numcft) ' must also have a separate crop landunit, and vice versa)'//& errMsg(sourcefile, __LINE__)) end if - + + ! Obtain hillslope hydrology information and modify pft weights + if (use_hillslope) then + call surfrd_hillslope(begg, endg, ncid, ns) + endif + ! Convert from percent to fraction wt_lunit(begg:endg,istsoil) = wt_lunit(begg:endg,istsoil) / 100._r8 wt_lunit(begg:endg,istcrop) = wt_lunit(begg:endg,istcrop) / 100._r8 @@ -883,6 +888,115 @@ subroutine surfrd_veg_dgvm(begg, endg) end subroutine surfrd_veg_dgvm !----------------------------------------------------------------------- + subroutine surfrd_hillslope(begg, endg, ncid, ns) + ! + ! !DESCRIPTION: + ! Determine number of hillslopes and columns for hillslope hydrology mode + ! + ! !USES: + use clm_instur, only : ncolumns_hillslope, wt_nat_patch + use clm_varctl, only : nhillslope,max_columns_hillslope + use clm_varpar, only : natpft_size, natpft_lb, natpft_ub + use ncdio_pio, only : ncd_inqdid, ncd_inqdlen + use pftconMod , only : noveg + use HillslopeHydrologyMod, only : pft_distribution_method, pft_standard, pft_from_file, pft_uniform_dominant_pft, pft_lowland_dominant_pft, pft_lowland_upland + use array_utils, only: find_k_max_indices + use surfrdUtilsMod, only: collapse_to_dominant + + ! + ! !ARGUMENTS: + integer, intent(in) :: begg, endg + type(file_desc_t),intent(inout) :: ncid ! netcdf id + integer ,intent(in) :: ns ! domain size + ! + ! !LOCAL VARIABLES: + integer :: g, nh, m, n ! index + integer :: dimid,varid ! netCDF id's + integer :: ier ! error status + integer, allocatable :: max_indices(:) ! largest weight pft indices + logical :: readvar ! is variable on dataset + integer,pointer :: arrayl(:) ! local array (needed because ncd_io expects a pointer) + character(len=32) :: subname = 'surfrd_hillslope' ! subroutine name + logical, allocatable :: do_not_collapse(:) + integer :: n_dominant + !----------------------------------------------------------------------- + + ! number of hillslopes per landunit + call ncd_inqdid(ncid,'nhillslope',dimid,readvar) + if (.not. readvar) then + call endrun( msg=' ERROR: nhillslope not on surface data file'//errMsg(sourcefile, __LINE__)) + else + call ncd_inqdlen(ncid,dimid,nh) + nhillslope = nh + endif + ! maximum number of columns per landunit + call ncd_inqdid(ncid,'nmaxhillcol',dimid,readvar) + if (.not. readvar) then + call endrun( msg=' ERROR: nmaxhillcol not on surface data file'//errMsg(sourcefile, __LINE__)) + else + call ncd_inqdlen(ncid,dimid,nh) + max_columns_hillslope = nh + endif + ! actual number of columns per landunit + allocate(arrayl(begg:endg)) + call ncd_io(ncid=ncid, varname='nhillcolumns', flag='read', data=arrayl, & + dim1name=grlnd, readvar=readvar) + if (.not. readvar) then + call endrun( msg=' ERROR: nhillcolumns not on surface data file'//errMsg(sourcefile, __LINE__)) + else + ncolumns_hillslope(begg:endg) = arrayl(begg:endg) + endif + deallocate(arrayl) + + ! pft_from_file and pft_lowland_upland assume that 1 pft + ! will exist on each hillslope column. In prepration, set one + ! pft weight to 100 and the rest to 0. The vegetation type + ! (patch%itype) will be reassigned when initHillslope is called later. + if(pft_distribution_method == pft_from_file .or. & + pft_distribution_method == pft_lowland_upland) then + do g = begg, endg + ! If hillslopes will be used in a gridcell, modify wt_nat_patch, otherwise use original patch distribution + if(ncolumns_hillslope(g) > 0) then + ! First patch gets 100% weight; all other natural patches are zeroed out + wt_nat_patch(g,:) = 0._r8 + wt_nat_patch(g,natpft_lb) = 100._r8 + endif + enddo + + else if (pft_distribution_method == pft_uniform_dominant_pft & + .or. pft_distribution_method == pft_lowland_dominant_pft) then + + ! If hillslopes will be used in a gridcell, modify wt_nat_patch, + ! otherwise use original patch distribution + allocate(do_not_collapse(begg:endg)) + do_not_collapse(begg:endg) = .false. + do g = begg, endg + if (ncolumns_hillslope(g) == 0) then + do_not_collapse(g) = .true. + end if + end do + + if (pft_distribution_method == pft_uniform_dominant_pft) then + ! pft_uniform_dominant_pft uses the patch with the + ! largest weight for all hillslope columns in the gridcell + n_dominant = 1 + else if (pft_distribution_method == pft_lowland_dominant_pft) then + ! pft_lowland_dominant_pft uses the two patches with the + ! largest weights for the hillslope columns in the gridcell + n_dominant = 2 + else + call endrun( msg=' ERROR: unrecognized hillslope_pft_distribution_method'//errMsg(sourcefile, __LINE__)) + end if + + call collapse_to_dominant(wt_nat_patch(begg:endg,:), natpft_lb, natpft_ub, begg, endg, n_dominant, do_not_collapse) + deallocate(do_not_collapse) + + else if (pft_distribution_method /= pft_standard) then + call endrun( msg=' ERROR: unrecognized hillslope_pft_distribution_method'//errMsg(sourcefile, __LINE__)) + endif + + end subroutine surfrd_hillslope + subroutine surfrd_lakemask(begg, endg) ! ! !DESCRIPTION: diff --git a/src/main/surfrdUtilsMod.F90 b/src/main/surfrdUtilsMod.F90 index 6b581a59c1..97f5b7d80f 100644 --- a/src/main/surfrdUtilsMod.F90 +++ b/src/main/surfrdUtilsMod.F90 @@ -235,7 +235,7 @@ subroutine collapse_individual_lunits(wt_lunit, begg, endg, toosmall_soil, & end subroutine collapse_individual_lunits !----------------------------------------------------------------------- - subroutine collapse_to_dominant(weight, lower_bound, upper_bound, begg, endg, n_dominant) + subroutine collapse_to_dominant(weight, lower_bound, upper_bound, begg, endg, n_dominant, do_not_collapse) ! ! DESCRIPTION ! Collapse to the top N dominant pfts or landunits (n_dominant) @@ -251,6 +251,7 @@ subroutine collapse_to_dominant(weight, lower_bound, upper_bound, begg, endg, n_ integer, intent(in) :: lower_bound ! lower bound of pft or landunit indices integer, intent(in) :: upper_bound ! upper bound of pft or landunit indices integer, intent(in) :: n_dominant ! # dominant pfts or landunits + logical, intent(in), optional :: do_not_collapse(begg:endg) ! This array modified in-place ! Weights of pfts or landunits per grid cell ! Dimensioned [g, lower_bound:upper_bound] @@ -277,6 +278,14 @@ subroutine collapse_to_dominant(weight, lower_bound, upper_bound, begg, endg, n_ if (n_dominant > 0 .and. n_dominant < upper_bound) then allocate(max_indices(n_dominant)) do g = begg, endg + + ! original sum of all the weights + wt_sum(g) = sum(weight(g,:)) + + if (present(do_not_collapse) .and. do_not_collapse(g)) then + cycle + end if + max_indices = 0 ! initialize call find_k_max_indices(weight(g,:), lower_bound, n_dominant, & max_indices) @@ -286,7 +295,6 @@ subroutine collapse_to_dominant(weight, lower_bound, upper_bound, begg, endg, n_ ! Typically the original sum of weights = 1, but if ! collapse_urban = .true., it equals the sum of the urban landunits. ! Also set the remaining weights to 0. - wt_sum(g) = sum(weight(g,:)) ! original sum of all the weights wt_dom_sum = 0._r8 ! initialize the dominant pft or landunit sum do n = 1, n_dominant m = max_indices(n) diff --git a/src/main/test/atm2lnd_test/test_downscale_forcings.pf b/src/main/test/atm2lnd_test/test_downscale_forcings.pf index d688ad809d..ddd097d16c 100644 --- a/src/main/test/atm2lnd_test/test_downscale_forcings.pf +++ b/src/main/test/atm2lnd_test/test_downscale_forcings.pf @@ -9,6 +9,7 @@ module test_downscale_forcings use unittestSimpleSubgridSetupsMod use unittestArrayMod use atm2lndType, only : atm2lnd_type, atm2lnd_params_type + use SurfaceAlbedoType, only : surfalb_type use Wateratm2lndBulkType, only : wateratm2lndbulk_type use WaterInfoBulkType, only : water_info_bulk_type use TopoMod, only : topo_type @@ -25,6 +26,7 @@ module test_downscale_forcings @TestCase type, extends(TestCase) :: TestDownscaleForcings type(atm2lnd_type) :: atm2lnd_inst + type(surfalb_type) :: surfalb_inst type(wateratm2lndbulk_type) :: wateratm2lndbulk_inst type(topo_type_always_downscale) :: topo_inst real(r8), allocatable :: eflx_sh_precip_conversion(:) @@ -204,8 +206,13 @@ contains class(TestDownscaleForcings), intent(inout) :: this this%eflx_sh_precip_conversion = col_array() - call downscale_forcings(bounds, this%topo_inst, & - this%atm2lnd_inst, this%wateratm2lndbulk_inst, & + call downscale_forcings(bounds, & + this%topo_inst, & + this%atm2lnd_inst, & + ! Currently surfalb_inst is only used for hillslope downscaling; we need to pass + ! it to satisfy the interface but we haven't bothered setting it up + this%surfalb_inst, & + this%wateratm2lndbulk_inst, & this%eflx_sh_precip_conversion) end subroutine call_downscale_forcings diff --git a/src/main/test/atm2lnd_test/test_partition_precip.pf b/src/main/test/atm2lnd_test/test_partition_precip.pf index 48c12c3f3c..56febc1b30 100644 --- a/src/main/test/atm2lnd_test/test_partition_precip.pf +++ b/src/main/test/atm2lnd_test/test_partition_precip.pf @@ -5,6 +5,7 @@ module test_partition_precip use funit use atm2lndMod use atm2lndType + use ColumnType, only : col use shr_kind_mod, only : r8 => shr_kind_r8 use unittestSubgridMod use unittestSimpleSubgridSetupsMod @@ -64,6 +65,7 @@ contains logical :: l_repartition_rain_snow type(atm2lnd_params_type) :: atm2lnd_params + integer :: c, g if (present(repartition_rain_snow)) then l_repartition_rain_snow = repartition_rain_snow @@ -89,6 +91,15 @@ contains this%wateratm2lndbulk_inst%forc_rain_not_downscaled_grc(bounds%begg:bounds%endg) = rain(:) this%wateratm2lndbulk_inst%forc_snow_not_downscaled_grc(bounds%begg:bounds%endg) = snow(:) this%atm2lnd_inst%forc_t_downscaled_col(bounds%begc:bounds%endc) = temperature(:) + + ! In the production code, column-level versions of forc_rain and forc_snow are + ! initialized to the gridcell-level versions prior to the call to partition_precip; do + ! that here + do c = bounds%begc, bounds%endc + g = col%gridcell(c) + this%wateratm2lndbulk_inst%forc_rain_downscaled_col(c) = this%wateratm2lndbulk_inst%forc_rain_not_downscaled_grc(g) + this%wateratm2lndbulk_inst%forc_snow_downscaled_col(c) = this%wateratm2lndbulk_inst%forc_snow_not_downscaled_grc(g) + end do end subroutine set_inputs @Test diff --git a/src/main/test/surfrdUtils_test/test_surfrdUtils.pf b/src/main/test/surfrdUtils_test/test_surfrdUtils.pf index 98191fbe99..f2fcae7af9 100644 --- a/src/main/test/surfrdUtils_test/test_surfrdUtils.pf +++ b/src/main/test/surfrdUtils_test/test_surfrdUtils.pf @@ -129,7 +129,7 @@ contains call check_sums_equal_1( wt_in_out, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_in_out(begg:,:), wt_expected(begg:,:), tolerance=tol) + @assertEqual(wt_expected(begg:,:), wt_in_out(begg:,:), tolerance=tol) deallocate( wt_expected ) deallocate( wt_in_out ) @@ -249,7 +249,7 @@ contains call check_sums_equal_1( wt_in_out, begg, "test_check_sums_add_to_1", & "should not trigger an error for wt_in_out") - @assertEqual(wt_in_out(begg:,:), wt_expected(begg:,:), tolerance=tol) + @assertEqual(wt_expected(begg:,:), wt_in_out(begg:,:), tolerance=tol) end do @@ -318,7 +318,7 @@ contains isturb_MIN, isturb_MAX, begg, endg, & n_dom_urban) - @assertEqual(wt_in_out(begg:,:), wt_expected(begg:,:), tolerance=tol) + @assertEqual(wt_expected(begg:,:), wt_in_out(begg:,:), tolerance=tol) deallocate( wt_expected ) deallocate( wt_in_out ) @@ -444,7 +444,7 @@ contains call check_sums_equal_1( wt_in_out, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_in_out(begg:,:), wt_expected(begg:,:), tolerance=tol) + @assertEqual(wt_expected(begg:,:), wt_in_out(begg:,:), tolerance=tol) end do ! loop of tests @@ -558,7 +558,7 @@ contains call check_sums_equal_1( wt_nat_patch_in_out, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_nat_patch_in_out(begg:,:), wt_nat_patch_expected(begg:,:), tolerance=tol) + @assertEqual(wt_nat_patch_expected(begg:,:), wt_nat_patch_in_out(begg:,:), tolerance=tol) end do ! loop of tests @@ -570,6 +570,143 @@ contains end subroutine test_collapse_to_dom_pfts + + @Test + subroutine test_collapse_to_dom_do_not_collapse() + ! Tests subroutine collapse_to_dominant when used with an optional logical array indicating which gridcells should actually be collapsed + ! + use pftconMod, only: pftcon + use clm_instur, only: wt_nat_patch + use clm_varpar, only: natpft_lb, natpft_ub + + implicit none + integer, parameter :: begg = 2, endg = 4, natpft_size = 15 + real(r8), allocatable :: wt_nat_patch_expected(:,:) + real(r8), allocatable :: wt_nat_patch_in_out(:,:) ! used in subr. call + real(r8) :: expctd(9) + logical, allocatable :: do_not_collapse(:) + + ! Set relevant pftcon values to defaults; override where necessary + call pftcon%InitForTesting() + natpft_ub = natpft_size - 1 + allocate( wt_nat_patch(begg:endg,natpft_lb:natpft_ub) ) + allocate( wt_nat_patch_expected(begg:endg,natpft_lb:natpft_ub) ) + allocate( wt_nat_patch_in_out(begg:endg,natpft_lb:natpft_ub) ) + allocate( do_not_collapse(begg:endg) ) + + ! INPUT VALUES + wt_nat_patch(begg:,:) = 0._r8 ! initialize + wt_nat_patch(begg:,0) = (/ 30._r8, 40._r8, 0._r8/) ! pft0 + wt_nat_patch(begg:,1) = (/ 15._r8, 11._r8, 15._r8/) ! pft1 + wt_nat_patch(begg:,2) = (/ 5._r8, 5._r8, 5._r8/) ! pft2 + wt_nat_patch(begg:,3) = (/ 0._r8, 4._r8, 35._r8/) ! pft3 + wt_nat_patch(begg:,4) = (/ 10._r8, 10._r8, 35._r8/) ! pft4 + wt_nat_patch(begg:,5) = (/ 40._r8, 30._r8, 10._r8/) ! pft5 + wt_nat_patch(:,:) = wt_nat_patch(:,:) / 100._r8 + call check_sums_equal_1( wt_nat_patch, begg, "test_check_sums_add_to_1", & + "should not trigger an error") + do_not_collapse(begg:) = .true. + + ! OUTPUT VALUES EXPECTED + wt_nat_patch_expected = wt_nat_patch + + call check_sums_equal_1( wt_nat_patch_expected, begg, "test_check_sums_add_to_1", & + "should not trigger an error") + + ! Collapse pfts + wt_nat_patch_in_out = wt_nat_patch ! reset argument for next call + call collapse_to_dominant(wt_nat_patch_in_out(begg:endg,:), & + natpft_lb, natpft_ub, begg, endg, & + 1, & + do_not_collapse(begg:endg)) + + ! Now check that are correct + call check_sums_equal_1( wt_nat_patch_in_out, begg, "test_check_sums_add_to_1", & + "should not trigger an error") + + @assertEqual(wt_nat_patch_expected(begg:,:), wt_nat_patch_in_out(begg:,:), tolerance=0._r8) + + deallocate( wt_nat_patch_expected ) + deallocate( wt_nat_patch_in_out ) + deallocate( wt_nat_patch ) + deallocate( do_not_collapse ) + + call pftcon%clean() + + end subroutine test_collapse_to_dom_do_not_collapse + + + @Test + subroutine test_collapse_to_dom_do_not_collapse_present_false() + ! Tests subroutine collapse_to_dominant when used with an optional logical array indicating which gridcells should actually be collapsed + ! + use pftconMod, only: pftcon + use clm_instur, only: wt_nat_patch + use clm_varpar, only: natpft_lb, natpft_ub + + implicit none + integer, parameter :: begg = 2, endg = 4, natpft_size = 15 + real(r8), allocatable :: wt_nat_patch_expected(:,:) + real(r8), allocatable :: wt_nat_patch_in_out(:,:) ! used in subr. call + real(r8) :: expctd(9) + logical, allocatable :: do_not_collapse(:) + + ! Set relevant pftcon values to defaults; override where necessary + call pftcon%InitForTesting() + natpft_ub = natpft_size - 1 + allocate( wt_nat_patch(begg:endg,natpft_lb:natpft_ub) ) + allocate( wt_nat_patch_expected(begg:endg,natpft_lb:natpft_ub) ) + allocate( wt_nat_patch_in_out(begg:endg,natpft_lb:natpft_ub) ) + allocate( do_not_collapse(begg:endg) ) + + ! INPUT VALUES + wt_nat_patch(begg:,:) = 0._r8 ! initialize + wt_nat_patch(begg:,0) = (/ 30._r8, 40._r8, 0._r8/) ! pft0 + wt_nat_patch(begg:,1) = (/ 15._r8, 11._r8, 15._r8/) ! pft1 + wt_nat_patch(begg:,2) = (/ 5._r8, 5._r8, 5._r8/) ! pft2 + wt_nat_patch(begg:,3) = (/ 0._r8, 4._r8, 35._r8/) ! pft3 + wt_nat_patch(begg:,4) = (/ 10._r8, 10._r8, 35._r8/) ! pft4 + wt_nat_patch(begg:,5) = (/ 40._r8, 30._r8, 10._r8/) ! pft5 + wt_nat_patch(:,:) = wt_nat_patch(:,:) / 100._r8 + call check_sums_equal_1( wt_nat_patch, begg, "test_check_sums_add_to_1", & + "should not trigger an error") + do_not_collapse(begg:) = .false. + + ! OUTPUT VALUES EXPECTED + expctd(1) = 40._r8 / 40._r8 + expctd(2) = 35._r8 / 35._r8 + wt_nat_patch_expected(begg:,:) = 0._r8 ! initialize + wt_nat_patch_expected(begg:,0) = (/ 0._r8, expctd(1), 0._r8 /) ! pft 0 + wt_nat_patch_expected(begg:,3) = (/ 0._r8, 0._r8, expctd(2) /) ! pft 3 + wt_nat_patch_expected(begg:,5) = (/ expctd(1), 0._r8, 0._r8 /) ! pft 5 + + + call check_sums_equal_1( wt_nat_patch_expected, begg, "test_check_sums_add_to_1", & + "should not trigger an error") + + ! Collapse pfts + wt_nat_patch_in_out = wt_nat_patch ! reset argument for next call + call collapse_to_dominant(wt_nat_patch_in_out(begg:endg,:), & + natpft_lb, natpft_ub, begg, endg, & + 1, & + do_not_collapse(begg:endg)) + + ! Now check that are correct + call check_sums_equal_1( wt_nat_patch_in_out, begg, "test_check_sums_add_to_1", & + "should not trigger an error") + + @assertEqual(wt_nat_patch_expected(begg:,:), wt_nat_patch_in_out(begg:,:), tolerance=0._r8) + + deallocate( wt_nat_patch_expected ) + deallocate( wt_nat_patch_in_out ) + deallocate( wt_nat_patch ) + deallocate( do_not_collapse ) + + call pftcon%clean() + + end subroutine test_collapse_to_dom_do_not_collapse_present_false + + @Test subroutine test_collapse_crop_types_none() ! This test sets cftsize = 0, ie crops are lumped together with unmanaged @@ -598,8 +735,8 @@ contains call collapse_crop_types( wt_cft, fert_cft, cftsize, begg, endg, verbose = .true.) ! Now check that are correct - @assertEqual(wt_cft(begg:,:), wt_cft_expected(begg:,:)) - @assertEqual(fert_cft(begg:,:), fert_cft_expected(begg:,:)) + @assertEqual(wt_cft_expected(begg:,:), wt_cft(begg:,:)) + @assertEqual(fert_cft_expected(begg:,:), fert_cft(begg:,:)) call pftcon%clean() end subroutine test_collapse_crop_types_none @@ -645,11 +782,11 @@ contains ! Now check that are correct call check_sums_equal_1( wt_cft/100.0_r8, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_cft(begg:,:), wt_cft_expected(begg:,:)) + @assertEqual(wt_cft_expected(begg:,:), wt_cft(begg:,:)) ! INTENTIONAL? As written, subr. collapse_crop_types does NOT take ! ----------- the avg fert_cft of the irrigated and unirrigated when ! irrigate = .false.. Assuming intentional for now. - @assertEqual(fert_cft(begg:,:), fert_cft_expected(begg:,:)) + @assertEqual(fert_cft_expected(begg:,:), fert_cft(begg:,:)) call pftcon%clean() end subroutine test_collapse_crop_types_16_to_15 @@ -694,8 +831,8 @@ contains ! Now check that are correct call check_sums_equal_1( wt_cft/100.0_r8, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_cft(begg:,:), wt_cft_expected(begg:,:)) - @assertEqual(fert_cft(begg:,:), fert_cft_expected(begg:,:)) + @assertEqual(wt_cft_expected(begg:,:), wt_cft(begg:,:)) + @assertEqual(fert_cft_expected(begg:,:), fert_cft(begg:,:)) call pftcon%clean() end subroutine test_collapse_crop_types_16_to_16 @@ -750,8 +887,8 @@ contains ! Now check that are correct call check_sums_equal_1( wt_cft/100.0_r8, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_cft(begg:,:2), wt_cft_expected(begg:,:2)) - @assertEqual(fert_cft(begg:,:2), fert_cft_expected(begg:,:2)) + @assertEqual(wt_cft_expected(begg:,:2), wt_cft(begg:,:2)) + @assertEqual(fert_cft_expected(begg:,:2), fert_cft(begg:,:2)) call pftcon%clean() end subroutine test_collapse_crop_types_18_to_16 @@ -806,8 +943,8 @@ contains ! Now check that are correct call check_sums_equal_1( wt_cft/100.0_r8, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_cft(begg:,:2), wt_cft_expected(begg:,:2)) - @assertEqual(fert_cft(begg:,1), fert_cft_expected(begg:,1)) + @assertEqual(wt_cft_expected(begg:,:2), wt_cft(begg:,:2)) + @assertEqual(fert_cft_expected(begg:,1), fert_cft(begg:,1)) call pftcon%clean() end subroutine test_collapse_crop_types_18_to_15 @@ -855,8 +992,8 @@ contains ! Now check that are correct call check_sums_equal_1( wt_cft/100.0_r8, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_cft(begg:,:), wt_cft_expected(begg:,:)) - @assertEqual(fert_cft(begg:,:), fert_cft_expected(begg:,:)) + @assertEqual(wt_cft_expected(begg:,:), wt_cft(begg:,:)) + @assertEqual(fert_cft_expected(begg:,:), fert_cft(begg:,:)) call pftcon%clean() end subroutine test_collapse_crop_types_18_to_18 @@ -914,8 +1051,8 @@ contains ! Now check that are correct call check_sums_equal_1( wt_cft/100.0_r8, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_cft(begg:,:), wt_cft_expected(begg:,:)) - @assertEqual(fert_cft(begg:,:), fert_cft_expected(begg:,:)) + @assertEqual(wt_cft_expected(begg:,:), wt_cft(begg:,:)) + @assertEqual(fert_cft_expected(begg:,:), fert_cft(begg:,:)) call pftcon%clean() end subroutine test_collapse_crop_types_20_to_18 @@ -972,7 +1109,7 @@ contains call check_sums_equal_1( wt_nat_patch, begg, "test_check_sums_add_to_1", & "should not trigger an error") @assertEqual(wtpft,wt_nat_patch) - @assertEqual(wt_lunit(begg:,istsoil),(/1.00_r8,1.00_r8/)) + @assertEqual((/1.00_r8,1.00_r8/), wt_lunit(begg:,istsoil)) deallocate( wt_nat_patch ) deallocate( wtpft ) @@ -1023,10 +1160,10 @@ contains "should not trigger an error") call check_sums_equal_1( wt_nat_patch, begg, "test_check_sums_add_to_1", & "should not trigger an error") - @assertEqual(wt_lunit(begg:,istsoil), (/1.00_r8,1.00_r8/)) - @assertEqual(wt_nat_patch(begg:,ndllf_evr_tmp_tree),(/0.25_r8,0.25_r8/)) - @assertEqual(wt_nat_patch(begg:,nc3crop), (/0.1875_r8,0.1875_r8/)) - @assertEqual(wt_nat_patch(begg:,nc3irrig), (/0.5625_r8,0.5625_r8/)) + @assertEqual((/1.00_r8,1.00_r8/), wt_lunit(begg:,istsoil)) + @assertEqual((/0.25_r8,0.25_r8/), wt_nat_patch(begg:,ndllf_evr_tmp_tree)) + @assertEqual((/0.1875_r8,0.1875_r8/), wt_nat_patch(begg:,nc3crop)) + @assertEqual((/0.5625_r8,0.5625_r8/), wt_nat_patch(begg:,nc3irrig)) call pftcon%clean() end subroutine test_convert_cft_to_pft @@ -1071,7 +1208,7 @@ contains array(lb+1,lb2+2) = array(lb+1,lb2+2) + eps call check_sums_equal_1( array, lb, "test_check_sums_add_to_1_fail", & "should trigger an error", ier) - @assertEqual(ier,-10) + @assertEqual(-10, ier) end subroutine test_check_sums_add_to_1_fail @Test subroutine test_renormalize @@ -1096,7 +1233,7 @@ contains ! Make the normalized result 100, so multiply the expected result by 100 expected(:,:) = expected(:,:)*100.0d00 call renormalize(array, lb, 100.0d00) - @assertEqual(array, expected, tolerance=tol) + @assertEqual(expected, array, tolerance=tol) ! divide by 100 and should add to one array = array / 100.0d00 call check_sums_equal_1( array, lb, "test_check_sums_add_to_1", & @@ -1104,7 +1241,7 @@ contains ! Call again returning error code, make sure error code is zero call check_sums_equal_1( array, lb, "test_check_sums_add_to_1", & "should not trigger an error", ier) - @assertEqual(ier,0) + @assertEqual(0, ier) end subroutine test_renormalize @Test @@ -1118,7 +1255,7 @@ contains array(:,:) = 0.0d00 expected(:,:) = array call renormalize(array, lb, 100.0d00) - @assertEqual(array, expected, tolerance=tol) + @assertEqual(expected, array, tolerance=tol) end subroutine test_renormalize_zero end module test_surfrdUtils diff --git a/src/utils/clmfates_interfaceMod.F90 b/src/utils/clmfates_interfaceMod.F90 index 7039884847..fcd5cb5230 100644 --- a/src/utils/clmfates_interfaceMod.F90 +++ b/src/utils/clmfates_interfaceMod.F90 @@ -64,6 +64,7 @@ module CLMFatesInterfaceMod use clm_varctl , only : fates_inventory_ctrl_filename use clm_varctl , only : use_nitrif_denitrif use clm_varctl , only : use_lch4 + use clm_varctl , only : fates_history_dimlevel use clm_varcon , only : tfrz use clm_varcon , only : spval use clm_varcon , only : denice @@ -133,7 +134,7 @@ module CLMFatesInterfaceMod use FatesParametersInterface, only : fates_parameters_type use FatesInterfaceMod , only : DetermineGridCellNeighbors - + use FatesIOVariableKindMod, only : group_dyna_simple, group_dyna_complx use FatesHistoryInterfaceMod, only : fates_hist use FatesRestartInterfaceMod, only : fates_restart_interface_type @@ -274,6 +275,7 @@ module CLMFatesInterfaceMod public :: CLMFatesGlobals1 public :: CLMFatesGlobals2 + public :: CrossRefHistoryFields contains @@ -406,6 +408,10 @@ subroutine CLMFatesGlobals2() call set_fates_ctrlparms('parteh_mode',ival=fates_parteh_mode) call set_fates_ctrlparms('seeddisp_cadence',ival=fates_seeddisp_cadence) + + call set_fates_ctrlparms('hist_hifrq_dimlevel',ival=fates_history_dimlevel(1)) + call set_fates_ctrlparms('hist_dynam_dimlevel',ival=fates_history_dimlevel(2)) + ! CTSM-FATES is not fully coupled (yet) ! So lets tell fates to use the RD competition mechanism ! which has fewer boundary conditions (simpler) @@ -571,6 +577,90 @@ subroutine CLMFatesGlobals2() return end subroutine CLMFatesGlobals2 + ! =================================================================================== + + subroutine CrossRefHistoryFields + + ! This routine only needs to be called on the masterproc. + ! Here we cross reference the CLM history master + ! list and make sure that all fields that start + ! with fates have been allocated. If it has + ! not, then we give a more constructive error + ! message than what is possible in PIO. The user + ! most likely needs to increase the history density + ! level + + use histFileMod, only: getname + use histFileMod, only: hist_fincl1,hist_fincl2,hist_fincl3,hist_fincl4 + use histFileMod, only: hist_fincl5,hist_fincl6,hist_fincl7,hist_fincl8 + use histFileMod, only: hist_fincl9,hist_fincl10 + use histFileMod, only: max_tapes, max_flds, max_namlen + + integer :: t ! iterator index for history tapes + integer :: f ! iterator index for registered history field names + integer :: nh ! iterator index for fates registered history + logical :: is_fates_field ! Does this start with FATES_ ? + logical :: found ! if true, than the history field is either + ! not part of the fates set, or was found in + ! the fates set + character(len=64) :: fincl_name + ! This is a copy of the public in histFileMod, copied + ! here because it isn't filled at the time of this call + character(len=max_namlen+2) :: fincl(max_flds,max_tapes) + + fincl(:,1) = hist_fincl1(:) + fincl(:,2) = hist_fincl2(:) + fincl(:,3) = hist_fincl3(:) + fincl(:,4) = hist_fincl4(:) + fincl(:,5) = hist_fincl5(:) + fincl(:,6) = hist_fincl6(:) + fincl(:,7) = hist_fincl7(:) + fincl(:,8) = hist_fincl8(:) + fincl(:,9) = hist_fincl9(:) + fincl(:,10) = hist_fincl10(:) + + do t = 1,max_tapes + + f = 1 + search_fields: do while (f < max_flds .and. fincl(f,t) /= ' ') + + fincl_name = getname(fincl(f,t)) + is_fates_field = fincl_name(1:6)=='FATES_' + + if(is_fates_field) then + found = .false. + do_fates_hist: do nh = 1,fates_hist%num_history_vars() + if(trim(fates_hist%hvars(nh)%vname) == & + trim(fincl_name)) then + found=.true. + exit do_fates_hist + end if + end do do_fates_hist + + if(.not.found)then + write(iulog,*) 'the history field: ',trim(fincl_name) + write(iulog,*) 'was requested in the namelist, but was' + write(iulog,*) 'not found in the list of fates_hist%hvars.' + write(iulog,*) 'Most likely, this is because this history variable' + write(iulog,*) 'was specified in the user namelist, but the user' + write(iulog,*) 'specified a FATES history output dimension level' + write(iulog,*) 'that does not contain that variable in its valid set.' + write(iulog,*) 'You may have to increase the namelist setting: fates_history_dimlevel' + write(iulog,*) 'current fates_history_dimlevel: ',fates_history_dimlevel(:) + !uncomment if you want to list all fates history variables in registry + !do_fates_hist2: do nh = 1,fates_hist%num_history_vars() + ! write(iulog,*) trim(fates_hist%hvars(nh)%vname) + !end do do_fates_hist2 + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + end if + f = f + 1 + end do search_fields + + end do + end subroutine CrossRefHistoryFields + + ! =================================================================================== subroutine CLMFatesTimesteps() @@ -1106,9 +1196,9 @@ subroutine dynamics_driv(this, nc, bounds_clump, & ! Flush arrays to values defined by %flushval (see registry entry in ! subroutine define_history_vars() ! --------------------------------------------------------------------------------- - call fates_hist%flush_hvars(nc,upfreq_in=1) + call fates_hist%flush_hvars(nc,upfreq_in=group_dyna_simple) - call fates_hist%flush_hvars(nc,upfreq_in=5) + call fates_hist%flush_hvars(nc,upfreq_in=group_dyna_complx) ! --------------------------------------------------------------------------------- ! Part II: Call the FATES model now that input boundary conditions have been @@ -1883,11 +1973,20 @@ subroutine restart( this, bounds_proc, ncid, flag, waterdiagnosticbulk_inst, & ! ------------------------------------------------------------------------ ! Update history IO fields that depend on ecosystem dynamics ! ------------------------------------------------------------------------ - 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), & - upfreq_in=1) - end do + if(fates_history_dimlevel(2)>0) then + call fates_hist%flush_hvars(nc,upfreq_in=group_dyna_simple) + do s = 1,this%fates(nc)%nsites + call fates_hist%zero_site_hvars(this%fates(nc)%sites(s), & + upfreq_in=group_dyna_simple) + end do + if(fates_history_dimlevel(2)>1) then + call fates_hist%flush_hvars(nc,upfreq_in=group_dyna_complx) + do s = 1,this%fates(nc)%nsites + call fates_hist%zero_site_hvars(this%fates(nc)%sites(s), & + upfreq_in=group_dyna_complx) + end do + end if + end if call fates_hist%update_history_dyn( nc, & this%fates(nc)%nsites, & this%fates(nc)%sites, & @@ -2067,18 +2166,25 @@ subroutine init_coldstart(this, waterstatebulk_inst, waterdiagnosticbulk_inst, & ! ------------------------------------------------------------------------ ! Update history IO fields that depend on ecosystem dynamics ! ------------------------------------------------------------------------ - 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), & - upfreq_in=1) - end do - call fates_hist%update_history_dyn( nc, & + if(fates_history_dimlevel(2)>0) then + call fates_hist%flush_hvars(nc,upfreq_in=group_dyna_simple) + do s = 1,this%fates(nc)%nsites + call fates_hist%zero_site_hvars(this%fates(nc)%sites(s), & + upfreq_in=group_dyna_simple) + end do + if(fates_history_dimlevel(2)>1) then + call fates_hist%flush_hvars(nc,upfreq_in=group_dyna_complx) + do s = 1,this%fates(nc)%nsites + call fates_hist%zero_site_hvars(this%fates(nc)%sites(s), & + upfreq_in=group_dyna_complx) + end do + end if + end if + 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 @@ -2123,7 +2229,7 @@ subroutine wrap_sunfrac(this,nc,atm2lnd_inst,canopystate_inst) call t_startf('fates_wrapsunfrac') - associate( forc_solad => atm2lnd_inst%forc_solad_grc, & + associate( forc_solad => atm2lnd_inst%forc_solad_not_downscaled_grc, & forc_solai => atm2lnd_inst%forc_solai_grc, & fsun => canopystate_inst%fsun_patch, & laisun => canopystate_inst%laisun_patch, & @@ -2885,6 +2991,7 @@ subroutine wrap_update_hifrq_hist(this, bounds_clump, & this%fates(nc)%nsites, & this%fates(nc)%sites, & this%fates(nc)%bc_in, & + this%fates(nc)%bc_out, & dtime) end associate @@ -3198,6 +3305,8 @@ subroutine init_history_io(this,bounds_proc) call fates_hist%initialize_history_vars() nvar = fates_hist%num_history_vars() + call CrossRefHistoryFields() + do ivar = 1, nvar associate( vname => fates_hist%hvars(ivar)%vname, &