343 lines
15 KiB
Python
343 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Unit tests for fsurdat_modifier subroutines:
|
|
"""
|
|
|
|
import unittest
|
|
import os
|
|
import sys
|
|
import shutil
|
|
|
|
import tempfile
|
|
from configparser import ConfigParser
|
|
import xarray as xr
|
|
|
|
from ctsm import unit_testing
|
|
from ctsm.path_utils import path_to_ctsm_root
|
|
from ctsm.modify_input_files.fsurdat_modifier import fsurdat_modifier_arg_process
|
|
from ctsm.modify_input_files.fsurdat_modifier import read_cfg_subgrid
|
|
from ctsm.modify_input_files.fsurdat_modifier import read_cfg_option_control
|
|
from ctsm.modify_input_files.fsurdat_modifier import read_cfg_var_list
|
|
from ctsm.modify_input_files.fsurdat_modifier import check_no_subgrid_section
|
|
from ctsm.modify_input_files.fsurdat_modifier import check_no_varlist_section
|
|
from ctsm.modify_input_files.modify_fsurdat import ModifyFsurdat
|
|
|
|
# Allow test names that pylint doesn't like; otherwise hard to make them
|
|
# readable
|
|
# pylint: disable=invalid-name
|
|
|
|
# pylint: disable=protected-access
|
|
|
|
|
|
# Allow as many public methods as needed...
|
|
# pylint: disable=too-many-public-methods
|
|
# Allow all the instance attributes that we need
|
|
# pylint: disable=too-many-instance-attributes
|
|
class TestFSurdatModifier(unittest.TestCase):
|
|
"""Tests the fsurdat_modifier subroutines"""
|
|
|
|
def setUp(self):
|
|
"""Setup for trying out the methods"""
|
|
testinputs_path = os.path.join(path_to_ctsm_root(), "python/ctsm/test/testinputs")
|
|
self._cfg_file_path = os.path.join(testinputs_path, "modify_fsurdat_opt_sections.cfg")
|
|
self._testinputs_path = testinputs_path
|
|
self._fsurdat_in = os.path.join(
|
|
testinputs_path,
|
|
"surfdata_5x5_amazon_hist_16pfts_CMIP6_2000_c231031.nc",
|
|
)
|
|
self._previous_dir = os.getcwd()
|
|
self._tempdir = tempfile.mkdtemp()
|
|
self._fsurdat_out = os.path.join(self._tempdir, "fsurdat_out.nc")
|
|
sys.argv = [
|
|
"fsurdat_modifier",
|
|
self._cfg_file_path,
|
|
"-i",
|
|
self._fsurdat_in,
|
|
"-o",
|
|
self._fsurdat_out,
|
|
]
|
|
parser = fsurdat_modifier_arg_process()
|
|
self.cfg_path = str(parser.cfg_path)
|
|
self.config = ConfigParser()
|
|
self.config.read(self.cfg_path)
|
|
my_data = xr.open_dataset(self._fsurdat_in)
|
|
self.modify_fsurdat = ModifyFsurdat(
|
|
my_data=my_data,
|
|
lon_1=0.0,
|
|
lon_2=360.0,
|
|
lat_1=90.0,
|
|
lat_2=90.0,
|
|
landmask_file=None,
|
|
lat_dimname=None,
|
|
lon_dimname=None,
|
|
)
|
|
|
|
def tearDown(self):
|
|
"""
|
|
Remove temporary directory
|
|
"""
|
|
os.chdir(self._previous_dir)
|
|
shutil.rmtree(self._tempdir, ignore_errors=True)
|
|
|
|
def test_subgrid_and_idealized_fails(self):
|
|
"""test that subgrid and idealized fails gracefully"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "True")
|
|
self.config.set(section, "include_nonveg", "False")
|
|
self.config.set(section, "process_subgrid_section", "True")
|
|
self.config.set(section, "dom_pft", "UNSET")
|
|
with self.assertRaisesRegex(
|
|
SystemExit,
|
|
"idealized AND process_subgrid_section can NOT both be on, pick one or the other",
|
|
):
|
|
read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path)
|
|
|
|
def test_dompft_and_splitcropland_fails(self):
|
|
"""test that setting dompft crop with evenly_split_cropland True fails gracefully"""
|
|
section = "modify_fsurdat_basic_options"
|
|
crop_pft = max(self.modify_fsurdat.file.natpft.values) + 1
|
|
self.config.set(section, "dom_pft", str(crop_pft))
|
|
self.config.set(section, "evenly_split_cropland", "True")
|
|
with self.assertRaisesRegex(
|
|
SystemExit,
|
|
"dom_pft must not be set to a crop PFT when evenly_split_cropland is True",
|
|
):
|
|
read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path)
|
|
|
|
def test_optional_only_true_and_false(self):
|
|
"""test that optional settings can only be true or false"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "dom_pft", "1")
|
|
varlist = (
|
|
"idealized",
|
|
"include_nonveg",
|
|
"process_subgrid_section",
|
|
"process_var_list_section",
|
|
)
|
|
for var in varlist:
|
|
self.config.set(section, var, "True")
|
|
self.config.set(section, "idealized", "False")
|
|
read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path)
|
|
for var in varlist:
|
|
self.config.set(section, var, "False")
|
|
read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path)
|
|
self.config.set(section, "dom_pft", "UNSET")
|
|
read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path)
|
|
varlist = (
|
|
"idealized",
|
|
"evenly_split_cropland",
|
|
)
|
|
for var in varlist:
|
|
orig_value = self.config.get(section, var)
|
|
self.config.set(section, var, "Thing")
|
|
with self.assertRaisesRegex(
|
|
SystemExit, "Non-boolean value found for .cfg file variable: " + var
|
|
):
|
|
read_cfg_option_control(self.modify_fsurdat, self.config, section, self.cfg_path)
|
|
self.config.set(section, var, orig_value)
|
|
|
|
def test_read_subgrid(self):
|
|
"""test a simple read of subgrid"""
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_read_subgrid_allglacier(self):
|
|
"""test a read of subgrid that's for all glacier"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "pct_urban", "0. 0. 0.")
|
|
self.config.set(section, "pct_lake", "0.")
|
|
self.config.set(section, "pct_wetland", "0.")
|
|
self.config.set(section, "pct_ocean", "0.")
|
|
self.config.set(section, "pct_glacier", "100.")
|
|
self.config.set(section, "pct_natveg", "0.")
|
|
self.config.set(section, "pct_crop", "0.")
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_read_subgrid_allspecial(self):
|
|
"""test a read of subgrid that's all special landunits"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "pct_urban", "0. 0. 0.")
|
|
self.config.set(section, "pct_lake", "25.")
|
|
self.config.set(section, "pct_wetland", "35.")
|
|
self.config.set(section, "pct_ocean", "0.")
|
|
self.config.set(section, "pct_glacier", "40.")
|
|
self.config.set(section, "pct_natveg", "0.")
|
|
self.config.set(section, "pct_crop", "0.")
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_read_subgrid_allurban(self):
|
|
"""test a read of subgrid that's all urban"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "pct_urban", "100.0 0.0 0.0")
|
|
self.config.set(section, "pct_lake", "0.")
|
|
self.config.set(section, "pct_wetland", "0.")
|
|
self.config.set(section, "pct_ocean", "0.")
|
|
self.config.set(section, "pct_glacier", "0.")
|
|
self.config.set(section, "pct_natveg", "0.")
|
|
self.config.set(section, "pct_crop", "0.")
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_read_subgrid_split_cropland(self):
|
|
"""
|
|
test a read of subgrid that's 50/50 natural and
|
|
cropland, with cropland split evenly among
|
|
crop types
|
|
"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
self.config.set(section, "evenly_split_cropland", "True")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "pct_urban", "0.0 0.0 0.0")
|
|
self.config.set(section, "pct_lake", "0.")
|
|
self.config.set(section, "pct_wetland", "0.")
|
|
self.config.set(section, "pct_glacier", "0.")
|
|
self.config.set(section, "pct_natveg", "50.")
|
|
self.config.set(section, "pct_crop", "50.")
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_read_var_list(self):
|
|
"""test a simple read of var_list"""
|
|
read_cfg_var_list(self.config, idealized=True)
|
|
|
|
def test_subgrid_outofrange(self):
|
|
"""test a read of subgrid that's out of range"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "pct_urban", "101. 0. 0.")
|
|
with self.assertRaisesRegex(SystemExit, "is out of range of 0 to 100 ="):
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_subgrid_pct_urban_toosmall(self):
|
|
"""test a read of subgrid for PCT_URBAN that's an array too small"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "pct_urban", "100. 0.")
|
|
with self.assertRaisesRegex(
|
|
SystemExit, "PCT_URBAN is not a list of the expected size of 3"
|
|
):
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_subgrid_pct_urban_toobig(self):
|
|
"""test a read of subgrid for PCT_URBAN that's an array too big"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "pct_urban", "100. 0. 0. 0.")
|
|
with self.assertRaisesRegex(
|
|
SystemExit, "PCT_URBAN is not a list of the expected size of 3"
|
|
):
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_subgrid_pct_urban_singlevalue(self):
|
|
"""test a read of subgrid for PCT_URBAN that's a single value"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "pct_urban", "100.")
|
|
with self.assertRaisesRegex(
|
|
SystemExit, "PCT_URBAN is not a list of the expected size of 3"
|
|
):
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_subgrid_notsumtohundred(self):
|
|
"""test a read of subgrid that's doesn't sum to a hundred"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "pct_urban", "0. 0. 0.")
|
|
self.config.set(section, "pct_lake", "0.")
|
|
self.config.set(section, "pct_wetland", "0.")
|
|
self.config.set(section, "pct_ocean", "0.")
|
|
self.config.set(section, "pct_glacier", "0.")
|
|
self.config.set(section, "pct_natveg", "0.")
|
|
self.config.set(section, "pct_crop", "0.")
|
|
with self.assertRaisesRegex(
|
|
SystemExit, "PCT fractions in subgrid section do NOT sum to a hundred as they should"
|
|
):
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_subgrid_badvar(self):
|
|
"""test a read of subgrid for a variable thats not in the list"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.set(section, "badvariable", "100.")
|
|
with self.assertRaisesRegex(SystemExit, "is not a valid variable name. Valid vars ="):
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_varlist_varinidealized(self):
|
|
"""test a read of varlist for a variable thats in the idealized list,
|
|
when idealized is on"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "True")
|
|
section = "modify_fsurdat_variable_list"
|
|
self.config.set(section, "PCT_SAND", "100.")
|
|
with self.assertRaisesRegex(
|
|
SystemExit,
|
|
"is a special variable handled in the idealized section."
|
|
+ " This should NOT be handled in the variable list section. Special idealized vars =",
|
|
):
|
|
read_cfg_var_list(self.config, idealized=True)
|
|
|
|
def test_varlist_varinsubgrid(self):
|
|
"""test a read of varlist for a variable thats in the subgrid list"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "idealized", "False")
|
|
section = "modify_fsurdat_variable_list"
|
|
self.config.set(section, "PCT_GLACIER", "100.")
|
|
with self.assertRaisesRegex(
|
|
SystemExit,
|
|
"is a variable handled in the subgrid section."
|
|
+ " This should NOT be handled in the variable list section. Subgrid vars =",
|
|
):
|
|
read_cfg_var_list(self.config, idealized=False)
|
|
|
|
def test_varlist_monthlyvar(self):
|
|
"""test a read of varlist for a variable thats one of the monthly
|
|
variables handled in the dom_pft section"""
|
|
section = "modify_fsurdat_variable_list"
|
|
self.config.set(section, "MONTHLY_LAI", "100.")
|
|
with self.assertRaisesRegex(
|
|
SystemExit,
|
|
"is a variable handled as part of the dom_pft handling."
|
|
+ " This should NOT be handled in the variable list section."
|
|
+ " Monthly vars handled this way =",
|
|
):
|
|
read_cfg_var_list(self.config, idealized=False)
|
|
|
|
def test_subgrid_remove(self):
|
|
"""test a read of subgrid when it's section has been removed"""
|
|
section = "modify_fsurdat_subgrid_fractions"
|
|
self.config.remove_section(section)
|
|
with self.assertRaisesRegex(SystemExit, "Config file does not have the expected section"):
|
|
read_cfg_subgrid(self.config, self.cfg_path)
|
|
|
|
def test_subgrid_not_thereifoff(self):
|
|
"""test that a graceful error happens if subgrid section is off,
|
|
but it appears in the file"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "process_subgrid_section", "False")
|
|
with self.assertRaisesRegex(SystemExit, "Config file does have a section"):
|
|
check_no_subgrid_section(self.config)
|
|
|
|
def test_varlist_not_thereifoff(self):
|
|
"""test that a graceful error happens if varlist section is off,
|
|
but it appears in the file"""
|
|
section = "modify_fsurdat_basic_options"
|
|
self.config.set(section, "process_var_list_section", "False")
|
|
with self.assertRaisesRegex(SystemExit, "Config file does have a section"):
|
|
check_no_varlist_section(self.config)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unit_testing.setup_for_tests()
|
|
unittest.main()
|