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

Issue:
Clean up the code
There are some debug codes, ```if __name__ == '__main__'```, in some of the scripts. They should be removed. Test scripts, e.g. unittests, still need those to run the tests.

- [x] `detector.py`
- [x] todos in the root __init__.py


Pull request:
Cleanup containers
The SupportingObject class did not extend a lot of additional functionality, and would complicate the integration of objects that may not be connected to parsers, i.e. material objects #12 
This PR removes the SupportingObject. Now, containers either inherit directly from object or from NamedObject.

This PR also closes #81 by

- improved pep8 compliance (mainly line lengths)
- removed TODOs in module-level `__init__`
- Removed `if __name__ == '__main__'` blocks from some detector scripts

Patch:
diff --git a/serpentTools/__init__.py b/serpentTools/__init__.py
index 8933bae..6343059 100644
--- a/serpentTools/__init__.py
+++ b/serpentTools/__init__.py
@@ -4,16 +4,6 @@ ROOT_DIR = os.path.dirname(__file__)
 
 from serpentTools.parsers import read
 from serpentTools import messages
-
-# List TODOS/feature requests here for now
-# Compatibility
-# TODO: Test compatibility with earlier numpy releases
-# Usage/scripting
-# TODO: Update rc with dictionary
-# TODO: Update rc with yaml file into dictionary
-# TODO: Capture materials with underscores for depletion
-# TODO: Find a way to capture some or all of log messages for testing
-
 from ._version import get_versions
 __version__ = get_versions()['version']
 del get_versions
diff --git a/serpentTools/messages.py b/serpentTools/messages.py
index 8321264..643f994 100644
--- a/serpentTools/messages.py
+++ b/serpentTools/messages.py
@@ -58,7 +58,7 @@ def info(message):
 
 
 def warning(message):
-    """Log a warning that something that could go wrong or should be avoided."""
+    """Log a warning that something could go wrong or should be avoided."""
     __logger__.warning('%s', message)
 
 
diff --git a/serpentTools/objects/__init__.py b/serpentTools/objects/__init__.py
index 11bf9ec..fc528bc 100644
--- a/serpentTools/objects/__init__.py
+++ b/serpentTools/objects/__init__.py
@@ -1,44 +1,24 @@
 """Objects used to support the parsing."""
 
 
-class SupportingObject(object):
-    """
-    Base supporting object.
-
-    Parameters
-    ----------
-    container: Some parser from serpentTools.parsers
-        Container that created this object
-
-    """
-
-    def __init__(self, container):
-        self.filePath = container.filePath
-
-    def __str__(self):
-        return '<{} from {}>'.format(self.__class__.__name__, self.filePath)
-
-    @staticmethod
-    def _convertVariableName(variable):
-        """Converta a SERPENT variable to camelCase."""
-        lowerSplits = [item.lower() for item in variable.split('_')]
-        if len(lowerSplits) == 1:
-            return lowerSplits[0]
-        else:
-            return lowerSplits[0] + ''.join([item.capitalize()
-                                             for item in lowerSplits[1:]])
-
-
-class NamedObject(SupportingObject):
+class NamedObject(object):
     """Class for named objects like materials and detectors."""
 
-    def __init__(self, container, name):
-        SupportingObject.__init__(self, container)
+    def __init__(self, name):
         self.name = name
 
     def __str__(self):
-        return '<{} {} from {}>'.format(self.__class__.__name__,
-                                        self.name, self.filePath)
+        return '<{} {}>'.format(self.__class__.__name__, self.name)
+
+
+def convertVariableName(variable):
+    """Convert a SERPENT variable to camelCase"""
+    lowerSplits = [item.lower() for item in variable.split('_')]
+    if len(lowerSplits) == 1:
+        return lowerSplits[0]
+    else:
+        return lowerSplits[0] + ''.join([item.capitalize()
+                                         for item in lowerSplits[1:]])
 
 
 def splitItems(items):
diff --git a/serpentTools/objects/containers.py b/serpentTools/objects/containers.py
index afe5756..c8ed16b 100644
--- a/serpentTools/objects/containers.py
+++ b/serpentTools/objects/containers.py
@@ -10,12 +10,12 @@ Contents
 """
 from collections import OrderedDict
 
-from numpy import array, arange, unique, log, divide, ones_like
-
 from matplotlib import pyplot
 
+from numpy import array, arange, unique, log, divide, ones_like
+
 from serpentTools.plot import cartMeshPlot
-from serpentTools.objects import SupportingObject, NamedObject
+from serpentTools.objects import NamedObject, convertVariableName
 from serpentTools.messages import warning, SerpentToolsException, debug
 
 DET_COLS = ('value', 'energy', 'universe', 'cell', 'material', 'lattice',
@@ -23,15 +23,12 @@ DET_COLS = ('value', 'energy', 'universe', 'cell', 'material', 'lattice',
 """Name of the columns of the data"""
 
 
-class HomogUniv(SupportingObject):
+class HomogUniv(NamedObject):
     """
     Class for storing homogenized universe specifications and retrieving them
 
     Parameters
     ----------
-    container: serpentTools.objects.readers.BaseReader or
-        serpentTools.objects.containers.BranchContainer
-        Object to which this universe is attached
     name: str
         name of the universe
     bu: float
@@ -63,9 +60,8 @@ class HomogUniv(SupportingObject):
         Other values that do not not conform to inf/b1 dictionaries
     """
 
-    def __init__(self, container, name, bu, step, day):
-        SupportingObject.__init__(self, container)
-        self.name = name
+    def __init__(self, name, bu, step, day):
+        NamedObject.__init__(self, name)
         self.bu = bu
         self.step = step
         self.day = day
@@ -81,6 +77,7 @@ class HomogUniv(SupportingObject):
         Sets the value of the variable and, optionally, the associate s.d.
 
         .. warning::
+
             This method will overwrite data for variables that already exist
 
         Parameters
@@ -101,10 +98,10 @@ class HomogUniv(SupportingObject):
         """
 
         # 1. Check the input type
-        variableName = SupportingObject._convertVariableName(variableName)
+        variableName = convertVariableName(variableName)
         if not isinstance(uncertainty, bool):
-            raise TypeError('The variable uncertainty has type %s.\n ...'
-                            'It should be boolean.', type(uncertainty))
+            raise TypeError('The variable uncertainty has type {}, '
+                            'should be boolean.'.format(type(uncertainty)))
         # 2. Pointer to the proper dictionary
         setter = self._lookup(variableName, uncertainty)
         # 3. Check if variable is already present. Then set the variable.
@@ -186,8 +183,6 @@ class Detector(NamedObject):
 
     Parameters
     ----------
-    parser: :py:class:`~serpentTools.parsers.detector.DetectorReader`
-        Detector reader that created this detector
     name: str
         Name of this detector
 
@@ -207,8 +202,8 @@ class Detector(NamedObject):
         Collection of unique indexes for each requested bin
     """
 
-    def __init__(self, parser, name):
-        NamedObject.__init__(self, parser, name)
+    def __init__(self, name):
+        NamedObject.__init__(self, name)
         self.bins = None
         self.tallies = None
         self.errors = None
@@ -223,9 +218,6 @@ class Detector(NamedObject):
             return self.bins.shape[0]
         return 0
 
-    def __str__(self):
-        return 'Detector {} from {}'.format(self.name, self.filePath)
-
     def addTallyData(self, bins):
         """Add tally data to this detector"""
         self.__reshaped = False
@@ -312,13 +304,13 @@ class Detector(NamedObject):
                 'Slicing requires detector to be reshaped')
         if data not in self._map:
             raise KeyError(
-                'Slicing function only works with the following data arguments:'
-                '\n{}'.format(', '.join(self._map.keys())))
+                'Data argument {} not in allowed options'
+                '\n{}'.format(', '.join(data, self._map.keys())))
         work = self._map[data]
         if work is None:
             raise SerpentToolsException(
                 '{} data for detector {} is None. Cannot perform slicing'
-                    .format(data, self.name))
+                .format(data, self.name))
         return work[self._getSlices(fixed)]
 
     def _getSlices(self, fixed):
@@ -397,7 +389,7 @@ class Detector(NamedObject):
         if not len(slicedTallies.shape) == 1:
             raise SerpentToolsException(
                 'Sliced data must be one-dimensional for spectrum plot, not {}'
-                    .format(slicedTallies.shape)
+                .format(slicedTallies.shape)
             )
         if normalize:
             lethBins = log(
@@ -609,7 +601,7 @@ class Detector(NamedObject):
         return ax
 
 
-class BranchContainer(SupportingObject):
+class BranchContainer(object):
     """
     Class that stores data for a single branch.
 
@@ -621,8 +613,8 @@ class BranchContainer(SupportingObject):
 
     Parameters
     ----------
-    parser: serpentTools.objects.readers.BaseReader
-        Parser that read the file that created this object
+    filePath: str
+        Path to input file from which this container was connected
     branchID: int
         Index for the run for this branch
     branchNames: tuple
@@ -641,8 +633,8 @@ class BranchContainer(SupportingObject):
         ``(universeID, burnup, burnIndex)``
     """
 
-    def __init__(self, parser, branchID, branchNames, stateData):
-        SupportingObject.__init__(self, parser)
+    def __init__(self, filePath, branchID, branchNames, stateData):
+        self.filePath = filePath
         self.branchID = branchID
         self.stateData = stateData
         self.universes = {}
@@ -696,7 +688,7 @@ class BranchContainer(SupportingObject):
         -------
         newUniv: serpentTools.objects.containers.HomogUniv
         """
-        newUniv = HomogUniv(self, univID, burnup, burnIndex, burnDays)
+        newUniv = HomogUniv(univID, burnup, burnIndex, burnDays)
         key = tuple(
             [univID, burnup, burnIndex] + ([burnDays] if burnDays else []))
         if key in self.__keys:
@@ -747,16 +739,3 @@ class BranchContainer(SupportingObject):
         raise KeyError(
             'Could not find a universe that matched requested universe {} and '
             '{} {}'.format(univID, searchName, searchValue))
-
-
-if __name__ == '__main__':
-    import os
-    from matplotlib import pyplot
-
-    from serpentTools import ROOT_DIR, read
-
-    bwrF = os.path.join(ROOT_DIR, '..', 'examples', 'bwr_det0.m')
-    bwr = read(bwrF)
-    s = bwr.detectors['spectrum']
-    s.meshPlot('e', 'reaction', xscale='log')
-    pyplot.show()
diff --git a/serpentTools/objects/materials.py b/serpentTools/objects/materials.py
index f5706d1..77abf43 100644
--- a/serpentTools/objects/materials.py
+++ b/serpentTools/objects/materials.py
@@ -4,7 +4,7 @@ import numpy
 from matplotlib import pyplot
 
 from serpentTools import messages
-from serpentTools.objects import NamedObject
+from serpentTools.objects import NamedObject, convertVariableName
 
 
 class DepletedMaterial(NamedObject):
@@ -26,7 +26,7 @@ class DepletedMaterial(NamedObject):
     names: numpy.array or None
         Names of isotopes
     days: numpy.array or None
-        Days overwhich the material was depleted
+        Days over which the material was depleted
     adens: numpy.array or None
         Atomic density over time for each nuclide
     mdens: numpy.array or None
@@ -37,14 +37,15 @@ class DepletedMaterial(NamedObject):
     """
 
     def __init__(self, parser, name):
-        NamedObject.__init__(self, parser, name)
+        NamedObject.__init__(self, name)
         self.data = {}
         self.zai = parser.metadata.get('zai', None)
         self.names = parser.metadata.get('names', None)
         self.days = parser.metadata.get('days', None)
-        self.__burnup__ = None
-        self.__adens__ = None
-        self.__mdens__ = None
+        self.filePath = parser.filePath
+        self.__burnup = None
+        self.__adens = None
+        self.__mdens = None
 
     def __getitem__(self, item):
         if item not in self.data:
@@ -57,27 +58,27 @@ class DepletedMaterial(NamedObject):
         if 'burnup' not in self.data:
             raise AttributeError('Burnup for material {} has not been loaded'
                                  .format(self.name))
-        if self.__burnup__ is None:
-            self.__burnup__ = self.data['burnup']
-        return self.__burnup__
+        if self.__burnup is None:
+            self.__burnup = self.data['burnup']
+        return self.__burnup
 
     @property
     def adens(self):
         if 'adens' not in self.data:
             raise AttributeError('Atomic densities for material {} have not '
                                  'been loaded'.format(self.name))
-        if self.__adens__ is None:
-            self.__adens__ = self.data['adens']
-        return self.__adens__
+        if self.__adens is None:
+            self.__adens = self.data['adens']
+        return self.__adens
 
     @property
     def mdens(self):
         if 'mdens' not in self.data:
             raise AttributeError('Mass densities for material {} has not been '
                                  'loaded'.format(self.name))
-        if self.__mdens__ is None:
-            self.__mdens__ = self.data['mdens']
-        return self.__mdens__
+        if self.__mdens is None:
+            self.__mdens = self.data['mdens']
+        return self.__mdens
 
     def addData(self, variable, rawData):
         """
@@ -90,7 +91,7 @@ class DepletedMaterial(NamedObject):
         rawData: list
             List of strings corresponding to the raw data from the file
         """
-        newName = self._convertVariableName(variable)
+        newName = convertVariableName(variable)
         messages.debug('Adding {} data to {}'.format(newName, self.name))
         if isinstance(rawData, str):
             scratch = [float(item) for item in rawData.split()]
@@ -148,8 +149,8 @@ class DepletedMaterial(NamedObject):
         if timePoints is not None:
             timeCheck = self._checkTimePoints(xUnits, timePoints)
             if any(timeCheck):
-                raise KeyError('The following times were not present in file {}'
-                               '\n{}'.format(self.filePath,
+                raise KeyError('The following times were not present in file'
+                               '{}\n{}'.format(self.filePath,
                                              ', '.join(timeCheck)))
         if names and self.names is None:
             raise AttributeError(
diff --git a/serpentTools/parsers/__init__.py b/serpentTools/parsers/__init__.py
index 978afc9..fb980bf 100644
--- a/serpentTools/parsers/__init__.py
+++ b/serpentTools/parsers/__init__.py
@@ -70,7 +70,8 @@ def inferReader(filePath):
     for reg, reader in six.iteritems(REGEXES):
         match = re.match(reg, filePath)
         if match and match.group() == filePath:
-            info('Inferred reader for {}: {}'.format(filePath, reader.__name__))
+            info('Inferred reader for {}: {}'
+                 .format(filePath, reader.__name__))
             return reader
     raise SerpentToolsException(
         'Failed to infer filetype and thus accurate reader from'
@@ -140,7 +141,8 @@ def read(filePath, reader='infer'):
                     'Reader type {} not supported'.format(reader)
                 )
     else:
-        assert callable(reader), 'Reader {} is not callable'.format(str(reader))
+        assert callable(reader), (
+                'Reader {} is not callable'.format(str(reader)))
         loader = reader
     returnedFromLoader = loader(filePath)
     returnedFromLoader.read()
@@ -202,7 +204,8 @@ def depmtx(fileP):
         if not nMatch:
             raise SerpentToolsException(failMsg + line)
 
-        n0Storage, line, numIso = _parseIsoBlock(f, {}, nMatch, line, nDensRegex)
+        n0Storage, line, numIso = _parseIsoBlock(f, {}, nMatch, line,
+                                                 nDensRegex)
         debug('Found {} isotopes for file {}'.format(numIso, fileP))
         n0 = empty((numIso, 1), dtype=longfloat)
         for indx, v in six.iteritems(n0Storage):
diff --git a/serpentTools/parsers/branching.py b/serpentTools/parsers/branching.py
index 3daadf8..3d92a86 100644
--- a/serpentTools/parsers/branching.py
+++ b/serpentTools/parsers/branching.py
@@ -74,7 +74,8 @@ class BranchingReader(XSReader):
         if branchNames not in self.branches:
             branchState = self._processBranchStateData()
             self.branches[branchNames] = (
-                BranchContainer(self, coefIndx, branchNames, branchState))
+                BranchContainer(self.filePath, coefIndx, branchNames,
+                                branchState))
         else:
             self._advance()
         return self.branches[branchNames], int(totUniv)
@@ -98,7 +99,8 @@ class BranchingReader(XSReader):
         unvID, numVariables = [int(xx) for xx in self._advance()]
         univ = branch.addUniverse(unvID, burnup, burnupIndex)
         for step in range(numVariables):
-            splitList = self._advance(possibleEndOfFile=step == numVariables-1)
+            splitList = self._advance(
+                possibleEndOfFile=step == numVariables - 1)
             varName = splitList[0]
             varValues = [float(xx) for xx in splitList[2:]]
             if self._checkAddVariable(varName):
diff --git a/serpentTools/parsers/detector.py b/serpentTools/parsers/detector.py
index 9b08305..a2570aa 100644
--- a/serpentTools/parsers/detector.py
+++ b/serpentTools/parsers/detector.py
@@ -1,6 +1,5 @@
 """Parser responsible for reading the ``*det<n>.m`` files"""
 
-import six
 import numpy
 
 from serpentTools.engines import KeywordParser
@@ -60,7 +59,7 @@ class DetectorReader(BaseReader):
             data[indx] = [float(xx) for xx in line.split()]
         if detName not in self.detectors:
             # new detector, this data is the tallies
-            detector = Detector(self, detName)
+            detector = Detector(detName)
             detector.addTallyData(data)
             self.detectors[detName] = detector
             messages.debug('Adding detector {}'.format(detName))
@@ -70,17 +69,3 @@ class DetectorReader(BaseReader):
         detector.grids[binType] = data
         messages.debug('Added bin data {} to detector {}'
                        .format(binType, detName))
-
-
-if __name__ == '__main__':
-    from os.path import join
-    from matplotlib import pyplot
-    import serpentTools
-
-    det = DetectorReader(join(serpentTools.ROOT_DIR, 'tests', 'ref_det0.m'))
-    det.read()
-    xy = det.detectors['xyFissionCapt']
-    xy.plot(fixed={'reaction': 1, 'ymesh': 2})
-    pyplot.show()
-    xy.plot(fixed={'reaction': 1, 'ymesh': 2}, sigma=1)
-    pyplot.show()
diff --git a/serpentTools/settings.py b/serpentTools/settings.py
index c49c1dc..788f674 100644
--- a/serpentTools/settings.py
+++ b/serpentTools/settings.py
@@ -158,7 +158,7 @@ class DefaultSettingsLoader(dict):
         for name, value in defaultSettings.items():
             if 'options' in value:
                 options = (value['default'] if value['options'] == 'default'
-                else value['options'])
+                           else value['options'])
             else:
                 options = None
             settingsOptions = {'name': name,
@@ -278,8 +278,9 @@ class UserSettingsLoader(dict):
             dictionary
         """
         settings = {}
-        settingsPreffix = ([settingsPreffix] if isinstance(settingsPreffix, str)
-        else settingsPreffix)
+        settingsPreffix = (
+                [settingsPreffix] if isinstance(settingsPreffix, str)
+                else settingsPreffix)
         for setting, value in self.items():
             settingPath = setting.split('.')
             if settingPath[0] in settingsPreffix:
@@ -346,11 +347,11 @@ class UserSettingsLoader(dict):
         """
         messages.debug('Attempting to read from {}'.format(filePath))
         with open(filePath) as yFile:
-            l = yaml.safe_load(yFile)
+            loaded = yaml.safe_load(yFile)
         messages.info('Loading settings onto object with strict:{}'
                       .format(strict))
 
-        for key, value in six.iteritems(l):
+        for key, value in six.iteritems(loaded):
             if isinstance(value, dict):
                 self.__recursiveLoad(value, strict, key)
             else:


Test patch:
diff --git a/serpentTools/tests/test_container.py b/serpentTools/tests/test_container.py
index bba2a3a..fbed4dc 100644
--- a/serpentTools/tests/test_container.py
+++ b/serpentTools/tests/test_container.py
@@ -10,7 +10,7 @@ class HomogenizedUniverseTester(unittest.TestCase):
 
     @classmethod
     def setUpClass(cls):
-        cls.univ = containers.HomogUniv(DepletionReader(None), 'dummy', 0, 0, 0)
+        cls.univ = containers.HomogUniv('dummy', 0, 0, 0)
         cls.Exp = {}
         cls.Unc = {}
         # Data definition