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

Issue:
DEV Use pyplot.gca for obtaining new axes
## Summary of issue
Matplotlib is going to change the behavior of the creating an axes object, which we use across some of our plotting routines. This causes a deprecation warning to be raised, which we can easily silence
From https://travis-ci.org/CORE-GATECH-GROUP/serpent-tools/jobs/487005354#L1551
```
/home/travis/virtualenv/python3.6.3/lib/python3.6/site-packages/matplotlib/figure.py:98: MatplotlibDeprecationWarning: 
Adding an axes using the same arguments as a previous axes currently reuses the earlier instance.  
In a future version, a new instance will always be created and returned.  Meanwhile, this warning can 
be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
```
## Code for reproducing the issue

## Actual outcome including console output and error traceback if applicable

## Expected outcome

## Versions

* Version from serpentTools.__version__ 0.6.1+51g94c9412

* Python version - python --version - 3.7.2 

## Culprits
The following calls to `pyplot.axes` were found using
```
$ find serpentTools/ -iname "*py" -exec grep axes\(\) {} \+
serpentTools/plot.py:116:    ax = ax or pyplot.axes()
serpentTools/plot.py:160:    ax = ax or pyplot.axes()
serpentTools/samplers/detector.py:250:        ax = ax or pyplot.axes()
serpentTools/samplers/depletion.py:274:        ax = ax or pyplot.axes()
serpentTools/samplers/depletion.py:333:        ax = ax or pyplot.axes()
serpentTools/objects/xsdata.py:248:        ax = ax or pyplot.axes()
serpentTools/objects/containers.py:361:        ax = ax or pyplot.axes()
serpentTools/objects/detectors.py:324:            ax = ax or (fig.axes[0] if fig.axes else axes())
serpentTools/objects/detectors.py:340:        ax = ax or axes()
serpentTools/objects/materials.py:361:        ax = ax or pyplot.axes()
serpentTools/objects/base.py:427:        ax = ax or axes()
serpentTools/parsers/depletion.py:100:        ax = ax or pyplot.axes()
serpentTools/parsers/sensitivity.py:312:        ax = ax or axes()
```

Replacing these with `pyplot.gca()` should suppress the warnings. :rotating_light: This will attach the plot to the active plot

Pull request:
DEV Obtain plot axes with gca()
Closes #267 by refactoring calls to and imports of pyplot.axes to pyplot.gca.

No user change needed if used in jupyter notebooks or if no plotting is used. The default behavior of matplotlib was to reuse the existing plot anyway, but with a deprecation warning. 

Patch:
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 970e877..8301113 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -29,6 +29,8 @@ Next
   collecting group constants from coefficient files. Collects
   group constants in in multi-dimensional matrices according
   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`
 
 .. warning::
 
diff --git a/serpentTools/objects/base.py b/serpentTools/objects/base.py
index e580f0e..2ec54af 100644
--- a/serpentTools/objects/base.py
+++ b/serpentTools/objects/base.py
@@ -6,7 +6,7 @@ from abc import ABCMeta, abstractmethod
 from six import add_metaclass
 
 from numpy import arange, hstack, log, divide
-from matplotlib.pyplot import axes
+from matplotlib.pyplot import gca
 
 from serpentTools.messages import (
     debug, warning, SerpentToolsException, info,
@@ -424,7 +424,7 @@ class DetectorBase(NamedObject):
         xdata, autoX = self._getPlotXData(xdim, data)
         xlabel = xlabel or autoX
         ylabel = ylabel or "Tally data"
-        ax = ax or axes()
+        ax = ax or gca()
 
         if steps:
             if 'drawstyle' in kwargs:
diff --git a/serpentTools/objects/containers.py b/serpentTools/objects/containers.py
index e1c3fd2..621c786 100644
--- a/serpentTools/objects/containers.py
+++ b/serpentTools/objects/containers.py
@@ -358,7 +358,7 @@ class HomogUniv(NamedObject):
 
         """
         qtys = [qtys, ] if isinstance(qtys, str) else qtys
-        ax = ax or pyplot.axes()
+        ax = ax or pyplot.gca()
         onlyXS = True
         sigma = max(0, int(sigma))
         drawstyle = 'steps-post' if steps else None
diff --git a/serpentTools/objects/detectors.py b/serpentTools/objects/detectors.py
index 717db5a..acb07c0 100644
--- a/serpentTools/objects/detectors.py
+++ b/serpentTools/objects/detectors.py
@@ -29,7 +29,7 @@ from numpy import unique, array, empty, inf
 from matplotlib.figure import Figure
 from matplotlib.patches import RegularPolygon
 from matplotlib.collections import PatchCollection
-from matplotlib.pyplot import axes
+from matplotlib.pyplot import gca
 
 from serpentTools.messages import warning, debug, SerpentToolsException
 from serpentTools.objects.base import DetectorBase
@@ -321,7 +321,7 @@ class HexagonalDetector(Detector):
             if ax and fig.axes and ax not in fig.axes:
                 raise IndexError("Passed argument for 'figure' and 'ax', "
                                  "but ax is not attached to figure.")
-            ax = ax or (fig.axes[0] if fig.axes else axes())
+            ax = ax or (fig.axes[0] if fig.axes else gca())
         alpha = kwargs.get('alpha', None)
 
         ny = len(self.indexes['ycoord'])
@@ -337,7 +337,7 @@ class HexagonalDetector(Detector):
         values = empty(nItems)
         coords = self.grids['COORD']
 
-        ax = ax or axes()
+        ax = ax or gca()
         pos = 0
         xmax, ymax = [-inf, ] * 2
         xmin, ymin = [inf, ] * 2
diff --git a/serpentTools/objects/materials.py b/serpentTools/objects/materials.py
index 07d18a9..4357c54 100644
--- a/serpentTools/objects/materials.py
+++ b/serpentTools/objects/materials.py
@@ -358,7 +358,7 @@ class DepletedMaterial(DepletedMaterialBase):
             if isinstance(zai, str):
                 zai = [zai, ]
         yVals = self.getValues(xUnits, yUnits, xVals, names, zai)
-        ax = ax or pyplot.axes()
+        ax = ax or pyplot.gca()
         labels = self._formatLabel(labelFmt, names, zai)
         for row in range(yVals.shape[0]):
             ax.plot(xVals, yVals[row], label=labels[row], **kwargs)
diff --git a/serpentTools/objects/xsdata.py b/serpentTools/objects/xsdata.py
index a5f3cec..b9e9458 100644
--- a/serpentTools/objects/xsdata.py
+++ b/serpentTools/objects/xsdata.py
@@ -245,7 +245,7 @@ class XSData(NamedObject):
             if mt not in self.MT:
                 error("{} not in collected MT numbers, {}".format(mt, self.MT))
 
-        ax = ax or pyplot.axes()
+        ax = ax or pyplot.gca()
 
         x = self.metadata['egrid']
         for mt in mts:
diff --git a/serpentTools/parsers/depletion.py b/serpentTools/parsers/depletion.py
index dbd2e7f..54552f9 100644
--- a/serpentTools/parsers/depletion.py
+++ b/serpentTools/parsers/depletion.py
@@ -97,7 +97,7 @@ class DepPlotMixin(object):
             raise KeyError("Plot method only uses x-axis data from <days> and "
                            "<burnup>, not {}".format(xUnits))
         missing = set()
-        ax = ax or pyplot.axes()
+        ax = ax or pyplot.gca()
         materials = materials or self.materials.keys()
         labelFmt = labelFmt or '{mat} {iso}'
         for mat in materials:
diff --git a/serpentTools/parsers/sensitivity.py b/serpentTools/parsers/sensitivity.py
index c668efa..84bc4aa 100644
--- a/serpentTools/parsers/sensitivity.py
+++ b/serpentTools/parsers/sensitivity.py
@@ -6,7 +6,7 @@ from itertools import product
 
 from six import iteritems
 from numpy import transpose, hstack
-from matplotlib.pyplot import axes
+from matplotlib.pyplot import gca
 
 from serpentTools.utils.plot import magicPlotDocDecorator, formatPlot
 from serpentTools.engines import KeywordParser
@@ -309,7 +309,7 @@ class SensitivityReader(BaseReader):
         perts = self._getCleanedPertOpt('perts', pert)
         mats = self._getCleanedPertOpt('materials', mat)
 
-        ax = ax or axes()
+        ax = ax or gca()
 
         sigma = max(int(sigma), 0)
         resMat = self.sensitivities[resp]
diff --git a/serpentTools/plot.py b/serpentTools/plot.py
index 16cf648..207848c 100644
--- a/serpentTools/plot.py
+++ b/serpentTools/plot.py
@@ -113,7 +113,7 @@ def cartMeshPlot(data, xticks=None, yticks=None, ax=None, cmap=None,
     norm = normalizerFactory(data, normalizer, logColor, xticks, yticks)
 
     # make the plot
-    ax = ax or pyplot.axes()
+    ax = ax or pyplot.gca()
     if xticks is None:
         mappable = ax.imshow(data, cmap=cmap, norm=norm)
     else:
@@ -157,7 +157,7 @@ def plot(xdata, plotData, ax=None, labels=None, yerr=None, **kwargs):
         of ``plotData``
 
     """
-    ax = ax or pyplot.axes()
+    ax = ax or pyplot.gca()
 
     if yerr is not None:
         if not yerr.shape == plotData.shape:
diff --git a/serpentTools/samplers/depletion.py b/serpentTools/samplers/depletion.py
index de226c3..4c29bbd 100644
--- a/serpentTools/samplers/depletion.py
+++ b/serpentTools/samplers/depletion.py
@@ -271,7 +271,7 @@ class SampledDepletedMaterial(SampledContainer, DepletedMaterialBase):
                                          colIndices))
         else:
             xUncs = zeros_like(xVals)
-        ax = ax or pyplot.axes()
+        ax = ax or pyplot.gca()
         labels = self._formatLabel(labelFmt, names)
         yVals = yVals.copy(order='F')
         yUncs = yUncs.copy(order='F') * sigma
@@ -330,7 +330,7 @@ class SampledDepletedMaterial(SampledContainer, DepletedMaterialBase):
         if not self.allData:
             raise SamplerError("Data from all sampled files has been freed "
                                "and cannot be used in this plot method")
-        ax = ax or pyplot.axes()
+        ax = ax or pyplot.gca()
         if xUnits not in ('days', 'burnup'):
             raise KeyError("Plot method only uses x-axis data from <days> "
                            "and <burnup>, not {}".format(xUnits))
diff --git a/serpentTools/samplers/detector.py b/serpentTools/samplers/detector.py
index 772fd81..311f7ed 100644
--- a/serpentTools/samplers/detector.py
+++ b/serpentTools/samplers/detector.py
@@ -247,7 +247,7 @@ class SampledDetector(SampledContainer, DetectorBase):
                     samplerData.shape))
         xdata, autoX = self._getPlotXData(xdim, samplerData)
         xlabel = xlabel or autoX
-        ax = ax or pyplot.axes()
+        ax = ax or pyplot.gca()
         N = self._index
         allTallies = self.allTallies.copy(order='F')
         for n in range(N):


Test patch:
diff --git a/serpentTools/tests/test_detector.py b/serpentTools/tests/test_detector.py
index dcbfd23..4b34ad3 100644
--- a/serpentTools/tests/test_detector.py
+++ b/serpentTools/tests/test_detector.py
@@ -151,6 +151,16 @@ class CartesianDetectorTester(DetectorHelper):
     },
     }
 
+    def test_sharedPlot(self):
+        """Verify that the same axes object is returned on subsequent plots
+        """
+        det = self.detectors[self.DET_NAME]
+        # plot along two reactions with no axes call
+        # ensure that returned objects are equal
+        ax0 = det.plot(fixed={'reaction': 0, 'ymesh': 0})
+        ax1 = det.plot(fixed={'reaction': 1, 'ymesh': 0})
+        self.assertTrue(ax0 is ax1)
+
 
 class HexagonalDetectorTester(DetectorHelper):
     """