diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index c00226b7dd..c6bbe1227f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -10,5 +10,8 @@ b88e1cd1b28e3609684c79a2ec0e88f26cfc362b b771971e3299c4fa56534b93421f7a2b9c7282fd 9de88bb57ea9855da408cbec1dc8acb9079eda47 8bc4688e52ea23ef688e283698f70a44388373eb +0a5a9e803b56ec1bbd6232eff1c99dbbeef25eb7 +810cb346f05ac1aabfff931ab1a2b7b584add241 +5933b0018f8e29413e30dda9b906370d147bad45 # Ran SystemTests and python/ctsm through black python formatter 5364ad66eaceb55dde2d3d598fe4ce37ac83a93c diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py index 4fd812b84a..bfa8ead151 100644 --- a/cime_config/SystemTests/rxcropmaturity.py +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -134,8 +134,8 @@ def run_phase(self): case_gddgen.check_all_input_data() # Make custom version of surface file - logger.info("RXCROPMATURITY log: run make_fsurdat_all_crops_everywhere") - self._run_make_fsurdat_all_crops_everywhere() + logger.info("RXCROPMATURITY log: run fsurdat_modifier") + self._run_fsurdat_modifier() # ------------------------------------------------------------------- # (2) Perform GDD-generating run and generate prescribed GDDs file @@ -239,7 +239,7 @@ def _setup_all(self): logger.info("RXCROPMATURITY log: _setup_all done") # Make a surface dataset that has every crop in every gridcell - def _run_make_fsurdat_all_crops_everywhere(self): + def _run_fsurdat_modifier(self): # fsurdat should be defined. Where is it? self._fsurdat_in = None @@ -255,19 +255,28 @@ def _run_make_fsurdat_all_crops_everywhere(self): raise RuntimeError(error_message) # Where we will save the fsurdat version for this test - self._fsurdat_out = os.path.join(self._path_gddgen, "fsurdat.nc") + path, ext = os.path.splitext(self._fsurdat_in) + dir_in, filename_in_noext = os.path.split(path) + self._fsurdat_out = os.path.join(self._path_gddgen, f"{filename_in_noext}.all_crops_everywhere{ext}") # Make fsurdat for this test, if not already done if not os.path.exists(self._fsurdat_out): tool_path = os.path.join( self._ctsm_root, - "python", - "ctsm", - "crop_calendars", - "make_fsurdat_all_crops_everywhere.py", + "tools", + "modify_input_files", + "fsurdat_modifier", ) + + # Create configuration file for fsurdat_modifier + self._cfg_path = os.path.join( + self._path_gddgen, + "modify_fsurdat_allcropseverywhere.cfg", + ) + self._create_config_file_evenlysplitcrop() + command = ( - f"python3 {tool_path} " + f"-i {self._fsurdat_in} " + f"-o {self._fsurdat_out}" + f"python3 {tool_path} {self._cfg_path} " ) stu.run_python_script( self._get_caseroot(), @@ -287,6 +296,41 @@ def _run_make_fsurdat_all_crops_everywhere(self): ] ) + def _create_config_file_evenlysplitcrop(self): + """ + Open the new and the template .cfg files + Loop line by line through the template .cfg file + When string matches, replace that line's content + """ + cfg_template_path = os.path.join( + self._ctsm_root, "tools/modify_input_files/modify_fsurdat_template.cfg" + ) + + with open(self._cfg_path, "w", encoding="utf-8") as cfg_out: + # Copy template, replacing some lines + with open(cfg_template_path, "r", encoding="utf-8") as cfg_in: + for line in cfg_in: + if re.match(r" *evenly_split_cropland *=", line): + line = f"evenly_split_cropland = True" + elif re.match(r" *fsurdat_in *=", line): + line = f"fsurdat_in = {self._fsurdat_in}" + elif re.match(r" *fsurdat_out *=", line): + line = f"fsurdat_out = {self._fsurdat_out}" + elif re.match(r" *process_subgrid_section *=", line): + line = f"process_subgrid_section = True" + cfg_out.write(line) + + # Add new lines + cfg_out.write("\n") + cfg_out.write("[modify_fsurdat_subgrid_fractions]\n") + cfg_out.write("PCT_CROP = 100.0\n") + cfg_out.write("PCT_NATVEG = 0.0\n") + cfg_out.write("PCT_GLACIER = 0.0\n") + cfg_out.write("PCT_WETLAND = 0.0\n") + cfg_out.write("PCT_LAKE = 0.0\n") + cfg_out.write("PCT_URBAN = 0.0 0.0 0.0\n") + + def _run_check_rxboth_run(self): output_dir = os.path.join(self._get_caseroot(), "run") diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index aa5dd98697..2b46310a9a 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -2551,7 +2551,24 @@ - + + + + + + + + + + + + + + + + + + diff --git a/python/ctsm/crop_calendars/make_fsurdat_all_crops_everywhere.py b/python/ctsm/crop_calendars/make_fsurdat_all_crops_everywhere.py deleted file mode 100755 index f8c5e8b316..0000000000 --- a/python/ctsm/crop_calendars/make_fsurdat_all_crops_everywhere.py +++ /dev/null @@ -1,55 +0,0 @@ -import numpy as np -import xarray as xr -import sys -import argparse - - -def main(file_in, file_out): - # Import - - in_ds = xr.open_dataset(file_in) - - out_ds = in_ds.copy() - in_ds.close() - - # Move all natural land into crop - out_ds["PCT_CROP"] += in_ds["PCT_NATVEG"] - out_ds["PCT_NATVEG"] -= in_ds["PCT_NATVEG"] - - # Put some of every crop in every gridcell - pct_cft = np.full_like(in_ds["PCT_CFT"].values, 100 / in_ds.dims["cft"]) - out_ds["PCT_CFT"] = xr.DataArray( - data=pct_cft, attrs=in_ds["PCT_CFT"].attrs, dims=in_ds["PCT_CFT"].dims - ) - - # Save - out_ds.to_netcdf(file_out, format="NETCDF3_64BIT") - - -if __name__ == "__main__": - ############################### - ### Process input arguments ### - ############################### - parser = argparse.ArgumentParser( - description="Creates a surface dataset with all natural land moved into crops, and with every crop present in every gridcell." - ) - - # Required - parser.add_argument( - "-i", - "--input-file", - help="Surface dataset (fsurdat) file to process", - required=True, - ) - parser.add_argument( - "-o", - "--output-file", - help="Where to save the new surface dataset file", - required=True, - ) - - # Get arguments - args = parser.parse_args(sys.argv[1:]) - - # Process - main(args.input_file, args.output_file) diff --git a/python/ctsm/modify_input_files/fsurdat_modifier.py b/python/ctsm/modify_input_files/fsurdat_modifier.py index 492fa74230..eedafeecbb 100644 --- a/python/ctsm/modify_input_files/fsurdat_modifier.py +++ b/python/ctsm/modify_input_files/fsurdat_modifier.py @@ -237,6 +237,7 @@ def modify_optional( std_elev, soil_color, dom_pft, + evenly_split_cropland, lai, sai, hgt_top, @@ -281,6 +282,10 @@ def modify_optional( ) logger.info("dom_pft complete") + if evenly_split_cropland: + modify_fsurdat.evenly_split_cropland() + logger.info("evenly_split_cropland complete") + def read_cfg_optional_basic_opts(modify_fsurdat, config, cfg_path, section): """Read the optional parts of the main section of the config file. @@ -429,10 +434,26 @@ def read_cfg_option_control( logger.info("dom_pft option is on and = %s", str(dom_pft)) else: logger.info("dom_pft option is off") + evenly_split_cropland = get_config_value( + config=config, + section=section, + item="evenly_split_cropland", + file_path=cfg_path, + convert_to_type=bool, + ) + if evenly_split_cropland and dom_pft and dom_pft > int(max(modify_fsurdat.file.natpft.values)): + abort("dom_pft must not be set to a crop PFT when evenly_split_cropland is True") if process_subgrid and idealized: abort("idealized AND process_subgrid_section can NOT both be on, pick one or the other") - return (idealized, process_subgrid, process_var_list, include_nonveg, dom_pft) + return ( + idealized, + process_subgrid, + process_var_list, + include_nonveg, + dom_pft, + evenly_split_cropland, + ) def read_cfg_required_basic_opts(config, section, cfg_path): @@ -555,6 +576,7 @@ def fsurdat_modifier(parser): process_var_list, include_nonveg, dom_pft, + evenly_split_cropland, ) = read_cfg_option_control( modify_fsurdat, config, @@ -584,6 +606,7 @@ def fsurdat_modifier(parser): std_elev, soil_color, dom_pft, + evenly_split_cropland, lai, sai, hgt_top, diff --git a/python/ctsm/modify_input_files/modify_fsurdat.py b/python/ctsm/modify_input_files/modify_fsurdat.py index 9b3760e303..53f06d7dc8 100644 --- a/python/ctsm/modify_input_files/modify_fsurdat.py +++ b/python/ctsm/modify_input_files/modify_fsurdat.py @@ -165,6 +165,18 @@ def write_output(self, fsurdat_in, fsurdat_out): logger.info("Successfully created fsurdat_out: %s", fsurdat_out) self.file.close() + def evenly_split_cropland(self): + """ + Description + ----------- + In rectangle selected by user (or default -90 to 90 and 0 to 360), + replace fsurdat file's PCT_CFT with equal values for all crop types. + """ + pct_cft = np.full_like(self.file["PCT_CFT"].values, 100 / self.file.dims["cft"]) + self.file["PCT_CFT"] = xr.DataArray( + data=pct_cft, attrs=self.file["PCT_CFT"].attrs, dims=self.file["PCT_CFT"].dims + ) + def set_dom_pft(self, dom_pft, lai, sai, hgt_top, hgt_bot): """ Description diff --git a/python/ctsm/site_and_regional/single_point_case.py b/python/ctsm/site_and_regional/single_point_case.py index 96544ff9c1..456bebee91 100644 --- a/python/ctsm/site_and_regional/single_point_case.py +++ b/python/ctsm/site_and_regional/single_point_case.py @@ -52,6 +52,8 @@ class SinglePointCase(BaseCase): flag for creating user mods directories and files dom_pft : int dominant pft type for this single point (None if not specified) + evenly_split_cropland : bool + flag for splitting cropland evenly among all crop types pct_pft : list weight or percentage of each pft. num_pft : list @@ -105,6 +107,7 @@ def __init__( create_datm, create_user_mods, dom_pft, + evenly_split_cropland, pct_pft, num_pft, include_nonveg, @@ -125,6 +128,7 @@ def __init__( self.plon = plon self.site_name = site_name self.dom_pft = dom_pft + self.evenly_split_cropland = evenly_split_cropland self.pct_pft = pct_pft self.num_pft = num_pft self.include_nonveg = include_nonveg @@ -437,6 +441,9 @@ def modify_surfdata_atpoint(self, f_orig): f_mod["PCT_CROP"] = f_mod["PCT_CROP"] / tot_pct * 100 f_mod["PCT_NATVEG"] = f_mod["PCT_NATVEG"] / tot_pct * 100 + if self.evenly_split_cropland: + f_mod["PCT_CFT"][:, :, :] = 100.0 / f_mod["PCT_CFT"].shape[2] + else: logger.info( "You chose --include-nonveg --> \ diff --git a/python/ctsm/subset_data.py b/python/ctsm/subset_data.py index 4a3a5801f1..a99d42cc14 100644 --- a/python/ctsm/subset_data.py +++ b/python/ctsm/subset_data.py @@ -551,6 +551,7 @@ def subset_point(args, file_dict: dict): create_datm=args.create_datm, create_user_mods=args.create_user_mods, dom_pft=args.dom_pft, + evenly_split_cropland=args.evenly_split_cropland, pct_pft=args.pct_pft, num_pft=num_pft, include_nonveg=args.include_nonveg, diff --git a/python/ctsm/test/test_sys_fsurdat_modifier.py b/python/ctsm/test/test_sys_fsurdat_modifier.py index 72d38732cf..f83aff0b64 100755 --- a/python/ctsm/test/test_sys_fsurdat_modifier.py +++ b/python/ctsm/test/test_sys_fsurdat_modifier.py @@ -215,7 +215,6 @@ def test_opt_sections(self): np.testing.assert_array_equal(fsurdat_out_data.PCT_CROP, zero0d) np.testing.assert_array_equal(fsurdat_out_data.PCT_LAKE, zero0d) np.testing.assert_array_equal(fsurdat_out_data.PCT_WETLAND, zero0d) - np.testing.assert_array_equal(fsurdat_out_data.PCT_LAKE, zero0d) np.testing.assert_array_equal(fsurdat_out_data.PCT_GLACIER, zero0d) np.testing.assert_array_equal(fsurdat_out_data.PCT_URBAN, pct_urban) np.testing.assert_array_equal(fsurdat_out_data.LAKEDEPTH, one0d * 200.0) @@ -223,6 +222,30 @@ def test_opt_sections(self): np.testing.assert_array_equal(fsurdat_out_data.ALB_ROOF_DIR, lev2_two) np.testing.assert_array_equal(fsurdat_out_data.TK_ROOF, lev2_five) + def test_evenly_split_cropland(self): + """ + Test that evenly splitting cropland works + """ + self._create_config_file_evenlysplitcrop() + sys.argv = [ + "fsurdat_modifier", + self._cfg_file_path, + ] + parser = fsurdat_modifier_arg_process() + fsurdat_modifier(parser) + # Read the resultant output file and make sure the fields are changed as expected + fsurdat_in_data = xr.open_dataset(self._fsurdat_in) + fsurdat_out_data = xr.open_dataset(self._fsurdat_out) + Ncrops = fsurdat_out_data.dims["cft"] + pct_cft = np.full_like(fsurdat_out_data.PCT_CFT, 100 / Ncrops) + np.testing.assert_array_equal(fsurdat_in_data.PCT_NATVEG, fsurdat_out_data.PCT_NATVEG) + np.testing.assert_array_equal(fsurdat_in_data.PCT_CROP, fsurdat_out_data.PCT_CROP) + np.testing.assert_array_equal(fsurdat_in_data.PCT_LAKE, fsurdat_out_data.PCT_LAKE) + np.testing.assert_array_equal(fsurdat_in_data.PCT_WETLAND, fsurdat_out_data.PCT_WETLAND) + np.testing.assert_array_equal(fsurdat_in_data.PCT_GLACIER, fsurdat_out_data.PCT_GLACIER) + np.testing.assert_array_equal(fsurdat_in_data.PCT_URBAN, fsurdat_out_data.PCT_URBAN) + np.testing.assert_array_equal(fsurdat_out_data.PCT_CFT, pct_cft) + def test_1x1_mexicocity(self): """ Test that the mexicocity file is handled correctly @@ -407,6 +430,23 @@ def _create_config_file_minimal(self): line = f"fsurdat_out = {self._fsurdat_out}" cfg_out.write(line) + def _create_config_file_evenlysplitcrop(self): + """ + Open the new and the template .cfg files + Loop line by line through the template .cfg file + When string matches, replace that line's content + """ + with open(self._cfg_file_path, "w", encoding="utf-8") as cfg_out: + with open(self._cfg_template_path, "r", encoding="utf-8") as cfg_in: + for line in cfg_in: + if re.match(r" *evenly_split_cropland *=", line): + line = f"evenly_split_cropland = True" + elif re.match(r" *fsurdat_in *=", line): + line = f"fsurdat_in = {self._fsurdat_in}" + elif re.match(r" *fsurdat_out *=", line): + line = f"fsurdat_out = {self._fsurdat_out}" + cfg_out.write(line) + def _create_config_file_crop(self): """ Open the new and the template .cfg files @@ -430,6 +470,8 @@ def _create_config_file_crop(self): line = "lnd_lon_2 = 300\n" elif re.match(r" *dom_pft *=", line): line = "dom_pft = 15" + elif re.match(r" *evenly_split_cropland *=", line): + line = "evenly_split_cropland = False" elif re.match(r" *lai *=", line): line = "lai = 0 1 2 3 4 5 5 4 3 2 1 0\n" elif re.match(r" *sai *=", line): @@ -465,6 +507,8 @@ def _create_config_file_complete(self): line = "lnd_lon_2 = 300\n" elif re.match(r" *dom_pft *=", line): line = "dom_pft = 1" + elif re.match(r" *evenly_split_cropland *=", line): + line = "evenly_split_cropland = False" elif re.match(r" *lai *=", line): line = "lai = 0 1 2 3 4 5 5 4 3 2 1 0\n" elif re.match(r" *sai *=", line): diff --git a/python/ctsm/test/test_unit_fsurdat_modifier.py b/python/ctsm/test/test_unit_fsurdat_modifier.py index 0ea862a8e4..166924903b 100755 --- a/python/ctsm/test/test_unit_fsurdat_modifier.py +++ b/python/ctsm/test/test_unit_fsurdat_modifier.py @@ -95,6 +95,18 @@ def test_subgrid_and_idealized_fails(self): ): read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + def test_dompft_and_splitcropland_fails(self): + """test that setting dompft crop with evenly_split_cropland True fails gracefully""" + section = "modify_fsurdat_basic_options" + crop_pft = max(self.modify_fsurdat.file.natpft.values) + 1 + self.config.set(section, "dom_pft", str(crop_pft)) + self.config.set(section, "evenly_split_cropland", "True") + with self.assertRaisesRegex( + SystemExit, + "dom_pft must not be set to a crop PFT when evenly_split_cropland is True", + ): + read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + def test_optional_only_true_and_false(self): """test that optional settings can only be true or false""" section = "modify_fsurdat_basic_options" @@ -114,12 +126,18 @@ def test_optional_only_true_and_false(self): read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) self.config.set(section, "dom_pft", "UNSET") read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) - var = "idealized" - self.config.set(section, var, "Thing") - with self.assertRaisesRegex( - SystemExit, "Non-boolean value found for .cfg file variable: " + var - ): - read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + varlist = ( + "idealized", + "evenly_split_cropland", + ) + for var in varlist: + orig_value = self.config.get(section, var) + self.config.set(section, var, "Thing") + with self.assertRaisesRegex( + SystemExit, "Non-boolean value found for .cfg file variable: " + var + ): + read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path) + self.config.set(section, var, orig_value) def test_read_subgrid(self): """test a simple read of subgrid""" @@ -164,6 +182,24 @@ def test_read_subgrid_allurban(self): self.config.set(section, "pct_crop", "0.") read_cfg_subgrid(self.config, self.cfg_path) + def test_read_subgrid_split_cropland(self): + """ + test a read of subgrid that's 50/50 natural and + cropland, with cropland split evenly among + crop types + """ + section = "modify_fsurdat_basic_options" + self.config.set(section, "idealized", "False") + self.config.set(section, "evenly_split_cropland", "True") + section = "modify_fsurdat_subgrid_fractions" + self.config.set(section, "pct_urban", "0.0 0.0 0.0") + self.config.set(section, "pct_lake", "0.") + self.config.set(section, "pct_wetland", "0.") + self.config.set(section, "pct_glacier", "0.") + self.config.set(section, "pct_natveg", "50.") + self.config.set(section, "pct_crop", "50.") + read_cfg_subgrid(self.config, self.cfg_path) + def test_read_var_list(self): """test a simple read of var_list""" read_cfg_var_list(self.config, idealized=True) diff --git a/python/ctsm/test/test_unit_singlept_data.py b/python/ctsm/test/test_unit_singlept_data.py index 570207fa26..6fdc3109d9 100755 --- a/python/ctsm/test/test_unit_singlept_data.py +++ b/python/ctsm/test/test_unit_singlept_data.py @@ -36,6 +36,7 @@ class TestSinglePointCase(unittest.TestCase): create_datm = True create_user_mods = True dom_pft = [8] + evenly_split_cropland = False pct_pft = None num_pft = 16 include_nonveg = False @@ -58,6 +59,7 @@ def test_create_tag_noname(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -84,6 +86,7 @@ def test_create_tag_name(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -111,6 +114,7 @@ def test_check_dom_pft_too_big(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -138,6 +142,7 @@ def test_check_dom_pft_too_small(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -165,6 +170,7 @@ def test_check_dom_pft_numpft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -193,6 +199,7 @@ def test_check_dom_pft_mixed_range(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -223,6 +230,7 @@ def test_check_nonveg_nodompft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -254,6 +262,7 @@ def test_check_pct_pft_notsamenumbers(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -284,6 +293,7 @@ def test_check_pct_pft_sum_not1(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -314,6 +324,7 @@ def test_check_pct_pft_fraction_topct(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, diff --git a/python/ctsm/test/test_unit_singlept_data_surfdata.py b/python/ctsm/test/test_unit_singlept_data_surfdata.py index 9623975452..0052e796d1 100755 --- a/python/ctsm/test/test_unit_singlept_data_surfdata.py +++ b/python/ctsm/test/test_unit_singlept_data_surfdata.py @@ -44,6 +44,7 @@ class TestSinglePointCaseSurfaceNoCrop(unittest.TestCase): create_datm = True create_user_mods = True dom_pft = [8] + evenly_split_cropland = False pct_pft = None num_pft = 16 include_nonveg = False @@ -155,6 +156,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_pctnatpft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -187,6 +189,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_pctnatveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -215,6 +218,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_pctcrop(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -243,6 +247,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_glacier(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -272,6 +277,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_wetland(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -301,6 +307,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_lake(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -330,6 +337,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_unisnow(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -360,6 +368,7 @@ def test_modify_surfdata_atpoint_nocrop_1pft_capsat(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -390,6 +399,7 @@ def test_modify_surfdata_atpoint_nocrop_multipft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -426,6 +436,7 @@ def test_modify_surfdata_atpoint_nocrop_urban_nononveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -460,6 +471,7 @@ def test_modify_surfdata_atpoint_nocrop_urban_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -497,6 +509,7 @@ def test_modify_surfdata_atpoint_nocrop_wetland_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -527,6 +540,7 @@ def test_modify_surfdata_atpoint_nocrop_nopft_zero_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -560,6 +574,7 @@ def test_modify_surfdata_atpoint_nocrop_nopft_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -594,6 +609,7 @@ class TestSinglePointCaseSurfaceCrop(unittest.TestCase): create_datm = True create_user_mods = True dom_pft = [17] + evenly_split_cropland = False pct_pft = None num_pft = 78 include_nonveg = False @@ -705,6 +721,7 @@ def test_modify_surfdata_atpoint_crop_1pft_pctnatpft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -737,6 +754,7 @@ def test_modify_surfdata_atpoint_crop_1pft_pctnatveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -765,6 +783,7 @@ def test_modify_surfdata_atpoint_crop_1pft_pctcrop(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -793,6 +812,7 @@ def test_modify_surfdata_atpoint_crop_1pft_glacier(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -822,6 +842,7 @@ def test_modify_surfdata_atpoint_crop_1pft_wetland(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -851,6 +872,7 @@ def test_modify_surfdata_atpoint_crop_1pft_lake(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -880,6 +902,7 @@ def test_modify_surfdata_atpoint_crop_1pft_unisnow(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -909,6 +932,7 @@ def test_modify_surfdata_atpoint_crop_1pft_capsat(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -939,6 +963,7 @@ def test_modify_surfdata_atpoint_crop_multipft(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -973,6 +998,7 @@ def test_modify_surfdata_atpoint_crop_urban_nononveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -1007,6 +1033,7 @@ def test_modify_surfdata_atpoint_crop_urban_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -1044,6 +1071,7 @@ def test_modify_surfdata_atpoint_crop_lake_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -1074,6 +1102,7 @@ def test_modify_surfdata_atpoint_crop_nopft_zero_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, @@ -1107,6 +1136,7 @@ def test_modify_surfdata_atpoint_crop_nopft_include_nonveg(self): create_datm=self.create_datm, create_user_mods=self.create_user_mods, dom_pft=self.dom_pft, + evenly_split_cropland=self.evenly_split_cropland, pct_pft=self.pct_pft, num_pft=self.num_pft, include_nonveg=self.include_nonveg, diff --git a/python/ctsm/test/testinputs/modify_fsurdat_1x1mexicocity.cfg b/python/ctsm/test/testinputs/modify_fsurdat_1x1mexicocity.cfg index a4118a3255..0d8a751f32 100644 --- a/python/ctsm/test/testinputs/modify_fsurdat_1x1mexicocity.cfg +++ b/python/ctsm/test/testinputs/modify_fsurdat_1x1mexicocity.cfg @@ -11,6 +11,7 @@ lat_dimname = lsmlat lon_dimname = lsmlon dom_pft = UNSET +evenly_split_cropland = False lai = UNSET sai = UNSET diff --git a/python/ctsm/test/testinputs/modify_fsurdat_opt_sections.cfg b/python/ctsm/test/testinputs/modify_fsurdat_opt_sections.cfg index c068c5d851..b1fcf8a2e1 100644 --- a/python/ctsm/test/testinputs/modify_fsurdat_opt_sections.cfg +++ b/python/ctsm/test/testinputs/modify_fsurdat_opt_sections.cfg @@ -11,6 +11,7 @@ lat_dimname = lsmlat lon_dimname = lsmlon dom_pft = UNSET +evenly_split_cropland = False lai = UNSET sai = UNSET diff --git a/python/ctsm/test/testinputs/modify_fsurdat_short.cfg b/python/ctsm/test/testinputs/modify_fsurdat_short.cfg index 74c6639899..38b88795e8 100644 --- a/python/ctsm/test/testinputs/modify_fsurdat_short.cfg +++ b/python/ctsm/test/testinputs/modify_fsurdat_short.cfg @@ -14,6 +14,7 @@ lat_dimname = lsmlat lon_dimname = lsmlon dom_pft = UNSET +evenly_split_cropland = False lai = UNSET sai = UNSET diff --git a/tools/mksurfdata_map/modify_1x1_mexicocityMEX.cfg b/tools/mksurfdata_map/modify_1x1_mexicocityMEX.cfg index 5e9de7968b..6eab73a159 100644 --- a/tools/mksurfdata_map/modify_1x1_mexicocityMEX.cfg +++ b/tools/mksurfdata_map/modify_1x1_mexicocityMEX.cfg @@ -36,15 +36,19 @@ lnd_lon_2 = 360 landmask_file = UNSET # PFT/CFT to be set to 100% according to user-defined mask. -# If idealized = True and dom_plant = UNSET, the latter defaults to 0 +# If idealized = True and dom_pft = UNSET, the latter defaults to 0 # (bare soil). Valid values range from 0 to a max value (int) that one can # obtain from the fsurdat_in file using ncdump (or method preferred by user). # The max valid value will equal (lsmpft - 1) and will also equal the last -# value of cft(cft). +# value of cft(cft). Cannot be set with evenly_split_cropland = True. dom_pft = UNSET -# LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_plant -# If dom_plant = 0, the next four default to 0 (space-delimited list +# If True, evenly split each gridcell's cropland among all crop types (CFTs). +# Can only be True if dom_pft is UNSET. +evenly_split_cropland = False + +# LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_pft +# If dom_pft = 0, the next four default to 0 (space-delimited list # of floats without brackets). lai = UNSET sai = UNSET diff --git a/tools/mksurfdata_map/modify_1x1_urbanc_alpha.cfg b/tools/mksurfdata_map/modify_1x1_urbanc_alpha.cfg index 38b8ce6372..d704b629bd 100644 --- a/tools/mksurfdata_map/modify_1x1_urbanc_alpha.cfg +++ b/tools/mksurfdata_map/modify_1x1_urbanc_alpha.cfg @@ -36,15 +36,19 @@ lnd_lon_2 = 360 landmask_file = UNSET # PFT/CFT to be set to 100% according to user-defined mask. -# If idealized = True and dom_plant = UNSET, the latter defaults to 0 +# If idealized = True and dom_pft = UNSET, the latter defaults to 0 # (bare soil). Valid values range from 0 to a max value (int) that one can # obtain from the fsurdat_in file using ncdump (or method preferred by user). # The max valid value will equal (lsmpft - 1) and will also equal the last -# value of cft(cft). +# value of cft(cft). Cannot be set with evenly_split_cropland = True. dom_pft = UNSET -# LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_plant -# If dom_plant = 0, the next four default to 0 (space-delimited list +# If True, evenly split each gridcell's cropland among all crop types (CFTs). +# Can only be True if dom_pft is UNSET. +evenly_split_cropland = False + +# LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_pft +# If dom_pft = 0, the next four default to 0 (space-delimited list # of floats without brackets). lai = UNSET sai = UNSET diff --git a/tools/mksurfdata_map/modify_1x1_vancouverCAN.cfg b/tools/mksurfdata_map/modify_1x1_vancouverCAN.cfg index abc5df16b1..f46593d653 100644 --- a/tools/mksurfdata_map/modify_1x1_vancouverCAN.cfg +++ b/tools/mksurfdata_map/modify_1x1_vancouverCAN.cfg @@ -36,15 +36,19 @@ lnd_lon_2 = 360 landmask_file = UNSET # PFT/CFT to be set to 100% according to user-defined mask. -# If idealized = True and dom_plant = UNSET, the latter defaults to 0 +# If idealized = True and dom_pft = UNSET, the latter defaults to 0 # (bare soil). Valid values range from 0 to a max value (int) that one can # obtain from the fsurdat_in file using ncdump (or method preferred by user). # The max valid value will equal (lsmpft - 1) and will also equal the last -# value of cft(cft). +# value of cft(cft). Cannot be set with evenly_split_cropland = True. dom_pft = UNSET -# LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_plant -# If dom_plant = 0, the next four default to 0 (space-delimited list +# If True, evenly split each gridcell's cropland among all crop types (CFTs). +# Can only be True if dom_pft is UNSET. +evenly_split_cropland = False + +# LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_pft +# If dom_pft = 0, the next four default to 0 (space-delimited list # of floats without brackets). lai = UNSET sai = UNSET diff --git a/tools/modify_input_files/modify_fsurdat_template.cfg b/tools/modify_input_files/modify_fsurdat_template.cfg index 3661784521..1dfb33ce53 100644 --- a/tools/modify_input_files/modify_fsurdat_template.cfg +++ b/tools/modify_input_files/modify_fsurdat_template.cfg @@ -72,9 +72,13 @@ lon_dimname = UNSET # (bare soil). Valid values range from 0 to a max value (int) that one can # obtain from the fsurdat_in file using ncdump (or method preferred by user). # The max valid value will equal (lsmpft - 1) and will also equal the last -# value of cft(cft). +# value of cft(cft). Cannot be set with evenly_split_cropland = True. dom_pft = UNSET +# If True, evenly split each gridcell's cropland among all crop types (CFTs). +# Can only be True if dom_pft is UNSET. +evenly_split_cropland = False + # LAI, SAI, HEIGHT_TOP, and HEIGHT_BOT values by month for dom_pft # If dom_pft = 0, the next four default to 0 (space-delimited list # of floats without brackets).