Repository: CORE-GATECH-GROUP/serpent-tools (python)

Issue:
BUG Burnup and day values wrong for results with multiple gcu, no burnup
## Summary of issue
When reading a result file with multiple homogenized universes and no burnup, the homogenized universes have incorrect values of burnup and days. 

## Code for reproducing the issue
Reading a file with four homogenized universes, (50, 60, 70, 80) and no burnup
```
>>> r = serpentTools.read("core_res.m")
>>> r.universes.keys()
dict_keys([
    UnivTuple(universe='50', burnup=0, step=0, days=0), 
    UnivTuple(universe='60', burnup=1, step=0, days=1), 
    UnivTuple(universe='70', burnup=2, step=0, days=2), 
    UnivTuple(universe='80', burnup=3, step=0, days=3)])
```
## Expected outcome
All values of `burnup` and `days` should be zero. The bug exists on the develop branch and master branch [0.7.1] but the keys are plain tuples.

## Versions

* Version  from ``serpentTools.__version__`` `0.7.1-48-g09a5fea` and `0.7.1`
* Python version - ``python --version`` 3.7.4

Pull request description:
BUG Properly set burnup, days for universes without burnup
This PR closes #345 by setting the value of burnup and days to be zero if no burnup / burn_day data has been found so far. In the current versions we support, the BURNUP and BURN_DAY vectors are created/appended before we hit the GC_UNIVERSE block. Previously, the burnup/days was taken to be the number of "metadata" blocks, so the first universe would be at day 0, second at day 1, third at day 2, etc [as seen from the referenced issue]. 

Commit f90035b introduces a test that, as a standalone commit, verifies the issue exists. Once the fix in added in 7dd8840 , the test ensures the fix works.

Subsequent commits do some cleanup on the temporary variables the results reader needs to determine file location. These temporary variables are all discarded as a part of the postcheck process.

Patch:
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 2c68415..5e70766 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -13,6 +13,12 @@ Next
 * |HomogUniv| objects no longer automatically convert data to arrays
 * Serpent 2.1.31 is the default version for :ref:`serpentVersion` setting
 
+Bug Fixes
+---------
+
+* Burnup and days are properly set on homogenized universes when reading a
+  result file with multiple universes but no burnup
+
 Incompatible API Changes
 ------------------------
 
diff --git a/serpentTools/parsers/results.py b/serpentTools/parsers/results.py
index d9f83ce..796a7ff 100644
--- a/serpentTools/parsers/results.py
+++ b/serpentTools/parsers/results.py
@@ -45,7 +45,7 @@ MapStrVersions = {
     '2.1.29': {
         'meta': 'VERSION ', 'rslt': 'MIN_MACROXS',
         'univ': 'GC_UNIVERSE_NAME', 'days': 'BURN_DAYS',
-        'burn': 'BURNUP', 'infxs': 'INF_', 'b1xs': 'B1_',
+        'burnup': 'BURNUP', 'infxs': 'INF_', 'b1xs': 'B1_',
         'varsUnc': ['MICRO_NG', 'MICRO_E', 'MACRO_NG', 'MACRO_E'],
     },
 }
@@ -204,15 +204,22 @@ class ResultsReader(XSReader):
 
     def __init__(self, filePath):
         XSReader.__init__(self, filePath, 'results')
-        self.__serpentVersion = rc['serpentVersion']
+
+        self.metadata = {}
+        self.resdata = {}
+        self.universes = {}
+
+    def _read(self):
+        """Read through the results file and store requested data."""
+
         self._counter = {'meta': 0, 'rslt': 0}
-        self._numUniverses = 0
         self._univlist = []
-        self.metadata, self.resdata, self.universes = {}, {}, {}
         self._varTypeLookup = {}
 
-    def _read(self):
-        """Read through the results file and store requested data."""
+        self.metadata.clear()
+        self.resdata.clear()
+        self.universes.clear()
+
         with open(self.filePath, 'r') as fObject:
             for tline in fObject:
                 self._processResults(tline)
@@ -303,22 +310,24 @@ class ResultsReader(XSReader):
     def _getBUstate(self):
         """Define unique branch state"""
         burnIdx = self._counter['rslt'] - 1
-        varPyDays = convertVariableName(self._keysVersion['days'])  # Py style
-        varPyBU = convertVariableName(self._keysVersion['burn'])
-        if varPyDays in self.resdata.keys():
-            if burnIdx > 0:
-                days = self.resdata[varPyDays][-1, 0]
-            else:
-                days = self.resdata[varPyDays][-1]
+        dayVec = self.resdata.get(self._burnupKeys["days"])
+
+        if dayVec is None:
+            days = 0
+        elif burnIdx:
+            days = dayVec[-1, 0]
         else:
-            days = self._counter['meta'] - 1
-        if varPyBU in self.resdata.keys():
-            if burnIdx > 0:
-                burnup = self.resdata[varPyBU][-1, 0]
-            else:
-                burnup = self.resdata[varPyBU][-1]
+            days = dayVec[0]
+
+        burnupVec = self.resdata.get(self._burnupKeys["burnup"])
+
+        if burnupVec is None:
+            burnup = 0
+        elif burnIdx:
+            burnup = burnupVec[-1, 0]
         else:
-            burnup = self._counter['meta'] - 1
+            burnup = burnupVec[-1]
+
         return UnivTuple(self._univlist[-1], burnup, burnIdx, days)
 
     def _getVarName(self, tline):
@@ -397,13 +406,20 @@ class ResultsReader(XSReader):
 
     def _precheck(self):
         """do a quick scan to ensure this looks like a results file."""
-        if self.__serpentVersion in MapStrVersions:
-            self._keysVersion = MapStrVersions[self.__serpentVersion]
-        else:
+        serpentV = rc['serpentVersion']
+        keys = MapStrVersions.get(serpentV)
+
+        if keys is None:
             warning("SERPENT {} is not supported by the "
-                    "ResultsReader".format(self.__serpentVersion))
+                    "ResultsReader".format(serpentV))
             warning("  Attemping to read anyway. Please report strange "
                     "behaviors/failures to developers.")
+            keys = MapStrVersions[max(MapStrVersions)]
+
+        self._keysVersion = keys
+
+        self._burnupKeys = {k: convertVariableName(keys[k]) for k in {"days", "burnup"}}
+
         univSet = set()
         verWarning = True
         with open(self.filePath) as fid:
@@ -415,11 +431,11 @@ class ResultsReader(XSReader):
                 if verWarning and self._keysVersion['meta'] in tline:
                     verWarning = False
                     varType, varVals = self._getVarValues(tline)  # version
-                    if self.__serpentVersion not in varVals:
+                    if serpentV not in varVals:
                         warning("SERPENT {} found in {}, but version {} is "
                                 "defined in settings"
                                 .format(varVals, self.filePath,
-                                        self.__serpentVersion))
+                                        serpentV))
                         warning("  Attemping to read anyway. Please report "
                                 "strange behaviors/failures to developers.")
                 if self._keysVersion['univ'] in tline:
@@ -451,8 +467,8 @@ class ResultsReader(XSReader):
     def _postcheck(self):
         self._inspectData()
         self._cleanMetadata()
-        del self._varTypeLookup
-        self._univList = []
+        del (self._varTypeLookup, self._burnupKeys, self._keysVersion,
+             self._counter, self._univlist)
 
     def _compare(self, other, lower, upper, sigma):
         similar = self.compareMetadata(other)


Test patch:
diff --git a/tests/test_results.py b/tests/test_results.py
new file mode 100644
index 0000000..057debd
--- /dev/null
+++ b/tests/test_results.py
@@ -0,0 +1,40 @@
+import re
+import os
+
+import pytest
+import serpentTools
+
+
+@pytest.fixture
+def multipleGcuNoBu():
+    origFile = serpentTools.data.getFile("InnerAssembly_res.m")
+    newFile = "MultipleGcuNoBu_res.m"
+    nGcu = 3
+    counter = re.compile("Increase counter")
+    burnKey = re.compile("BURN")
+    counts = 0
+
+    with open(origFile, "r") as orig, open(newFile, "w") as new:
+        for line in orig:
+            if burnKey.match(line):
+                continue
+            if counter.search(line):
+                counts += 1
+            if counts > nGcu:
+                break
+            new.write(line)
+
+    with serpentTools.settings.rc as temprc:
+        temprc["serpentVersion"] = "2.1.30"
+        yield newFile
+
+    os.remove(newFile)
+
+
+def test_multipleGcuNoBu(multipleGcuNoBu):
+    r = serpentTools.read(multipleGcuNoBu)
+    assert len(r.universes) == 3
+    for key in r.universes:
+        assert key.step == 0
+        assert key.burnup == 0
+        assert key.days == 0