Copyright (c) 2017-2020 Serpent-Tools developer team, GTRC
THE 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. For this tutorial, the files are placed in the directory identified with the SERPENT_TOOLS_DATA
environment variable.
In [1]:
import os
resFile = os.path.join(
os.environ["SERPENT_TOOLS_DATA"],
"InnerAssembly_res.m")
This notebook demonstrates the capabilities of the serpentTools to read Serpent results files. SERPENT [1] produces a result file (i.e. _res.m
), containing general results (e.g. k-eff
), metadata (e.g. title
) and homogenized cross-sections. The homogenized cross-section sets are printed in the results file for all the requested universes. The ResultsReader is capable of reading this file, and storing the data inside univ objects. Each such object has methods and attributes that should ease the analyses.
In [2]:
import numpy as np
import serpentTools
from serpentTools.settings import rc
rc['serpentVersion'] = '2.1.30'
In [3]:
res = serpentTools.read(resFile)
metadata
is a collective data that describes the problem. These are values that do not change over burnup and across homogenized universes. The following data is included: titles, data paths, and other descriptive data exist on the reader.
In [4]:
print(res.metadata['version']) # Serpent version used for the execution
print(res.metadata['decayDataFilePath']) # Directory path for data libraries
print(res.metadata['inputFileName']) # Directory path for data libraries
In [5]:
res.metadata.keys()
Out[5]:
In [6]:
res.metadata['startDate']
Out[6]:
In [7]:
res.metadata['pop'], res.metadata['skip'] , res.metadata['cycles']
Out[7]:
Results are stored as a function of time/burnup/index and include integral parameters of the system.
Results, such as k-eff
, total flux
, and execution times are included in .resdata
. Some results include values and uncertainities (e.g. criticality) and some just the values (e.g. CPU resources).
In [8]:
sorted(res.resdata.keys())[0:5]
Out[8]:
Values are presented in similar fashion as if they were read in to Matlab, with one exception. Serpent currently appends a new row for each burnup step, but also for each homogenized universe. This results in repetition of many quantities as Serpent loops over group constant data. The ResultsReader
understands Serpent outputs and knows when to append "new" result data to avoid repetition.
The structure of the data is otherwise identical to Matlab. For many quantities, the first column indicates expected value, while the second column contains relative uncertainties. For a better reference, please consult the Serpent wiki on structure of the main output file.
In [9]:
res.resdata['absKeff']
Out[9]:
In [10]:
res.resdata['absKeff'][:,0]
Out[10]:
In [11]:
res.resdata['burnup']
Out[11]:
In [12]:
res.resdata['burnDays']
Out[12]:
In [13]:
res.resdata['totCpuTime']
Out[13]:
Data in the resdata
dictionary can be obtained by indexing directly into the reader with
In [13]:
res["burnup"]
Out[13]:
In [15]:
res.get("absKeff")
Out[15]:
The ResultsReader
has a versatile plot method, used to plot primary time-dependent data from the result file. With it, one can plot
data from one or more quanties against various metrics of time. Control over formatting axis, legend placement, and label formatting is easily yielded to the user.
In [ ]:
res.plot('absKeff')
Out[ ]:
In [ ]:
res.plot('burnup', ['absKeff', 'colKeff'])
Out[ ]:
Pass a dictionary of {variable: label}
pairs to set plot labels
In [ ]:
# plot multiple values with better labels and formatting
res.plot(
'burnup', {'absKeff': '$k_{eff}^{abs}$', 'colKeff': '$k_{eff}^{col}$'},
ylabel=r'Criticality $\pm 3\sigma$',
legend='above', ncol=2)
Out[ ]:
Using the right
argument, quantities can be plotted on the left and right y-axis. Similar formatting options are available
In [ ]:
res.plot(
'burnStep', {'actinideIngTox': 'Actinide Ingestion Tox'},
right={'totCpuTime': "CPU Time [right]"}, sigma=0, rightlabel='CPU Time',
logy=[False, True])
Out[ ]:
Universe data is stored for each state point in the universes
dictionary. Keys are UnivTuple
representing ('univ',burnup, step, time)
'univ'
: universe ID (e.g., '0'
)burnup
: in MWd/kgstep
: step index, time
: in days.
In [ ]:
for key in sorted(res.universes):
break
key
Out[ ]:
In [ ]:
key[0]
Out[ ]:
In [ ]:
assert key.burnup == key[1]
Results, such as infinite cross-sections, b1-leakage corrected cross-sections, kinetic parameters, are included in .universes
. All the results include values and uncertainties.
In [24]:
sorted(res.universes)[:10]
Out[24]:
One can directly index into the universes
dictionary to obtain data for a specific universe.
In [ ]:
print(res.universes['0', 0, 0, 0])
However this requires knowledge of all four parameters which may be difficult. The getUniv
method retrieves a specific universe that matches universe id and time of interest.
While all four identifers, (universe id, burnup, step, and time) can be provided, the latter three are usually redundant.
In [ ]:
univ0 = res.getUniv('0', index=0)
print(univ0)
In [ ]:
univ3101 = res.getUniv('3101', index=3)
print(univ3101)
In [ ]:
univ3102 = res.getUniv('3102', burnup=0.1)
print(univ3102)
In [ ]:
print(res.getUniv('0', timeDays=24.0096))
Each state contains the same data fields, which can be obatined by using the following attributes on the HomogUniv
objects:
infExp
: infinite values, e.g. INF_ABS
,
infUnc
: infinite uncertainties,
b1Exp
: b1 (leakage corrected) values, e.g. B1_ABS
,
b1Exp
: b1 (leakage corrected) uncertainties,
gc
: variables that are not included in 'inf' or 'b1', e.g. BETA
gcUnc
: group uncertainties
groups
: macro energy group structure, MeV
microGroups
: micro energy group structure, MeV
The parser reads all variables by default, and the HomogUniv
objects stores data in various dictionaries. Data are energy dependent, exactly as they would appear in Serpent outputs.
In [30]:
sorted(univ0.infExp)[:10]
Out[30]:
In [31]:
univ0.infExp['infAbs']
Out[31]:
In [ ]:
univ0["infAbs"]
Out[ ]:
In [ ]:
univ0.infExp['infFlx']
Out[ ]:
Uncertainties can be obtained by using the infUnc
, b1Unc
, and gcUnc
dictionaries. Uncertainties are relative, as they appear in the output files.
In [ ]:
univ0.infUnc['infFlx']
Out[ ]:
Serpent also outputs the B1
cross-sections. However, the user must enable the B1
option by setting the fum
card. If this card is not enabled by the user, the B1_
variables will all be zeros.
In [36]:
sorted(univ0.b1Exp)[:10]
Out[36]:
In [37]:
univ0.b1Exp['b1Flx']
Out[37]:
In [ ]:
univ0["b1Flx"]
Out[ ]:
In [ ]:
univ0.b1Exp['b1Abs']
Out[ ]:
Data that does not contain the prefix INF_
or B1_
is stored under the gc
and gcUnc
fields. Criticality, kinetic, and other variables are stored under this field.
In [41]:
sorted(univ0.gc)[:5]
Out[41]:
In [42]:
univ3101.gc['betaEff']
Out[42]:
In [ ]:
univ3101["betaEff"]
Out[ ]:
Macro- and micro-group structures are stored directly in the universe in MeV as they appear in the Serpent output files. This means that the macro-group structure is in order of descending energy, while micro-group are in order of increasing energy.
In [ ]:
univ3101.groups
Out[ ]:
In [ ]:
univ3101.microGroups[:5:]
Out[ ]:
In [ ]:
univ0.plot(['infAbs', 'b1Abs']);
Macroscopic and microscopic quantities, such as micro-group flux, can be plotted on the same figure. Note The units and presentation of the micro- and macro-group fluxes are dissimilar, and the units do not agree with that of the assumed group constants. This will adjust the default y-label, as demonstrated below.
In [ ]:
univ0.plot(['infTot', 'infFlx', 'infMicroFlx'], legend='right');
For plotting data from multiple universes, pass the returned matplotlib.axes.Axes
object, on which the plot was drawn, into the plot method for the next universe. The labelFmt
argument can be used to differentiate between plotted data. The following strings are replaced when creating the labels:
String | Replaced value |
---|---|
{k} |
Name of variable plotted |
{u} |
Name of this universe |
{b} |
Value of burnup in MWd/kgU |
{d} |
Value of burnup in days |
{i} |
Burnup index |
These can be used in conjunction with the matplotlib
$\LaTeX$ rendering system.
In [ ]:
fmt = r"Universe {u} - $\Sigma_{abs}^\infty$"
ax = univ3101.plot('infFiss', labelFmt=fmt)
univ3102.plot('infFiss', ax=ax, labelFmt=fmt, legend='above', ncol=2);
The user is able to filter the required information by using the settings option.
A detailed description on how to use the settings can be found on: http://serpent-tools.readthedocs.io/en/latest/settingsTop.html
In [51]:
rc.keys()
Out[51]:
The rc
object and various xs.*
settings can be used to control the ResultsReader
. Specifically, these settings can be used to store only specific bits of information. Here, we will store the version of Serpent, various cross sections, eigenvalues, and burnup data.
In [52]:
rc['xs.variableGroups'] = ['versions', 'xs', 'eig', 'burnup-coeff']
Further, instruct the reader to only read infinite medium cross sections, not critical spectrum B1 cross sections.
In [53]:
rc['xs.getB1XS'] = False
In [54]:
resFilt = serpentTools.read(resFile)
In [55]:
resFilt.metadata.keys()
Out[55]:
In [56]:
resFilt.resdata.keys()
Out[56]:
In [57]:
univ0Filt = resFilt.getUniv('0', index=1)
In [58]:
univ0Filt.infExp.keys()
Out[58]:
In [59]:
univ0Filt.b1Exp
Out[59]: