Repository: ESMCI/cime

Issue:
Settings in component config_compsets.xml not being honored
In cam config_compsets.xml there is:
```
    <entry id="NTHRDS_ATM">
      <values>
        <value compset="SPCAMS">1</value>
        <value compset="SPCAMM">1</value>
      </values>
    </entry>
```
This request does not appear to be honored when creating a test
SMS_D.f19_f19.FSPCAMS.yellowstone_intel.cam-outfrq9s


Discussion:

Resolving patch:
diff --git a/utils/python/CIME/XML/machines.py b/utils/python/CIME/XML/machines.py
--- a/utils/python/CIME/XML/machines.py
+++ b/utils/python/CIME/XML/machines.py
@@ -28,7 +28,7 @@ def __init__(self, infile=None, files=None, machine=None):
                 files = Files()
             infile = files.get_value("MACHINES_SPEC_FILE", resolved=False)
             schema = files.get_schema("MACHINES_SPEC_FILE")
-            logger.warn("Verifying using schema %s"%schema)
+            logger.debug("Verifying using schema %s"%schema)
             infile = files.get_resolved_value(infile)
 
         self.machines_dir = os.path.dirname(infile)
diff --git a/utils/python/CIME/XML/pes.py b/utils/python/CIME/XML/pes.py
--- a/utils/python/CIME/XML/pes.py
+++ b/utils/python/CIME/XML/pes.py
@@ -17,20 +17,52 @@ def __init__(self, infile):
         GenericXML.__init__(self, infile)
 
     def find_pes_layout(self, grid, compset, machine, pesize_opts='M', mpilib=None):
-        pe_select = None
-        grid_match = None
-        mach_match = None
-        compset_match = None
-        pesize_match = None
+        opes_ntasks = {}
+        opes_nthrds = {}
+        opes_rootpe = {}
+        oother_settings = {}
+        other_settings = {}
+        o_grid_nodes = []
+        # Get any override nodes
+        overrides = self.get_optional_node("overrides")
+        if overrides is not None:
+            o_grid_nodes = self.get_nodes("grid", root = overrides)
+            opes_ntasks, opes_nthrds, opes_rootpe, oother_settings = self._find_matches(o_grid_nodes, grid, compset, machine, pesize_opts, True)
+        # Get all the nodes
+        grid_nodes = self.get_nodes("grid")
+        if o_grid_nodes:
+            gn_set = set(grid_nodes)
+            ogn_set = set(o_grid_nodes)
+            gn_set.difference_update(ogn_set)
+            grid_nodes = list(gn_set)
+
+
+        pes_ntasks, pes_nthrds, pes_rootpe, other_settings = self._find_matches(grid_nodes, grid, compset, machine, pesize_opts, False)
+        pes_ntasks.update(opes_ntasks)
+        pes_nthrds.update(opes_nthrds)
+        pes_rootpe.update(opes_rootpe)
+        other_settings.update(oother_settings)
+
+        if mpilib == "mpi-serial":
+            for i in iter(pes_ntasks):
+                pes_ntasks[i] = 1
+            for i in iter(pes_rootpe):
+                pes_rootpe[i] = 0
+
+        logger.info("Pes setting: grid          is %s " %grid)
+        logger.info("Pes setting: compset       is %s " %compset)
+        logger.info("Pes setting: tasks       is %s " %pes_ntasks)
+        logger.info("Pes other settings: %s"%other_settings)
+        return pes_ntasks, pes_nthrds, pes_rootpe, other_settings
+
+    def _find_matches(self, grid_nodes, grid, compset, machine, pesize_opts, override=False):
         grid_choice = None
         mach_choice = None
         compset_choice = None
         pesize_choice = None
-        other_settings = {}
         max_points = -1
-
-        # Get all the nodes
-        grid_nodes = self.get_nodes("grid")
+        pes_ntasks, pes_nthrds, pes_rootpe, other_settings = {}, {}, {}, {}
+        pe_select = []
         for grid_node in grid_nodes:
             grid_match = grid_node.get("name")
             if grid_match == "any" or  re.search(grid_match,grid):
@@ -49,47 +81,57 @@ def find_pes_layout(self, grid, compset, machine, pesize_opts='M', mpilib=None):
 
                                 points = int(grid_match!="any")*4+int(mach_match!="any")*3+\
                                     int(compset_match!="any")*2+int(pesize_match!="any")
-                                if points > max_points:
-                                    pe_select = pes_node
-                                    max_points = points
-                                    mach_choice = mach_match
-                                    grid_choice = grid_match
-                                    compset_choice = compset_match
-                                    pesize_choice = pesize_match
-                                elif points == max_points:
-                                    logger.warn("mach_choice %s mach_match %s"%(mach_choice, mach_match))
-                                    logger.warn("grid_choice %s grid_match %s"%(grid_choice, grid_match))
-                                    logger.warn("compset_choice %s compset_match %s"%(compset_choice, compset_match))
-                                    logger.warn("pesize_choice %s pesize_match %s"%(pesize_choice, pesize_match))
-                                    logger.warn("points = %d"%points)
-                                    expect(False, "We dont expect to be here" )
+                                if override and points > 0:
+                                    for node in pes_node:
+                                        vid = node.tag
+                                        logger.info("vid is %s"%vid)
+                                        if "ntasks" in vid:
+                                            for child in node:
+                                                pes_ntasks[child.tag.upper()] = child.text
+                                        elif "nthrds" in vid:
+                                            for child in node:
+                                                pes_nthrds[child.tag.upper()] = child.text
+                                        elif "rootpe" in vid:
+                                            for child in node:
+                                                pes_rootpe[child.tag.upper()] = child.text
+                                    # if the value is already upper case its something else we are trying to set
+                                        elif vid == node.tag:
+                                            other_settings[vid] = node.text
 
-        pes_ntasks, pes_nthrds, pes_rootpe, other_settings = {}, {}, {}, {}
-        for node in pe_select:
-            vid = node.tag
-            logger.debug("vid is %s"%vid)
-            if "ntasks" in vid:
-                for child in node:
-                    pes_ntasks[child.tag.upper()] = child.text
-            elif "nthrds" in vid:
-                for child in node:
-                    pes_nthrds[child.tag.upper()] = child.text
-            elif "rootpe" in vid:
-                for child in node:
-                    pes_rootpe[child.tag.upper()] = child.text
+                                else:
+                                    if points > max_points:
+                                        pe_select = pes_node
+                                        max_points = points
+                                        mach_choice = mach_match
+                                        grid_choice = grid_match
+                                        compset_choice = compset_match
+                                        pesize_choice = pesize_match
+                                    elif points == max_points:
+                                        logger.warn("mach_choice %s mach_match %s"%(mach_choice, mach_match))
+                                        logger.warn("grid_choice %s grid_match %s"%(grid_choice, grid_match))
+                                        logger.warn("compset_choice %s compset_match %s"%(compset_choice, compset_match))
+                                        logger.warn("pesize_choice %s pesize_match %s"%(pesize_choice, pesize_match))
+                                        logger.warn("points = %d"%points)
+                                        expect(False, "We dont expect to be here" )
+        if not override:
+            for node in pe_select:
+                vid = node.tag
+                logger.debug("vid is %s"%vid)
+                if "ntasks" in vid:
+                    for child in node:
+                        pes_ntasks[child.tag.upper()] = child.text
+                elif "nthrds" in vid:
+                    for child in node:
+                        pes_nthrds[child.tag.upper()] = child.text
+                elif "rootpe" in vid:
+                    for child in node:
+                        pes_rootpe[child.tag.upper()] = child.text
             # if the value is already upper case its something else we are trying to set
-            elif vid == node.tag:
-                other_settings[vid] = node.text
+                elif vid == node.tag:
+                    other_settings[vid] = node.text
+            logger.info("Pes setting: grid match    is %s " %grid_choice )
+            logger.info("Pes setting: machine match is %s " %mach_choice)
+            logger.info("Pes setting: compset_match is %s " %compset_choice)
+            logger.info("Pes setting: pesize match  is %s " %pesize_choice)
 
-        logger.info("Pes setting: grid          is %s " %grid)
-        logger.info("Pes setting: compset       is %s " %compset)
-        logger.info("Pes setting: grid match    is %s " %grid_choice )
-        logger.info("Pes setting: machine match is %s " %mach_choice)
-        logger.info("Pes setting: compset_match is %s " %compset_choice)
-        logger.info("Pes setting: pesize match  is %s " %pesize_choice)
-        logger.info("Pes other settings: %s"%other_settings)
-        if mpilib == "mpi-serial":
-            for i in iter(pes_ntasks):
-                pes_ntasks[i] = 1
-                pes_rootpe[i] = 1
         return pes_ntasks, pes_nthrds, pes_rootpe, other_settings
diff --git a/utils/python/CIME/case.py b/utils/python/CIME/case.py
--- a/utils/python/CIME/case.py
+++ b/utils/python/CIME/case.py
@@ -97,7 +97,6 @@ def __init__(self, case_root=None, read_only=True):
         self.num_nodes = None
         self.tasks_per_numa = None
         self.cores_per_task = None
-
         # check if case has been configured and if so initialize derived
         if self.get_value("CASEROOT") is not None:
             self.initialize_derived_attributes()
@@ -121,6 +120,7 @@ def initialize_derived_attributes(self):
         env_mach_pes = self.get_env("mach_pes")
         comp_classes = self.get_values("COMP_CLASSES")
         total_tasks = env_mach_pes.get_total_tasks(comp_classes)
+
         self.thread_count = env_mach_pes.get_max_thread_count(comp_classes)
         self.tasks_per_node = env_mach_pes.get_tasks_per_node(total_tasks, self.thread_count)
         logger.debug("total_tasks %s thread_count %s"%(total_tasks, self.thread_count))
@@ -486,7 +486,7 @@ def _set_comp_classes(self, comp_classes):
             env_file.set_components(comp_classes)
 
     def _get_component_config_data(self):
-        # attributes used for multi valued defaults 
+        # attributes used for multi valued defaults
         # attlist is a dictionary used to determine the value element that has the most matches
         attlist = {"compset":self._compsetname, "grid":self._gridname, "cime_model":self._cime_model}
 
@@ -669,13 +669,11 @@ def configure(self, compset_name, grid_name, machine_name=None,
 
             pes_ntasks, pes_nthrds, pes_rootpe, other = pesobj.find_pes_layout(self._gridname, self._compsetname,
                                                                     machine_name, pesize_opts=pecount, mpilib=mpilib)
-
         mach_pes_obj = self.get_env("mach_pes")
         totaltasks = {}
-        # Since other items may include PES_PER_NODE we need to do this first
-        # we can get rid of this code when all of the perl is removed
-        for key, value in other.items():
-            self.set_value(key, value)
+        if other is not None:
+            for key, value in other.items():
+                self.set_value(key, value)
         for key, value in pes_ntasks.items():
             totaltasks[key[-3:]] = int(value)
             mach_pes_obj.set_value(key,int(value))
@@ -688,12 +686,11 @@ def configure(self, compset_name, grid_name, machine_name=None,
 
         maxval = 1
         pes_per_node = self.get_value("PES_PER_NODE")
-        if mpilib != "mpi-serial":
-            for key, val in totaltasks.items():
-                if val < 0:
-                    val = -1*val*pes_per_node
-                if val > maxval:
-                    maxval = val
+        for key, val in totaltasks.items():
+            if val < 0:
+                val = -1*val*pes_per_node
+            if val > maxval:
+                maxval = val
 
         # Make sure that every component has been accounted for
         # set, nthrds and ntasks to 1 otherwise. Also set the ninst values here.
@@ -709,11 +706,7 @@ def configure(self, compset_name, grid_name, machine_name=None,
             if compclass not in pes_nthrds.keys():
                 mach_pes_obj.set_value(compclass,1)
 
-        # FIXME - this is a short term fix for dealing with the restriction that
-        # CISM1 cannot run on multiple cores
-        if "CISM1" in self._compsetname:
-            mach_pes_obj.set_value("NTASKS_GLC",1)
-            mach_pes_obj.set_value("NTHRDS_GLC",1)
+
 
         #--------------------------------------------
         # batch system


Test patch: