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
branchFile = os.path.join(
    os.environ["SERPENT_TOOLS_DATA"],
    "demo.coe",
)

Branching Reader

Basic Operation

This notebook demonstrates the capability of the serpentTools package to read branching coefficient files. The format of these files is structured to iterate over:

  1. Branch states, e.g. burnup, material properties
  2. Homogenized universes
  3. Group constant data

The output files are described in more detail on the SERPENT Wiki

Note

Without modifying the settings, the BranchingReader assumes that all group constant data is presented without the associated uncertainties. See below for examples on the various ways to adjust the UserSettings


In [2]:
%matplotlib inline
import serpentTools

In [3]:
r0 = serpentTools.read(branchFile)

The branches are stored in custom dictionary-like BranchContainer objects in the branches dictionary


In [4]:
r0.branches.keys()


Out[4]:
dict_keys([('nom', 'nom'), ('B750', 'nom'), ('B1000', 'nom'), ('nom', 'FT1200'), ('B750', 'FT1200'), ('B1000', 'FT1200'), ('nom', 'FT600'), ('B750', 'FT600'), ('B1000', 'FT600')])

Here, the keys are tuples of strings indicating what perturbations/branch states were applied for each SERPENT solution. Examining a particular case


In [5]:
b0 = r0.branches['B1000', 'FT600']
print(b0)


<BranchContainer for B1000, FT600 from /home/drew/serpent-tools/examples/../data/demo.coe>

SERPENT allows the user to define variables for each branch through:

var V1_name V1_value

cards. These are stored in the stateData attribute


In [6]:
b0.stateData


Out[6]:
{'VERSION': '2.1.29',
 'DATE': '17/12/19',
 'TIME': '09:48:54',
 'BOR': '1000',
 'TFU': '600'}

The keys 'DATE', 'TIME', and 'VERSION' are included by default in the output, while the 'BOR' and 'TFU' have been defined for this branch.

Group Constant Data

Note: Group constants are converted from SERPENT_STYLE to mixedCase to fit the overall style of the project.

The BranchContainer stores group constant data in HomogUniv objects as a dictionary.


In [7]:
for key in b0:
    print(key)


UnivTuple(universe='0', burnup=0.0, step=0, days=None)
UnivTuple(universe='10', burnup=0.0, step=0, days=None)
UnivTuple(universe='20', burnup=0.0, step=0, days=None)
UnivTuple(universe='30', burnup=0.0, step=0, days=None)
UnivTuple(universe='40', burnup=0.0, step=0, days=None)
UnivTuple(universe='0', burnup=1.0, step=1, days=None)
UnivTuple(universe='10', burnup=1.0, step=1, days=None)
UnivTuple(universe='20', burnup=1.0, step=1, days=None)
UnivTuple(universe='30', burnup=1.0, step=1, days=None)
UnivTuple(universe='40', burnup=1.0, step=1, days=None)
UnivTuple(universe='0', burnup=10.0, step=2, days=None)
UnivTuple(universe='10', burnup=10.0, step=2, days=None)
UnivTuple(universe='20', burnup=10.0, step=2, days=None)
UnivTuple(universe='30', burnup=10.0, step=2, days=None)
UnivTuple(universe='40', burnup=10.0, step=2, days=None)

The keys here are UnivTuple instances indicating the universe ID and point in burnup schedule. These universes can be obtained by indexing into the BranchContainer, or by using the getUniv method.


In [8]:
univ0 = b0['0', 1, 1, None]
univ0


Out[8]:
<serpentTools.objects.containers.HomogUniv at 0x7f87c7797e50>

In [9]:
univ0.name, univ0.bu, univ0.step, univ0.day


Out[9]:
('0', 1.0, 1, None)

In [10]:
univ1 = b0.getUniv('0', burnup=1)
univ2 = b0.getUniv('0', index=1)
assert univ0 is univ1 is univ2

Group constant data is spread out across sub-dictionaries:

  1. infExp: Expected values for infinite medium group constants
  2. infUnc: Relative uncertainties for infinite medium group constants
  3. b1Exp: Expected values for leakge-corrected group constants
  4. b1Unc: Relative uncertainties for leakge-corrected group constants
  5. gc: Group constant data that does not match the INF nor B1 scheme
  6. gcUnc: Relative uncertainties for data in gc

For this problem, only expected values for infinite and critical spectrum (B1) group constants are returned, so only the infExp and b1Exp dictionaries contain data


In [11]:
univ0.infExp


Out[11]:
{'infTot': array([0.310842, 0.618286]),
 'infFiss': array([0.00271604, 0.059773  ]),
 'infS0': array([0.298689  , 0.00197521, 0.00284247, 0.470054  ]),
 'infS1': array([0.0847372 , 0.00047366, 0.00062865, 0.106232  ]),
 'infDiffcoef': array([1.83961 , 0.682022])}

In [12]:
univ0.infUnc


Out[12]:
{}

In [13]:
univ0.b1Exp


Out[13]:
{'b1Tot': array([0.314521, 0.618361]),
 'b1Fiss': array([0.00278366, 0.0597712 ]),
 'b1S0': array([0.301766  , 0.0021261 , 0.00283866, 0.470114  ]),
 'b1S1': array([0.0856397 , 0.00051071, 0.00062781, 0.106232  ]),
 'b1Diffcoef': array([1.79892 , 0.765665])}

In [14]:
univ0.gc


Out[14]:
{}

In [15]:
univ0.gcUnc


Out[15]:
{}

Group constants and their associated uncertainties can be obtained using the get method.


In [16]:
univ0.get('infFiss')


Out[16]:
array([0.00271604, 0.059773  ])

In [17]:
try:
    univ0.get('infS0', uncertainty=True)
except KeyError as ke:  # no uncertainties here
    print(str(ke))


'Variable infS0 absent from uncertainty dictionary'

Plotting Universe Data

HomogUniv objects are capable of plotting homogenized data using the plot method. This method is tuned to plot group constants, such as cross sections, for a known group structure. This is reflected in the default axis scaling, but can be adjusted on a per case basis. If the group structure is not known, then the data is plotted simply against bin-index.


In [18]:
univ0.plot('infFiss')


Out[18]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f87c77d1910>

In [19]:
univ0.plot(['infFiss', 'b1Tot'], loglog=False);


The ResultsReader example has a more thorough example of this plot method, including formatting the line labels.

Iteration

The branching reader has a iterBranches method that works to yield branch names and their associated BranchContainer objects. This can be used to efficiently iterate over all the branches presented in the file.


In [20]:
for names, branch in r0.iterBranches():
    print(names, branch)


('nom', 'nom') <BranchContainer for nom, nom from /home/drew/serpent-tools/examples/../data/demo.coe>
('B750', 'nom') <BranchContainer for B750, nom from /home/drew/serpent-tools/examples/../data/demo.coe>
('B1000', 'nom') <BranchContainer for B1000, nom from /home/drew/serpent-tools/examples/../data/demo.coe>
('nom', 'FT1200') <BranchContainer for nom, FT1200 from /home/drew/serpent-tools/examples/../data/demo.coe>
('B750', 'FT1200') <BranchContainer for B750, FT1200 from /home/drew/serpent-tools/examples/../data/demo.coe>
('B1000', 'FT1200') <BranchContainer for B1000, FT1200 from /home/drew/serpent-tools/examples/../data/demo.coe>
('nom', 'FT600') <BranchContainer for nom, FT600 from /home/drew/serpent-tools/examples/../data/demo.coe>
('B750', 'FT600') <BranchContainer for B750, FT600 from /home/drew/serpent-tools/examples/../data/demo.coe>
('B1000', 'FT600') <BranchContainer for B1000, FT600 from /home/drew/serpent-tools/examples/../data/demo.coe>

User Control

The SERPENT set coefpara card already restricts the data present in the coeffient file to user control, and the BranchingReader includes similar control. Below are the various settings that the BranchingReader uses to read and process coefficient files.

In our example above, the BOR and TFU variables represented boron concentration and fuel temperature, and can easily be cast into numeric values using the branching.floatVariables and branching.intVariables settings. From the previous example, we see that the default action is to store all state data variables as strings.


In [21]:
assert isinstance(b0.stateData['BOR'], str)

As demonstrated in the Settings example, use of xs.variableExtras xs.variableGroupscontrols what data is stored on the HomogUniv objects. By default, all variables present in the coefficient file are stored.


In [22]:
from serpentTools.settings import rc
rc['branching.floatVariables'] = ['BOR']
rc['branching.intVariables'] = ['TFU']
rc['xs.getB1XS'] = False
rc['xs.variableExtras'] = ['INF_TOT', 'INF_SCATT0']
r1 = serpentTools.read(branchFile)

In [23]:
b1 = r1.branches['B1000', 'FT600']

In [24]:
b1.stateData


Out[24]:
{'VERSION': '2.1.29',
 'DATE': '17/12/19',
 'TIME': '09:48:54',
 'BOR': 1000.0,
 'TFU': 600}

In [25]:
assert isinstance(b1.stateData['BOR'], float)
assert isinstance(b1.stateData['TFU'], int)

Inspecting the data stored on the homogenized universes reveals only the variables explicitly requested are present


In [26]:
univ4 = b1.getUniv('0', 0)
univ4.infExp


Out[26]:
{'infTot': array([0.313338, 0.54515 ])}

In [27]:
univ4.b1Exp


Out[27]:
{}

Conclusion

The BranchingReader is capable of reading coefficient files created by the SERPENT automated branching process. The data is stored according to the branch parameters, universe information, and burnup. This reader also supports user control of the processing by selecting what state parameters should be converted from strings to numeric types, and further down-selection of data.