Repository: ESMCI/cime

Issue:
PEA_P1 fails on yellowstone
This is due to differences in the contents of the Macros file between a default mpi library build and a mpi-serial build.   Explicitly removing the file in the test allows case.setup to update the file for each build.    I believe that issue 421 may also fix this problem.  



Discussion:

Resolving patch:
diff --git a/utils/python/CIME/SystemTests/pea.py b/utils/python/CIME/SystemTests/pea.py
--- a/utils/python/CIME/SystemTests/pea.py
+++ b/utils/python/CIME/SystemTests/pea.py
@@ -1,100 +1,38 @@
 """
-Implementation of the CIME PEA test.  This class inherits from SystemTestsCommon
+Implementation of the CIME PEA test.
+
+Builds runs and compares a single processor mpi model to a model built using mpi-serial
+(1) do a run with default mpi library (suffix base)
+(2) do a run with mpi-serial (suffix mpi-serial)
 """
-import shutil
+
+from CIME.SystemTests.system_tests_compare_two import SystemTestsCompareTwo
 from CIME.XML.standard_module_setup import *
 from CIME.case_setup import case_setup
-import CIME.utils
-from CIME.SystemTests.system_tests_common import SystemTestsCommon
 
 logger = logging.getLogger(__name__)
 
-class PEA(SystemTestsCommon):
+class PEA(SystemTestsCompareTwo):
 
     def __init__(self, case):
-        """
-        initialize a test object
-        """
-        SystemTestsCommon.__init__(self, case)
-
-    def build_phase(self, sharedlib_only=False, model_only=False):
-        exeroot = self._case.get_value("EXEROOT")
-        cime_model = CIME.utils.get_model()
-
-        # first set all component mpi tasks to 1
-        for comp in ['ATM','CPL','OCN','WAV','GLC','ICE','ROF','LND']:
+        SystemTestsCompareTwo.__init__(self, case,
+                                       separate_builds = True,
+                                       run_two_suffix = 'mpi-serial',
+                                       run_one_description = 'default mpi library',
+                                       run_two_description = 'mpi-serial')
+
+    def _common_setup(self):
+        for comp in self._case.get_value("COMP_CLASSES").split(','):
+            if comp == "DRV":
+                comp = "CPL"
             self._case.set_value("NTASKS_%s"%comp, 1)
-
-        build1 = os.path.join("LockedFiles","env_build.PEA1.xml")
-        if ( os.path.isfile(build1) ):
-            shutil.copy(build1,"env_build.xml")
-
-        mpilib = self._case.get_value("MPILIB")
-        for mpilib in [mpilib, "mpi-serial"]:
-            logging.warn("Starting bld for %s"%mpilib)
-            self._case.set_value("MPILIB",mpilib)
-            self._case.flush()
-            # We need to explicitly remove the Macros file for this test
+            self._case.set_value("NTHRDS_%s"%comp, 1)
+            self._case.set_value("ROOTPE_%s"%comp, 0)
+
+    def _case_one_setup(self):
+        pass
+    def _case_two_setup(self):
+        self._case.set_value("MPILIB","mpi-serial")
+        if os.path.isfile("Macros"):
             os.remove("Macros")
-            case_setup(self._case, reset=True)
-            self.clean_build()
-            self.build_indv(sharedlib_only=sharedlib_only, model_only=model_only)
-            if (not sharedlib_only):
-                shutil.move("%s/%s.exe"%(exeroot,cime_model),
-                            "%s/%s.exe.PEA_%s"%(exeroot,cime_model,mpilib))
-            shutil.copy("env_build.xml",os.path.join("LockedFiles",
-                                                     "env_build_PEA_%s.xml"%mpilib))
-
-    def _pea_first_phase(self):
-
-        exeroot = self._case.get_value("EXEROOT")
-        cime_model = CIME.utils.get_model()
-        exefile  = "%s/%s.exe"%(exeroot,cime_model)
-        exefile1 = "%s/%s.exe.PEA1"%(exeroot,cime_model)
-        if (os.path.isfile(exefile)):
-            os.remove(exefile)
-        shutil.copy(exefile1, exefile)
-
-        self._case.set_value("CONTINUE_RUN",False)
-        self._case.set_value("REST_OPTION","none")
-        self._case.set_value("HIST_OPTION","$STOP_OPTION")
-        self._case.set_value("HIST_N","$STOP_N")
-        self._case.flush()
-
-        stop_n = self._case.get_value("STOP_N")
-        stop_option = self._case.get_value("STOP_OPTION")
-        logger.info("doing an %d %s initial test with 1pe and mpi, no restarts written"
-                    % (stop_n, stop_option))
-
-        self.run_indv()
-
-    def _pea_second_phase(self):
-
-        expect(os.path.isfile("env_mach_pes.xml.2"),
-               "ERROR: env_mach_pes.xml.2 does not exist, run case.build" )
-
-        exeroot = self._case.get_value("EXEROOT")
-        cime_model = CIME.utils.get_model()
-        exefile  = "%s/%s.exe"%(exeroot,cime_model)
-        exefile2 = "%s/%s.exe.PEA2"%(exeroot,cime_model)
-        if (os.path.isfile(exefile)):
-            os.remove(exefile)
-        shutil.copy(exefile2, exefile)
-
-        self._case.set_value("CONTINUE_RUN",False)
-        self._case.set_value("REST_OPTION","none")
-        self._case.set_value("HIST_OPTION","$STOP_OPTION")
-        self._case.set_value("HIST_N","$STOP_N")
-        self._case.flush()
-
-        stop_n = self._case.get_value("STOP_N")
-        stop_option = self._case.get_value("STOP_OPTION")
-        logger.info("doing an %d %s initial test with 1pe and serial mpi, no restarts written"
-                    % (stop_n, stop_option))
-
-        self.run_indv(suffix="mpiserial")
-        self._component_compare_test("base", "mpiserial")
-
-    def run_phase(self):
-        self._pea_first_phase()
-        self._pea_second_phase()
+        case_setup(self._case, reset=True, test_mode=True)
diff --git a/utils/python/CIME/buildnml.py b/utils/python/CIME/buildnml.py
--- a/utils/python/CIME/buildnml.py
+++ b/utils/python/CIME/buildnml.py
@@ -154,7 +154,7 @@ def _build_data_nml(case, caseroot, compclass):
 
         rc, out, err = run_cmd(cmd, from_dir=confdir)
         expect(rc==0,"Command %s failed rc=%d\nout=%s\nerr=%s"%(cmd,rc,out,err))
-        if out is not None:
+        if out is not None and len(out) > 0:
             logger.debug("cmd=%s"%cmd)
             logger.info("out = %s"%out)
         # copy namelist files and stream text files, to rundir
diff --git a/utils/python/CIME/preview_namelists.py b/utils/python/CIME/preview_namelists.py
--- a/utils/python/CIME/preview_namelists.py
+++ b/utils/python/CIME/preview_namelists.py
@@ -71,11 +71,12 @@ def preview_namelists(case, dryrun=False):
         logger.info("Running %s:"%cmd)
         if (logger.level == logging.DEBUG):
             rc, out, err = run_cmd("PREVIEW_NML=1 %s %s" % (cmd, caseroot))
-            expect(rc==0,"Command %s failed rc=%d\nout=%s\nerr=%s"%(cmd,rc,out,err))      
+            expect(rc==0,"Command %s failed rc=%d\nout=%s\nerr=%s"%(cmd,rc,out,err))
         else:
             rc, out, err = run_cmd("%s %s" % (cmd, caseroot))
             expect(rc==0,"Command %s failed rc=%d\nout=%s\nerr=%s"%(cmd,rc,out,err))
-        logger.info("     %s"%out)
+        if out is not None:
+            logger.info("     %s"%out)
     # refresh case xml object from file
     case.read_xml()
     # Save namelists to docdir


Test patch:
diff --git a/cime_config/config_tests.xml b/cime_config/config_tests.xml
--- a/cime_config/config_tests.xml
+++ b/cime_config/config_tests.xml
@@ -348,9 +348,12 @@ LII    CLM initial condition interpolation test
   <test NAME="PEA">
     <DESC>single pe bfb test (default length)</DESC>
     <INFO_DBUG>1</INFO_DBUG>
-    <REST_OPTION>never</REST_OPTION>
     <CCSM_TCOST>1</CCSM_TCOST>
     <DOUT_S>FALSE</DOUT_S>
+    <CONTINUE_RUN>FALSE</CONTINUE_RUN>
+    <REST_OPTION>never</REST_OPTION>
+    <HIST_OPTION>$STOP_OPTION</HIST_OPTION>
+    <HIST_N>$STOP_N</HIST_N>
   </test>
 
   <test NAME="PEM">
diff --git a/utils/python/CIME/SystemTests/system_tests_compare_two.py b/utils/python/CIME/SystemTests/system_tests_compare_two.py
--- a/utils/python/CIME/SystemTests/system_tests_compare_two.py
+++ b/utils/python/CIME/SystemTests/system_tests_compare_two.py
@@ -21,8 +21,6 @@
     that's needed in both cases. This is called before _case_one_setup or
     _case_two_setup.
 
-NOTE: Currently assumes that only one build is needed - i.e., there are no
-build-time settings that need to be changed between the two cases.
 """
 
 from CIME.XML.standard_module_setup import *
@@ -61,8 +59,6 @@ def __init__(self,
         SystemTestsCommon.__init__(self, case)
 
         self._separate_builds = separate_builds
-        expect(not self._separate_builds,
-               "ERROR: separate builds not yet implemented")
 
         # run_one_suffix is just used as the suffix for the netcdf files
         # produced by the first case; we may eventually remove this, but for now
@@ -140,18 +136,18 @@ def _common_setup(self):
     # ========================================================================
 
     def build_phase(self, sharedlib_only=False, model_only=False):
-        # TODO(wjs, 2016-08-05) This currently assumes that the two cases use
-        # the same build. Once we relax that assumption, we'll need a
-        # conditional here: If the two cases use the same build (based on
-        # self._separate_builds), then use the below logic; otherwise, do two
-        # builds.
-        self._activate_case1()
-        self.build_indv(sharedlib_only=sharedlib_only, model_only=model_only)
-
-        # The following is needed when _case_two_setup has a case_setup call
-        # despite sharing the build (e.g., to change NTHRDS)
-        self._case2.set_value("BUILD_COMPLETE",True)
-        self._case2.flush()
+        if self._separate_builds:
+            self._activate_case1()
+            self.build_indv(sharedlib_only=sharedlib_only, model_only=model_only)
+            self._activate_case2()
+            self.build_indv(sharedlib_only=sharedlib_only, model_only=model_only)
+        else:
+            self._activate_case1()
+            self.build_indv(sharedlib_only=sharedlib_only, model_only=model_only)
+            # The following is needed when _case_two_setup has a case_setup call
+            # despite sharing the build (e.g., to change NTHRDS)
+            self._case2.set_value("BUILD_COMPLETE",True)
+            self._case2.flush()
 
     def run_phase(self):
         """
@@ -227,11 +223,9 @@ def _setup_cases_if_not_yet_done(self):
             self._case2 = self._case_from_existing_caseroot(self._caseroot2)
         else:
             try:
-                # TODO(wjs, 2016-08-05) For now, we're hard-coding keepexe=True; in
-                # the future, make this based on self._separate_builds.
                 self._case2 = self._case1.create_clone(
                     newcase = self._caseroot2,
-                    keepexe = True)
+                    keepexe = self._separate_builds==False)
                 self._setup_cases()
             except:
                 # If a problem occurred in setting up the test cases, it's