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

Issue:
Can't Parse Cross Section Data From Serpent 2.1.32
## Summary of issue
I am getting `ValueError: Unidentifiable entry` when trying to load my `*_xs0.m` file, because `serpentTools` doesn't know what to do with entries suffixed by `pspec`.  These appear to be new to Serpent 2.1.32, and the code runs fine when I manually remove all entries suffixed by `pspec`.  However, the code still exits non-zero when I turn off raising errors and attempt to skip the pre-check with the `pspec` entries still there.

## Code for reproducing the issue
Obviously, the path is different, and the file I've taken straight from Serpent is [main.input.inp_xs0.m.txt](https://github.com/CORE-GATECH-GROUP/serpent-tools/files/6780212/main.input.inp_xs0.m.txt).  Also, I had to add the `*.txt` extension so that GitHub would allow me to upload.
```
import serpentTools
serpentTools.settings.rc["sampler.raiseErrors"] = False
serpentTools.settings.rc["sampler.skipPrecheck"] = True
serpentTools.settings.rc["serpentVersion"] = '2.1.32'
xsreader = serpentTools.read('/home/ec2-user/PyCharm_KACEGEN/test/serpent/endfb71_mcnp/main.input.inp_xs0.m')
```

## Actual outcome including console output and error traceback if applicable
```
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/serpentTools-0.9.4rc0-py3.7.egg/serpentTools/parsers/xsplot.py", line 149, in _read
    raise ValueError("Unidentifiable entry\n{}".format(chunk))
ValueError: Unidentifiable entry
['i36085_82c_pspec = [\n', '5.13997E-01 4.34000E-03\n', '1.33539E-02 1.02414E-05\n', '1.32919E-02 5.32352E-06\n', '1.49217E-02 1.48786E-06\n', '1.49123E-02 7.63269E-07\n', '1.68737E-03 4.61757E-07\n', '1.75360E-03 2.45265E-07\n', '1.51392E-02 1.60258E-07\n', '1.50285E-03 1.33284E-07\n', '1.51383E-02 8.31602E-08\n', '1.51180E-01 2.17000E-08\n', '3.62810E-01 2.17000E-08\n', '1.94166E-03 4.43158E-09\n', '1.50414E-02 3.50630E-09\n', '1.50398E-02 2.45196E-09\n', '1.29810E-01 2.17000E-09\n']

```
## Expected outcome
I expected to be able to follow [the tutorial](https://serpent-tools.readthedocs.io/en/master/examples/XSPlot.html).

## Versions
* Serpent 2.1.32
* serpentTools 0.9.4rc0
* Python 3.7.6


Pull request:
BUG Support more values like psepc on xsplot reader
xsplot objects now have a `misc` dictionary that contains data
we don't store directly on attributes. The primary use for this is
reading photon line spectrum, `pspec` entries from xsplot files.
These fields can be controlled by the `_misc` "private" attribute
on the xsplotreader if more rapid turnaround is needed by users.

Tests have been added to demonstrate that the reader is reading
these data and that a warning is raised for unidentified fields.

Closes #454

Patch:
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 5f652a4..5da1ed1 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -16,6 +16,10 @@ Changelog
 
 .. _v0.9.4:
 
+0.9.4rc1
+========
+* Support for reading ``pspec`` fields in xsplot files
+
 :release-tag:`0.9.4rc0`
 ====================
 
diff --git a/serpentTools/data/plut_xs0.m b/serpentTools/data/plut_xs0.m
index f7f5610..c06b1b2 100644
--- a/serpentTools/data/plut_xs0.m
+++ b/serpentTools/data/plut_xs0.m
@@ -277,3 +277,35 @@ majorant_xs = [
 2.93887E-01
 ];
 
+i94239_03c_pspec = [
+1.36082E-02 1.47639E-02
+3.27184E-03 5.25321E-03
+1.64140E-02 3.43399E-03
+1.37229E-02 1.50625E-03
+1.29750E-02 3.69478E-04
+5.16240E-02 2.72200E-04
+3.86610E-02 1.04400E-04
+1.29296E-01 6.31000E-05
+9.89280E-02 5.69788E-05
+3.75054E-01 1.55400E-05
+4.13713E-01 1.46600E-05
+9.87800E-02 1.46500E-05
+1.11820E-01 1.29767E-05
+5.68280E-02 1.15200E-05
+1.10928E-01 6.61565E-06
+2.03550E-01 5.69000E-06
+9.92700E-01 2.70000E-10
+8.16000E-01 2.40000E-10
+7.63600E-01 2.20000E-10
+9.86900E-01 2.10000E-10
+6.93200E-01 2.00000E-10
+7.92900E-01 2.00000E-10
+4.12300E-01 1.80000E-10
+1.00570E+00 1.80000E-10
+7.62600E-01 1.00000E-10
+6.70800E-01 9.00000E-11
+6.70990E-01 9.00000E-11
+9.18700E-01 8.40000E-11
+1.72560E-01 3.00000E-11
+1.23228E-01 1.60000E-11
+];
diff --git a/serpentTools/objects/xsdata.py b/serpentTools/objects/xsdata.py
index 96cad3b..ea1429a 100644
--- a/serpentTools/objects/xsdata.py
+++ b/serpentTools/objects/xsdata.py
@@ -44,6 +44,9 @@ class XSData(NamedObject):
     metadata : dict
         File-wide metadata from the reader. Alias for accessing
         :attr:`energies`. Will be removed in the future
+    misc : dict of str to numpy.ndarray
+        Other arrays that do not have a dedicated attribute, e.g.,
+        ``pspec`` for photon line spectrum
 
     """
 
@@ -89,6 +92,8 @@ class XSData(NamedObject):
         # whether nu data present for fissionables
         self.hasNuData = False
 
+        self.misc = {}
+
     def __len__(self):
         """Number of reactions stored"""
         return len(self.MT)
diff --git a/serpentTools/parsers/xsplot.py b/serpentTools/parsers/xsplot.py
index 62927ad..3b5b30c 100644
--- a/serpentTools/parsers/xsplot.py
+++ b/serpentTools/parsers/xsplot.py
@@ -1,4 +1,7 @@
 """Parser responsible for reading the ``*_xsplot.m`` files"""
+import warnings
+import re
+
 from numpy import array, float64
 
 from serpentTools.messages import error, warning
@@ -44,6 +47,7 @@ class XSPlotReader(BaseReader):
         likely be removed in the future
 
     """
+    _misc = {"pspec", }
 
     def __init__(self, filePath):
         super().__init__(filePath, 'xsplot')
@@ -146,7 +150,22 @@ class XSPlotReader(BaseReader):
                     self.xsections[xsname].setData(chunk)
 
                 else:
-                    raise ValueError("Unidentifiable entry\n{}".format(chunk))
+                    self._processMisc(lead, data)
+
+    def _processMisc(self, lead, data):
+        variable = lead.split()[0]
+        for key in self._misc:
+            start = variable.find(key)
+            if start == -1:
+                continue
+            xsname = re.sub("_$", "", variable[:start])
+            cleaned = list(map(str.split, data))
+            self.xsections[xsname].misc[key] = array(cleaned, dtype=float)
+            return
+
+        warnings.warn(
+            "Unidentifiable entry in {}: {}".format(self.filePath, lead)
+        )
 
     def _precheck(self):
         """do a quick scan to ensure this looks like a xsplot file."""


Test patch:
diff --git a/tests/test_xsplot.py b/tests/test_xsplot.py
index b87a39c..1ae5a6c 100644
--- a/tests/test_xsplot.py
+++ b/tests/test_xsplot.py
@@ -1,12 +1,14 @@
 """Test the xsplot reader."""
 import unittest
 from numpy import ndarray, array, newaxis
-
 from numpy.testing import assert_array_equal
+import pytest
 
 from serpentTools.parsers.xsplot import XSPlotReader
 from serpentTools.data import getFile
 
+DATA_FILE = getFile("plut_xs0.m")
+
 
 def findDiff(d1, d2, path=""):
     """ Diffs two dictionaries with nested structure
@@ -48,8 +50,7 @@ class XSPlotTester(unittest.TestCase):
 
     @classmethod
     def setUpClass(cls):
-        cls.file = getFile('plut_xs0.m')
-        cls.reader = XSPlotReader(cls.file)
+        cls.reader = XSPlotReader(DATA_FILE)
         cls.reader.read()
 
     def test1_allXS(self):
@@ -782,3 +783,59 @@ class XSPlotTester(unittest.TestCase):
 """
         actual = self.reader.xsections['mfissile'].showMT(retstring=True)
         self.assertEqual(refString.rstrip(), actual.rstrip())
+
+
+def test_goodMisc():
+    reader = XSPlotReader(DATA_FILE)
+    reader.read()
+    pu = reader["i94239_03c"]
+    assert "pspec" in pu.misc
+    actualPspec = array(
+        [
+            [1.36082E-02, 1.47639E-02],
+            [3.27184E-03, 5.25321E-03],
+            [1.64140E-02, 3.43399E-03],
+            [1.37229E-02, 1.50625E-03],
+            [1.29750E-02, 3.69478E-04],
+            [5.16240E-02, 2.72200E-04],
+            [3.86610E-02, 1.04400E-04],
+            [1.29296E-01, 6.31000E-05],
+            [9.89280E-02, 5.69788E-05],
+            [3.75054E-01, 1.55400E-05],
+            [4.13713E-01, 1.46600E-05],
+            [9.87800E-02, 1.46500E-05],
+            [1.11820E-01, 1.29767E-05],
+            [5.68280E-02, 1.15200E-05],
+            [1.10928E-01, 6.61565E-06],
+            [2.03550E-01, 5.69000E-06],
+            [9.92700E-01, 2.70000E-10],
+            [8.16000E-01, 2.40000E-10],
+            [7.63600E-01, 2.20000E-10],
+            [9.86900E-01, 2.10000E-10],
+            [6.93200E-01, 2.00000E-10],
+            [7.92900E-01, 2.00000E-10],
+            [4.12300E-01, 1.80000E-10],
+            [1.00570E+00, 1.80000E-10],
+            [7.62600E-01, 1.00000E-10],
+            [6.70800E-01, 9.00000E-11],
+            [6.70990E-01, 9.00000E-11],
+            [9.18700E-01, 8.40000E-11],
+            [1.72560E-01, 3.00000E-11],
+            [1.23228E-01, 1.60000E-11],
+        ]
+    )
+    assert pu.misc["pspec"] == pytest.approx(actualPspec, rel=0, abs=0)
+
+
+@pytest.fixture
+def xsreaderNoPspec():
+    reader = XSPlotReader(DATA_FILE)
+    reader._misc.difference_update({"pspec", })
+    return reader
+
+
+def test_missingPspec(xsreaderNoPspec):
+    with pytest.warns(UserWarning, match="pspec"):
+        xsreaderNoPspec.read()
+    pu = xsreaderNoPspec["i94239_03c"]
+    assert "pspec" not in pu.misc