diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index b4eefd984c..fd8def8845 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -651,9 +651,9 @@ sub process_namelist_commandline_options { setup_cmdl_dynamic_vegetation($opts, $nl_flags, $definition, $defaults, $nl); setup_cmdl_fates_mode($opts, $nl_flags, $definition, $defaults, $nl); setup_cmdl_vichydro($opts, $nl_flags, $definition, $defaults, $nl); + setup_logic_lnd_tuning($opts, $nl_flags, $definition, $defaults, $nl, $physv); setup_cmdl_run_type($opts, $nl_flags, $definition, $defaults, $nl); setup_cmdl_output_reals($opts, $nl_flags, $definition, $defaults, $nl); - setup_logic_lnd_tuning($opts, $nl_flags, $definition, $defaults, $nl, $physv); } #------------------------------------------------------------------------------- @@ -1265,6 +1265,8 @@ sub setup_cmdl_simulation_year { sub setup_cmdl_run_type { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + # Set the clm_start_type and the st_year, start year + # This MUST be done after lnd_tuning_mode is set my $val; my $var = "clm_start_type"; @@ -1279,20 +1281,19 @@ sub setup_cmdl_run_type { my $group = $definition->get_group_name($date); $nl->set_variable_value($group, $date, $ic_date ); } + my $set = undef; if (defined $opts->{$var}) { - if ($opts->{$var} eq "default" ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, - 'use_cndv'=>$nl_flags->{'use_cndv'}, 'use_fates'=>$nl_flags->{'use_fates'}, - 'sim_year'=>$st_year, 'sim_year_range'=>$nl_flags->{'sim_year_range'}, - 'bgc_spinup'=>$nl_flags->{'bgc_spinup'} ); - } else { + if ($opts->{$var} ne "default" ) { + $set = 1; my $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, quote_string( $opts->{$var} ) ); } - } else { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, - 'use_cndv'=>$nl_flags->{'use_cndv'}, 'use_fates'=>$nl_flags->{'use_fates'}, - 'sim_year'=>$st_year ); + } + if ( ! defined $set ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, + 'use_cndv'=>$nl_flags->{'use_cndv'}, 'use_fates'=>$nl_flags->{'use_fates'}, + 'sim_year'=>$st_year, 'sim_year_range'=>$nl_flags->{'sim_year_range'}, + 'bgc_spinup'=>$nl_flags->{'bgc_spinup'}, 'lnd_tuning_mode'=>$nl_flags->{'lnd_tuning_mode'} ); } $nl_flags->{'clm_start_type'} = $nl->get_value($var); $nl_flags->{'st_year'} = $st_year; @@ -1697,6 +1698,11 @@ sub process_namelist_inline_logic { ################################# setup_logic_fire_emis($opts, $nl_flags, $definition, $defaults, $nl); + ###################################### + # namelist options for dust emissions + ###################################### + setup_logic_dust_emis($opts, $nl_flags, $definition, $defaults, $nl); + ################################# # namelist group: megan_emis_nl # ################################# @@ -3959,6 +3965,56 @@ sub setup_logic_fire_emis { #------------------------------------------------------------------------------- +sub setup_logic_dust_emis { + # Logic to handle the dust emissions + my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; + + # First get the dust emission method + my $var = "dust_emis_method"; + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var ); + + my $dust_emis_method = remove_leading_and_trailing_quotes( $nl->get_value($var) ); + + my @zender_files_in_lnd_opts = ( "stream_fldfilename_zendersoilerod", "stream_meshfile_zendersoilerod", + "zendersoilerod_mapalgo" ); + if ( $dust_emis_method eq "Zender_2003" ) { + # get the zender_soil_erod_source + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, + "zender_soil_erod_source", 'dust_emis_method'=>$dust_emis_method ); + + my $zender_source = remove_leading_and_trailing_quotes( $nl->get_value('zender_soil_erod_source') ); + if ( $zender_source eq "lnd" ) { + foreach my $option ( @zender_files_in_lnd_opts ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $option, + 'dust_emis_method'=>$dust_emis_method, 'zender_soil_erod_source'=>$zender_source, + 'hgrid'=>$nl_flags->{'res'}, 'lnd_tuning_mod'=>$nl_flags->{'lnd_tuning_mode'} ); + } + } else { + foreach my $option ( @zender_files_in_lnd_opts ) { + if ( defined($nl->get_value($option)) ) { + $log->fatal_error("zender_soil_erod_source is NOT lnd, but the file option $option is being set" . + " and should NOT be unless you want it handled here in the LAND model, " . + "otherwise the equivalent option is set in CAM" ); + } + } + } + } else { + # Verify that NONE of the Zender options are being set if Zender is NOT being used + push @zender_files_in_lnd_opts, "zender_soil_erod_source"; + foreach my $option ( @zender_files_in_lnd_opts ) { + if ( defined($nl->get_value($option)) ) { + $log->fatal_error("dust_emis_method is NOT set to Zender_2003, but one of it's options " . + "$option is being set, need to change one or the other" ); + } + } + if ( $dust_emis_method eq "Leung_2023" ) { + $log->warning("dust_emis_method is Leung_2023 and that option has NOT been brought into CTSM yet"); + } + } +} + +#------------------------------------------------------------------------------- + sub setup_logic_megan { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; @@ -4644,6 +4700,7 @@ sub write_output_files { push @groups, "exice_streams"; push @groups, "soilbgc_decomp"; push @groups, "clm_canopy_inparm"; + push @groups, "zendersoilerod"; if (remove_leading_and_trailing_quotes($nl->get_value('snow_cover_fraction_method')) eq 'SwensonLawrence2012') { push @groups, "scf_swenson_lawrence_2012_inparm"; } diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 5c86d230fd..a01839836d 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -279,6 +279,10 @@ attributes from the config_cache.xml file (with keys converted to upper-case). >30.0d00 20.0d00 +20.0d00 +20.0d00 80.0d00 0.85d00 0.98d00 @@ -290,6 +294,10 @@ attributes from the config_cache.xml file (with keys converted to upper-case). >0.010d00 0.008d00 +0.008d00 +0.008d00 0.17d-3 1.6d-4 0.33d00 @@ -657,7 +665,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). -.true. +.true. .true. -.true. -.true. +.true. +.true. .true. .true. .true. + + +.false. +.false. +.false. +.false. +.false. +.false. .false. + hgrid=0.9x1.25 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.false. glc_nec=10 do_transient_pfts=.false. @@ -761,6 +776,7 @@ attributes from the config_cache.xml file (with keys converted to upper-case). hgrid=1.9x2.5 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. + hgrid=1.9x2.5 maxpft=79 mask=gx1v7 use_cn=.true. use_crop=.true. irrigate=.true. glc_nec=10 do_transient_pfts=.false. @@ -2759,6 +2775,60 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1 lnd/clm2/paramdata/finundated_inversiondata_0.9x1_ESMFmesh_cdf5_130621.nc + + + + + +Zender_2003 +atm +bilinear +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2_cam5.4-forCLM_cdf5_c240202.nc +lnd/clm2/dustemisdata/dst_source2x2_cam5.4-forCLM_cdf5_c240202.nc +lnd/clm2/dustemisdata/dst_source2x2_cam5.4-forCLM_cdf5_c240202.nc +lnd/clm2/dustemisdata/dst_source2x2tuned-cam4-forCLM_cdf5_c240202.nc +lnd/clm2/dustemisdata/dst_source2x2tuned-cam4-forCLM_cdf5_c240202.nc +lnd/clm2/dustemisdata/dst_source2x2tuned-cam4-forCLM_cdf5_c240202.nc +lnd/clm2/dustemisdata/dst_source1x1tuned-cam4-forCLM_cdf5_c240202.nc +lnd/clm2/dustemisdata/dst_source1x1tuned-cam4-forCLM_cdf5_c240202.nc +lnd/clm2/dustemisdata/dst_source1x1tuned-cam4-forCLM_cdf5_c240202.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc +lnd/clm2/dustemisdata/dst_source2x2tunedcam6-2x2-forCLM_cdf5_c230312.nc + +lnd/clm2/dustemisdata/dust_2x2_ESMFmesh_cdf5_c230730.nc + diff --git a/bld/namelist_files/namelist_defaults_overall.xml b/bld/namelist_files/namelist_defaults_overall.xml index 96db00478a..df9981efdf 100644 --- a/bld/namelist_files/namelist_defaults_overall.xml +++ b/bld/namelist_files/namelist_defaults_overall.xml @@ -22,6 +22,12 @@ determine default values for namelists. --> +cold +cold +cold +cold +cold +cold arb_ic arb_ic arb_ic diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 8b292294c9..a2e9fb86da 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1832,6 +1832,49 @@ Mapping method from Nitrogen deposition input file to the model resolution copy = copy using the same indices + + + + + +Which dust emission method is going to be used. Either the Zender 2003 scheme or the Leung 2023 +scheme. +(NOTE: The Leung 2023 method is NOT currently available) + + + +Option only applying for the Zender_2003 method for whether the soil erodibility file is handled +here in CTSM, or in the ATM model. +(only used when dust_emis_method is Zender_2003) + + + +Option only applying for the Zender_2003 method for whether the soil erodibility file is handled +here in CTSM, or in the ATM model. +(only used when dust_emis_method is Zender_2003) + bilinear = bilinear interpolation + nn = nearest neighbor + nnoni = nearest neighbor on the "i" (longitude) axis + nnonj = nearest neighbor on the "j" (latitude) axis + spval = set to special value + copy = copy using the same indices + + + +Filename of input stream data for Zender's soil erodibility source function +(only used when dust_emis_method is Zender_2003, and zender_soil_erod_source is lnd) + + + +mesh filename of input stream data for Zender's soil erodibility source function +(only used when dust_emis_method is Zender_2003, and zender_soil_erod_source is lnd) + + @@ -2227,7 +2270,7 @@ Land mask description + valid_values="clm4_5_CRUv7,clm4_5_GSWP3v1,clm4_5_cam6.0,clm4_5_cam5.0,clm4_5_cam4.0,clm5_0_cam6.0,clm5_0_cam5.0,clm5_0_cam4.0,clm5_0_CRUv7,clm5_0_GSWP3v1,clm5_1_GSWP3v1,clm5_1_CRUv7,clm5_1_cam6.0,clm5_1_cam5.0,clm5_1_cam4.0"> General configuration of model version and atmospheric forcing to tune the model to run under. This sets the model to run with constants and initial conditions that were set to run well under the configuration of model version and atmospheric forcing. To run well constants would need to be changed diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index 6224acc815..f207053924 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 = 1549; +my $ntests = 1611; if ( defined($opts{'compare'}) ) { - $ntests += 907; + $ntests += 945; } plan( tests=>$ntests ); @@ -464,6 +464,7 @@ sub cat_and_create_namelistinfile { "-bgc fates -use_case 1850_control -no-megan -namelist \"&a use_fates_sp=T, soil_decomp_method='None'/\"", "-bgc sp -use_case 2000_control -res 0.9x1.25 -namelist '&a use_soil_moisture_streams = T/'", "-bgc bgc -use_case 1850-2100_SSP5-8.5_transient -namelist '&a start_ymd=19101023/'", + "-namelist \"&a dust_emis_method='Zender_2003', zender_soil_erod_source='lnd' /'\"", "-bgc bgc -use_case 2000_control -namelist \"&a fire_method='nofire'/\" -crop", "-res 0.9x1.25 -bgc sp -use_case 1850_noanthro_control -drydep -fire_emis", "-res 0.9x1.25 -bgc bgc -use_case 1850_noanthro_control -drydep -fire_emis -light_res 360x720", @@ -1200,7 +1201,7 @@ sub cat_and_create_namelistinfile { phys=>"clm5_0", }, "NOlunabutsetJmaxb1" =>{ options=>"-envxml_dir . -bgc sp", - namelst=>"use_luna=.false., jmaxb1=1.0", + namelst=>"use_luna=.fwlse., jmaxb1=1.0", GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, @@ -1234,6 +1235,18 @@ sub cat_and_create_namelistinfile { GLC_TWO_WAY_COUPLING=>"FALSE", phys=>"clm5_0", }, + "soil_erod_wo_Zender" =>{ options=>"--envxml_dir . --ignore_warnings", + namelst=>"dust_emis_method='Leung_2023', " . + "stream_meshfile_zendersoilerod = '/dev/null'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, + "soil_erod_wo_lnd_source" =>{ options=>"--envxml_dir .", + namelst=>"dust_emis_method='Zender_2003', " . + "stream_fldfilename_zendersoilerod = '/dev/null', zender_soil_erod_source='atm'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, ); foreach my $key ( keys(%failtest) ) { print( "$key\n" ); @@ -1255,6 +1268,11 @@ sub cat_and_create_namelistinfile { my %warntest = ( # Warnings without the -ignore_warnings option given + "dustemisLeung" =>{ options=>"-envxml_dir .", + namelst=>"dust_emis_method = 'Leung_2023'", + GLC_TWO_WAY_COUPLING=>"FALSE", + phys=>"clm5_1", + }, "coldwfinidat" =>{ options=>"-envxml_dir . -clm_start_type cold", namelst=>"finidat = 'testfile.nc'", GLC_TWO_WAY_COUPLING=>"FALSE", @@ -1700,9 +1718,9 @@ sub cat_and_create_namelistinfile { &make_config_cache($phys); my @forclist = (); if ( $phys == "clm5_1" ) { - @forclist = ( "GSWP3v1" ); + @forclist = ( "GSWP3v1", "cam6.0", "cam5.0", "cam4.0" ); } else { - @forclist = ( "CRUv7", "GSWP3v1", "cam6.0" ); + @forclist = ( "CRUv7", "GSWP3v1", "cam6.0", "cam5.0", "cam4.0" ); } foreach my $forc ( @forclist ) { foreach my $bgc ( "sp", "bgc" ) { diff --git a/cime_config/config_component.xml b/cime_config/config_component.xml index 281a94a0b2..8fecd2f17b 100644 --- a/cime_config/config_component.xml +++ b/cime_config/config_component.xml @@ -63,18 +63,28 @@ Tuning parameters and initial conditions should be optimized for what CLM model version and what meteorlogical forcing combination? UNSET - clm5_0_cam6.0,clm5_0_GSWP3v1,clm5_0_CRUv7,clm4_5_CRUv7,clm4_5_GSWP3v1,clm4_5_cam6.0,clm5_1_GSWP3v1,clm5_1_cam6.0 + clm5_0_cam6.0,clm5.0_cam5.0,clm5.0_cam4.0,clm5_0_GSWP3v1,clm5_0_CRUv7,clm4_5_CRUv7,clm4_5_GSWP3v1,clm4_5_cam6.0,clm4_5_cam5.0,clm4_5_cam4.0,clm5_1_GSWP3v1,clm5_1_cam6.0,clm5_1_cam5.0,clm5_1_cam4.0 clm4_5_CRUv7 clm4_5_CRUv7 clm4_5_GSWP3v1 - clm4_5_cam6.0 + clm4_5_cam4.0 + clm4_5_cam5.0 + clm4_5_cam6.0 + clm4_5_cam6.0 clm4_5_cam6.0 clm5_0_CRUv7 clm5_0_CRUv7 clm5_0_GSWP3v1 - clm5_0_cam6.0 + clm5_0_cam4.0 + clm5_0_cam5.0 + clm5_0_cam6.0 + clm5_0_cam6.0 clm5_0_cam6.0 + clm5_1_cam4.0 + clm5_1_cam5.0 + clm5_1_cam6.0 + clm5_1_cam6.0 clm5_1_GSWP3v1 clm5_1_cam6.0 diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 0e1424fd7c..8354a40c60 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -52,14 +52,44 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/shell_commands b/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/shell_commands new file mode 100644 index 0000000000..010b5b5680 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/shell_commands @@ -0,0 +1,5 @@ +#!/bin/bash + +./xmlchange LND_TUNING_MODE="clm4_5_cam4.0" +./xmlchange ROF_NCPL='$ATM_NCPL' + diff --git a/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/user_nl_clm new file mode 100644 index 0000000000..93b7ee2e48 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm45cam4LndTuningModeZDustSoilErod/user_nl_clm @@ -0,0 +1,3 @@ +! Turn on using the soil eroditability file in CTSM +dust_emis_method = 'Zender_2003' +zender_soil_erod_source = 'lnd' diff --git a/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/include_user_mods new file mode 100644 index 0000000000..fe0e18cf88 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/include_user_mods @@ -0,0 +1 @@ +../default diff --git a/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/shell_commands b/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/shell_commands new file mode 100644 index 0000000000..753bc2f045 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/shell_commands @@ -0,0 +1,5 @@ +#!/bin/bash + +./xmlchange LND_TUNING_MODE="clm5_0_cam5.0" +./xmlchange ROF_NCPL='$ATM_NCPL' + diff --git a/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/user_nl_clm new file mode 100644 index 0000000000..93b7ee2e48 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm50cam5LndTuningModeZDustSoilErod/user_nl_clm @@ -0,0 +1,3 @@ +! Turn on using the soil eroditability file in CTSM +dust_emis_method = 'Zender_2003' +zender_soil_erod_source = 'lnd' diff --git a/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/include_user_mods new file mode 100644 index 0000000000..aa76c52034 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/include_user_mods @@ -0,0 +1 @@ +../clm51cam6LndTuningMode diff --git a/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/user_nl_clm new file mode 100644 index 0000000000..93b7ee2e48 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/clm51cam6LndTuningModeZDustSoilErod/user_nl_clm @@ -0,0 +1,3 @@ +! Turn on using the soil eroditability file in CTSM +dust_emis_method = 'Zender_2003' +zender_soil_erod_source = 'lnd' diff --git a/src/biogeochem/DUSTMod.F90 b/src/biogeochem/DUSTMod.F90 index a86531ba62..ffa2bfe6f0 100644 --- a/src/biogeochem/DUSTMod.F90 +++ b/src/biogeochem/DUSTMod.F90 @@ -30,6 +30,8 @@ module DUSTMod use LandunitType , only : lun use ColumnType , only : col use PatchType , only : patch + use ZenderSoilErodStreamType, only : soil_erod_stream_type + use clm_varctl , only : dust_emis_method ! ! !PUBLIC TYPES implicit none @@ -59,7 +61,8 @@ module DUSTMod real(r8), pointer, private :: vlc_trb_2_patch (:) ! turbulent deposition velocity 2(m/s) real(r8), pointer, private :: vlc_trb_3_patch (:) ! turbulent deposition velocity 3(m/s) real(r8), pointer, private :: vlc_trb_4_patch (:) ! turbulent deposition velocity 4(m/s) - real(r8), pointer, private :: mbl_bsn_fct_col (:) ! basin factor + type(soil_erod_stream_type), private :: soil_erod_stream ! Zender soil erodibility stream data + real(r8), pointer, private :: mbl_bsn_fct_col (:) ! [dimensionless] basin factor, or soil erodibility, time-constant contains @@ -78,11 +81,13 @@ module DUSTMod contains !------------------------------------------------------------------------ - subroutine Init(this, bounds) + subroutine Init(this, bounds, NLFilename) class(dust_type) :: this type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename + call this%soil_erod_stream%Init( bounds, NLFilename ) call this%InitAllocate (bounds) call this%InitHistory (bounds) call this%InitCold (bounds) @@ -112,7 +117,7 @@ subroutine InitAllocate(this, bounds) allocate(this%vlc_trb_2_patch (begp:endp)) ; this%vlc_trb_2_patch (:) = nan allocate(this%vlc_trb_3_patch (begp:endp)) ; this%vlc_trb_3_patch (:) = nan allocate(this%vlc_trb_4_patch (begp:endp)) ; this%vlc_trb_4_patch (:) = nan - allocate(this%mbl_bsn_fct_col (begc:endc)) ; this%mbl_bsn_fct_col (:) = nan + allocate(this%mbl_bsn_fct_col (begc:endc)) ; this%mbl_bsn_fct_col (:) = nan end subroutine InitAllocate @@ -129,9 +134,11 @@ subroutine InitHistory(this, bounds) ! ! !LOCAL VARIABLES: integer :: begp,endp + integer :: begc,endc !------------------------------------------------------------------------ begp = bounds%begp; endp = bounds%endp + begc = bounds%begc; endc = bounds%endc this%flx_mss_vrt_dst_tot_patch(begp:endp) = spval call hist_addfld1d (fname='DSTFLXT', units='kg/m2/s', & @@ -158,6 +165,15 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='turbulent deposition velocity 4', & ptr_patch=this%vlc_trb_4_patch, default='inactive') + if (dust_emis_method == 'Zender_2003') then + if ( this%soil_erod_stream%UseStreams() )then + this%mbl_bsn_fct_col(begc:endc) = spval + call hist_addfld1d (fname='LND_MBL', units='fraction', & + avgflag='A', long_name='Soil erodibility factor', & + ptr_col=this%mbl_bsn_fct_col, default='inactive') + end if + end if + end subroutine InitHistory !----------------------------------------------------------------------- @@ -173,13 +189,26 @@ subroutine InitCold(this, bounds) ! Set basin factor to 1 for now - do c = bounds%begc, bounds%endc - l = col%landunit(c) - - if (.not.lun%lakpoi(l)) then - this%mbl_bsn_fct_col(c) = 1.0_r8 + if (dust_emis_method == 'Leung_2023') then + !do c = bounds%begc, bounds%endc + ! l = col%landunit(c) + + ! if (.not.lun%lakpoi(l)) then + ! this%mbl_bsn_fct_col(c) = 1.0_r8 + ! end if + !end do + call endrun( msg="Leung_2023 dust_emis_method is currently not available"//errMsg(sourcefile, __LINE__)) + else if (dust_emis_method == 'Zender_2003') then + if ( this%soil_erod_stream%UseStreams() )then + call this%soil_erod_stream%CalcDustSource( bounds, & + this%mbl_bsn_fct_col(bounds%begc:bounds%endc) ) + else + this%mbl_bsn_fct_col(:) = 1.0_r8 end if - end do + else + write(iulog,*) 'dust_emis_method not recognized = ', trim(dust_emis_method) + call endrun( msg="dust_emis_method namelist item is not valid "//errMsg(sourcefile, __LINE__)) + end if end subroutine InitCold @@ -262,7 +291,7 @@ subroutine DustEmission (bounds, & fv => frictionvel_inst%fv_patch , & ! Input: [real(r8) (:) ] friction velocity (m/s) (for dust model) u10 => frictionvel_inst%u10_patch , & ! Input: [real(r8) (:) ] 10-m wind (m/s) (created for dust model) - mbl_bsn_fct => dust_inst%mbl_bsn_fct_col , & ! Input: [real(r8) (:) ] basin factor + mbl_bsn_fct => dust_inst%mbl_bsn_fct_col , & ! Input: [real(r8) (:) ] basin factor flx_mss_vrt_dst => dust_inst%flx_mss_vrt_dst_patch , & ! Output: [real(r8) (:,:) ] surface dust emission (kg/m**2/s) flx_mss_vrt_dst_tot => dust_inst%flx_mss_vrt_dst_tot_patch & ! Output: [real(r8) (:) ] total dust flux back to atmosphere (pft) ) @@ -681,10 +710,6 @@ subroutine InitDustVars(this, bounds) real(r8), parameter :: dns_slt = 2650.0_r8 ! [kg m-3] Density of optimal saltation particles !------------------------------------------------------------------------ - associate(& - mbl_bsn_fct => this%mbl_bsn_fct_col & ! Output: [real(r8) (:)] basin factor - ) - ! allocate module variable allocate (ovr_src_snk_mss(dst_src_nbr,ndst)) allocate (dmt_vwr(ndst)) @@ -920,8 +945,6 @@ subroutine InitDustVars(this, bounds) stk_crc(m) = vlc_grv(m) / vlc_stk(m) end do - end associate - end subroutine InitDustVars end module DUSTMod diff --git a/src/cpl/share_esmf/ZenderSoilErodStreamType.F90 b/src/cpl/share_esmf/ZenderSoilErodStreamType.F90 new file mode 100644 index 0000000000..194e022132 --- /dev/null +++ b/src/cpl/share_esmf/ZenderSoilErodStreamType.F90 @@ -0,0 +1,374 @@ +module ZenderSoilErodStreamType +#include "shr_assert.h" + + !----------------------------------------------------------------------- + ! !DESCRIPTION: + ! Contains methods for reading in the Zender et al. (2003b) Dust source function streams file that has been read in from CAM instead of CLM. dmleung 11 Mar 2023 + ! pathname in CAM: /glade/p/cesmdata/cseg/inputdata/atm/cam/dst/ + ! relevant filenames: (CAM6) dst_source2x2tunedcam6-2x2-04062017.nc (default) + ! (CAM5) dst_source2x2_cam5.4_c150327.nc + ! (CAM4) dst_source2x2tuned-cam4-06132012.nc + ! These files are largely similar and the differences are mainly only a little tuning. + ! This .F90 file for now only deals with the CAM6 source function, which can be used in CAM5 and CAM4 too. Not sure if we will expand the code to include a namelist control to deal + ! with the other files. + ! + ! !USES + use ESMF , only : ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU, ESMF_Finalize, ESMF_END_ABORT + use dshr_strdata_mod , only : shr_strdata_type + use shr_kind_mod , only : r8 => shr_kind_r8, CL => shr_kind_cl + use shr_log_mod , only : errMsg => shr_log_errMsg + use spmdMod , only : mpicom, masterproc + use clm_varctl , only : iulog + use abortutils , only : endrun + use decompMod , only : bounds_type + + ! !PUBLIC TYPES: + implicit none + private + + type, public :: soil_erod_stream_type + real(r8), pointer, private :: soil_erodibility (:) ! Zender et al. (2003b) dust source function (or soil erodibility) + contains + + ! !PUBLIC MEMBER FUNCTIONS: + procedure, public :: Init ! Initialize and read data in + procedure, public :: CalcDustSource ! Calculate dust source spatial filter (basically truncating stream data value smaller than 0.1 following CAM's practice) based on input streams + procedure, public :: UseStreams ! If streams will be used + + ! !PRIVATE MEMBER FUNCTIONS: + procedure, private :: InitAllocate ! Allocate data + + end type soil_erod_stream_type + + ! ! PRIVATE DATA: + type, private :: streamcontrol_type + character(len=CL) :: zender_soil_erod_source ! if calculed in lnd or atm + character(len=CL) :: stream_fldFileName_zendersoilerod ! data Filename + character(len=CL) :: stream_meshfile_zendersoilerod ! mesh Filename + character(len=CL) :: zendersoilerod_mapalgo ! map algo + logical :: namelist_set = .false. ! if namelist was set yet + contains + procedure, private :: ReadNML ! Read in namelist + end type streamcontrol_type + + type(streamcontrol_type), private :: control ! Stream control data + + character(len=*), parameter, private :: sourcefile = & + __FILE__ + +!============================================================================== +contains +!============================================================================== + + subroutine Init(this, bounds, NLFilename) + ! + ! Initialize the Zender soil eroditability stream object + ! + ! Uses: + use spmdMod , only : iam + use lnd_comp_shr , only : mesh, model_clock + use dshr_strdata_mod , only : shr_strdata_init_from_inline, shr_strdata_print + use dshr_strdata_mod , only : shr_strdata_advance + use dshr_methods_mod , only : dshr_fldbun_getfldptr + ! + ! arguments + implicit none + class(soil_erod_stream_type) :: this + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! Namelist filename + ! + ! local variables + integer :: ig, g, n ! Indices + integer :: year ! year (0, ...) for nstep+1 + integer :: mon ! month (1, ..., 12) for nstep+1 + integer :: day ! day of month (1, ..., 31) for nstep+1 + integer :: sec ! seconds into current date for nstep+1 + integer :: mcdate ! Current model date (yyyymmdd) + type(shr_strdata_type) :: sdat_erod ! input data stream + character(len=16), allocatable :: stream_varnames(:) ! array of stream field names + integer :: rc ! error code + real(r8), pointer :: dataptr1d(:) ! temporary pointer + character(len=*), parameter :: stream_name = 'zendersoilerod' + !----------------------------------------------------------------------- + + call control%ReadNML( bounds, NLFileName ) + call this%InitAllocate( bounds ) + + if ( this%useStreams() )then ! is this a namelist input and is it set in namelist default + + allocate(stream_varnames(1)) + stream_varnames = (/"mbl_bsn_fct_geo"/) ! varname in the dust source file; the variable is dimensionless + + if (masterproc) then + write(iulog,*) ' stream_varnames = ',stream_varnames + flush(iulog) + end if + + ! Initialize the cdeps data type sdat_erod + call shr_strdata_init_from_inline(sdat_erod, & ! what is this function and where does it come from? + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = control%stream_meshfile_zendersoilerod, & + stream_lev_dimname = 'null', & + stream_mapalgo = control%zendersoilerod_mapalgo, & + stream_filenames = (/trim(control%stream_fldFileName_zendersoilerod)/), & + stream_fldlistFile = stream_varnames, & + stream_fldListModel = stream_varnames, & + stream_yearFirst = 2003, & + stream_yearLast = 2003, & + stream_yearAlign = 1, & + stream_offset = 0, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = 'linear', & + stream_name = 'Zender soil erodibility', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + write(iulog,*) 'Error on stream initialize -- see PET*.ESMF_LogFile(s)' + call endrun("ESMF log error") + end if + + ! Explicitly set current date to a hardcoded constant value. Otherwise + ! using the real date can cause roundoff differences that are + ! detrected as issues with exact restart. EBK M05/20/2017 + ! call get_curr_date(year, mon, day, sec) + year = 2003 + mon = 12 + day = 31 + sec = 0 + mcdate = year*10000 + mon*100 + day + + call shr_strdata_advance(sdat_erod, ymd=mcdate, tod=sec, logunit=iulog, istr='zendersoilerod', rc=rc) ! what is istr and do I need to change elsewhere because the change of istr here + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + write(iulog,*) 'Error on stream advance -- see PET*.ESMF_LogFile(s)' + call endrun("ESMF log error") + end if + + ! Get pointer for stream data that is time and spatially interpolate to model time and grid + do n = 1,size(stream_varnames) + call dshr_fldbun_getFldPtr(sdat_erod%pstrm(1)%fldbun_model, stream_varnames(n), fldptr1=dataptr1d, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + write(iulog,*) 'Error on get field pointer -- see PET*.ESMF_LogFile(s)' + call endrun("ESMF log error") + end if + if (trim(stream_varnames(n)) == 'mbl_bsn_fct_geo') then + ig = 0 + do g = bounds%begg,bounds%endg + ig = ig+1 + this%soil_erodibility(g) = dataptr1d(ig) + end do + + end if + + end do + ! TODO: EBK 03/25/2024: When shr_strdata adds a clean method we should invoke it here to save memory + ! This is talked about in https://github.com/ESCOMP/CDEPS/issues/261 + + end if + + end subroutine Init + + !============================================================================== + logical function UseStreams(this) + ! + ! !DESCRIPTION: + ! Return true if the Zender method is being used and the soil erodability + ! file is being used with it + ! + ! !USES: + use clm_varctl, only : dust_emis_method + ! + ! !ARGUMENTS: + implicit none + class(soil_erod_stream_type) :: this + ! + ! !LOCAL VARIABLES: + if ( .not. control%namelist_set )then + call endrun(msg=' ERROR namelist NOT set before being used'//errMsg(sourcefile, __LINE__)) + end if + if ( (trim(dust_emis_method) == 'Zender_2003') .and. (control%zender_soil_erod_source == "lnd") )then + UseStreams = .true. + else + UseStreams = .false. + end if + end function UseStreams + + !============================================================================== + subroutine InitAllocate(this, bounds) + ! + ! !DESCRIPTION: + ! Allocate module variables and data structures + ! + ! !USES: + use shr_infnan_mod, only: nan => shr_infnan_nan, assignment(=) + ! + ! !ARGUMENTS: + implicit none + class(soil_erod_stream_type) :: this + type(bounds_type), intent(in) :: bounds + ! + ! !LOCAL VARIABLES: + integer :: begg, endg + !--------------------------------------------------------------------- + + begg = bounds%begg; endg = bounds%endg + + if ( this%useStreams() ) then + allocate(this%soil_erodibility (begg:endg)) + else + allocate(this%soil_erodibility (0)) + end if + this%soil_erodibility (:) = nan + + end subroutine InitAllocate + + !============================================================================== + subroutine CalcDustSource(this, bounds, soil_erod) + ! + ! !DESCRIPTION: + ! Calculate the soil eroditability for the Zender dust method. + ! + ! !USES: + use ColumnType , only : col + !use PatchType , only : patch + !USES + use landunit_varcon , only : istdlak + use LandunitType , only : lun + ! + ! !ARGUMENTS: + implicit none + class(soil_erod_stream_type) :: this + type(bounds_type) , intent(in) :: bounds + real(r8) , intent(inout) :: soil_erod(bounds%begc:) ! [fraction] rock drag partition factor (roughness effect) + ! + ! !LOCAL VARIABLES: + !integer :: g, c, fc ! Indices + integer :: g, p, fp, l, c ! Indices + !real(r8) :: z0s ! smooth roughness length (m) + + ! constants + real(r8),parameter :: soil_erod_threshold = 0.1_r8 ! CAM soil erodibility threshold; below threshold -> soil_erod = 0_r8 11 Mar 2023 + !--------------------------------------------------------------------- + + SHR_ASSERT_ALL_FL((ubound(soil_erod) == (/bounds%endc/)), sourcefile, __LINE__) + + !associate( & + !z => col%z & ! Input: [real(r8) (:,:) ] layer depth (m) (-nlevsno+1:nlevsoi) + !) + + + ! dmleung: this loop truncates soil erodibility values smaller than a threshold value (set as 0.1). We save the drag partition factor as a grid level quantity. + do c = bounds%begc,bounds%endc + g = col%gridcell(c) + l = col%landunit(c) + if (lun%itype(l) /= istdlak) then ! not lake (can only be used during initialization) + + if (this%soil_erodibility(g) .lt. soil_erod_threshold ) then + soil_erod(c) = 0._r8 + else + soil_erod(c) = this%soil_erodibility(g) + end if + + end if + end do + + !end associate + + end subroutine CalcDustSource + + !============================================================================== + subroutine ReadNML(this, bounds, NLFilename) + ! + ! Read the namelist data stream information for the Zender method soil + ! eroditability file + ! + ! Uses: + use shr_nl_mod , only : shr_nl_find_group_name + use shr_log_mod , only : errMsg => shr_log_errMsg + use shr_mpi_mod , only : shr_mpi_bcast + ! + ! arguments + implicit none + class(streamcontrol_type) :: this + type(bounds_type), intent(in) :: bounds + character(len=*), intent(in) :: NLFilename ! Namelist filename + ! + ! local variables + integer :: i ! Indices + integer :: nu_nml ! unit for namelist file + integer :: nml_error ! namelist i/o error flag + character(len=CL) :: stream_fldFileName_zendersoilerod = ' ' + character(len=CL) :: stream_meshfile_zendersoilerod = ' ' + character(len=CL) :: zendersoilerod_mapalgo = ' ' + character(len=CL) :: tmp_file_array(3) + character(len=3) :: zender_soil_erod_source = 'atm' + character(len=*), parameter :: namelist_name = 'zendersoilerod' ! MUST agree with group name in namelist definition to read. + character(len=*), parameter :: subName = "('zendersoilerod::ReadNML')" + !----------------------------------------------------------------------- + + namelist /zendersoilerod/ & ! MUST agree with namelist_name above + zendersoilerod_mapalgo, stream_fldFileName_zendersoilerod, & + stream_meshfile_zendersoilerod, zender_soil_erod_source + + ! Default values for namelist + + ! Read zenderdustsource namelist + if (masterproc) then + open( newunit=nu_nml, file=trim(NLFilename), status='old', iostat=nml_error ) + call shr_nl_find_group_name(nu_nml, namelist_name, status=nml_error) + if (nml_error == 0) then + read(nu_nml, nml=zendersoilerod, iostat=nml_error) ! MUST agree with namelist_name above + if (nml_error /= 0) then + call endrun(msg=' ERROR reading '//namelist_name//' namelist'//errMsg(sourcefile, __LINE__)) + end if + else + call endrun(msg=' ERROR finding '//namelist_name//' namelist'//errMsg(sourcefile, __LINE__)) + end if + close(nu_nml) + endif + + call shr_mpi_bcast(zender_soil_erod_source , mpicom) + call shr_mpi_bcast(zendersoilerod_mapalgo , mpicom) + call shr_mpi_bcast(stream_fldFileName_zendersoilerod , mpicom) + call shr_mpi_bcast(stream_meshfile_zendersoilerod , mpicom) + + if (masterproc .and. (zender_soil_erod_source == "lnd") ) then + write(iulog,*) ' ' + write(iulog,*) namelist_name, ' stream settings:' + write(iulog,*) ' stream_fldFileName_zendersoilerod = ',stream_fldFileName_zendersoilerod + write(iulog,*) ' stream_meshfile_zendersoilerod = ',stream_meshfile_zendersoilerod + write(iulog,*) ' zendersoilerod_mapalgo = ',zendersoilerod_mapalgo + endif + + if ( (trim(zender_soil_erod_source) /= 'atm') .and. (trim(zender_soil_erod_source) /= 'lnd') )then + call endrun(msg=' ERROR zender_soil_erod_source must be either lnd or atm and is NOT'//errMsg(sourcefile, __LINE__)) + end if + tmp_file_array(1) = stream_fldFileName_zendersoilerod + tmp_file_array(2) = stream_meshfile_zendersoilerod + tmp_file_array(3) = zendersoilerod_mapalgo + if ( trim(zender_soil_erod_source) == 'lnd' )then + do i = 1, size(tmp_file_array) + if ( len_trim(tmp_file_array(i)) == 0 )then + call endrun(msg=' ERROR '//trim(tmp_file_array(i))//' must be set when Zender_2003 is being used and zender_soil_erod_source is lnd'//errMsg(sourcefile, __LINE__)) + end if + end do + else + do i = 1, size(tmp_file_array) + if ( len_trim(tmp_file_array(i)) > 0 )then + call endrun(msg=' ERROR '//trim(tmp_file_array(i))//' is set and MUST iNOT be when Zender_2003 is NOT being used or zender_soil_erod_source is atm'//errMsg(sourcefile, __LINE__)) + end if + end do + end if + this%stream_fldFileName_zendersoilerod = stream_fldFileName_zendersoilerod + this%stream_meshfile_zendersoilerod = stream_meshfile_zendersoilerod + this%zendersoilerod_mapalgo = zendersoilerod_mapalgo + this%zender_soil_erod_source = zender_soil_erod_source + + this%namelist_set = .true. + + end subroutine ReadNML + +end module ZenderSoilErodStreamType diff --git a/src/main/clm_instMod.F90 b/src/main/clm_instMod.F90 index 43390ca8b7..b9d74c418a 100644 --- a/src/main/clm_instMod.F90 +++ b/src/main/clm_instMod.F90 @@ -350,7 +350,7 @@ subroutine clm_instInit(bounds) call surfrad_inst%Init(bounds) - call dust_inst%Init(bounds) + call dust_inst%Init(bounds, NLFilename) allocate(scf_method, source = CreateAndInitSnowCoverFraction( & snow_cover_fraction_method = snow_cover_fraction_method, & diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 267c425d7d..389ed50f88 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -271,6 +271,11 @@ module clm_varctl ! option to activate OC in snow in SNICAR logical, public :: do_sno_oc = .false. ! control to include organic carbon (OC) in snow + !---------------------------------------------------------- + ! DUST emission method + !---------------------------------------------------------- + character(len=25), public :: dust_emis_method = 'Zender_2003' ! Dust emisison method to use: Zender_2003 or Leung_2023 + !---------------------------------------------------------- ! C isotopes !---------------------------------------------------------- diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index a5da9c3082..dc9622ddac 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -207,6 +207,12 @@ subroutine control_init(dtime) for_testing_no_crop_seed_replenishment, & z0param_method, use_z0m_snowmelt + ! NOTE: EBK 02/26/2024: dust_emis_method is here in CTSM temporarily until it's moved to CMEPS + ! See: https://github.com/ESCOMP/CMEPS/pull/429 + ! Normally this should also need error checking and a broadcast, but since + ! there is only one hardcoded option right now that is unneeded. + namelist /clm_inparm/ dust_emis_method + ! vertical soil mixing variables namelist /clm_inparm/ & som_adv_flux, max_depth_cryoturb