diff --git a/CIME/Servers/__init__.py b/CIME/Servers/__init__.py index 5cd14c0f89d..fb8307ba59d 100644 --- a/CIME/Servers/__init__.py +++ b/CIME/Servers/__init__.py @@ -1,9 +1,9 @@ # pylint: disable=import-error -from distutils.spawn import find_executable +from shutil import which -has_gftp = find_executable("globus-url-copy") -has_svn = find_executable("svn") -has_wget = find_executable("wget") +has_gftp = which("globus-url-copy") +has_svn = which("svn") +has_wget = which("wget") has_ftp = True try: from ftplib import FTP diff --git a/CIME/SystemTests/ers.py b/CIME/SystemTests/ers.py index bebed8f04c4..0e4bccd953a 100644 --- a/CIME/SystemTests/ers.py +++ b/CIME/SystemTests/ers.py @@ -16,29 +16,18 @@ def __init__(self, case, **kwargs): SystemTestsCommon.__init__(self, case, **kwargs) def _ers_first_phase(self): - stop_n = self._case.get_value("STOP_N") - stop_option = self._case.get_value("STOP_OPTION") - rest_n = self._case.get_value("REST_N") - expect(stop_n > 0, "Bad STOP_N: {:d}".format(stop_n)) - - expect(stop_n > 2, "ERROR: stop_n value {:d} too short".format(stop_n)) - logger.info( - "doing an {0} {1} initial test with restart file at {2} {1}".format( - str(stop_n), stop_option, str(rest_n) - ) - ) + self._rest_n = self._set_restart_interval() self.run_indv() def _ers_second_phase(self): stop_n = self._case.get_value("STOP_N") stop_option = self._case.get_value("STOP_OPTION") - rest_n = int(stop_n / 2 + 1) - stop_new = stop_n - rest_n + stop_new = stop_n - self._rest_n expect( stop_new > 0, "ERROR: stop_n value {:d} too short {:d} {:d}".format( - stop_new, stop_n, rest_n + stop_new, stop_n, self._rest_n ), ) rundir = self._case.get_value("RUNDIR") diff --git a/CIME/SystemTests/restart_tests.py b/CIME/SystemTests/restart_tests.py index 5faf2252d1b..4252739d326 100644 --- a/CIME/SystemTests/restart_tests.py +++ b/CIME/SystemTests/restart_tests.py @@ -1,6 +1,5 @@ """ Abstract class for restart tests - """ from CIME.SystemTests.system_tests_compare_two import SystemTestsCompareTwo @@ -34,6 +33,7 @@ def __init__( def _case_one_setup(self): stop_n = self._case1.get_value("STOP_N") expect(stop_n >= 3, "STOP_N must be at least 3, STOP_N = {}".format(stop_n)) + self._set_restart_interval() def _case_two_setup(self): rest_n = self._case1.get_value("REST_N") diff --git a/CIME/SystemTests/system_tests_common.py b/CIME/SystemTests/system_tests_common.py index 93ab4cf4c60..3c1f3e99830 100644 --- a/CIME/SystemTests/system_tests_common.py +++ b/CIME/SystemTests/system_tests_common.py @@ -1,6 +1,7 @@ """ Base class for CIME system tests """ + from CIME.XML.standard_module_setup import * from CIME.XML.env_run import EnvRun from CIME.XML.env_test import EnvTest @@ -117,6 +118,74 @@ def __init__( self._dry_run = False self._user_separate_builds = False self._expected_num_cmp = None + self._rest_n = None + + def _set_restart_interval(self): + stop_n = self._case.get_value("STOP_N") + stop_option = self._case.get_value("STOP_OPTION") + self._case.set_value("REST_OPTION", stop_option) + # We need to make sure the run is long enough and to set REST_N to a + # value that makes sense for all components + maxncpl = 10000 + minncpl = 0 + for comp in self._case.get_values("COMP_CLASSES"): + if comp == "CPL": + continue + compname = self._case.get_value("COMP_{}".format(comp)) + + # ignore stub components in this test. + if compname == "s{}".format(comp.lower()): + ncpl = None + else: + ncpl = self._case.get_value("{}_NCPL".format(comp)) + + if ncpl and maxncpl > ncpl: + maxncpl = ncpl + if ncpl and minncpl < ncpl: + minncpl = ncpl + + ncpl_base_period = self._case.get_value("NCPL_BASE_PERIOD") + if ncpl_base_period == "hour": + coupling_secs = 3600 / maxncpl + timestep = 3600 / minncpl + elif ncpl_base_period == "day": + coupling_secs = 86400 / maxncpl + timestep = 86400 / minncpl + elif ncpl_base_period == "year": + coupling_secs = 31536000 / maxncpl + timestep = 31536000 / minncpl + elif ncpl_base_period == "decade": + coupling_secs = 315360000 / maxncpl + timestep = 315360000 / minncpl + + # Convert stop_n to units of coupling intervals + factor = 1 + if stop_option == "nsteps": + factor = timestep + elif stop_option == "nminutes": + factor = 60 + elif stop_option == "nhours": + factor = 3600 + elif stop_option == "ndays": + factor = 86400 + elif stop_option == "nyears": + factor = 315360000 + else: + expect(False, f"stop_option {stop_option} not available for this test") + + stop_n = int(stop_n * factor // coupling_secs) + rest_n = int((stop_n // 2 + 1) * coupling_secs / factor) + + expect(stop_n > 0, "Bad STOP_N: {:d}".format(stop_n)) + + expect(stop_n > 2, "ERROR: stop_n value {:d} too short".format(stop_n)) + logger.info( + "doing an {0} {1} initial test with restart file at {2} {1}".format( + str(stop_n), stop_option, str(rest_n) + ) + ) + self._case.set_value("REST_N", rest_n) + return rest_n def _init_environment(self, caseroot): """ diff --git a/CIME/case/case.py b/CIME/case/case.py index e83d3099fe8..2f2d44aaca3 100644 --- a/CIME/case/case.py +++ b/CIME/case/case.py @@ -270,10 +270,10 @@ def initialize_derived_attributes(self): self.tasks_per_node = env_mach_pes.get_tasks_per_node( self.total_tasks, self.thread_count ) - self.num_nodes, self.spare_nodes = env_mach_pes.get_total_nodes( self.total_tasks, self.thread_count ) + self.num_nodes += self.spare_nodes logger.debug( diff --git a/CIME/case/case_submit.py b/CIME/case/case_submit.py index 7e0e09881e4..70dfe3dec7e 100644 --- a/CIME/case/case_submit.py +++ b/CIME/case/case_submit.py @@ -234,6 +234,8 @@ def submit( caseroot = self.get_value("CASEROOT") if self.get_value("TEST"): casebaseid = self.get_value("CASEBASEID") + if os.path.exists(os.path.join(caseroot, "env_test.xml")): + self.set_initial_test_values() # This should take care of the race condition where the submitted job # begins immediately and tries to set RUN phase. We proactively assume # a passed SUBMIT phase. If this state is already PASS, don't set it again diff --git a/CIME/code_checker.py b/CIME/code_checker.py index ba92fc87ea5..4c05f22e477 100644 --- a/CIME/code_checker.py +++ b/CIME/code_checker.py @@ -24,6 +24,7 @@ logger = logging.getLogger(__name__) + ############################################################################### def _run_pylint(all_files, interactive): ############################################################################### @@ -80,16 +81,6 @@ def _run_pylint(all_files, interactive): return result - # if stat != 0: - # if interactive: - # logger.info("File %s has pylint problems, please fix\n Use command: %s" % (on_file, cmd)) - # logger.info(out + "\n" + err) - # return (on_file, out + "\n" + err) - # else: - # if interactive: - # logger.info("File %s has no pylint problems" % on_file) - # return (on_file, "") - ############################################################################### def _matches(file_path, file_ends): diff --git a/CIME/data/config/config_tests.xml b/CIME/data/config/config_tests.xml index 0352b5207ca..0d5a4b86c68 100644 --- a/CIME/data/config/config_tests.xml +++ b/CIME/data/config/config_tests.xml @@ -320,7 +320,6 @@ NODEFAIL Tests restart upon detected node failure. Generates fake failu 1 ndays 7 - $STOP_N / 2 + 1 $STOP_OPTION $STOP_N $STOP_OPTION @@ -333,7 +332,6 @@ NODEFAIL Tests restart upon detected node failure. Generates fake failu 1 ndays 7 - $STOP_N / 2 + 1 $STOP_OPTION $STOP_N $STOP_OPTION @@ -540,7 +538,6 @@ NODEFAIL Tests restart upon detected node failure. Generates fake failu nsteps $ATM_NCPL 11 - $STOP_N / 2 + 1 $STOP_OPTION $STOP_N $STOP_OPTION