def spreadPlot(self, xdim=None, fixed=None, sampleKwargs=None, meanKwargs=None, ax=None, xlabel=None, ylabel=None, logx=False, logy=False, loglog=False, legend=True): """ Plot the mean tally value against all sampled detector data. Parameters ---------- xdim: str Bin index to place on the x-axis fixed: None or dict Dictionary controlling the reduction in data down to one dimension sampleKwargs : dict, optional Additional matplotlib-acceptable arguments to be passed into the plot when plotting data from unique runs, e.g. ``{"c": k, "alpha": 0.5}``. meanKwargs : dict, optional Additional matplotlib-acceptable argumentst to be used when plotting the mean value, e.g. ``{"c": "b", "marker": "o"}`` {ax} {xlabel} {ylabel} {logx} {logy} {loglog} {legend} Returns ------- {rax} """ if self.allTallies is None: raise AttributeError( "allTallies is None, cannot plot all tally data") samplerData = self.slice(fixed, 'tallies') slices = self._getSlices(fixed) if len(samplerData.shape) != 1: raise SerpentToolsException( 'Data must be constrained to 1D, not {}'.format( samplerData.shape)) xdata, autoX = self._getPlotXData(xdim, samplerData) xlabel = xlabel or autoX if sampleKwargs is None: sampleKwargs = {"c": "k", "alpha": 0.5, "marker": ""} if meanKwargs is None: meanKwargs = {"c": "#0173b2", "marker": "o"} ax = ax or pyplot.gca() for data in self.allTallies: ax.plot(xdata, data[slices], **sampleKwargs) ax.plot(xdata, samplerData, label='Mean value', **meanKwargs) formatPlot(ax, logx=logx, logy=logy, loglog=loglog, xlabel=xlabel, ylabel=ylabel, legend=legend) return ax
def test_formatPlot(self): """Test the capabilities of the formatPlot function""" ax = self.buildPlot() newX = 'new x label' newY = 'new y label' title = 'plot title' formatPlot(ax, xlabel=newX, ylabel=newY, loglog=True, title=title) plotAttrTest(self, ax, xlabel=newX, ylabel=newY, xscale='log', yscale='log', title=title)
def spreadPlot(self, xUnits, yUnits, isotope=None, zai=None, sampleKwargs=None, meanKwargs=None, timePoints=None, ax=None, xlabel=None, ylabel=None, logx=False, logy=False, loglog=False, legend=True): """ Plot the mean quantity and data from all sampled files. Parameters ---------- xUnits : str name of x value to obtain, e.g. ``'days'``, ``'burnup'`` yUnits : str name of y value to return, e.g. ``'adens'``, ``'burnup'`` isotope : str, optional Plot data for this isotope zai : int, optional Plot data for this isotope. Not allowed if ``isotope`` given. sampleKwargs : dict, optional Additional matplotlib-acceptable arguments to be passed into the plot when plotting data from unique runs, e.g. ``{"c": k, "alpha": 0.5}``. meanKwargs : dict, optional Additional matplotlib-acceptable argumentst to be used when plotting the mean value, e.g. ``{"c": "b", "marker": "o"}`` timePoints : list or None If given, select the time points according to those specified here. Otherwise, select all points {ax} {xlabel} {ylabel} {logx} {logy} {loglog} {legend} Returns ------- {rax} """ if not self.allData: raise SamplerError("Data from all sampled files has been freed " "and cannot be used in this plot method") if isotope is not None and zai is not None: raise ValueError("Please specify isotope name or zai, not both") elif isotope is None and zai is None: raise ValueError("Isotope name or zai needed") if sampleKwargs is None: sampleKwargs = {"c": "k", "alpha": 0.5, "marker": ""} if meanKwargs is None: meanKwargs = {"c": "#0173b2", "marker": "o"} 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)) xVals = timePoints if timePoints is not None else ( self.days if xUnits == 'days' else self.burnup) if isotope is not None: rows = self._getRowIndices("names", [isotope]) else: rows = self._getRowIndices("zai", [zai]) cols = self._getColIndices(xUnits, timePoints) primaryData = self._slice(self.data[yUnits], rows, cols)[0] for data in self.allData[yUnits][:self._index]: plotData = self._slice(data, rows, cols)[0] ax.plot(xVals, plotData, **sampleKwargs) ax.plot(xVals, primaryData, label='Mean value', **meanKwargs) ax = sigmaLabel(ax, xlabel or DEPLETION_PLOT_LABELS[xUnits], ylabel or DEPLETION_PLOT_LABELS[yUnits]) formatPlot(ax, legend=legend, logx=logx, logy=logy, loglog=loglog) return ax
def plot(self, xUnits, yUnits, timePoints=None, names=None, ax=None, sigma=3, xlabel=None, ylabel=None, logx=False, logy=False, loglog=False, legend=None, ncol=1, labelFmt=None, **kwargs): """ Plot the average of some data vs. time for some or all isotopes. .. note:: ``kwargs`` will be passed to the errorbar plot for all isotopes. If ``c='r'`` is passed, to make a plot red, then data for all isotopes plotted will be red and potentially very confusing. Parameters ---------- xUnits: str name of x value to obtain, e.g. ``'days'``, ``'burnup'`` yUnits: str name of y value to return, e.g. ``'adens'``, ``'burnup'`` timePoints: list or None If given, select the time points according to those specified here. Otherwise, select all points names: list or None If given, return y values corresponding to these isotope names. Otherwise, return values for all isotopes. {ax} {sigma} {xlabel} {ylabel} {logx} {logy} {loglog} {legend} {ncol} {matLabelFmt} {kwargs} :py:func:`matplotlib.pyplot.errorbar` Returns ------- {rax} See Also -------- * :meth:`~serpentTools.objects.materials. DepletedMaterialBase.getValues` * :func:`matplotlib.pyplot.errorbar` """ if sigma and yUnits not in self.uncertainties: raise KeyError("Uncertainties for {} not stored".format(yUnits)) if xUnits not in ('days', 'burnup'): raise KeyError("Plot method only uses x-axis data from <days> " "and <burnup>, not {}".format(xUnits)) xVals = timePoints if timePoints is not None else ( self.days if xUnits == 'days' else self.burnup) sigma = int(fabs(sigma)) colIndices = self._getColIndices(xUnits, timePoints) rowIndices = self._getRowIndices(names) yVals = self._slice(self.data[yUnits], rowIndices, colIndices) yUncs = self._slice(self.uncertainties[yUnits], rowIndices, colIndices) if xUnits in self.uncertainties and sigma: xUncs = (sigma * self._slice(self.uncertainties[xUnits], None, colIndices)) else: xUncs = zeros_like(xVals) ax = ax or pyplot.gca() labels = self._formatLabel(labelFmt, names) yVals = yVals.copy(order='F') yUncs = yUncs.copy(order='F') * sigma for row in range(yVals.shape[0]): ax.errorbar(xVals, yVals[row], yerr=yUncs[row], xerr=xUncs, label=labels[row], **kwargs) ax = sigmaLabel(ax, xlabel or DEPLETION_PLOT_LABELS[xUnits], ylabel or DEPLETION_PLOT_LABELS[yUnits], sigma) formatPlot(ax, legend=legend, ncol=ncol, loglog=loglog, logx=logx, logy=logy) return ax
def plotDensity(self, what='both', ax=None, logx=False, logy=True, loglog=None, legend=None, title=None, labels=None, markers=None, xlabel=None, ylabel=None, ylim=None): """ Plot initial, final, or both number densities. Parameters ---------- what: str Concentrations to plot. 1. ``'both'``: plot initial and final 2. ``'n0'`` or ``'initial'``: only initial 3. ``'n1'`` or ``'final'``: only final {ax} {logx} {logy} {loglog} {legend} {title} labels: None or str or list of strings Labels to apply to concentration plot(s). If given, must have length equal to the number of quantities plotted, e.g. plotting both concentrations and passing a single string for ``labels`` is not allowed markers: None or str or list of strings Markers to apply to each plot. Must be a valid ``matplotlib`` marker such as ``'o'``, ``'>'``, etc. If given, the number of markers given must equal the number of quantities to plot. {xlabel} {ylabel} ylim: None or float or list of floats If a single value is given, set the lower y-axis limit to this value. If two values are given, set the upper and lower y-axis limits to this value Returns ------- {rax} """ from matplotlib.pyplot import gca ax = gca() if ax is None else ax if what == 'both': plotAttrs = [self.n0, self.n1] elif what in ['n0', 'initial']: plotAttrs = self.n0, elif what in ['n1', 'final']: plotAttrs = self.n1, else: raise ValueError( "Value of {} not understood. Please pass one of {}".format( what, ' '.join(DENS_PLOT_WHAT_VALS))) if isinstance(labels, str): labels = labels, elif labels is None: if len(plotAttrs) == 2: labels = ('Initial', 'Final') else: labels = (None, ) if len(labels) != len(plotAttrs): raise ValueError( "Number of labels {} not equal to number of quantities " "to plot {}".format(len(labels), len(plotAttrs))) if isinstance(markers, str): markers = markers, elif markers is None: markers = ['x'] if len(plotAttrs) == 1 else ['o', 'x'] if len(markers) != len(plotAttrs): raise ValueError( "Number of markers {} not equal to number of quantities " "to plot {}".format(len(markers), len(plotAttrs))) for qty, label, marker in zip(plotAttrs, labels, markers): ax.plot(self.zai, qty, 'o', marker=marker, label=label) if ylim is not None: ax.set_ylim(ylim) ylabel = r"Atomic Density $[\#/b-cm]$" if ylabel is None else ylabel formatPlot( ax, legend=legend, title=title, logx=logx, logy=logy, loglog=loglog, xlabel="Isotope ZAI" if xlabel is None else xlabel, ylabel=ylabel, ) return ax
def plot(self, xUnits, yUnits=None, timePoints=None, names=None, zai=None, ax=None, legend=None, xlabel=None, ylabel=None, logx=False, logy=False, loglog=False, labelFmt=None, ncol=1, title=None, **kwargs): """ Plot some data as a function of time for some or all isotopes. .. note:: ``kwargs`` will be passed to the plot for all isotopes. If ``c='r'`` is passed, to make a plot red, then data for all isotopes plotted will be red and potentially very confusing. Parameters ---------- xUnits: str name of x value to obtain, e.g. ``'days'``, ``'burnup'`` If ``xUnits`` is given and ``yUnits`` is ``None``, then the plotted data will be ``xUnits`` against ``'days'`` yUnits: str name of y value to return, e.g. ``'adens'``, ``'burnup'`` timePoints: list or None If given, select the time points according to those specified here. Otherwise, select all points .. deprecated:: 0.7.0 Will plot against all time points names: str or list or None If given, plot values corresponding to these isotope names. Otherwise, plot values for all isotopes. zai: int or list or None If given, plot values corresponding to these isotope ``ZZAAAI`` values. Otherwise, plot for all isotopes .. versionadded:: 0.5.1 {ax} {legend} {xlabel} Otherwise, use ``xUnits`` {ylabel} Otherwise, use ``yUnits`` {logx} {logy} {loglog} {matLabelFmt} {ncol} {title} {kwargs} :py:func:`matplotlib.pyplot.plot` Returns ------- {rax} See Also -------- * :py:func:`~serpentTools.objects.materials.DepletedMaterial.getValues` * :py:func:`matplotlib.pyplot.plot` * :py:meth:`str.format` - used for formatting labels Raises ------ KeyError If x axis units are not ``'days'`` nor ``'burnup'`` TypeError If both ``names`` and ``zai`` are given """ if yUnits is None: yUnits = xUnits xUnits = 'days' if xUnits not in ('days', 'burnup'): raise KeyError("Plot method only uses x-axis data from <days> " "and <burnup>, not {}".format(xUnits)) if timePoints is not None: deprecateTimepointsWarning() xVals = timePoints else: xVals = self.days if xUnits == 'days' else self.burnup if names is None and zai is None: names = self.names zai = self.zai if names is None else None else: if isinstance(names, str): names = [names, ] if isinstance(zai, (int, str)): zai = [zai, ] yVals = self.getValues(xUnits, yUnits, xVals, names, zai) 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) ax = formatPlot(ax, loglog=loglog, logx=logx, logy=logy, ncol=ncol, xlabel=xlabel or DEPLETION_PLOT_LABELS[xUnits], ylabel=ylabel or DEPLETION_PLOT_LABELS[yUnits], title=title, legend=legend) return ax
def plot(self, qtys, limitE=True, ax=None, logx=None, logy=None, loglog=None, sigma=3, xlabel=None, ylabel=None, legend=None, ncol=1, steps=True, labelFmt=None, labels=None): """ Plot homogenized data as a function of energy. Parameters ---------- qtys: str or iterable Plot this or these value against energy. limitE: bool If given, set the maximum energy value to be that of the micro group structure. By default, SERPENT macro group structures can reach 1E37, leading for a very large tail on the plots. {ax} {labels} {logx} {logy} {loglog} {sigma} {xlabel} {ylabel} {legend} {ncol} steps: bool If ``True``, plot values as constant within energy bins. {univLabelFmt} Returns ------- {rax} """ qtys = [ qtys, ] if isinstance(qtys, str) else qtys ax = ax or pyplot.gca() onlyXS = True sigma = max(0, int(sigma)) drawstyle = 'steps-post' if steps else None limitE = limitE and (self.groups is not None and self.microGroups is not None) macroBins = self.numGroups + 1 if self.numGroups is not None else None microBins = (self.numMicroGroups + 1 if self.numMicroGroups is not None else None) labelFmt = labelFmt or "{k}" if limitE: eneCap = min(self.microGroups.max(), self.groups.max()) if isinstance(labels, str): labels = [ labels, ] if labels is None: labels = [ labelFmt, ] * len(qtys) else: if len(labels) != len(qtys): raise IndexError( "Need equal number of labels for plot quantities. " "Given {} expected: {}".format(len(labels), len(qtys))) for key, label in zip(qtys, labels): yVals = self.__getitem__(key) if len(yVals.shape) != 1 and 1 not in yVals.shape: warning("Data for {} is not 1D. Will not plot".format(key)) continue uncD = self._lookup(key, True) yUncs = uncD.get(key, zeros_like(yVals)) if 'Flx' in key: onlyXS = False yVals = hstack((yVals, yVals[-1])) nbins = yVals.size yUncs = hstack((yUncs, yUncs[-1])) * yVals * sigma xdata, foundE = self.__getEGrid(nbins, macroBins, microBins) if limitE and foundE: xdata = xdata.copy() xdata[xdata.argmax()] = eneCap label = self.__formatLabel(label, key) ax.errorbar(xdata, yVals, yerr=yUncs, label=label, drawstyle=drawstyle) if ylabel is None: ylabel, yUnits = (("Cross Section", "[cm$^{-1}$]") if onlyXS else ("Group Constant", "")) sigStr = r" $\pm{}\sigma$".format(sigma) if sigma else "" ylabel = ' '.join((ylabel, sigStr, yUnits)) if xlabel is None: xlabel = "Energy [MeV]" if foundE else "Energy Group" if legend is None: legend = len(qtys) > 1 if loglog is not None: logx = logy = loglog else: if logx is None: logx = foundE if logy is None: logy = inferAxScale(ax, 'y') formatPlot(ax, logx=logx, logy=logy, legendcols=ncol, legend=legend, xlabel=xlabel or "Energy [MeV]", ylabel=ylabel) return ax
def plot(self, xUnits, yUnits=None, timePoints=None, names=None, zai=None, materials=None, ax=None, legend=None, logx=False, logy=False, loglog=False, labelFmt=None, xlabel=None, ylabel=None, ncol=1, **kwargs): """ Plot properties for all materials in this file together. Parameters ---------- xUnits: str If ``xUnits`` is given and ``yUnits`` is ``None``, then the plotted data will be ``xUnits`` against ``'days'`` name of x value to obtain, e.g. ``'days'``, ``'burnup'`` yUnits: str name of y value to return, e.g. ``'adens'``, ``'ingTox'`` timePoints: list or None If given, select the time points according to those specified here. Otherwise, select all points .. deprecated:: 0.7.0 Will plot against all time points names: str or list or None If given, plot values corresponding to these isotope names. Otherwise, plot values for all isotopes. zai: int or list or None If given, plot values corresponding to these isotope ``ZZAAAI`` values. Otherwise, plot for all isotopes .. versionadded:: 0.5.1 materials: None or list Selection of materials from ``self.materials`` to plot. If None, plot all materials, potentially including ``tot`` {ax} {legend} {xlabel} Otherwise, use ``xUnits`` {ylabel} Otherwise, use ``yUnits`` {logx} {logy} {loglog} {matLabelFmt} {ncol} {kwargs} :py:func:`matplotlib.pyplot.plot` Returns ------- {rax} See Also -------- * :py:func:`~serpentTools.objects.materials.DepletedMaterial.getValues` * :py:func:`matplotlib.pyplot.plot` * :py:meth:`str.format` - used for formatting labels * :py:func:`~serpentTools.objects.materials.DepletedMaterial.plot` Raises ------ KeyError If x axis units are not ``'days'`` nor ``'burnup'`` SerpentToolsException If the materials dictionary does not contain any items """ if yUnits is None: yUnits = xUnits xUnits = 'days' if not self.materials: raise SerpentToolsException("Material dictionary is empty") if xUnits not in ('days', 'burnup'): raise KeyError("Plot method only uses x-axis data from <days> and " "<burnup>, not {}".format(xUnits)) missing = set() ax = ax or pyplot.gca() materials = materials or self.materials.keys() labelFmt = labelFmt or '{mat} {iso}' for mat in materials: if mat not in self.materials: missing.add(mat) continue ax = self.materials[mat].plot( xUnits, yUnits, timePoints, names, zai, ax, legend=False, xlabel=xlabel, ylabel=ylabel, logx=False, logy=False, loglog=False, labelFmt=labelFmt, **kwargs) if missing: warning("The following materials were not found in materials " "dictionary: {}".format(', '.join(missing))) formatPlot(ax, legend=legend, legendcols=ncol, logx=logx, logy=logy, loglog=loglog, xlabel=xlabel or DEPLETION_PLOT_LABELS[xUnits], ylabel=ylabel or DEPLETION_PLOT_LABELS[yUnits], ) return ax
def plot(self, **kwargs): """Emulate plotting.""" self.ax = formatPlot(self.ax, **kwargs)
def meshPlot(self, xdim='x', ydim='y', what='tallies', fixed=None, ax=None, cmap=None, cbarLabel=None, logColor=False, xlabel=None, ylabel=None, logx=False, logy=False, loglog=False, title=None, **kwargs): """ Plot tally data as a function of two bin types on a cartesian mesh. Parameters ---------- xdim: str Primary dimension - will correspond to x-axis on plot ydim: str Secondary dimension - will correspond to y-axis on plot what: {'tallies', 'errors', 'scores'} Color meshes from tally data, uncertainties, or scores fixed: None or dict Dictionary controlling the reduction in data down to one dimension {ax} {cmap} logColor: bool If true, apply a logarithmic coloring to the data positive data {xlabel} {ylabel} {logx} {logy} {loglog} {title} cbarLabel: str Label to apply to colorbar. If not given, infer from ``what`` {kwargs} :py:func:`~matplotlib.pyplot.pcolormesh` Returns ------- {rax} Raises ------ :class:`~serpentTools.SerpentToolsException` If data to be plotted, with or without constraints, is not 1D KeyError If the data set by ``what`` not in the allowed selection ValueError If the data contains negative quantities and ``logColor`` is ``True`` See Also -------- * :meth:`slice` * :func:`matplotlib.pyplot.pcolormesh` """ if fixed: for qty, name in zip((xdim, ydim), ('x', 'y')): if qty in fixed: raise SerpentToolsException( 'Requested {} dimension {} is one of the axis to be ' 'constrained. '.format(name, qty)) data = self.slice(fixed, what) dShape = data.shape if len(dShape) != 2: raise SerpentToolsException( 'Data must be 2D for mesh plot, currently is {}.\nConstraints:' '{}'.format(dShape, fixed)) xgrid = self._getGrid(xdim) ygrid = self._getGrid(ydim) if data.shape != (ygrid.size - 1, xgrid.size - 1): data = data.T if cbarLabel is None: cbarLabel = self._CBAR_LABELS[what] ax = cartMeshPlot(data, xgrid, ygrid, ax, cmap, logColor, cbarLabel=cbarLabel, **kwargs) if xlabel is None: xlabel = DETECTOR_PLOT_LABELS.get(xdim, xdim) if ylabel is None: ylabel = DETECTOR_PLOT_LABELS.get(ydim, ydim) ax = formatPlot(ax, loglog=loglog, logx=logx, logy=logy, xlabel=xlabel, ylabel=ylabel, title=title) return ax
def plot(self, xdim=None, what='tallies', sigma=None, fixed=None, ax=None, xlabel=None, ylabel=None, steps=False, labels=None, logx=False, logy=False, loglog=False, legend=None, ncol=1, title=None, **kwargs): """ Simple plot routine for 1- or 2-D data Parameters ---------- xdim: None or str If not None, use the array under this key in ``indexes`` as the x axis what: {'tallies', 'errors', 'scores'} Primary data to plot {sigma} fixed: None or dict Dictionary controlling the reduction in data down to one dimension {ax} {xlabel} If ``xdim`` is given and ``xlabel`` is ``None``, then ``xdim`` will be applied to the x-axis. {ylabel} steps: bool If true, plot the data as constant inside the respective bins. Sets ``drawstyle`` to be ``steps-post`` unless ``drawstyle`` given in ``kwargs`` {labels} {logx} {logy} {loglog} {legend} {ncol} {title} {kwargs} :py:func:`~matplotlib.pyplot.plot` or :py:func:`~matplotlib.pyplot.errorbar` function. Returns ------- {rax} Raises ------ :class:`~serpentTools.SerpentToolsException` If data contains more than 2 dimensions See Also -------- * :meth:`slice` * :meth:`spectrumPlot` better options for plotting energy spectra """ data = self.slice(fixed, what) if len(data.shape) > 2: raise SerpentToolsException( 'Data must be constrained to 1- or 2-D, not {}'.format( data.shape)) elif len(data.shape) == 1: data = data.reshape(data.size, 1) if sigma: if what != 'errors': yerr = (self.slice(fixed, 'errors').reshape(data.shape) * data * sigma) else: warning( 'Will not plot error bars on the error plot. Data to be ' 'plotted: {}. Sigma: {}'.format(what, sigma)) yerr = None else: yerr = None xdata, autoX = self._getPlotXData(xdim, data) xlabel = xlabel or autoX ylabel = ylabel or "Tally data" ax = ax or gca() if steps: if 'drawstyle' in kwargs: debug( 'Defaulting to drawstyle specified in kwargs as {}'.format( kwargs['drawstyle'])) else: kwargs['drawstyle'] = 'steps-post' ax = plot(xdata, data, ax, labels, yerr, **kwargs) if legend is None and labels: legend = True ax = formatPlot(ax, loglog=loglog, logx=logx, logy=logy, ncol=ncol, xlabel=xlabel, ylabel=ylabel, legend=legend, title=title) return ax
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): """ Quick plot of the detector value as a function of energy. Parameters ---------- fixed: None or dict Dictionary controlling the reduction in data {ax} normalize: bool Normalize quantities per unit lethargy {xlabel} {ylabel} steps: bool Plot tally as constant inside bin {logx} {logy} {loglog} {sigma} {labels} {legend} {ncol} {title} {kwargs} :py:func:`matplotlib.pyplot.plot` or :py:func:`matplotlib.pyplot.errorbar` Returns ------- {rax} Raises ------ :class:`~serpentTools.SerpentToolsException` if number of rows in data not equal to number of energy groups See Also -------- * :meth:`slice` """ slicedTallies = self.slice(fixed, 'tallies').copy() if len(slicedTallies.shape) > 2: raise SerpentToolsException( 'Sliced data cannot exceed 2-D for spectrum plot, not ' '{}'.format(slicedTallies.shape)) 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() if steps: if 'drawstyle' in kwargs: debug( 'Defaulting to drawstyle specified in kwargs as {}'.format( kwargs['drawstyle'])) else: kwargs['drawstyle'] = 'steps-post' if sigma: slicedErrors = sigma * self.slice(fixed, 'errors').copy() slicedErrors = slicedErrors.reshape(slicedTallies.shape) slicedErrors *= slicedTallies else: slicedErrors = None ax = plot(lowerE, slicedTallies, ax=ax, labels=labels, yerr=slicedErrors, **kwargs) if ylabel is None: ylabel = 'Tally data' ylabel += ' normalized per unit lethargy' if normalize else '' ylabel += r' $\pm{}\sigma$'.format(sigma) if sigma else '' if legend is None and labels: legend = True ax = formatPlot(ax, loglog=loglog, logx=logx, ncol=ncol, xlabel=xlabel or "Energy [MeV]", ylabel=ylabel, legend=legend, title=title) return ax
def plot(self, x, y=None, right=None, sigma=3, ax=None, legend=None, ncol=None, xlabel=True, ylabel=None, logx=False, logy=False, loglog=False, rightlabel=None, **kwargs): """ Plot quantities over time Parameters ---------- x: str or iterable of strings ``y`` is not given, then plot these quantites against burnup in days. Otherwise, plot this quantity as the x axis with same rules as if called by ``plot('burndays', x)``. Burnup options are ``{'burnup', 'days', 'burnDays', 'burnStep'}`` y: str or iterable of strings Quantity or quantities to plot. For all entries, only the first column, with respect to time, will be plotted. If the second column exists, and sigma is > 0, that column will be treated as the relative uncertainty for an errorbar plot. If a dictionary is passed, then plots will be labeled by the values of that dictionary, e.g. ``{'anaKeff': $k_{eff}$}`` would plot the first column of ``anaKeff`` with a ``LaTeX``-ready :math:`k_{eff}` right: str or iterable of strings Quantites to plot on the same plot, but with a different y axis and common x axis. Same rules apply as for arguments to ``y``. Each label will modified to have a unique identifier indicating the plot uses the right y-axis {ax} {sigma} {legend} {ncol} {xlabel} {ylabel} {logx} logy: bool or list or tuple Apply a log scale to the y-axis. If passing values to ``right``, this can be a two item list or tuple, corresponding to log-scaling the left and right axis, respectively. {loglog} rlabel: str or None If given and passing values to ``right``, use this to label the y-axis. {kwargs} : pyplot plot or errorbar depending on if ``sigma`` evaluates to ``True``. Will be passed for both right and left plots. Passing ``"label"`` to label a single plot is not allowed. Instead, pass a dictionary for ``y`` and/or ``right``. Values will be used to label a given quantity. Returns ------- :class:`matplotlib.axes.Axes` or tuple of axes If right is not given, then only the primary axes object is returned. Otherwise, the primary and the "right" axes object are returned """ # cleanup some inputs if y is None: y = x x = "burnDays" sigma = max(int(sigma), 0) y = self._expandPlotIterables(y) if right is not None: right = self._expandPlotIterables(right, ' [right]') if xlabel is True: xlabel = RESULTS_PLOT_XLABELS[x] if "label" in kwargs: raise KeyError("Passing label as kwargs is not supported") if len(y) == 1 and ylabel is None: ylabel = next(iter(y.values())) if sigma: ylabel += r'$ \pm {}\sigma$'.format(sigma) ax = ax or gca() self._plot(x, y, ax, sigma, **kwargs) if right is None: formatPlot(ax, logx=logx, logy=logy, loglog=loglog, xlabel=xlabel, ylabel=ylabel, legend=legend, legendcols=ncol) return ax # plot some other quantity on the same x axis other = ax.twinx() # update color cycle to not repeat colors = rcParams['axes.prop_cycle'].by_key()['color'] colors = colors[len(y):] + colors[:len(y)] other.set_prop_cycle(cycler('color', colors)) self._plot(x, right, other, sigma, **kwargs) # formatting if logy is None or isinstance(logy, bool): logy = [ logy, ] * 2 if loglog is None or isinstance(loglog, bool): loglog = [loglog, None] if legend or legend is None: leftHandles, leftLabels = ax.get_legend_handles_labels() rightHandles, rightLabels = other.get_legend_handles_labels() placeLegend(ax, legend, (leftHandles + rightHandles, leftLabels + rightLabels), ncol=ncol) if len(right) == 1 and rightlabel is None: rightlabel = next(iter(right.values())) if sigma: rightlabel += r'$ \pm {}\sigma$'.format(sigma) formatPlot(ax, logx=logx, logy=logy[0], loglog=loglog[0], legend=False, xlabel=xlabel, ylabel=ylabel) formatPlot(other, logx=False, loglog=False, logy=logy[1], legend=False, ylabel=rightlabel.replace(' [right]', '')) return ax, other
def spreadPlot(self, xUnits, yUnits, isotope, timePoints=None, ax=None, xlabel=None, ylabel=None, logx=False, logy=False, loglog=False, legend=True): """ Plot the mean quantity and data from all sampled files. Parameters ---------- xUnits: str name of x value to obtain, e.g. ``'days'``, ``'burnup'`` yUnits: str name of y value to return, e.g. ``'adens'``, ``'burnup'`` isotope: str Plot data for this isotope timePoints: list or None If given, select the time points according to those specified here. Otherwise, select all points {ax} {xlabel} {ylabel} {logx} {logy} {loglog} {legend} Returns ------- {rax} Raises ------ :class:`~serpentTools.SamplerError` If ``self.allData`` is empty. Sampler was instructed to free all materials and does not retain data from all containers See Also -------- :meth:`~serpentTools.samplers.depletion.SampledDepletedMaterial.plot` """ 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.gca() if xUnits not in ('days', 'burnup'): raise KeyError("Plot method only uses x-axis data from <days> " "and <burnup>, not {}".format(xUnits)) xVals = timePoints if timePoints is not None else ( self.days if xUnits == 'days' else self.burnup) rows = self._getRowIndices([isotope]) cols = self._getColIndices(xUnits, timePoints) primaryData = self._slice(self.data[yUnits], rows, cols)[0] N = self._index sampledData = self.allData[yUnits][:N] for n in range(N): plotData = self._slice(sampledData[n], rows, cols)[0] ax.plot(xVals, plotData, **SPREAD_PLOT_KWARGS) ax.plot(xVals, primaryData, label='Mean value') ax = sigmaLabel(ax, xlabel or DEPLETION_PLOT_LABELS[xUnits], ylabel or DEPLETION_PLOT_LABELS[yUnits]) formatPlot(ax, legend=legend, logx=logx, logy=logy, loglog=loglog) return ax
def hexPlot(self, what='tallies', fixed=None, ax=None, cmap=None, logColor=False, xlabel=None, ylabel=None, logx=False, logy=False, loglog=False, title=None, normalizer=None, cbarLabel=None, borderpad=2.5, **kwargs): """ Create and return a hexagonal mesh plot. Parameters ---------- what: {'tallies', 'errors', 'scores'} Quantity to plot fixed: None or dict Dictionary of slicing arguments to pass to :meth:`slice` {ax} {cmap} {logColor} {xlabel} {ylabel} {logx} {logy} {loglog} {title} borderpad: int or float Percentage of total plot to apply as a border. A value of zero means that the extreme edges of the hexagons will touch the x and y axis. {kwargs} :class:`matplotlib.patches.RegularPolygon` Raises ------ AttributeError If :attr:`pitch` and :attr:`hexType` are not set. """ borderpad = max(0, float(borderpad)) if fixed and ('xcoord' in fixed or 'ycoord' in fixed): raise KeyError("Refusing to restrict along one of the hexagonal " "dimensions {x/y}coord") for attr in {'pitch', 'hexType'}: if getattr(self, attr) is None: raise AttributeError("{} is not set.".format(attr)) for key in {'color', 'fc', 'facecolor', 'orientation'}: checkClearKwargs(key, 'hexPlot', **kwargs) ec = kwargs.get('ec', None) or kwargs.get('edgecolor', None) if ec is None: ec = 'k' kwargs['ec'] = kwargs['edgecolor'] = ec if 'figure' in kwargs and kwargs['figure'] is not None: fig = kwargs['figure'] if not isinstance(fig, Figure): raise TypeError( "Expected 'figure' to be of type Figure, is {}".format( type(fig))) if len(fig.axes) != 1 and not ax: raise TypeError("Don't know where to place the figure since" "'figure' argument has multiple axes.") 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()) alpha = kwargs.get('alpha', None) ny = len(self.indexes['ycoord']) nx = len(self.indexes['xcoord']) data = self.slice(fixed, what) if data.shape != (ny, nx): raise IndexError("Constrained data does not agree with hexagonal " "grid structure. Coordinate grid: {}. " "Constrained shape: {}".format((ny, nx), data.shape)) nItems = ny * nx patches = empty(nItems, dtype=object) values = empty(nItems) coords = self.grids['COORD'] ax = ax or axes() pos = 0 xmax, ymax = [ -inf, ] * 2 xmin, ymin = [ inf, ] * 2 radius = self.pitch / sqrt(3) for xy, val in zip(coords, data.flat): values[pos] = val h = RegularPolygon(xy, 6, radius, self.__hexRot, **kwargs) verts = h.get_verts() vmins = verts.min(0) vmaxs = verts.max(0) xmax = max(xmax, vmaxs[0]) xmin = min(xmin, vmins[0]) ymax = max(ymax, vmaxs[1]) ymin = min(ymin, vmins[1]) patches[pos] = h pos += 1 normalizer = normalizerFactory(values, normalizer, logColor, coords[:, 0], coords[:, 1]) pc = PatchCollection(patches, cmap=cmap, alpha=alpha) pc.set_array(values) pc.set_norm(normalizer) ax.add_collection(pc) addColorbar(ax, pc, None, cbarLabel) formatPlot( ax, loglog=loglog, logx=logx, logy=logy, xlabel=xlabel or "X [cm]", ylabel=ylabel or "Y [cm]", title=title, ) setAx_xlims(ax, xmin, xmax, pad=borderpad) setAx_ylims(ax, ymin, ymax, pad=borderpad) return ax
def plot(self, qtys, limitE=True, ax=None, logx=None, logy=None, loglog=None, sigma=3, xlabel=None, ylabel=None, legend=None, ncol=1, steps=True, labelFmt=None, labels=None, **kwargs): """ Plot homogenized data as a function of energy. Parameters ---------- qtys: str or iterable Plot this or these value against energy. limitE: bool If given, set the maximum energy value to be that of the micro group structure. By default, SERPENT macro group structures can reach 1E37, leading for a very large tail on the plots. {ax} {labels} {logx} {logy} {loglog} {sigma} {xlabel} {ylabel} {legend} {ncol} steps : bool If ``True``, plot values as constant within energy bins. {univLabelFmt} {kwargs} :func:`matplotlib.pyplot.plot` or :func:`matplotlib.pyplot.errorbar` Returns ------- {rax} """ qtys = [ qtys, ] if isinstance(qtys, str) else qtys ax = ax or pyplot.gca() onlyXS = True sigma = max(0, int(sigma)) limitE = limitE and (self.groups is not None and self.microGroups is not None) macroBins = self.numGroups + 1 if self.numGroups is not None else None microBins = (self.numMicroGroups + 1 if self.numMicroGroups is not None else None) labelFmt = labelFmt or "{k}" if limitE: eneCap = min(self.microGroups.max(), self.groups.max()) # Check kwargs if "drawstyle" in kwargs and steps: # Conflicting arguments but defer to user value for now warnings.warn( "Passing steps and drawstyle will default to using the " "drawstyle value and will cause an error after 0.11.0. See " "https://github.com/CORE-GATECH-GROUP/serpent-tools/issues/433", PendingDeprecationWarning) elif steps: kwargs.setdefault("drawstyle", "steps-post") if "label" in kwargs: if len(qtys) > 1: raise ValueError( "Passing label while plotting multiple entries {} is " "not allowed".format(qtys)) if labels is not None: raise ValueError("Passing label and labels is not allowed") labels = kwargs.pop("label") if isinstance(labels, str): labels = [ labels, ] elif labels is None: labels = [ labelFmt, ] * len(qtys) elif len(labels) != len(qtys): raise IndexError( "Need equal number of labels for plot quantities. " "Given {} expected: {}".format(len(labels), len(qtys))) for key, label in zip(qtys, labels): yVals = self.__getitem__(key) if len(yVals.shape) != 1 and 1 not in yVals.shape: warning("Data for {} is not 1D. Will not plot".format(key)) continue uncD = self._lookup(key, True) yUncs = uncD.get(key, zeros_like(yVals)) if 'Flx' in key: onlyXS = False yVals = hstack((yVals, yVals[-1])) nbins = yVals.size yUncs = hstack((yUncs, yUncs[-1])) * yVals * sigma xdata, foundE = self.__getEGrid(nbins, macroBins, microBins) if limitE and foundE: xdata = xdata.copy() xdata[xdata.argmax()] = eneCap label = self.__formatLabel(label, key) ax.errorbar(xdata, yVals, yerr=yUncs, label=label, **kwargs) if ylabel is None: ylabel, yUnits = (("Cross Section", "[cm$^{-1}$]") if onlyXS else ("Group Constant", "")) sigStr = r" $\pm{}\sigma$".format(sigma) if sigma else "" ylabel = ' '.join((ylabel, sigStr, yUnits)) if xlabel is None: xlabel = "Energy [MeV]" if foundE else "Energy Group" if legend is None: legend = len(qtys) > 1 if loglog is not None: logx = logy = loglog else: if logx is None: logx = foundE if logy is None: logy = inferAxScale(ax, 'y') formatPlot(ax, logx=logx, logy=logy, legendcols=ncol, legend=legend, xlabel=xlabel or "Energy [MeV]", ylabel=ylabel) return ax