diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 8960800172..1e73e65b4f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -58,3 +58,4 @@ bd535c710db78420b8e8b9d71d88d8339e899c59 4b20bbd7003e6f77dab4e3268cc4a43f9b5a3b5d cf433215b58ba8776ec5edfb0b0d80c0836ed3a0 16d57ff37859b34dab005693e3085d64e2bcd95a +e8fc526e0d7818d45f171488c78392c4ff63902a diff --git a/python/ctsm/site_and_regional/neon_site.py b/python/ctsm/site_and_regional/neon_site.py index df8a514ea3..3da43ac27a 100755 --- a/python/ctsm/site_and_regional/neon_site.py +++ b/python/ctsm/site_and_regional/neon_site.py @@ -50,7 +50,15 @@ def build_base_case( self.cesmroot, "cime_config", "usermods_dirs", "clm", "NEON", self.name ) ] - case_path = super().build_base_case(cesmroot, output_root, res, compset, user_mods_dirs) + case_path = super().build_base_case( + cesmroot, + output_root, + res, + compset, + user_mods_dirs, + overwrite=overwrite, + setup_only=setup_only, + ) return case_path @@ -68,6 +76,8 @@ def run_case( no_batch=False, rerun=False, experiment=False, + no_input_data_check=False, + xmlchange=None, ): """ Run case. @@ -92,6 +102,8 @@ def run_case( default False experiment: str, opt name of experiment, default False + no_input_data_check: bool, opt + default False """ user_mods_dirs = [ os.path.join(self.cesmroot, "cime_config", "usermods_dirs", "clm", "NEON", self.name) @@ -110,6 +122,8 @@ def run_case( no_batch, rerun, experiment, + no_input_data_check, + xmlchange, ) def modify_user_nl(self, case_root, run_type, rundir, site_lines=None): diff --git a/python/ctsm/site_and_regional/plumber_site.py b/python/ctsm/site_and_regional/plumber_site.py index 3f06f2949b..4048312627 100755 --- a/python/ctsm/site_and_regional/plumber_site.py +++ b/python/ctsm/site_and_regional/plumber_site.py @@ -49,7 +49,15 @@ def build_base_case( self.cesmroot, "cime_config", "usermods_dirs", "clm", "PLUMBER2", self.name ) ] - case_path = super().build_base_case(cesmroot, output_root, res, compset, user_mods_dirs) + case_path = super().build_base_case( + cesmroot, + output_root, + res, + compset, + user_mods_dirs, + overwrite=overwrite, + setup_only=setup_only, + ) return case_path @@ -67,6 +75,8 @@ def run_case( no_batch=False, rerun=False, experiment=False, + no_input_data_check=False, + xmlchange=None, ): """ Run case. @@ -113,6 +123,8 @@ def run_case( no_batch, rerun, experiment, + no_input_data_check, + xmlchange, ) def set_ref_case(self, case): diff --git a/python/ctsm/site_and_regional/run_tower.py b/python/ctsm/site_and_regional/run_tower.py index e65e26a064..aecda90742 100755 --- a/python/ctsm/site_and_regional/run_tower.py +++ b/python/ctsm/site_and_regional/run_tower.py @@ -233,7 +233,9 @@ def main(description): setup_only, no_batch, rerun, + no_input_data_check, user_version, + xmlchange, ) = get_parser(sys.argv, description, valid_neon_sites, valid_plumber_sites) if output_root: @@ -278,6 +280,8 @@ def main(description): no_batch=no_batch, rerun=rerun, experiment=experiment, + no_input_data_check=no_input_data_check, + xmlchange=xmlchange, ) # -- check for available plumber data: @@ -306,4 +310,6 @@ def main(description): no_batch=no_batch, rerun=rerun, experiment=experiment, + no_input_data_check=no_input_data_check, + xmlchange=xmlchange, ) diff --git a/python/ctsm/site_and_regional/tower_arg_parse.py b/python/ctsm/site_and_regional/tower_arg_parse.py index c4491ffa99..33b3db2afa 100644 --- a/python/ctsm/site_and_regional/tower_arg_parse.py +++ b/python/ctsm/site_and_regional/tower_arg_parse.py @@ -104,6 +104,19 @@ def get_parser(args, description, valid_neon_sites, valid_plumber_sites): default=False, ) + parser.add_argument( + "--no-input-data-check", + "--no-check-input-data", + help=""" + Don't check for input data. Implies --setup-only. + [default: %(default)s] + """, + action="store_true", + dest="no_input_data_check", + required=False, + default=False, + ) + parser.add_argument( "--rerun", help=""" @@ -185,6 +198,17 @@ def get_parser(args, description, valid_neon_sites, valid_plumber_sites): choices=["v1", "v2", "v3"], ) + parser.add_argument( + "--xmlchange", + help=""" + Any xmlchanges (e.g., CLM_CO2_TYPE=constant,CCSM_CO2_PPMV=500) + [default: %(default)s] + """, + required=False, + type=str, + default=None, + ) + args = parse_args_and_handle_standard_logging_options(args, parser) if args.neon_sites: @@ -230,6 +254,10 @@ def get_parser(args, description, valid_neon_sites, valid_plumber_sites): root_logger = logging.getLogger() root_logger.setLevel(logging.WARN) + # --no-input-data-check implies --setup-only + if args.no_input_data_check and not args.setup_only: + args.setup_only = True + return ( neon_sites, plumber_sites, @@ -243,5 +271,7 @@ def get_parser(args, description, valid_neon_sites, valid_plumber_sites): args.setup_only, args.no_batch, args.rerun, + args.no_input_data_check, args.user_version, + args.xmlchange, ) diff --git a/python/ctsm/site_and_regional/tower_site.py b/python/ctsm/site_and_regional/tower_site.py index 8a2c55c64d..aad6e0203c 100644 --- a/python/ctsm/site_and_regional/tower_site.py +++ b/python/ctsm/site_and_regional/tower_site.py @@ -296,6 +296,8 @@ def run_case( no_batch, rerun, experiment, + no_input_data_check, + xmlchange, ): """ Run case. @@ -445,11 +447,19 @@ def run_case( if not rundir: rundir = case.get_value("RUNDIR") + if xmlchange: + xmlchange_list = xmlchange.split(",") + for setting in xmlchange_list: + setting_split = setting.split("=") + case.set_value(*setting_split) + self.modify_user_nl(case_root, run_type, rundir) case.create_namelists() + # explicitly run check_input_data - case.check_all_input_data() + if not no_input_data_check: + case.check_all_input_data() if not setup_only: case.submit(no_batch=no_batch) print("-----------------------------------") diff --git a/python/ctsm/test/test_sys_run_tower.py b/python/ctsm/test/test_sys_run_tower.py index b1eef6c3a9..04a318b46a 100755 --- a/python/ctsm/test/test_sys_run_tower.py +++ b/python/ctsm/test/test_sys_run_tower.py @@ -10,11 +10,16 @@ import tempfile import shutil import sys +import pathlib from ctsm import unit_testing from ctsm.site_and_regional.run_tower import main from ctsm.path_utils import path_to_ctsm_root +# pylint: disable=import-error +# pylint: disable=wrong-import-order +from CIME.case import Case + # Allow test names that pylint doesn't like; otherwise hard to make them # readable # pylint: disable=invalid-name @@ -58,13 +63,12 @@ def test_one_site(self): os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_tower"), "--neon-sites", "BART", - "--setup-only", + "--no-input-data-check", "--experiment", "TEST", "--output-root", self._tempdir, ] - print(sys.argv) main("") # assert that BART directories were created during setup @@ -82,7 +86,7 @@ def test_ad_site(self): os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_tower"), "--neon-sites", "ABBY", - "--setup-only", + "--no-input-data-check", "--run-type", "ad", "--output-root", @@ -104,7 +108,7 @@ def test_plumber_site(self): os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_tower"), "--plumber-sites", "AR-SLu", - "--setup-only", + "--no-input-data-check", "--experiment", "TEST", "--output-root", @@ -115,6 +119,182 @@ def test_plumber_site(self): # assert that AR-SLu directories were created during setup self.assertTrue("AR-SLu" in glob.glob(self._tempdir + "/AR-SLu*")[0]) + def test_xmlchange_neon(self): + """ + This test checks that the --xmlchange argument is obeyed for a NEON site. + """ + + # run the run_tower tool + sys.argv = [ + os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_tower"), + "--neon-sites", + "BART", + "--no-input-data-check", + "--experiment", + "TEST", + "--xmlchange", + "CLM_CO2_TYPE=constant,CCSM_CO2_PPMV=1987", + "--output-root", + self._tempdir, + ] + main("") + + # Check that the --xmlchange argument is obeyed + case_dir = os.path.join(self._tempdir, "BART.TEST.transient") + self.assertTrue(os.path.exists(case_dir)) + with Case(case_dir, read_only=True) as case: + value = case.get_value("CLM_CO2_TYPE") + print(f"CLM_CO2_TYPE = {value}") + self.assertTrue(value == "constant") + value = int(case.get_value("CCSM_CO2_PPMV")) + print(f"CCSM_CO2_PPMV = {value}") + self.assertTrue(int(value) == 1987) + + def test_xmlchange_plumber(self): + """ + This test checks that the --xmlchange argument is obeyed for a PLUMBER site. + """ + + # run the run_tower tool for plumber site + sys.argv = [ + os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_tower"), + "--plumber-sites", + "AR-SLu", + "--no-input-data-check", + "--experiment", + "TEST", + "--xmlchange", + "CLM_CO2_TYPE=constant,CCSM_CO2_PPMV=1987", + "--output-root", + self._tempdir, + ] + main("") + + # Check that the --xmlchange argument is obeyed + case_dir = os.path.join(self._tempdir, "AR-SLu.TEST.ad") + self.assertTrue(os.path.exists(case_dir)) + with Case(case_dir, read_only=True) as case: + value = case.get_value("CLM_CO2_TYPE") + print(f"CLM_CO2_TYPE = {value}") + self.assertTrue(value == "constant") + value = int(case.get_value("CCSM_CO2_PPMV")) + print(f"CCSM_CO2_PPMV = {value}") + self.assertTrue(int(value) == 1987) + + def test_setup_only_neon(self): + """ + This test checks that the --setup-only argument is obeyed for NEON sites + """ + + # run the run_tower tool + site_name = "BART" + sys.argv = [ + os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_tower"), + "--neon-sites", + site_name, + "--setup-only", + "--experiment", + "TEST", + "--output-root", + self._tempdir, + ] + main("") + + # make sure that build didn't happen: this dir should be empty + case_dir = os.path.join(self._tempdir, site_name) + build_dir_to_check = os.path.join(case_dir, "bld", "cpl", "obj") + self.assertTrue(os.path.exists(build_dir_to_check)) + self.assertTrue(len(os.listdir(build_dir_to_check)) == 0) + + def test_setup_only_plumber(self): + """ + This test checks that the --setup-only argument is obeyed for PLUMBER sites + """ + + # run the run_tower tool for plumber site + site_name = "AR-SLu" + sys.argv = [ + os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_tower"), + "--plumber-sites", + site_name, + "--setup-only", + "--experiment", + "TEST", + "--output-root", + self._tempdir, + ] + main("") + + # make sure that build didn't happen: this dir should be empty + case_dir = os.path.join(self._tempdir, site_name) + build_dir_to_check = os.path.join(case_dir, "bld", "cpl", "obj") + self.assertTrue(os.path.exists(build_dir_to_check)) + self.assertTrue(len(os.listdir(build_dir_to_check)) == 0) + + def test_overwrite_neon(self): + """ + This test checks that the --overwrite argument is obeyed for NEON sites + """ + + # run the run_tower tool once + site_name = "BART" + sys.argv = [ + os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_tower"), + "--neon-sites", + site_name, + "--no-input-data-check", + "--experiment", + "TEST", + "--output-root", + self._tempdir, + ] + main("") + + # create a file that should be erased during the upcoming overwrite + case_dir = os.path.join(self._tempdir, site_name) + test_file = os.path.join(case_dir, "test_file") + pathlib.Path(test_file).touch() + self.assertTrue(os.path.exists(test_file)) + + # run the tool again, overwriting existing + sys.argv += ["--overwrite"] + main("") + + # ensure that file we created is gone + self.assertFalse(os.path.exists(test_file)) + + def test_overwrite_plumber(self): + """ + This test checks that the --overwrite argument is obeyed for PLUMBER sites + """ + + # run the run_tower tool once + site_name = "AR-SLu" + sys.argv = [ + os.path.join(path_to_ctsm_root(), "tools", "site_and_regional", "run_tower"), + "--plumber-sites", + site_name, + "--no-input-data-check", + "--experiment", + "TEST", + "--output-root", + self._tempdir, + ] + main("") + + # create a file that should be erased during the upcoming overwrite + case_dir = os.path.join(self._tempdir, site_name) + test_file = os.path.join(case_dir, "test_file") + pathlib.Path(test_file).touch() + self.assertTrue(os.path.exists(test_file)) + + # run the tool again, overwriting existing + sys.argv += ["--overwrite"] + main("") + + # ensure that file we created is gone + self.assertFalse(os.path.exists(test_file)) + if __name__ == "__main__": unit_testing.setup_for_tests()