Skip to content

Commit

Permalink
Merge branch 'make-fsurdat-ace-dev' into simplemerge
Browse files Browse the repository at this point in the history
  • Loading branch information
ekluzek committed Aug 10, 2023
2 parents 8c024cf + cee3c26 commit e1bc232
Show file tree
Hide file tree
Showing 19 changed files with 278 additions and 86 deletions.
3 changes: 3 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ b88e1cd1b28e3609684c79a2ec0e88f26cfc362b
b771971e3299c4fa56534b93421f7a2b9c7282fd
9de88bb57ea9855da408cbec1dc8acb9079eda47
8bc4688e52ea23ef688e283698f70a44388373eb
0a5a9e803b56ec1bbd6232eff1c99dbbeef25eb7
810cb346f05ac1aabfff931ab1a2b7b584add241
5933b0018f8e29413e30dda9b906370d147bad45
# Ran SystemTests and python/ctsm through black python formatter
5364ad66eaceb55dde2d3d598fe4ce37ac83a93c
62 changes: 53 additions & 9 deletions cime_config/SystemTests/rxcropmaturity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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(),
Expand All @@ -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")
Expand Down
19 changes: 18 additions & 1 deletion cime_config/testdefs/testlist_clm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2551,7 +2551,24 @@
</machines>
</test>

<test name="RXCROPMATURITY_Lm61" grid="f09_g17" compset="IHistClm50BgcCrop">
<test name="RXCROPMATURITY_Lm61" grid="f10_f10_mg37" compset="IHistClm50BgcCrop" testmods="clm/cropMonthOutput">
<machines>
<machine name="cheyenne" compiler="intel" category="aux_clm">
<options>
<option name="wallclock">3:00:00</option>
<option name="comment">This test is designed to test the ability to prescribe crop sowing dates and maturity requirements. It first performs a GDD-generating run, then calls Python code to generate the maturity requirement file. This is then used in a sowing+maturity forced run, which finally is tested to ensure correct behavior.</option>
</options>
</machine>
<machine name="cheyenne" compiler="intel" category="clm_pymods">
<options>
<option name="wallclock">3:00:00</option>
<option name="comment">This test invokes python code, so it should be run whenever changing python code (in addition to being run as part of ctsm_sci).</option>
</options>
</machine>
</machines>
</test>

<test name="RXCROPMATURITY_Lm61" grid="f09_g17" compset="IHistClm50BgcCrop" testmods="clm/cropMonthOutput">
<machines>
<machine name="cheyenne" compiler="intel" category="ctsm_sci">
<options>
Expand Down
55 changes: 0 additions & 55 deletions python/ctsm/crop_calendars/make_fsurdat_all_crops_everywhere.py

This file was deleted.

25 changes: 24 additions & 1 deletion python/ctsm/modify_input_files/fsurdat_modifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ def modify_optional(
std_elev,
soil_color,
dom_pft,
evenly_split_cropland,
lai,
sai,
hgt_top,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -584,6 +606,7 @@ def fsurdat_modifier(parser):
std_elev,
soil_color,
dom_pft,
evenly_split_cropland,
lai,
sai,
hgt_top,
Expand Down
12 changes: 12 additions & 0 deletions python/ctsm/modify_input_files/modify_fsurdat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions python/ctsm/site_and_regional/single_point_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -105,6 +107,7 @@ def __init__(
create_datm,
create_user_mods,
dom_pft,
evenly_split_cropland,
pct_pft,
num_pft,
include_nonveg,
Expand All @@ -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
Expand Down Expand Up @@ -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 --> \
Expand Down
1 change: 1 addition & 0 deletions python/ctsm/subset_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
46 changes: 45 additions & 1 deletion python/ctsm/test/test_sys_fsurdat_modifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,37 @@ 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)
np.testing.assert_array_equal(fsurdat_out_data.T_BUILDING_MIN, lev1)
np.testing.assert_array_equal(fsurdat_out_data.ALB_ROOF_DIR, lev2_two)
np.testing.assert_array_equal(fsurdat_out_data.TK_ROOF, lev2_five)

def test_evenly_split_cropland(self):
"""
Test that evenly splitting cropland works
"""
self._create_config_file_evenlysplitcrop()
sys.argv = [
"fsurdat_modifier",
self._cfg_file_path,
]
parser = fsurdat_modifier_arg_process()
fsurdat_modifier(parser)
# Read the resultant output file and make sure the fields are changed as expected
fsurdat_in_data = xr.open_dataset(self._fsurdat_in)
fsurdat_out_data = xr.open_dataset(self._fsurdat_out)
Ncrops = fsurdat_out_data.dims["cft"]
pct_cft = np.full_like(fsurdat_out_data.PCT_CFT, 100 / Ncrops)
np.testing.assert_array_equal(fsurdat_in_data.PCT_NATVEG, fsurdat_out_data.PCT_NATVEG)
np.testing.assert_array_equal(fsurdat_in_data.PCT_CROP, fsurdat_out_data.PCT_CROP)
np.testing.assert_array_equal(fsurdat_in_data.PCT_LAKE, fsurdat_out_data.PCT_LAKE)
np.testing.assert_array_equal(fsurdat_in_data.PCT_WETLAND, fsurdat_out_data.PCT_WETLAND)
np.testing.assert_array_equal(fsurdat_in_data.PCT_GLACIER, fsurdat_out_data.PCT_GLACIER)
np.testing.assert_array_equal(fsurdat_in_data.PCT_URBAN, fsurdat_out_data.PCT_URBAN)
np.testing.assert_array_equal(fsurdat_out_data.PCT_CFT, pct_cft)

def test_1x1_mexicocity(self):
"""
Test that the mexicocity file is handled correctly
Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
Loading

0 comments on commit e1bc232

Please sign in to comment.