Repository: ESMCI/cime

Issue:
problem with st_archive and short runs (< 1 month)
If the short term archiver is run prior to the end of month boundary then it errors out because it can't find the component monthly history file corresponding to the component restart history file. 

The st_archive log errors from a F-WACCM case are:

==> f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002.st_archive.570708 <==
out=
err=ncdump: nhfil: No such variable
histfiles_savein_rundir ['f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002.cam.h0.1979-01.nc', 'f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002.cam.h1.1979-01-01-00000.nc', 'f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002.cam.h2.1979-01-01-00000.nc'] 
doing short term archiving for clm (lnd)
removing interim restart file /glade/scratch/mmills/f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002/run/f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002.clm2.r.1979-01-06-00000.nc
 WARNING: ncdump -v locfnh /glade/scratch/mmills/f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002/run/f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002.clm2.rh0.1979-01-06-00000.nc  failed rc=1
out=
err=ncdump: locfnh: No such variable
removing interim restart file /glade/scratch/mmills/f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002/run/f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002.clm2.rh0.1979-01-06-00000.nc
ERROR: restart file /glade/scratch/mmills/f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002/run/f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002.clm2.h0.1979-01.nc does not exist


Discussion:
Can this be reproduced using CIME only?

I am still having this issue, and it also occurs when the run ends on an end-of-month boundary. I am using my copy of Francis’ Phase 3 sandbox, which I have here:

/glade/u/home/mmills/cesm/phase3_n03_cam5_4_90_compsets/

This is the latest case to fail:

/glade/p/work/mmills/case/f.e20.FSDW.f09_f09.phase3_n03_cam5_4_90.02

See f.e20.FSDW.f09_f09.phase3_n03_cam5_4_90.02.st_archive.259967

ERROR: restart file /glade/scratch/mmills/f.e20.FSDW.f09_f09.phase3_n03_cam5_4_90.02/run/f.e20.FSDW.f09_f09.phase3_n03_cam5_4_90.02.clm2.h0.1990-09.nc does not exist 

I am not seeing any _.clm2.h0._ files in the run directory or the short-term archive:

/glade/scratch/mmills/f.e20.FSDW.f09_f09.phase3_n03_cam5_4_90.02/run

/glade/scratch/mmills/archive/f.e20.FSDW.f09_f09.phase3_n03_cam5_4_90.02

I have located the CLM history output. They are in the short-term archive under atm/hist, instead of under lnd/hist. So are the history files for CICE and MOSART.

See:

/glade/scratch/mmills/archive/f.e15.FWscAMIP.f19_f19.misc08_cam5_4_81.002/atm/hist
/glade/scratch/mmills/archive/f.e20.FSDW.f09_f09.phase3_n03_cam5_4_90.02/atm/hist
/glade/scratch/mmills/archive/f.e20.FWAMIP.f09_f09.phase3_n03_cam5_4_90.03/atm/hist


Resolving patch:
diff --git a/scripts/lib/CIME/XML/compsets.py b/scripts/lib/CIME/XML/compsets.py
--- a/scripts/lib/CIME/XML/compsets.py
+++ b/scripts/lib/CIME/XML/compsets.py
@@ -7,7 +7,6 @@
 from CIME.XML.entry_id import EntryID
 from CIME.XML.files import Files
 
-
 logger = logging.getLogger(__name__)
 
 class Compsets(GenericXML):
@@ -85,6 +84,20 @@ def print_values(self, arg_help=True):
         logger.info("       --------------------------------------")
         logger.info("       Compset Alias: Compset Long Name ")
         logger.info("       --------------------------------------")
-        for v in compsets_text.iteritems():
-            label, definition = v
-            logger.info("   {:20} : {}".format(label, definition))
+        for key in sorted(compsets_text.iterkeys()):
+            logger.info("   {:20} : {}".format(key, compsets_text[key]))
+
+    def return_all_values(self):
+        all_compsets = dict()
+        science_compsets = dict()
+        help_text = self.get_value(name="help")
+        compsets_text = self.get_value("names")
+        for key in sorted(compsets_text.iterkeys()):
+            all_compsets[key] = compsets_text[key]
+
+        # get the matching science support grids
+        for alias in all_compsets.iterkeys():
+            science_compsets[alias] = self.get_compset_match(alias)
+            
+        return help_text, all_compsets
+
diff --git a/scripts/lib/CIME/XML/grids.py b/scripts/lib/CIME/XML/grids.py
--- a/scripts/lib/CIME/XML/grids.py
+++ b/scripts/lib/CIME/XML/grids.py
@@ -24,7 +24,6 @@ def __init__(self, infile=None, files=None):
 
         self._comp_gridnames = self._get_grid_names()
 
-
     def _get_grid_names(self):
         if self._version == 1.0:
             gridnames = ["atm", "lnd", "ocnice", "rof", "mask", "glc", "wav"]
@@ -565,3 +564,154 @@ def _print_values_v1(self, long_output=None):
                 logger.info("   ")
 
                 # write out XROT_FLOOD_MODE element TODO: (why is this in there???)
+
+    def _get_all_values_v1(self):
+        # return a list of grid elements in long form
+        grid_list = list()
+        default_comp_grids = list()
+
+        grid_nodes = self.get_nodes(nodename="grid")
+        for grid_node in grid_nodes:
+            grid_info = dict()
+            lname = self.get_element_text("lname",root=grid_node)
+            sname = self.get_element_text("sname",root=grid_node)
+            alias = self.get_element_text("alias",root=grid_node)
+            grid_info = {
+                "lname" : lname,
+                "sname" : sname,
+                "alias" : alias,
+                }
+            compset_list = list()
+            for attr, value in grid_node.items():
+                if  attr == 'compset':
+                    compset_list.append(value)
+
+            grid_info.update({'compsets': compset_list})
+
+            # add domain description and mapping fiels
+            # get component grids (will contain duplicates)
+            component_grids = self._get_component_grids(lname)
+
+            # write out out only unique non-null component grids
+            for domain in list(set(component_grids)):
+                if domain != 'null':
+                    domain_list = list()
+                    domain_node = self.get_node(nodename="domain", attributes={"name":domain})
+                    for child in domain_node:
+                        domain_list.append({'domain':child.tag, 'text':child.text})
+
+            grid_info.update({'domains': domain_list})
+                        
+            # add mapping files
+            grids = [ ("atm_grid", component_grids[0]), ("lnd_grid", component_grids[1]), ("ocn_grid", component_grids[2]), \
+                          ("rof_grid", component_grids[3]), ("glc_grid", component_grids[5]), ("wav_grid", component_grids[6]) ]
+
+            for idx, grid in enumerate(grids):
+                map_list = list()
+                for other_grid in grids[idx+1:]:
+                    nodes = self.get_nodes(nodename="gridmap", attributes={grid[0]:grid[1], other_grid[0]:other_grid[1]})
+                    for gridmap_node in nodes:
+                        for child in gridmap_node:
+                            map_list.append({'map':child.tag, 'file':child.text})
+
+            grid_info.update({'maps': map_list})
+
+            grid_list.append(grid_info)
+
+        return default_comp_grids, grid_list
+
+    def _get_all_values_v2(self):
+
+        default_comp_grids = list()
+        grid_list = list()
+
+        default_nodes = self.get_nodes(nodename="model_grid_defaults")
+        for default_node in default_nodes:
+            grid_nodes = self.get_nodes(nodename="grid", root=default_node)
+            for grid_node in grid_nodes:
+                name = grid_node.get("name")
+                compset = grid_node.get("compset")
+                value = grid_node.text
+                default_comp_grids.append({'component':name,
+                                           'compset':compset,
+                                           'value':value,})
+        domains = {}
+
+        domain_nodes = self.get_nodes(nodename="domain")
+        for domain_node in domain_nodes:
+            name = domain_node.get("name")
+            if name == 'null':
+                continue
+            nx = self.get_node("nx", root=domain_node).text
+            ny = self.get_node("ny", root=domain_node).text
+            desc = self.get_node("desc", root=domain_node).text
+            ##support = self.get_optional_node("support", root=domain_node).text
+            files = ""
+            file_nodes = self.get_nodes("file", root=domain_node)
+            for file_node in file_nodes:
+                filename = file_node.text
+                mask_attrib = file_node.get("mask")
+                grid_attrib = file_node.get("grid")
+                files += "\n       " + filename
+                if mask_attrib or grid_attrib:
+                    files += " (only for"
+                if mask_attrib:
+                    files += " mask: " + mask_attrib
+                if grid_attrib:
+                    files += " grid match: " + grid_attrib
+                if mask_attrib or grid_attrib:
+                    files += ")"
+            domains[name] = "\n       %s with domain file(s): %s " %(desc, files)
+
+        grids_dict = dict()
+        model_grid_nodes = self.get_nodes(nodename="model_grid")
+        for model_grid_node in model_grid_nodes:
+            alias = model_grid_node.get("alias")
+            compset = model_grid_node.get("compset")
+            not_compset = model_grid_node.get("not_compset")
+            restriction = ""
+            if compset:
+                restriction += "only for compsets that are %s " %compset
+            if not_compset:
+                restriction += "only for compsets that are not %s " %not_compset
+            if restriction:
+                aliases = "\n     alias: %s (%s)" % (alias,restriction)
+            else:
+                aliases = "\n     alias: %s" % (alias)
+
+            grid_nodes = self.get_nodes("grid", root=model_grid_node)
+            grids = ""
+            gridnames = []
+            for grid_node in grid_nodes:
+                gridnames.append(grid_node.text)
+                grids += grid_node.get("name") + ":" + grid_node.text + "  " 
+            grids = "       non-default grids are: %s" %grids
+
+            mask = ""
+            mask_nodes = self.get_nodes("mask", root=model_grid_node)
+            for mask_node in mask_nodes:
+                mask += "\n       mask is: %s" %(mask_node.text)
+            
+            grids_dict[alias] = {'aliases':aliases, 
+                                 'grids':grids, 
+                                 'mask':mask }
+
+            gridnames = set(gridnames)
+            for gridname in gridnames:
+                if gridname != "null":
+                    grid_list.append({gridname:(grids_dict[alias], domains[gridname])})
+
+        return default_comp_grids, grid_list
+
+    def return_all_values(self):
+        # parse grid data and return a dictionary
+        help_text = self.get_element_text("help")
+
+        # get a list of grid dictionaries
+        if self._version == 1.0:
+            (default_comp_grids, all_grids) = self._get_all_values_v1()
+        elif self._version >= 2.0:
+            (default_comp_grids, all_grids) = self._get_all_values_v2()
+
+        return help_text, default_comp_grids, all_grids
+
diff --git a/scripts/lib/CIME/XML/machines.py b/scripts/lib/CIME/XML/machines.py
--- a/scripts/lib/CIME/XML/machines.py
+++ b/scripts/lib/CIME/XML/machines.py
@@ -307,3 +307,31 @@ def print_values(self):
                 print( "      pes/node       ",pes_per_node.text)
             if max_tasks_per_node is not None:
                 print( "      max_tasks/node ",max_tasks_per_node.text)
+
+    def return_all_values(self):
+        # return a dictionary of machines
+        mach_dict = dict()
+        machines = self.get_nodes(nodename="machine")
+        for machine in machines:
+            name = machine.get("MACH")
+            desc = machine.find("DESC")
+            os_  = machine.find("OS")
+            compilers = machine.find("COMPILERS")
+            max_tasks_per_node = machine.find("MAX_TASKS_PER_NODE")
+            pes_per_node = machine.find("PES_PER_NODE")
+            ppn = ''
+            if pes_per_node is not None:
+                ppn = pes_per_node.text
+
+            max_tasks_pn = ''
+            if max_tasks_per_node is not None:
+                max_tasks_pn = max_tasks_per_node.text
+
+            mach_dict[name] = { 'description'    : desc.text, 
+                                'os'             : os_.text,
+                                'compilers'      : compilers.text,
+                                'pes/node'       : ppn,
+                                'max_tasks/node' : max_tasks_pn }
+
+        return mach_dict
+


Test patch: