Repository: ESMCI/cime

Issue:
User-defined compsets differ in RUN_STARTDATE and other settings from config_compsets.xml
I think this is related to an issue @mvertens brought up verbally a few days ago: Any settings made in a config_compsets.xml file are not applied to user-compsets (i.e., compsets without an alias). One common variable that is set there is RUN_STARTDATE. This means, for example, that a user-defined HIST compset currently starts in year 0001 rather than year 1850, which I think leads to incorrect behavior.

I think the solution here is to either:

1. Avoid making any settings in config_compsets.xml other than settings that only apply to specific compsets. i.e., matches on a full compset long name are okay (as is done for setting up a refcase, for example), but more general matches are not allowed in this file and must go somewhere else. The driver's config_component seems like a good place for things like RUN_STARTDATE's dependence on the compset time period.

2. ( @mvertens 's tentative suggestion) Parse the primary component's config_compsets.xml file even when using a user-defined compset. Looking through the latest version of case.py, it looks like this could be done by moving this line:

```python
                    self.set_lookup_value("COMPSETS_SPEC_FILE" ,
                                   files.get_value("COMPSETS_SPEC_FILE", {"component":component}, resolved=False))
```

out of `_set_compset` and into `_set_info_from_primary_component`. To be safe, we should also remove the `_compsetsfile` variable from case.py (which is currently unused... if it were used, it would currently be inconsistent for user-defined vs aliased compsets).

It would be easy enough to make the change in (2); I'm just not sure what far-reaching implications it might have....


Discussion:
We ran into this issue (I think) on the POP side... in `config_compsets.xml` we have

```
  <entries>

    <entry id="PRERUN_SCRIPT">
      <values>
	<value compset="POP2%PHYS-CYCLE">$SRCROOT/components/pop/cime_config/phys_cycle_prerun</value>
	<value compset="POP2%ECO%PHYS-CYCLE">$SRCROOT/components/pop/cime_config/phys_cycle_prerun</value>
      </values>
    </entry>

    <entry id="POSTRUN_SCRIPT">
      <values>
	<value compset="POP2%PHYS-CYCLE">$SRCROOT/components/pop/cime_config/phys_cycle_postrun</value>
	<value compset="POP2%ECO%PHYS-CYCLE">$SRCROOT/components/pop/cime_config/phys_cycle_postrun</value>
      </values>
    </entry>

    <entry id="RESUBMIT_SETS_CONTINUE_RUN">
      <values>
	<value compset="POP2%PHYS-CYCLE">FALSE</value>
	<value compset="POP2%ECO%PHYS-CYCLE">FALSE</value>
      </values>
    </entry>

  </entries>
```

We also define one compset to use `PHYS-CYCLE`:

```
  <compset>
    <alias>G1850ECO_CPLHIST_PHYS_CYCLE</alias>
    <lname>1850_DATM%CPLHIST_SLND_CICE_POP2%ECO%PHYS-CYCLE_DROF%CPLHIST_SGLC_WW3</lname>
  </compset>
```

The following commands both set `PRERUN_SCRIPT`, `POSTRUN_SCRIPT`, and `RESUBMIT_SETS_CONTINUE_RUN` to expected values:

```
$ ./create_newcase --compset G1850ECO_CPLHIST_PHYS_CYCLE [other options]
```

```
$ ./create_newcase --compset 1850_DATM%CPLHIST_SLND_CICE_POP2%ECO%PHYS-CYCLE_DROF%CPLHIST_SGLC_WW3 [other options]
```

Note that the long name in the second example is the same compset that the alias `G1850ECO_CPLHIST_PHYS_CYCLE` refers to. However, removing the `%ECO` (so meaning there is no longer an alias associated with the long name) results in empty values for `PRERUN_SCRIPT` and `POSTRUN_SCRIPT` and `RESUBMIT_SETS_CONTINUE_RUN = TRUE`

```
$ ./create_newcase --compset 1850_DATM%CPLHIST_SLND_CICE_POP2%PHYS-CYCLE_DROF%CPLHIST_SGLC_WW3 [other options]
```

This is not an urgent issue for us, but it seemed worthwhile to provide another example of this bug.
My head has been out of this for a little while, but I think the recommended solution in cases like this is to move these settings out of config_compsets.xml into config_component.xml. Is that feasible in this case?
I'll try to look into this soon.
@billsacks -- my understanding is that variables that only exist in `env_*.xml` in specific compsets belong in `config_component.xml` (e.g. `POP_TAVG_R8` only exists in `env_build.xml` if POP is the active ocean component) but changing existing variables (such as `PRERUN_SCRIPT`) is done in `config_compsets.xml`. Looking closer at `config_component.xml`, this may be an incorrect assumption on my part... but I think it makes sense because if [for example] `PRERUN_SCRIPT` gets moved from `env_run.xml` then that requires both a CIME tag (since CIME is currently responsible for dropping `PRERUN_SCRIPT` in `env_run.xml`) and a POP tag (since the old POP tag would be explicitly saying "change `PRERUN_SCRIPT` in `env_run.xml` under these conditions" if it's in `config_component.xml` rather than `config_compset.xml`).

@jedwards4b -- you had asked for step-by-step instructions for recreating the issue:

1. check out `cesm2_0_beta07`
1. update POP to a trunk tag `cesm_pop_2_1_20171117b` or later
   ```
   $ cd cesm2_0_beta07/components/pop/
   $ svn switch https://svn-ccsm-models.cgd.ucar.edu/pop2/trunk_tags/cesm_pop_2_1_20180103
   ```
1. Include `%PHYS-CYCLE` in valid descriptions (sorry, this is a bug in POP that we need to fix)
   ```
   $ svn diff cime_config/config_component.xml
   Index: cime_config/config_component.xml
   ===================================================================
   --- cime_config/config_component.xml	(revision 88413)
   +++ cime_config/config_component.xml	(working copy)
   @@ -2,10 +2,11 @@

    <entry_id version="3.0">
      <description modifier_mode="*">
   -    <desc ocn="POP2[%1D][%ECO][%DAR]"    >POP2 </desc>
   +    <desc ocn="POP2[%1D][%ECO][%DAR][%PHYS-CYCLE]"    >POP2 </desc>
        <desc option="1D" >Single column </desc>
        <desc option="ECO">Ecosystem</desc>
        <desc option="DAR">diagnostic</desc>
   +    <desc option="PHYS-CYCLE">with phys cycle</desc>
      </description>

      <entry id="COMP_OCN">
   ```
1. Create a case with `PRERUN_SCRIPT`, `POSTRUN_SCRIPT`, and `RESUBMIT_SETS_CONTINUE_RUN` set as expected from `config_compsets.xml`
   ```
   $ cd ../../cime/scripts/
   $ ./create_newcase --compset 1850_DATM%CPLHIST_SLND_CICE_POP2%ECO%PHYS-CYCLE_DROF%CPLHIST_SGLC_WW3 --res T62_g17 --case pop_test/good_case --run-unsupported
   ```
1. Remove the alias associated with that long name in POP's `config_compsets.xml`
   ```
   $ cd ../../components/pop
   $ svn diff cime_config/config_compsets.xml
   Index: cime_config/config_compsets.xml
   ===================================================================
   --- cime_config/config_compsets.xml	(revision 88413)
   +++ cime_config/config_compsets.xml	(working copy)
   @@ -94,11 +94,6 @@
        <lname>1850_DATM%CPLHIST_SLND_CICE_POP2%ECO_DROF%CPLHIST_SGLC_WW3</lname>
      </compset>

   -  <compset>
   -    <alias>G1850ECO_CPLHIST_PHYS_CYCLE</alias>
   -    <lname>1850_DATM%CPLHIST_SLND_CICE_POP2%ECO%PHYS-CYCLE_DROF%CPLHIST_SGLC_WW3</lname>
   -  </compset>
   -
      <!-- Single Column POP -->

      <compset>
   ```
1. Create a case with the same long name (same `create_newcase` call as above except changing the case name)
   ```
   $ cd ../../cime/scripts/
   $ ./create_newcase --compset 1850_DATM%CPLHIST_SLND_CICE_POP2%ECO%PHYS-CYCLE_DROF%CPLHIST_SGLC_WW3 --res T62_g17 --case pop_test/bad_case --run-unsupported
   ```
1. Compare the two `env_run.xml` files (I would expect them to be the same, but they aren't):
   ```
   $ cd pop_test/
   $ diff good_case/env_run.xml bad_case/
   249c249
   <     <entry id="RESUBMIT_SETS_CONTINUE_RUN" value="FALSE">
   ---
   >     <entry id="RESUBMIT_SETS_CONTINUE_RUN" value="TRUE">
   1072c1072
   <     <entry id="PRERUN_SCRIPT" value="$SRCROOT/components/pop/cime_config/phys_cycle_prerun">
   ---
   >     <entry id="PRERUN_SCRIPT">
   1076c1076
   <     <entry id="POSTRUN_SCRIPT" value="$SRCROOT/components/pop/cime_config/phys_cycle_postrun">
   ---
   >     <entry id="POSTRUN_SCRIPT">
   ```

The changes to these variables specified in `config_compsets.xml` are only applied if both of the following are true
* the `compset` attribute of `value` matches the active compset, AND
* the active compset has an alias in this file.

It's the second requirement that seems buggy (or, if it's an intentional restriction, should be better documented).
> @billsacks -- my understanding is that variables that only exist in `env_*.xml` in specific compsets belong in `config_component.xml` (e.g. `POP_TAVG_R8` only exists in `env_build.xml` if POP is the active ocean component) but changing existing variables (such as `PRERUN_SCRIPT`) is done in `config_compsets.xml`. Looking closer at `config_component.xml`, this may be an incorrect assumption on my part... but I think it makes sense because if [for example] `PRERUN_SCRIPT` gets moved from `env_run.xml` then that requires both a CIME tag (since CIME is currently responsible for dropping `PRERUN_SCRIPT` in `env_run.xml`) and a POP tag (since the old POP tag would be explicitly saying "change `PRERUN_SCRIPT` in `env_run.xml` under these conditions" if it's in `config_component.xml` rather than `config_compset.xml`).

Good point. I agree with your assessment.

With the current implementation, even with the fix suggested in this issue, I believe these settings would only get picked up for compsets where POP is the primary component (C and G?). Maybe that's what you want, but I just wanted to confirm that.

Resolving patch:
diff --git a/scripts/lib/CIME/case.py b/scripts/lib/CIME/case.py
--- a/scripts/lib/CIME/case.py
+++ b/scripts/lib/CIME/case.py
@@ -93,7 +93,6 @@ def __init__(self, case_root=None, read_only=True):
         self.set_lookup_value('MODEL', self._cime_model)
         self._compsetname = None
         self._gridname = None
-        self._compsetsfile = None
         self._pesfile = None
         self._gridfile = None
         self._components = []
@@ -457,10 +456,8 @@ def _set_compset(self, compset_name, files):
                 compsets = Compsets(compsets_filename)
                 match, compset_alias, science_support = compsets.get_compset_match(name=compset_name)
                 if match is not None:
-                    self._compsetsfile = compsets_filename
                     self._compsetname = match
-                    self.set_lookup_value("COMPSETS_SPEC_FILE" ,
-                                   files.get_value("COMPSETS_SPEC_FILE", {"component":component}, resolved=False))
+                    self.set_lookup_value("COMPSETS_SPEC_FILE", compsets_filename)
                     logger.info("Compset longname is {}".format(match))
                     logger.info("Compset specification file is {}".format(compsets_filename))
                     return compset_alias, science_support
@@ -541,6 +538,11 @@ def _set_info_from_primary_component(self, files, pesfile=None):
 
         component = self.get_primary_component()
 
+        compset_spec_file = files.get_value("COMPSETS_SPEC_FILE",
+                                            {"component":component}, resolved=False)
+
+        self.set_lookup_value("COMPSETS_SPEC_FILE" ,compset_spec_file)
+
         if pesfile is None:
             self._pesfile = files.get_value("PES_SPEC_FILE", {"component":component})
             pesfile_unresolved = files.get_value("PES_SPEC_FILE", {"component":component}, resolved=False)
@@ -631,12 +633,22 @@ def _get_component_config_data(self, files):
         if len(self._component_classes) > len(self._components):
             self._components.append('sesp')
 
+        # will need a change here for new cpl components
+        root_dir_node_name = 'COMP_ROOT_DIR_CPL'
+        comp_root_dir = files.get_value(root_dir_node_name,
+                                        {"component":"drv"}, resolved=False)
+        if comp_root_dir is not None:
+            files.set_value(root_dir_node_name, comp_root_dir)
+            self.set_value(root_dir_node_name, comp_root_dir)
+
+
         for i in range(1,len(self._component_classes)):
             comp_class = self._component_classes[i]
             comp_name  = self._components[i-1]
             root_dir_node_name = 'COMP_ROOT_DIR_' + comp_class
             node_name = 'CONFIG_' + comp_class + '_FILE'
             comp_root_dir = files.get_value(root_dir_node_name, {"component":comp_name}, resolved=False)
+
             if comp_root_dir is not None:
                 # the set_value in files is needed for the archiver setup below
                 files.set_value(root_dir_node_name, comp_root_dir)
@@ -813,6 +825,8 @@ def configure(self, compset_name, grid_name, machine_name=None,
 
         self._set_info_from_primary_component(files, pesfile=pesfile)
 
+        self.clean_up_lookups(allow_undefined=True)
+
         self.get_compset_var_settings()
 
         self.clean_up_lookups(allow_undefined=True)
@@ -981,7 +995,7 @@ def get_compset_var_settings(self):
         matches = compset_obj.get_compset_var_settings(self._compsetname, self._gridname)
         for name, value in matches:
             if len(value) > 0:
-                logger.debug("Compset specific settings: name is {} and value is {}".format(name, value))
+                logger.info("Compset specific settings: name is {} and value is {}".format(name, value))
                 self.set_lookup_value(name, value)
 
     def set_initial_test_values(self):
diff --git a/scripts/lib/CIME/utils.py b/scripts/lib/CIME/utils.py
--- a/scripts/lib/CIME/utils.py
+++ b/scripts/lib/CIME/utils.py
@@ -203,7 +203,8 @@ def get_model():
     # One last try
     if (model is None):
         srcroot = os.path.dirname(os.path.abspath(get_cime_root()))
-        if os.path.isfile(os.path.join(srcroot, "SVN_EXTERNAL_DIRECTORIES")):
+        if os.path.isfile(os.path.join(srcroot, "SVN_EXTERNAL_DIRECTORIES")) \
+           or os.path.isdir(os.path.join(srcroot, "manage_externals")):
             model = 'cesm'
         else:
             model = 'acme'


Test patch: