<jupyter_start><jupyter_text>Copyright (c) 2017-2020 [Serpent-Tools developer team](https://github.com/CORE-GATECH-GROUP/serpent-tools/graphs/contributors), GTRCTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Data files are not included with the python package, but can be downloaded from the [GitHub repository](https://github.com/CORE-GATECH-GROUP/serpent-tools). For this tutorial, the files are placed in the directory identified with the ``SERPENT_TOOLS_DATA`` environment variable.<jupyter_code>import os
pinFile = os.path.join(
    os.environ["SERPENT_TOOLS_DATA"],
    "fuelPin_det0.m",
)
bwrFile = os.path.join(
    os.environ["SERPENT_TOOLS_DATA"],
    "bwr_det0.m",
)<jupyter_output><empty_output><jupyter_text>DetectorReader  Basic OperationThis notebook details how to utilize the [`serpentTools`](https://github.com/CORE-GATECH-GROUP/serpent-tools) package for reading detector files, `[input]_det[N].m` produced by SERPENT [1]. Detectors can be defined with many binning parameters, listed [on the SERPENT Wiki](http://serpent.vtt.fi/mediawiki/index.php/Input_syntax_manualdet_.28detector_definition.29). One could define a detector that has a spatial mesh, `dx/dy/dz/`, but also includes reaction and material bins, `dr, dm`. Detectors are stored on the reader object in the ``detectors`` dictionary as custom ``Detector`` objects. Here, all energy and spatial grid data are stored, including other binning information such as reaction, universe, and lattice bins.<jupyter_code>%matplotlib inline
from matplotlib import pyplot
import serpentTools
pin = serpentTools.read(pinFile)
bwr = serpentTools.read(bwrFile)
print(pin.detectors)
print(bwr.detectors)<jupyter_output>{'nodeFlx': <serpentTools.detectors.Detector object at 0x7fe29eba2290>}
{'xymesh': <serpentTools.detectors.CartesianDetector object at 0x7fe29eb984d0>, 'spectrum': <serpentTools.detectors.Detector object at 0x7fe29f58ee50>}<jupyter_text>These detectors were defined for a single fuel pin with 16 axial layers and a BWR assembly, with a description of the detectors provided in the output:|Name| Description||----|------------||`nodeFlx`| One-group flux tallied in each axial layer ||`spectrum`|CSEWG 239 group stucture for flux and U-235 fission cross section||`xymesh`|Two-group flux for a 20x20 xy grid| For each `Detector` object, the full tally matrix from the file is stored in the `bins` array.<jupyter_code>nodeFlx = pin.detectors['nodeFlx']
print(nodeFlx.bins.shape)
nodeFlx.bins[:3,:].T<jupyter_output>(16, 12)<jupyter_text>Here, only three columns, shown as rows for readability, are changing: * column 0: universe column* column 10: tally column* column 11: errors Detectors can also be obtained by indexing into the reader object, as<jupyter_code>pin['nodeFlx']
bwr.get('spectrum')<jupyter_output><empty_output><jupyter_text>Tally data is reshaped corresponding to the bin information provided by Serpent. The tally and error columns are recast into multi-dimensional arrays where each dimension is some unique bin type like energy or spatial bin index. For this case, since the only variable bin quantity is that of the changing universe, the ``tallies`` and ``errors`` attributes will be 1D arrays.<jupyter_code>assert nodeFlx.tallies.shape == (16, )
assert nodeFlx.errors.shape == (16, )
nodeFlx.tallies
nodeFlx.errors<jupyter_output><empty_output><jupyter_text>Note: Python and numpy arrays are zero-indexed, meaning the first item is accessed with `array[0]`, rather than `array[1]`.Bin information is retained through the ``indexes`` attribute. Each entry indicates what bin type is changing along that dimension of ``tallies`` and ``errors``. Here, ``universe`` is the first item and indicates that the first dimension of ``tallies`` corresponds to a changing universe bin.<jupyter_code>nodeFlx.indexes<jupyter_output><empty_output><jupyter_text>For detectors that include some grid matrices, such as spatial or energy meshes `DETE`, these arrays are stored in the `grids` dictionary<jupyter_code>spectrum = bwr.detectors['spectrum']
print(spectrum.grids['E'][:5, :])<jupyter_output>[[1.00002e-11 4.13994e-07 2.07002e-07]
 [4.13994e-07 5.31579e-07 4.72786e-07]
 [5.31579e-07 6.25062e-07 5.78320e-07]
 [6.25062e-07 6.82560e-07 6.53811e-07]
 [6.82560e-07 8.33681e-07 7.58121e-07]]<jupyter_text>Multi-dimensional Detectors The `Detector` objects are capable of reshaping the detector data into an array where each axis corresponds to a varying bin. In the above examples, the reshaped data was one-dimensional, because the detectors only tallied data against one bin, universe and energy. In the following example, the detector has been configured to tally the fission and capture rates (two `dr` arguments) in an XY mesh.<jupyter_code>xy = bwr.detectors['xymesh']
xy.indexes<jupyter_output><empty_output><jupyter_text>Traversing the first axis in the `tallies` array corresponds to changing the value of the `energy`. The second axis corresponds to changing `ymesh` values, and the final axis reflects changes in `xmesh`.<jupyter_code>print(xy.bins.shape)
print(xy.tallies.shape)
print(xy.bins[:5, 10])
print(xy.tallies[0, 0, :5])<jupyter_output>(800, 12)
(2, 20, 20)
[8.19312e+17 7.18519e+17 6.90079e+17 6.22241e+17 5.97257e+17]
[8.19312e+17 7.18519e+17 6.90079e+17 6.22241e+17 5.97257e+17]<jupyter_text>SlicingAs the detectors produced by SERPENT can contain multiple bin types, obtaining data from the tally data can become complicated. This retrieval can be simplified using the `slice` method. This method takes an argument indicating what bins (keys in `indexes`) to fix at what position.If we want to retrive the tally data for the capture reaction in the `spectrum` detector, you would instruct the `slice` method to use column 1 along the axis that corresponds to the reaction bin, as the fission reaction corresponded to reaction tally 2 in the original matrix. Since python and numpy arrays are zero indexed, the second reaction tally is stored in column 1.<jupyter_code>spectrum.slice({'reaction': 1})[:20]<jupyter_output><empty_output><jupyter_text>This method also works for slicing the error, or score, matrix<jupyter_code>spectrum.slice({'reaction': 1}, 'errors')[:20]<jupyter_output><empty_output><jupyter_text>Plotting Routines Each `Detector` object is capable of simple 1D and 2D plotting routines. The simplest 1D plot method is simply `plot`, however a wide range of plot options are present.|Option|Description||-|-||`what`|What data to plot||`ax`|Preprepared figure on which to add this plot||`xdim`|Quantity from `indexes` to use as x-axis||`sigma`|Confidence interval to place on errors - 1D||`steps`|Draw tally values as constant inside bin - 1D||`xlabel`|Label to apply to x-axis||`ylabel`|Label to apply to y-axis||`loglog`|Use a log scalling on both of the axes||`logx`|Use a log scaling on the x-axis||`logy`|Use a log scaling on the y-axis||`legend`|Place a legend on the figure||`ncol`|Number of columns to apply to the legend|The plot routine also accepts various options, which can be found in the [matplotlib.pyplot.plot documentation](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html)<jupyter_code>nodeFlx.plot();
ax = nodeFlx.plot(steps=True, label='steps')
ax = nodeFlx.plot(sigma=100, ax=ax, c='k', alpha=0.6, marker='x', label='sigma')<jupyter_output><empty_output><jupyter_text>Passing `what='errors'` to the plot method plots the associated relative errors, rather than the tally data on the y-axis. Similarly, passing a key from `indexes` sets the x-axis to be that specific index.<jupyter_code>nodeFlx.plot(xdim='universe', what='errors', 
                        ylabel='Relative tally error [%]');<jupyter_output><empty_output><jupyter_text>Mesh PlotsFor data with dimensionality greater than one, the `meshPlot` method can be used to plot some 2D slice of the data on a Cartesian grid. Passing a dictionary as the `fixed` argument restricts the tally data down to two dimensions. The X and Y axis can be quantities from `grids` or `indexes`. If the quantity to be used for an axis is in the `grids` dictionary, then the appropriate spatial or energetic grid from the detector file will be used. Otherwise, the axis will reflect changes in a specific bin type. The following keyword arguments can be used in conjunction with the above options to format the mesh plots.|Option|Action||------|------||`cmap`|Colormap to apply to the figure||`cbarLabel`|Label to apply to the colorbar||`logScale`|If true, use a logarithmic scale for the colormap||`normalizer`|Apply a custom non-linear normalizer to the colormap|The `cmap` argument must be something that `matplotlib` can understand as a valid colormap [2]. This can be a string of any of the colormaps supported by matplotlib.Since the `xymesh` detector is three dimensions, (energy, x, and y), we must pick an energy group to plot.<jupyter_code>xy.meshPlot('x', 'y', fixed={'energy': 0}, 
                         cbarLabel='Mesh-integrated flux $[n/cm^2/s]$',
                         title="Fast spectrum flux $[>0.625 eV]$");<jupyter_output><empty_output><jupyter_text>The `meshPlot` also supports a range of labeling and plot options.Here, we attempt to plot the flux and U-235 fission reaction rate errors as a function of energy, withthe two reaction rates separated on the y-axis. Passing `logColor=True` applies a logarithmic color scale to all the positive data. Data that is zero is not shown, and errors will be raised if the data contain negative quantities.Here we also apply custom y-tick labels to reflect the reaction that is being plotted.<jupyter_code>ax = spectrum.meshPlot('e', 'reaction', what='errors', 
                       ylabel='Reaction type', cmap='PuBu_r',
                       cbarLabel="Relative error $[\%]$",
                       xlabel='Energy [MeV]', logColor=True,
                       logx=True);
ax.set_yticks([0.5, 1.5]);
ax.set_yticklabels([r'$\psi$', r'$U-235 \sigma_f$'], rotation=90,
                   verticalalignment='center');<jupyter_output><empty_output><jupyter_text>Using the `slicing` arguments allows access to the 1D plot methods from before<jupyter_code>xy.plot(fixed={'energy': 1, 'xmesh': 1}, 
        xlabel='Y position',
        ylabel='Thermal flux along x={}'
        .format(xy.grids['X'][1, 0]));<jupyter_output><empty_output><jupyter_text>Spectrum PlotsThe `Detector` objects are also capable of energy spectrum plots, if an associated energy grid is given. The `normalize` option will normalize the data per unit lethargy. This plot takes some additional assumptions with the scaling and labeling, but all the same controls as the above line plots. The `spectrum` plot method is designed to prepare plots of energy spectra. Supported arguments for the `spectrumPlot` method include|Option|Default|Description||-|-|-||`normalize`|`True`|Normalize tallies per unit lethargy||`fixed`| `None`|Dictionary that controls matrix reduction||`sigma`|3|Level of confidence for statistical errors||`xscale`|`'log'`|Set the x scale to be log or linear||`yscale`|`'linear'`|Set the y scale to be log or linear| The figure below demonstrates the default options and control in this `spectrumPlot`  routine by1. Using the less than helpful plot routine with no formatting2. Using `spectrumPlot` without normalization to show default labels and scaling3. Using `spectrumPlot` with normalizationSince our detector has energy bins and reaction bins, we need to reduce down to one-dimension with the `fixed` command.<jupyter_code>fig, axes = pyplot.subplots(1, 3, figsize=(16, 4))
fix = {'reaction': 0}
spectrum.plot(fixed=fix, ax=axes[0]);
spectrum.spectrumPlot(fixed=fix, ax=axes[1], normalize=False);
spectrum.spectrumPlot(fixed=fix, ax=axes[2]);<jupyter_output><empty_output><jupyter_text>Multiple line plotsPlots can be made against multiple bins, such as spectrum in different materials or reactions, with the ``plot`` and ``spectrumPlot`` methods. Below is the flux spectrum and spectrum of the U-235 fission reaction rate from the same detector. The ``labels`` argument is what is used to label each individual plot in the order of the bin index.<jupyter_code>labels = (
    'flux',
    r'$\sigma_f^{U-235}\psi$')  # render as mathtype
spectrum.plot(labels=labels, loglog=True);
spectrum.spectrumPlot(labels=labels, legend='above', ncol=2);<jupyter_output><empty_output><jupyter_text>Hexagonal DetectorsSERPENT allows the creation of hexagonal detectors with the `dh` card, like```det hex2 2 0.0 0.0 1 5 5 0.0 0.0 1det hex3 3 0.0 0.0 1 5 5 0.0 0.0 1```which would create two hexagonal detectors with different orientations. Type 2 detectors have two faces perpendicular to the x-axis, while type 3 detectors have faces perpendicular to the y-axis.For more information, see the [dh card from SERPENT wiki](http://serpent.vtt.fi/mediawiki/index.php/Input_syntax_manualdet_dh).`serpentTools` is capable of storing data tallies and grid structures from hexagonal detectors in [`HexagonalDetector`](http://serpent-tools.readthedocs.io/en/latest/api/detectors.htmlserpentTools.objects.detectors.HexagonalDetector) objects.<jupyter_code>hexFile = os.path.join(
    os.environ["SERPENT_TOOLS_DATA"],
    'hexplot_det0.m',
)
hexR = serpentTools.read(hexFile)
hexR.detectors<jupyter_output><empty_output><jupyter_text>Here, two `HexagonalDetector` objects are produced, with similar `tallies` and slicing methods as demonstrated above.<jupyter_code>hex2 = hexR.detectors['hex2']
hex2.tallies
hex2.indexes<jupyter_output><empty_output><jupyter_text>Creating hexagonal mesh plots with these objects requires setting the `pitch` and `hexType` attributes.<jupyter_code>hex2.pitch = 1
hex2.hexType = 2
hex2.hexPlot();
hex3 = hexR.detectors['hex3']
hex3.pitch = 1
hex3.hexType = 3
hex3.hexPlot();<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1002112676056338, 'fraction_numerical': 0.027887323943661974, 'mean_word_length': 5.4462097140263275, 'pattern_counts': {'":': 0, '<': 84, '<?xml version=': 0, '>': 85, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '21', 'dir': 'jupyter-structured-clean-dedup', 'id': '114841', 'license': 'MIT', 'n_tokens_mistral': 4599, 'n_tokens_neox': 4168, 'n_words': 1587, 'path': 'examples/Detector.ipynb', 'repo_name': 'drewejohnson/serpent-tools', 'size': '14200'}