Repository: ESMCI/cime

Issue:
config_component v3 description not printed correctly
I _think_ this is a bug, but I may be misunderstanding the intended functioning:

If I run this command from cesm2_0_alpha07c with the latest cime:

```
./create_newcase --case test_0829c --compset IHistClm50SpG --res f09_g17 --run-unsupported
```

It prints (among other things)

```
Compset longname is HIST_DATM%GSWP3v1_CLM50%SP_SICE_SOCN_MOSART_CISM2%EVOLVE_SWAV

ATM component is  Data driven ATM
```

I expected that the output would include `GSWP3v1 data set`  , based on this entry in datm's config_component.xml:

```xml
  <description modifier_mode="1">
    <desc atm="DATM[%QIA][%WISOQIA][%CRU][%CRUv7][%GSW][%GSWP3v1][%CPLHIST][%1PT][%NYF][%IAF]"> Data driven ATM </desc>
    <desc option="QIA"> QIAN data set </desc>
    <desc option="WISOQIA">QIAN with water isotopes</desc>
    <desc option="CRU"> CRUNCEP data set </desc>
    <desc option="CRUv7"> CLM CRU NCEP v7 data set </desc>
    <desc option="GSW"> GSWP3 data set </desc>
    <desc option="GSWP3v1"> GSWP3v1 data set </desc>
    <desc option="CPLHIST"> Coupler hist data set </desc>
    <desc option="1PT">single point tower site data set </desc>
    <desc option="NYF">COREv2 normal year forcing</desc>
    <desc option="IAF">COREv2 interannual forcing</desc>
  </description>
```

Yet it does not.

cc @jedwards4b @bertinia 


Discussion:
@billsacks - other than the output you see after create_newcase - is
everything else okay with the created case?

On Thu, Aug 31, 2017 at 4:34 PM, Bill Sacks <notifications@github.com>
wrote:

> I *think* this is a bug, but I may be misunderstanding the intended
> functioning:
>
> If I run this command from cesm2_0_alpha07c with the latest cime:
>
> ./create_newcase --case test_0829c --compset IHistClm50SpG --res f09_g17 --run-unsupported
>
> It prints (among other things)
>
> Compset longname is HIST_DATM%GSWP3v1_CLM50%SP_SICE_SOCN_MOSART_CISM2%EVOLVE_SWAV
>
> ATM component is  Data driven ATM
>
> I expected that the output would include GSWP3v1 data set , based on this
> entry in datm's config_component.xml:
>
>   <description modifier_mode="1">
>     <desc atm="DATM[%QIA][%WISOQIA][%CRU][%CRUv7][%GSW][%GSWP3v1][%CPLHIST][%1PT][%NYF][%IAF]"> Data driven ATM </desc>
>     <desc option="QIA"> QIAN data set </desc>
>     <desc option="WISOQIA">QIAN with water isotopes</desc>
>     <desc option="CRU"> CRUNCEP data set </desc>
>     <desc option="CRUv7"> CLM CRU NCEP v7 data set </desc>
>     <desc option="GSW"> GSWP3 data set </desc>
>     <desc option="GSWP3v1"> GSWP3v1 data set </desc>
>     <desc option="CPLHIST"> Coupler hist data set </desc>
>     <desc option="1PT">single point tower site data set </desc>
>     <desc option="NYF">COREv2 normal year forcing</desc>
>     <desc option="IAF">COREv2 interannual forcing</desc>
>   </description>
>
> Yet it does not.
>
> cc @jedwards4b <https://github.com/jedwards4b> @bertinia
> <https://github.com/bertinia>
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/ESMCI/cime/issues/1859>, or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AHlxE10MRl8eK-lhfuemG0JrVC53VAJNks5sdzUEgaJpZM4PJfh1>
> .
>

@mvertens I didn't check, but I assume this only affects the output, since my understanding is that that's separate from the parsing of anything that actually affects the case.
And indeed, I see this in env_run.xml, so it looks right:

```xml
env_run.xml:    <entry id="DATM_MODE" value="CLMGSWP3v1">
```


Resolving patch:
diff --git a/scripts/lib/CIME/XML/component.py b/scripts/lib/CIME/XML/component.py
--- a/scripts/lib/CIME/XML/component.py
+++ b/scripts/lib/CIME/XML/component.py
@@ -162,7 +162,6 @@ def _get_description_v3(self, compsetname, comp_class):
             modifier_mode = '*'
         expect(modifier_mode in ('*','1','?','+'),
                "Invalid modifier_mode {} in file {}".format(modifier_mode, self.filename))
-
         optiondesc = {}
         if comp_class == "forcing":
             for node in desc_nodes:
@@ -176,30 +175,36 @@ def _get_description_v3(self, compsetname, comp_class):
                 desc = compsetname.split('_')[0]
             return desc
 
+
         # first pass just make a hash of the option descriptions
         for node in desc_nodes:
             option = node.get('option')
             if option is not None:
                 optiondesc[option] = node.text
+
         #second pass find a comp_class match
+        desc = ""
         for node in desc_nodes:
             compdesc = node.get(comp_class)
 
-            if compdesc is None:
-                continue
-            opt_parts = [ x.rstrip("]") for x in compdesc.split("[%") ]
-            parts = opt_parts.pop(0).split("%")
+            if compdesc is not None:
+                opt_parts = [ x.rstrip("]") for x in compdesc.split("[%") ]
+                parts = opt_parts.pop(0).split("%")
+                reqset = set(parts)
+                fullset = set(parts+opt_parts)
+                match, complist =  self._get_description_match(compsetname, reqset, fullset, modifier_mode)
+                if match:
+                    desc = node.text
+                    for opt in complist:
+                        if opt in optiondesc:
+                            desc += optiondesc[opt]
+
 
-            reqset = set(parts)
-            fullset = set(parts+opt_parts)
-            if self._get_description_match(compsetname, reqset, fullset, modifier_mode):
-                desc = node.text
-                break
         # cpl and esp components may not have a description
         if comp_class not in ['cpl','esp']:
             expect(len(desc) > 0,
-                   "No description found for comp_class {} matching compsetname {} in file {}"\
-                       .format(comp_class,compsetname, self.filename))
+                   "No description found for comp_class {} matching compsetname {} in file {}, expected match in {} % {}"\
+                       .format(comp_class,compsetname, self.filename, list(reqset), list(opt_parts)))
         return desc
 
     def _get_description_match(self, compsetname, reqset, fullset, modifier_mode):
@@ -207,50 +212,53 @@ def _get_description_match(self, compsetname, reqset, fullset, modifier_mode):
 
         >>> obj = Component('testingonly', 'ATM')
         >>> obj._get_description_match("1850_DATM%CRU_FRED",set(["DATM"]), set(["DATM","CRU","HSI"]), "*")
-        True
+        (True, ['DATM', 'CRU'])
         >>> obj._get_description_match("1850_DATM%FRED_Barn",set(["DATM"]), set(["DATM","CRU","HSI"]), "*")
-        False
+        (False, None)
         >>> obj._get_description_match("1850_DATM_Barn",set(["DATM"]), set(["DATM","CRU","HSI"]), "?")
-        True
+        (True, ['DATM'])
         >>> obj._get_description_match("1850_DATM_Barn",set(["DATM"]), set(["DATM","CRU","HSI"]), "1")
         Traceback (most recent call last):
         ...
-        SystemExit: ERROR: Expected exactly one modifer found 0
+        SystemExit: ERROR: Expected exactly one modifer found 0 in ['DATM']
         >>> obj._get_description_match("1850_DATM%CRU%HSI_Barn",set(["DATM"]), set(["DATM","CRU","HSI"]), "1")
         Traceback (most recent call last):
         ...
-        SystemExit: ERROR: Expected exactly one modifer found 2
+        SystemExit: ERROR: Expected exactly one modifer found 2 in ['DATM', 'CRU', 'HSI']
         >>> obj._get_description_match("1850_CAM50%WCCM%RCO2_Barn",set(["CAM50", "WCCM"]), set(["CAM50","WCCM","RCO2"]), "*")
-        True
+        (True, ['CAM50', 'WCCM', 'RCO2'])
 
         # The following is not allowed because the required WCCM field is missing
         >>> obj._get_description_match("1850_CAM50%RCO2_Barn",set(["CAM50", "WCCM"]), set(["CAM50","WCCM","RCO2"]), "*")
-        False
+        (False, None)
         >>> obj._get_description_match("1850_CAM50_Barn",set(["CAM50", "WCCM"]), set(["CAM50","WCCM","RCO2"]), "+")
-        False
+        (False, None)
         >>> obj._get_description_match("1850_CAM50%WCCM_Barn",set(["CAM50", "WCCM"]), set(["CAM50","WCCM","RCO2"]), "+")
-        True
+        (True, ['CAM50', 'WCCM'])
         """
         match = False
         comparts = compsetname.split('_')
+        matchcomplist = None
+
         for comp in comparts:
             complist = comp.split('%')
             cset = set(complist)
             if cset == reqset or (cset > reqset and cset <= fullset):
                 if modifier_mode == '1':
                     expect(len(complist) == 2,
-                           "Expected exactly one modifer found {}".format(len(complist)-1))
+                           "Expected exactly one modifer found {} in {}".format(len(complist)-1,complist))
                 elif modifier_mode == '+':
                     expect(len(complist) >= 2,
-                           "Expected one or more modifers found {}".format(len(complist)-1))
+                           "Expected one or more modifers found {} in {}".format(len(complist)-1, list(reqset)))
                 elif modifier_mode == '?':
                     expect(len(complist) <= 2,
-                           "Expected 0 or one modifers found {}".format(len(complist)-1))
+                           "Expected 0 or one modifers found {} in {}".format(len(complist)-1, complist))
                 expect(not match,"Found multiple matches in file {} for {}".format(self.filename,comp))
                 match = True
+                matchcomplist = complist
                 # found a match
 
-        return match
+        return match, matchcomplist
 
 
 


Test patch: