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

Issue:
ENH Allow spectra to be normalized per unit lethargy, but not to a unit maximum
**Is your feature request related to a problem? Please describe.**
Sometimes it is useful to normalize spectra according to lethargy and not set the max value to be unity. This is particularly useful for comparing items over depletion (watching peaks shift _and_ grow) or comparing different concepts.

**Describe the solution you'd like**
Add a new keyword argument to `spectrumPlot` that controls if the spectra should be divided by the maximum value, e.g.,
```python
det.spectrumPlot(normalize=True, unitMax=False)
```
or, if we also want to allow normalized such that the area under the curve is one, maybe a `by="max"` or `by="area"` or `by=None` could work.

**Describe alternatives you've considered**
Doing it all by hand

**Additional context**
N/A



Discussion / hints:

Fix patch:
diff --git a/serpentTools/detectors.py b/serpentTools/detectors.py
index bf763a2b..e831e4d4 100644
--- a/serpentTools/detectors.py
+++ b/serpentTools/detectors.py
@@ -27,7 +27,7 @@
 
 from numpy import (
     unique, inf, hstack, arange, log, divide, asfortranarray,
-    ndarray, asarray,
+    ndarray, asarray, newaxis
 )
 from matplotlib.patches import RegularPolygon
 from matplotlib.collections import PatchCollection
@@ -495,10 +495,25 @@ def _compare(self, other, lower, upper, sigma):
         return similar
 
     @magicPlotDocDecorator
-    def spectrumPlot(self, fixed=None, ax=None, normalize=True, xlabel=None,
-                     ylabel=None, steps=True, logx=True, logy=False,
-                     loglog=False, sigma=3, labels=None, legend=None, ncol=1,
-                     title=None, **kwargs):
+    def spectrumPlot(
+            self,
+            fixed=None,
+            ax=None,
+            normalize=True,
+            by="max",
+            xlabel=None,
+            ylabel=None,
+            steps=True,
+            logx=True,
+            logy=False,
+            loglog=False,
+            sigma=3,
+            labels=None,
+            legend=None,
+            ncol=1,
+            title=None,
+            **kwargs,
+    ):
         """
         Quick plot of the detector value as a function of energy.
 
@@ -508,7 +523,12 @@ def spectrumPlot(self, fixed=None, ax=None, normalize=True, xlabel=None,
             Dictionary controlling the reduction in data
         {ax}
         normalize: bool
-            Normalize quantities per unit lethargy
+            Divide tally values by size of lethargy bins.
+        by : {"max", None}, optional
+            How to scale the spectrum after considering ``normalize``.
+            ``"max"`` (default) will divide by the maximum value such
+            that the max value is unity. ``None`` will not perform any
+            additional modifications.
         {xlabel}
         {ylabel}
         steps: bool
@@ -546,13 +566,17 @@ def spectrumPlot(self, fixed=None, ax=None, normalize=True, xlabel=None,
             )
         elif len(slicedTallies.shape) == 1:
             slicedTallies = slicedTallies.reshape(slicedTallies.size, 1)
+
         lowerE = self.grids['E'][:, 0]
         if normalize:
             lethBins = log(
                 divide(self.grids['E'][:, -1], lowerE))
-            for indx in range(slicedTallies.shape[1]):
-                scratch = divide(slicedTallies[:, indx], lethBins)
-                slicedTallies[:, indx] = scratch / scratch.max()
+            # Need to scale up the dimension of the energy grids to support
+            # broadcasting -> (E, N) / (E, )
+            slicedTallies /= lethBins[:, newaxis]
+
+        if by == "max":
+            slicedTallies /= slicedTallies.max(axis=0)
 
         if steps:
             if 'drawstyle' in kwargs:
@@ -576,9 +600,17 @@ def spectrumPlot(self, fixed=None, ax=None, normalize=True, xlabel=None,
 
         if legend is None and labels:
             legend = True
-        ax = formatPlot(ax, loglog=loglog, logx=logx, legendcols=ncol,
-                        xlabel=xlabel or "Energy [MeV]", ylabel=ylabel,
-                        legend=legend, title=title)
+        ax = formatPlot(
+            ax,
+            loglog=loglog,
+            logx=logx,
+            logy=logy,
+            legendcols=ncol,
+            xlabel=xlabel or "Energy [MeV]",
+            ylabel=ylabel,
+            legend=legend,
+            title=title,
+        )
         return ax
 
     @magicPlotDocDecorator


Test patch:
diff --git a/tests/plots/result_images/testSpectrumJustNormPerLethargy.png b/tests/plots/result_images/testSpectrumJustNormPerLethargy.png
new file mode 100644
index 00000000..4ee25714
Binary files /dev/null and b/tests/plots/result_images/testSpectrumJustNormPerLethargy.png differ
diff --git a/tests/plots/test_detectors.py b/tests/plots/test_detectors.py
index ea3c6dad..77a9d67c 100644
--- a/tests/plots/test_detectors.py
+++ b/tests/plots/test_detectors.py
@@ -17,3 +17,9 @@ def testBwrMeshPlot():
     with pytest.warns(None) as record:
         reader["xymesh"].meshPlot(fixed={"energy": 0})
     assert len(record) == 0
+
+
+@compare_or_update_plot
+def testSpectrumJustNormPerLethargy():
+    reader = readDataFile("bwr_det0.m")
+    reader["spectrum"].spectrumPlot(by=None, fixed={"reaction": 1})