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

Issue:
Support: Homogenized Universe Object
Implement a supporting object that will be used by the branching reader and results reader that stores data for a single homogenized universe at a single instance in time.

## Requirements
1. Should store the universe number, and, optionally, day, burnup, and burnup step for this instance. Optional because for a problem with no burnup, those values will not be present in the file.
1. Simple methods to add and get variable data, that will be sent from the readers.

## Optional
1. Output the data with some template engine like [Jinja2](http://jinja.pocoo.org/) or [Mako](http://www.makotemplates.org/)

## Holding up
#6
#7 

Pull request:
Homogenized Universe
Container for storing and getting data.
Closes #11 

Patch:
diff --git a/serpentTools/objects/containers.py b/serpentTools/objects/containers.py
new file mode 100644
index 0000000..ff8f941
--- /dev/null
+++ b/serpentTools/objects/containers.py
@@ -0,0 +1,144 @@
+"""
+Supporting object that will be used by the branching reader and
+results reader to store data for a single homogenized universe at a single
+instance in time.
+"""
+
+from serpentTools.objects import SupportingObject
+from serpentTools.settings import messages
+
+
+class HomogUniv(SupportingObject):
+    """
+    Class for storing homogenized universe specifications and retrieving them
+
+     Parameters
+    ----------
+    -container: str
+        name of the parser
+    -name: str
+        name of the universe
+    -bu: float
+        burnup value
+    -step: float
+        temporal step
+    -day: float
+        depletion day
+
+    Attributes
+    ----------
+    -name: str
+        name of the universe
+    -bu: float
+        burnup value
+    -step: float
+        temporal step
+    -day: float
+        depletion day
+
+    """
+
+    def __init__(self, container, name, bu, step, day):
+        SupportingObject.__init__(self, container)
+        self.name = name
+        self.bu = bu
+        self.step = step
+        self.day = day
+        # Dictionaries:
+        self.b1Exp = {}
+        self.infExp = {}
+        self.b1Unc = {}
+        self.infUnc = {}
+        self.metaData = {}
+
+    def addData(self, variableName, variableValue, uncertainty=False):
+        """
+        Sets the value of the variable and, optionally, the associate s.d.
+
+        Parameters
+        ----------
+        variableName: str
+            Variable Name
+        variableValue: str/tuple/list
+            Variable Value
+        uncertainty:   bool
+            Boolean Variable- set to True in order to retrieve the
+            uncertainty associated to the expected values
+
+        Notes:
+        ---------
+            Raises a warning if the value of the variable is overwritten
+
+        """
+
+        # 1. Check the input type
+        variableName = SupportingObject._convertVariableName(variableName)
+        if not isinstance(uncertainty, bool):
+            raise TypeError('The variable uncertainty has type %s.\n ...'
+                                 'It should be boolean.', type(uncertainty))
+        # 2. Pointer to the proper dictionary
+        setter = self._lookup(variableName, uncertainty)
+        # 3. Check if variable is already present. Then set the variable.
+        if variableName in setter:
+            messages.warning(
+                "The variable {} will be overwritten".format(variableName))
+        setter[variableName] = variableValue
+
+    def get(self, variableName, uncertainty=False):
+        """
+        Gets the value of the variable VariableName from the dictionaries
+
+        Parameters
+        ----------
+        variableName: str
+            Variable Name
+        uncertainty:   bool
+            Boolean Variable- set to True in order to retrieve the
+            uncertainty associated to the expected values
+
+        Returns
+        -------
+        x: str/tuple/list
+            Variable Value
+        dx: str/tuple/list
+            Associated uncertainty
+
+        """
+        # 1. Check the input values
+        if not isinstance(uncertainty, bool):
+            raise TypeError('The variable uncertainty has type %s.\n ...'
+                                 'It should be boolean.', type(uncertainty))
+        # 2. Pointer to the proper dictionary
+        setter = self._lookup(variableName, False)
+        if variableName not in setter:
+            raise KeyError(
+                "Variable {} absent from expected value dictionary".format(
+                    variableName))
+        x = setter.get(variableName)
+        # 3. Return the value of the variable
+        if not uncertainty:
+            return x
+        if setter is self.metaData:
+            messages.warning('No uncertainty is associated to metadata')
+            return x
+        setter = self._lookup(variableName, True)
+        if variableName not in setter:
+            raise KeyError(
+                "Variable {} absent from uncertainty dictionary".format(
+                    variableName))
+        dx = setter.get(variableName)
+        return x, dx
+
+    def _lookup(self, variableName, uncertainty):
+        if "inf" in variableName:
+            if not uncertainty:
+                return self.infExp
+            else:
+                return self.infUnc
+        elif "b1" in variableName:
+            if not uncertainty:
+                return self.b1Exp
+            else:
+                return self.b1Unc
+        else:
+            return self.metaData


Test patch:
diff --git a/serpentTools/tests/test_branching.py b/serpentTools/tests/test_branching.py
index 0d4881d..6693d98 100644
--- a/serpentTools/tests/test_branching.py
+++ b/serpentTools/tests/test_branching.py
@@ -11,7 +11,7 @@ REF_FILE = os.path.join(TEST_ROOT, 'ref_branch.txt')
 
 
 class BranchTester(unittest.TestCase):
-    """Classs for testing the branching coefficient file reader."""
+    """Class for testing the branching coefficient file reader."""
 
     @classmethod
     def setUpClass(cls):
diff --git a/serpentTools/tests/test_container.py b/serpentTools/tests/test_container.py
new file mode 100644
index 0000000..32321f2
--- /dev/null
+++ b/serpentTools/tests/test_container.py
@@ -0,0 +1,86 @@
+"""Test the container object. """
+
+import unittest
+from serpentTools.objects import containers
+from serpentTools.parsers import DepletionReader
+
+
+class HomogenizedUniverseTester(unittest.TestCase):
+    """ Class to test the Homogenized Universe """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.univ = containers.HomogUniv(DepletionReader(None), 'dummy', 0, 0, 0)
+        cls.Exp = {}
+        cls.Unc = {}
+        # Data definition
+        cls.Exp = {'B1_1': 1, 'B1_2': [1, 2], 'B1_3': [1, 2, 3],
+                   'INF_1': 3, 'INF_2': [4, 5], 'INF_3': [6, 7, 8],
+                   'MACRO_E': (.1, .2, .3, .1, .2, .3), 'Test_1': 'ciao'}
+        cls.Unc = {'B1_1': 1e-1, 'B1_2': [1e-1, 2e-1], 'B1_3': [1e-1, 2e-1,
+                                                                3e-1],
+                   'INF_1': 3e-1, 'INF_2': [4e-1, 5e-1], 'INF_3': [6e-1, 7e-1,
+                                                                   8e-1],
+                   'MACRO_E': (.1e-1, .2e-1, .3e-1, .1e-1, .2e-1, .3e-1),
+                   'Test_1': 'addio'}
+
+        # Partial dictionaries
+        cls.b1Exp = {'b11': 1, 'b12': [1, 2], 'b13': [1, 2, 3]}
+        cls.b1Unc = {'b11': (1, 0.1), 'b12': ([1, 2], [.1, .2]), 'b13':
+            ([1, 2, 3], [.1, .2, .3])}
+        cls.infExp = {'inf1': 3, 'inf2': [4, 5], 'inf3': [6, 7, 8]}
+        cls.infUnc = {'inf1': (3, .3), 'inf2': ([4, 5], [.4, .5]), 'inf3':
+            ([6, 7, 8], [.6, .7, .8])}
+        cls.meta = {'macroE': (.1e-1, .2e-1, .3e-1, .1e-1, .2e-1, .3e-1),
+                    'test1': 'addio'}
+        # Use addData
+        for kk in cls.Exp:
+            cls.univ.addData(kk, cls.Exp[kk], False)
+        for kk in cls.Unc:
+            cls.univ.addData(kk, cls.Unc[kk], True)
+
+    def test_getB1Exp(self):
+        """ Get Expected vales from B1 dictionary"""
+        d = {}
+        # Comparison
+        for kk in self.univ.b1Exp:
+            d[kk] = self.univ.get(kk, False)
+        self.assertDictEqual(self.b1Exp, d)
+
+    def test_getB1Unc(self):
+        """ Get Expected vales and associated uncertainties from B1 dictionary
+        """
+        d = {}
+        # Comparison
+        for kk in self.univ.b1Exp:
+            d[kk] = self.univ.get(kk, True)
+        self.assertDictEqual(self.b1Unc, d)
+
+    def test_getInfExp(self):
+        """ Get Expected vales from Inf dictionary"""
+        d = {}
+        # Comparison
+        for kk in self.univ.infExp:
+            d[kk] = self.univ.get(kk, False)
+        self.assertDictEqual(self.infExp, d)
+
+    def test_getInfUnc(self):
+        """ Get Expected vales and associated uncertainties from Inf dictionary
+        """
+        d = {}
+        # Comparison
+        for kk in self.univ.infUnc:
+            d[kk] = self.univ.get(kk, True)
+        self.assertDictEqual(self.infUnc, d)
+
+    def test_getMeta(self):
+        """ Get metaData from corresponding dictionary"""
+        d = {}
+        # Comparison
+        for kk in self.univ.metaData:
+            d[kk] = self.univ.get(kk, False)
+        self.assertDictEqual(self.meta, d)
+
+
+if __name__ == '__main__':
+    unittest.main()