Repository: ESMCI/cime

Issue:
MAX_TASKS_PER_NODE is static for each machine entry
For the E3SM-MMF configuration on Summit we have configurations to run the CRM on either CPUs or GPUs depending on whether the compiler is pgi or pgigpu, which requires changing MAX_TASKS_PER_NODE and MAX_MPITASKS_PER_NODE. However, in config_machines.xml these entries cannot be set based on the compiler, so we have created a separate machine entry "summit-cpu" which is identical to "summit" except for the task per node setting. 

We would like some way to fix this in order to get rid of the redundant machine configuration because it's causes headaches every time the summit config needs to be updated. 


Discussion:

Resolving patch:
diff --git a/scripts/lib/CIME/XML/generic_xml.py b/scripts/lib/CIME/XML/generic_xml.py
--- a/scripts/lib/CIME/XML/generic_xml.py
+++ b/scripts/lib/CIME/XML/generic_xml.py
@@ -254,7 +254,7 @@ def get_children(self, name=None, attributes=None, root=None):
         """
         This is the critical function, its interface and performance are crucial.
 
-        You can specify attributes={key:None} if you want to select chilren
+        You can specify attributes={key:None} if you want to select children
         with the key attribute but you don't care what its value is.
         """
         root = root if root is not None else self.root
@@ -286,12 +286,20 @@ def get_children(self, name=None, attributes=None, root=None):
         return children
 
     def get_child(self, name=None, attributes=None, root=None, err_msg=None):
-        children = self.get_children(root=root, name=name, attributes=attributes)
-        expect(len(children) == 1, err_msg if err_msg else "Expected one child, found {} with name '{}' and attribs '{}' in file {}".format(len(children), name, attributes, self.filename))
-        return children[0]
+        child = self.get_optional_child(root=root, name=name, attributes=attributes, err_msg=err_msg)
+        expect(child, err_msg if err_msg else "Expected one child, found None with name '{}' and attribs '{}' in file {}".format(name, attributes, self.filename))
+        return child
 
     def get_optional_child(self, name=None, attributes=None, root=None, err_msg=None):
         children = self.get_children(root=root, name=name, attributes=attributes)
+        if len(children) > 1:
+            # see if we can reduce to 1 based on attribute counts
+            if not attributes:
+                children = [c for c in children if not c.xml_element.attrib]
+            else:
+                attlen = len(attributes)
+                children = [c for c in children if len(c.xml_element.attrib) == attlen]
+
         expect(len(children) <= 1, err_msg if err_msg else "Multiple matches for name '{}' and attribs '{}' in file {}".format(name, attributes, self.filename))
         return children[0] if children else None
 
diff --git a/scripts/lib/CIME/case/case.py b/scripts/lib/CIME/case/case.py
--- a/scripts/lib/CIME/case/case.py
+++ b/scripts/lib/CIME/case/case.py
@@ -958,7 +958,8 @@ def configure(self, compset_name, grid_name, machine_name=None,
         nodenames = machobj.get_node_names()
         nodenames = [x for x in nodenames if
                      '_system' not in x and '_variables' not in x and 'mpirun' not in x and\
-                     'COMPILER' not in x and 'MPILIB' not in x]
+                     'COMPILER' not in x and 'MPILIB' not in x and 'MAX_MPITASKS_PER_NODE' not in x and\
+                     'MAX_TASKS_PER_NODE' not in x]
 
         for nodename in nodenames:
             value = machobj.get_value(nodename, resolved=False)
@@ -981,6 +982,11 @@ def configure(self, compset_name, grid_name, machine_name=None,
             expect(machobj.is_valid_MPIlib(mpilib, {"compiler":compiler}),
                    "MPIlib {} is not supported on machine {}".format(mpilib, machine_name))
         self.set_value("MPILIB",mpilib)
+        for name in ("MAX_TASKS_PER_NODE","MAX_MPITASKS_PER_NODE"):
+            dmax = machobj.get_value(name,{'compiler':compiler})
+            if not dmax:
+                dmax = machobj.get_value(name)
+            self.set_value(name, dmax)
 
         machdir = machobj.get_machines_dir()
         self.set_value("MACHDIR", machdir)


Test patch: