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

Issue:
BUG Branching Reader fails with uncertainties
## Summary of issue
When reading a file with uncertainties, and the `branching.areUncsPresent` setting is `True`, the code
errors.
Also, I think we can eliminate this setting entirely, because when a file has uncertainties,  the value N still indicates the number of groups/values present without uncertainties.
https://github.com/CORE-GATECH-GROUP/serpent-tools/blob/0e61685f19f644b9bb7a1c2a2b3d8aec30ffcaf5/serpentTools/data/demo.coe#L6-L13
From the [SERPENT wiki](http://serpent.vtt.fi/mediawiki/index.php/Automated_burnup_sequence#Output):
```
The relative statistical errors are not printed by default, but they can be included with an option in the 
set coefpara card, which also defines list of parameters included in the output. In such case the data is 
provided in mean value - statistical error pairs and N gives the number of pairs.
```
 
## Code for reproducing the issue
```
>>> import serpentTools
>>> serpentTools.settings.rc['branching.areUncsPresent'] = True
>>> serpentTools.readDataFile('demo.coe')
```
## Actual outcome including console output and error traceback if applicable
```
Traceback (most recent call last):
  File "test.py", line 3, in <module>
    serpentTools.readDataFile('demo.coe')
  File "/home/ajohnson400/github/my-serpent-tools/serpentTools/data/__init__.py", line 76, in readDataFile
    return read(filePath, **kwargs)
  File "/home/ajohnson400/github/my-serpent-tools/serpentTools/parsers/__init__.py", line 155, in read
    returnedFromLoader.read()
  File "/home/ajohnson400/github/my-serpent-tools/serpentTools/parsers/base.py", line 52, in read
    self._read()
  File "/home/ajohnson400/github/my-serpent-tools/serpentTools/parsers/branching.py", line 42, in _read
    self._processBranchBlock()
  File "/home/ajohnson400/github/my-serpent-tools/serpentTools/parsers/branching.py", line 69, in _processBranchBlock
    self._whereAmI['buIndx'])
  File "/home/ajohnson400/github/my-serpent-tools/serpentTools/parsers/branching.py", line 121, in _processBranchUniverses
    univ.addData(varName, array(vals), uncertaity=False)
TypeError: addData() got an unexpected keyword argument 'uncertaity'
```
## Expected outcome
Code does not fail. Even better, code can infer when the output file has uncertainties or not

## Versions

* Version  from ``serpentTools.__version__`` `0.6.1+55.g0e6185` (develop) and `0.6.1` (release)

* Python version - ``python --version`` `3.7.1`

Pull request:
BUG Branching reader can read uncertainties
Closes #270 by allowing the BranchingReader to parse files with uncertainties. Removed the `branching.areUncsPresent` setting that led to the original bug. Now, the precheck parses the first group constant given and inspects if there are uncertainties or not. For files without uncertainties, the line may be 
https://github.com/CORE-GATECH-GROUP/serpent-tools/blob/0e61685f19f644b9bb7a1c2a2b3d8aec30ffcaf5/serpentTools/data/demo.coe#L6
while with uncertainties, one would see
[`INF_TOT 2 4.92499E-01 0.00074 1.01767E+00 0.00072`](https://github.com/drewejohnson/serpent-tools/blob/dcd39b16d0c0daed7ddae54db33a149f22880418/serpentTools/data/demo_uncs.coe#L6)

Added a new, very small test file with two branches, two group constants, and one universe containing uncertainties.

New attribute `hasUncs` returns True or False if there are uncertainties present

Patch:
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 8301113..062242e 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -31,6 +31,8 @@ Next
   to perturbations, universes, and burnup.
 * Plotting routines now use attach to the active plot or generate
   a new plot figure if ``ax`` argument not given - :issue:`267`
+* :class:`~serpentTools.parsers.branching.BranchingReader` can
+  read coefficient files with uncertainties - :issue:`270`
 
 .. warning::
 
diff --git a/docs/defaultSettings.rst b/docs/defaultSettings.rst
index f3664fd..c7102ec 100644
--- a/docs/defaultSettings.rst
+++ b/docs/defaultSettings.rst
@@ -1,16 +1,3 @@
-.. _branching-areUncsPresent:
-
-----------------------------
-``branching.areUncsPresent``
-----------------------------
-
-True if the values in the .coe file contain uncertainties
-::
-
-  Default: False
-  Type: bool
-  
-
 .. _branching-floatVariables:
 
 ----------------------------
diff --git a/docs/examples/Settings.rst b/docs/examples/Settings.rst
index 852ea39..66e3aba 100644
--- a/docs/examples/Settings.rst
+++ b/docs/examples/Settings.rst
@@ -35,7 +35,7 @@ with the ``.keys`` method.
  
 
     dict_keys(['depletion.processTotal', 'verbosity', 'xs.getInfXS',
-    'branching.intVariables', 'branching.areUncsPresent', 'serpentVersion',
+    'branching.intVariables', 'serpentVersion',
     'sampler.raiseErrors', 'xs.getB1XS', 'xs.variableGroups', 'xs.variableExtras',
     'depletion.materialVariables', 'depletion.metadataKeys', 'sampler.freeAll',
     'sampler.allExist', 'depletion.materials', 'sampler.skipPrecheck',
@@ -206,7 +206,6 @@ However, the loader can also expand a nested dictionary structure, as
 ::
 
     branching:
-      areUncsPresent: False
       floatVariables: [Fhi, Blo]
     depletion:
       materials: [fuel*]
@@ -226,7 +225,6 @@ However, the loader can also expand a nested dictionary structure, as
     xs.variableGroups: [gc-meta, kinetics,
     xs]
     branching:
-      areUncsPresent: False
       floatVariables: [Fhi, Blo]
     depletion:
       materials: [fuel*]
@@ -240,7 +238,7 @@ However, the loader can also expand a nested dictionary structure, as
     
     >>> myConf = 'myConfig.yaml'
     >>> rc.loadYaml(myConf)
-    >>> rc['branching.areUncsPresent']
+    >>> rc['xs.getInfXS']
 
 .. parsed-literal::
  
diff --git a/examples/Branching.ipynb b/examples/Branching.ipynb
index 9967883..5b1a687 100644
--- a/examples/Branching.ipynb
+++ b/examples/Branching.ipynb
@@ -570,7 +570,6 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "* [`branching.areUncsPresent`](http://serpent-tools.readthedocs.io/en/latest/settingsTop.html#branching-areuncspresent)\n",
     "* [`branching.floatVariables`](http://serpent-tools.readthedocs.io/en/latest/settingsTop.html#branching-floatvariables)\n",
     "* [`branching.intVariables`](http://serpent-tools.readthedocs.io/en/latest/settingsTop.html#branching-intvariables)\n",
     "* [`xs.getB1XS`](http://serpent-tools.readthedocs.io/en/latest/settingsTop.html#xs-getb1xs)\n",
diff --git a/examples/Settings.ipynb b/examples/Settings.ipynb
index 385c525..27951fb 100644
--- a/examples/Settings.ipynb
+++ b/examples/Settings.ipynb
@@ -50,7 +50,7 @@
     {
      "data": {
       "text/plain": [
-       "dict_keys(['depletion.processTotal', 'verbosity', 'xs.getInfXS', 'branching.intVariables', 'branching.areUncsPresent', 'serpentVersion', 'sampler.raiseErrors', 'xs.getB1XS', 'xs.variableGroups', 'xs.variableExtras', 'depletion.materialVariables', 'depletion.metadataKeys', 'sampler.freeAll', 'sampler.allExist', 'depletion.materials', 'sampler.skipPrecheck', 'xs.reshapeScatter', 'branching.floatVariables', 'detector.names'])"
+       "dict_keys(['depletion.processTotal', 'verbosity', 'xs.getInfXS', 'branching.intVariables', 'serpentVersion', 'sampler.raiseErrors', 'xs.getB1XS', 'xs.variableGroups', 'xs.variableExtras', 'depletion.materialVariables', 'depletion.metadataKeys', 'sampler.freeAll', 'sampler.allExist', 'depletion.materials', 'sampler.skipPrecheck', 'xs.reshapeScatter', 'branching.floatVariables', 'detector.names'])"
       ]
      },
      "execution_count": 13,
@@ -264,7 +264,6 @@
     "However, the loader can also expand a nested dictionary structure, as\n",
     "```\n",
     "branching:\n",
-    "  areUncsPresent: False\n",
     "  floatVariables: [Fhi, Blo]\n",
     "depletion:\n",
     "  materials: [fuel*]\n",
@@ -286,7 +285,6 @@
       "xs.getB1XS: True\r\n",
       "xs.variableGroups: [gc-meta, kinetics, xs]\r\n",
       "branching:\r\n",
-      "  areUncsPresent: False\r\n",
       "  floatVariables: [Fhi, Blo]\r\n",
       "depletion:\r\n",
       "  materials: [fuel*]\r\n",
@@ -327,7 +325,7 @@
    "source": [
     "myConf = 'myConfig.yaml'\n",
     "rc.loadYaml(myConf)\n",
-    "rc['branching.areUncsPresent']"
+    "rc['xs.getInfXS']"
    ]
   }
  ],
diff --git a/examples/myConfig.yaml b/examples/myConfig.yaml
index 8953d66..523fa61 100644
--- a/examples/myConfig.yaml
+++ b/examples/myConfig.yaml
@@ -2,7 +2,6 @@ xs.getInfXS: False
 xs.getB1XS: True
 xs.variableGroups: [gc-meta, kinetics, xs]
 branching:
-  areUncsPresent: False
   floatVariables: [Fhi, Blo]
 depletion:
   materials: [fuel*]
diff --git a/serpentTools/data/demo_uncs.coe b/serpentTools/data/demo_uncs.coe
new file mode 100644
index 0000000..256c7d0
--- /dev/null
+++ b/serpentTools/data/demo_uncs.coe
@@ -0,0 +1,14 @@
+1 2 1 2 1
+1 nom
+3 VERSION 2.1.30 DATE 19/02/07 TIME 15:27:13
+0 1 1
+0 2
+INF_TOT 2  4.92499E-01 0.00074  1.01767E+00 0.00072
+INF_S0 4  4.66391E-01 0.00072  1.55562E-02 0.00148  1.28821E-03 0.01793  9.21487E-01 0.00091
+2 2 2 2 1
+1 hot
+3 VERSION 2.1.30 DATE 19/02/07 TIME 15:27:55
+0 1 1
+0 2
+INF_TOT 2  4.92806E-01 0.00063  1.01680E+00 0.00104
+INF_S0 4  4.66639E-01 0.00062  1.53978E-02 0.00214  1.38727E-03 0.03073  9.20270E-01 0.00112
diff --git a/serpentTools/parsers/branching.py b/serpentTools/parsers/branching.py
index daaa8f3..8ba6fb4 100644
--- a/serpentTools/parsers/branching.py
+++ b/serpentTools/parsers/branching.py
@@ -33,6 +33,12 @@ class BranchingReader(XSReader):
         self._whereAmI = {key: None for key in
                           {'runIndx', 'coefIndx', 'buIndx', 'universe'}}
         self._totalBranches = None
+        self._hasUncs = None
+
+    @property
+    def hasUncs(self):
+        """boolean if uncertainties are present in the file"""
+        return self._hasUncs
 
     def _read(self):
         """Read the branching file and store the coefficients."""
@@ -116,10 +122,10 @@ class BranchingReader(XSReader):
                       .format(varName))
                 continue
             if self._checkAddVariable(varName):
-                if self.settings['areUncsPresent']:
+                if self._hasUncs:
                     vals, uncs = splitValsUncs(varValues)
-                    univ.addData(varName, array(vals), uncertaity=False)
-                    univ.addData(varName, array(uncs), unertainty=True)
+                    univ.addData(varName, array(vals), uncertainty=False)
+                    univ.addData(varName, array(uncs), uncertainty=True)
                 else:
                     univ.addData(varName, array(varValues), uncertainty=False)
 
@@ -129,13 +135,19 @@ class BranchingReader(XSReader):
             yield bID, b
 
     def _precheck(self):
-        """Currently, just grabs total number of coeff calcs."""
+        """Total number of branches and check for uncertainties"""
         with open(self.filePath) as fObj:
             try:
                 self._totalBranches = int(fObj.readline().split()[1])
             except Exception as ee:
                 error("COE output at {} likely malformatted or misnamed\n{}"
                       .format(self.filePath, str(ee)))
+            # skip first few lines with integer until first named value
+            for _x in range(4):
+                fObj.readline()
+            lsplt = fObj.readline().split()
+            nvals = int(lsplt[1])
+            self._hasUncs = not nvals == len(lsplt) - 2
 
     def _postcheck(self):
         """Make sure Serpent finished printing output."""
diff --git a/serpentTools/settings.py b/serpentTools/settings.py
index 4a2b414..7954ee5 100644
--- a/serpentTools/settings.py
+++ b/serpentTools/settings.py
@@ -28,12 +28,6 @@ SETTING_DOC_FMTR = """.. _{tag}:
 
 SETTING_OPTIONS_FMTR = "Options: [{}]"
 defaultSettings = {
-    'branching.areUncsPresent': {
-        'default': False,
-        'type': bool,
-        'description': "True if the values in the .coe file contain "
-                       "uncertainties"
-    },
     'branching.intVariables': {
         'default': [],
         'description': 'Name of state data variables to convert to integers '


Test patch:
diff --git a/serpentTools/tests/test_branching.py b/serpentTools/tests/test_branching.py
index 8f60fb0..9780cd4 100644
--- a/serpentTools/tests/test_branching.py
+++ b/serpentTools/tests/test_branching.py
@@ -50,12 +50,13 @@ class BranchTester(_BranchTesterHelper):
                     'CMM_TRANSPXS_Z', 'CMM_DIFFCOEF', 'CMM_DIFFCOEF_X',
                     'CMM_DIFFCOEF_Y', 'CMM_DIFFCOEF_Z'}
         self.assertSetEqual(expected, self.reader.settings['variables'])
+        self.assertFalse(self.reader.hasUncs)
 
     def test_raiseError(self):
         """Verify that the reader raises an error for unexpected EOF"""
         badFile = 'bad_branching.coe'
         with open(self.file) as fObj, open(badFile, 'w') as badObj:
-            for _line in range(5):
+            for _line in range(10):
                 badObj.write(fObj.readline())
         badReader = BranchingReader(badFile)
         with self.assertRaises(EOFError):
@@ -123,5 +124,53 @@ class BranchContainerTester(_BranchTesterHelper):
             containerWithDays.addUniverse(101, 10, 1)
 
 
+class BranchWithUncsTester(unittest.TestCase):
+    """Tester that just tests a small file with uncertainties"""
+
+    BRANCH_DATA = {
+        'nom': {
+            'infTot': [
+                [4.92499E-01, 1.01767E+00],  # value
+                [0.00074, 0.00072],  # uncertainty
+            ],
+            'infS0': [
+                [0.466391, 0.0155562, 0.00128821, 0.921487],
+                [0.00072, 0.00148, 0.01793, 0.00091],
+            ],
+        },
+        'hot': {
+            'infTot': [
+                [4.92806E-1, 1.0168E00],  # value
+                [0.00063, 0.00104],  # uncertainty
+            ],
+            'infS0': [
+                [4.66639E-1, 1.53978E-02, 1.38727E-03, 9.20270E-01],
+                [0.00062, 0.00214, 0.03073, 0.00112],
+            ],
+        },
+    }
+    BRANCH_UNIVKEY = (0, 0, 0)
+
+    def setUp(self):
+        fp = getFile('demo_uncs.coe')
+        self.reader = BranchingReader(fp)
+        self.reader.read()
+
+    def test_valsWithUncs(self):
+        """Test that the branches and the uncertainties are present"""
+        self.assertTrue(self.reader.hasUncs)
+        for expKey, expSubData in iteritems(self.BRANCH_DATA):
+            self.assertTrue(expKey in self.reader.branches, msg=expKey)
+            actBranch = self.reader.branches[expKey]
+            self.assertTrue(self.BRANCH_UNIVKEY in actBranch.universes,
+                            msg=self.BRANCH_UNIVKEY)
+            univ = actBranch.universes[self.BRANCH_UNIVKEY]
+            for gcKey, gcVals in iteritems(expSubData):
+                actData = univ.get(gcKey, True)
+                for act, exp, what in zip(actData, gcVals, ['val', 'unc']):
+                    assert_allclose(act, exp, err_msg='{} {} {}'.format(
+                        expKey, gcKey, what))
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/serpentTools/tests/test_settings.py b/serpentTools/tests/test_settings.py
index b4d60a2..22671b9 100644
--- a/serpentTools/tests/test_settings.py
+++ b/serpentTools/tests/test_settings.py
@@ -112,7 +112,6 @@ class ConfigLoaderTester(TestCaseWithLogCapture):
     @classmethod
     def setUpClass(cls):
         cls.configSettings = {
-            'branching.areUncsPresent': True,
             'branching.floatVariables': ['Bhi', 'Tlo'],
             'verbosity': 'warning',
             'depletion.materials': ['fuel*'],
@@ -120,8 +119,6 @@ class ConfigLoaderTester(TestCaseWithLogCapture):
         }
         cls.nestedSettings = {
             'branching': {
-                'areUncsPresent':
-                    cls.configSettings['branching.areUncsPresent'],
                 'floatVariables':
                     cls.configSettings['branching.floatVariables']
             },