mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 06:05:58 -04:00
First implementation of MultiGroupXS class
This commit is contained in:
parent
88f53f6bdf
commit
cfc8164043
3 changed files with 879 additions and 41 deletions
|
|
@ -1 +1 @@
|
|||
__author__ = 'wboyd'
|
||||
from groups import EnergyGroups
|
||||
|
|
@ -1,9 +1,15 @@
|
|||
import copy
|
||||
from collections import Iterable
|
||||
from numbers import Real, Integral
|
||||
import copy
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openmc.checkvalue import *
|
||||
import openmc.checkvalue as cv
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
class EnergyGroups(object):
|
||||
"""An energy groups structure used for multi-group cross-sections.
|
||||
|
|
@ -54,8 +60,8 @@ class EnergyGroups(object):
|
|||
|
||||
@group_edges.setter
|
||||
def group_edges(self, edges):
|
||||
check_type('group edges', edges, list, Integral)
|
||||
check_length('number of group edges', edges, 2)
|
||||
cv.check_type('group edges', edges, Iterable, Integral)
|
||||
cv.check_length('number of group edges', edges, 2)
|
||||
self._group_edges = np.array(edges)
|
||||
self._num_groups = len(edges)-1
|
||||
|
||||
|
|
@ -80,19 +86,20 @@ class EnergyGroups(object):
|
|||
The spacing between groups ('linear' or 'logarithmic')
|
||||
|
||||
"""
|
||||
check_type('first edge', start, Real)
|
||||
check_type('last edge', stop, Real)
|
||||
check_type('number of groups', num_groups, Integral)
|
||||
check_type('type', type, str)
|
||||
check_greater_than('first edge', start, 0, equality=True)
|
||||
check_greater_than('first edge', stop, start, equality=False)
|
||||
check_greater_than('number of groups', num_groups, 0)
|
||||
check_value('type', type, ('linear', 'logarithmic'))
|
||||
|
||||
cv.check_type('first edge', start, Real)
|
||||
cv.check_type('last edge', stop, Real)
|
||||
cv.check_type('number of groups', num_groups, Integral)
|
||||
cv.check_type('type', type, basestring)
|
||||
cv.check_greater_than('first edge', start, 0, True)
|
||||
cv.check_greater_than('first edge', stop, start, False)
|
||||
cv.check_greater_than('number of groups', num_groups, 0)
|
||||
cv.check_value('type', type, ('linear', 'logarithmic'))
|
||||
|
||||
if type == 'linear':
|
||||
self._group_edges = np.linspace(start, stop, num_groups+1)
|
||||
self.group_edges = np.linspace(start, stop, num_groups+1)
|
||||
elif type == 'logarithmic':
|
||||
self._group_edges = \
|
||||
self.group_edges = \
|
||||
np.logspace(np.log10(start), np.log10(stop), num_groups+1)
|
||||
|
||||
self._num_groups = num_groups
|
||||
|
|
@ -117,13 +124,13 @@ class EnergyGroups(object):
|
|||
|
||||
"""
|
||||
|
||||
if self._group_edges is None:
|
||||
if self.group_edges is None:
|
||||
msg = 'Unable to get energy group for energy "{0}" eV since ' \
|
||||
'the group edges have not yet been set'.format(energy)
|
||||
raise ValueError(msg)
|
||||
|
||||
index = np.where(self._group_edges > energy)[0]
|
||||
group = self._num_groups - index
|
||||
index = np.where(self.group_edges > energy)[0]
|
||||
group = self.num_groups - index
|
||||
return group
|
||||
|
||||
def get_group_bounds(self, group):
|
||||
|
|
@ -146,16 +153,15 @@ class EnergyGroups(object):
|
|||
|
||||
"""
|
||||
|
||||
if self._group_edges is None:
|
||||
if self.group_edges is None:
|
||||
msg = 'Unable to get energy group bounds for group "{0}" since ' \
|
||||
'the group edges have not yet been set'.format(group)
|
||||
raise ValueError(msg)
|
||||
|
||||
lower = self._group_edges[self._num_groups-group]
|
||||
upper = self._group_edges[self._num_groups-group+1]
|
||||
lower = self.group_edges[self.num_groups-group]
|
||||
upper = self.group_edges[self.num_groups-group+1]
|
||||
return (lower, upper)
|
||||
|
||||
|
||||
def get_group_indices(self, groups='all'):
|
||||
"""Returns the array indices for one or more energy groups.
|
||||
|
||||
|
|
@ -178,27 +184,23 @@ class EnergyGroups(object):
|
|||
|
||||
"""
|
||||
|
||||
if self._group_edges is None:
|
||||
if self.group_edges is None:
|
||||
msg = 'Unable to get energy group indices for groups "{0}" since ' \
|
||||
'the group edges have not yet been set'.format(groups)
|
||||
raise ValueError(msg)
|
||||
|
||||
if groups == 'all':
|
||||
indices = np.arange(self._num_groups)
|
||||
indices = np.arange(self.num_groups)
|
||||
else:
|
||||
indices = np.zeros(len(groups), dtype=np.int64)
|
||||
|
||||
for i, group in enumerate(groups):
|
||||
if group > 0 and group <= self._num_groups:
|
||||
indices[i] = group - 1
|
||||
else:
|
||||
msg = 'Unable to get energy group index for group "{0}" ' \
|
||||
'since it is outside the group bounds'.format(group)
|
||||
raise ValueError(msg)
|
||||
cv.check_greater_than('group', group, 0)
|
||||
cv.check_less_than('group', group, self.num_groups, True)
|
||||
indices[i] = group - 1
|
||||
|
||||
return indices
|
||||
|
||||
|
||||
def get_condensed_groups(self, coarse_groups):
|
||||
"""Return a coarsened version of this EnergyGroups object.
|
||||
|
||||
|
|
@ -225,15 +227,15 @@ class EnergyGroups(object):
|
|||
If the group edges have not yet been set.
|
||||
"""
|
||||
|
||||
check_type('group edges', coarse_groups, list)
|
||||
cv.check_type('group edges', coarse_groups, Iterable)
|
||||
for group in coarse_groups:
|
||||
check_value('group edges', group, tuple)
|
||||
check_length('group edges', group, 2)
|
||||
check_greater_than('lower group', group[0], 1, True)
|
||||
check_less_than('lower group', group[0], self.num_groups, True)
|
||||
check_greater_than('upper group', group[0], 1, True)
|
||||
check_less_than('upper group', group[0], self.num_groups, True)
|
||||
check_less_than('lower group', group[0], group[1], False)
|
||||
cv.check_value('group edges', group, Iterable)
|
||||
cv.check_length('group edges', group, 2)
|
||||
cv.check_greater_than('lower group', group[0], 1, True)
|
||||
cv.check_less_than('lower group', group[0], self.num_groups, True)
|
||||
cv.check_greater_than('upper group', group[0], 1, True)
|
||||
cv.check_less_than('upper group', group[0], self.num_groups, True)
|
||||
cv.check_less_than('lower group', group[0], group[1], False)
|
||||
|
||||
# Compute the group indices into the coarse group
|
||||
group_bounds = list()
|
||||
|
|
@ -243,12 +245,12 @@ class EnergyGroups(object):
|
|||
|
||||
# Determine the indices mapping the fine-to-coarse energy groups
|
||||
group_bounds = np.asarray(group_bounds)
|
||||
group_indices = np.flipud(self._num_groups - group_bounds)
|
||||
group_indices = np.flipud(self.num_groups - group_bounds)
|
||||
group_indices[-1] += 1
|
||||
|
||||
# Determine the edges between coarse energy groups and sort
|
||||
# in increasing order in case the user passed in unordered groups
|
||||
group_edges = self._group_edges[group_indices]
|
||||
group_edges = self.group_edges[group_indices]
|
||||
group_edges = np.sort(group_edges)
|
||||
|
||||
# Create a new condensed EnergyGroups object
|
||||
|
|
|
|||
836
openmc/mgxs/mgxs.py
Normal file
836
openmc/mgxs/mgxs.py
Normal file
|
|
@ -0,0 +1,836 @@
|
|||
from collections import Iterable
|
||||
from numbers import Integral, Real
|
||||
import os
|
||||
import sys
|
||||
import copy
|
||||
import abc
|
||||
import pickle
|
||||
import subprocess
|
||||
|
||||
import numpy as np
|
||||
|
||||
import openmc
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mgxs import EnergyGroups
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
|
||||
# Supported cross-section types
|
||||
XS_TYPES = ['total',
|
||||
'transport',
|
||||
'absorption',
|
||||
'capture',
|
||||
'scatter',
|
||||
'nu-scatter',
|
||||
'scatter matrix',
|
||||
'nu-scatter matrix',
|
||||
'fission',
|
||||
'nu-fission',
|
||||
'chi']
|
||||
|
||||
# Supported domain types
|
||||
DOMAIN_TYPES = ['cell',
|
||||
'distribcell',
|
||||
'universe',
|
||||
'material',
|
||||
'mesh']
|
||||
|
||||
# Supported domain objects
|
||||
DOMAINS = [openmc.Cell,
|
||||
openmc.Universe,
|
||||
openmc.Material,
|
||||
openmc.Mesh]
|
||||
|
||||
# LaTeX Greek symbols for each cross-section type
|
||||
GREEK = dict()
|
||||
GREEK['total'] = '$\\Sigma_{t}$'
|
||||
GREEK['transport'] = '$\\Sigma_{tr}$'
|
||||
GREEK['absorption'] = '$\\Sigma_{a}$'
|
||||
GREEK['capture'] = '$\\Sigma_{c}$'
|
||||
GREEK['scatter'] = '$\\Sigma_{s}$'
|
||||
GREEK['nu-scatter'] = '$\\nu\\Sigma_{s}$'
|
||||
GREEK['scatter matrix'] = '$\\Sigma_{s}$'
|
||||
GREEK['nu-scatter matrix'] = '$\\nu\\Sigma_{s}$'
|
||||
GREEK['fission'] = '$\\Sigma_{f}$'
|
||||
GREEK['nu-fission'] = '$\\nu\\Sigma_{f}$'
|
||||
GREEK['chi'] = '$\\chi$'
|
||||
GREEK['diffusion'] = '$D$'
|
||||
|
||||
|
||||
def flip_axis(arr, axis=0):
|
||||
"""Flip contents of `axis` in array 'arr'
|
||||
Taken verbatim from:
|
||||
https://github.com/nipy/nibabel/blob/master/nibabel/orientations.py
|
||||
"""
|
||||
arr = np.asanyarray(arr)
|
||||
arr = arr.swapaxes(0, axis)
|
||||
arr = np.flipud(arr)
|
||||
return arr.swapaxes(axis, 0)
|
||||
|
||||
|
||||
class MultiGroupXS(object):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
# This is an abstract class which cannot be instantiated
|
||||
metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, name='', domain=None,
|
||||
domain_type=None, energy_groups=None):
|
||||
"""
|
||||
:param name:
|
||||
:param domain:
|
||||
:param domain_type:
|
||||
:param energy_groups:
|
||||
:return:
|
||||
"""
|
||||
|
||||
self._name = ''
|
||||
self._xs_type = None
|
||||
self._domain = None
|
||||
self._domain_type = None
|
||||
self._energy_groups = None
|
||||
self._num_groups = None
|
||||
self._tallies = dict()
|
||||
self._xs = None
|
||||
|
||||
# A dictionary used to compute indices into the xs array
|
||||
# Keys - Domain ID (ie, Material ID, Region ID for districell, etc)
|
||||
# Values - Offset/stride into xs array
|
||||
self._subdomain_offsets = dict()
|
||||
self._offset = None
|
||||
|
||||
self.name = name
|
||||
if not domain_type is None:
|
||||
self.domain_type = domain_type
|
||||
if not domain is None:
|
||||
self.domain = domain
|
||||
if not energy_groups is None:
|
||||
self.energy_groups = energy_groups
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
existing = memo.get(id(self))
|
||||
|
||||
# If this is the first time we have tried to copy this object, create a copy
|
||||
if existing is None:
|
||||
clone = type(self).__new__(type(self))
|
||||
clone._name = self._name
|
||||
clone._xs_type = self._xs_type
|
||||
clone._domain = self._domain
|
||||
clone._domain_type = self._domain_type
|
||||
clone._energy_groups = copy.deepcopy(self._energy_groups, memo)
|
||||
clone._num_groups = self._num_groups
|
||||
clone._xs = copy.deepcopy(self._xs, memo)
|
||||
clone._subdomain_offsets = copy.deepcopy(self._subdomain_offsets, memo)
|
||||
clone._offset = copy.deepcopy(self._offset, memo)
|
||||
|
||||
clone._tallies = dict()
|
||||
for tally_type, tally in self._tallies.items():
|
||||
clone._tallies[tally_type] = copy.deepcopy(tally, memo)
|
||||
|
||||
memo[id(self)] = clone
|
||||
|
||||
return clone
|
||||
|
||||
# If this object has been copied before, return the first copy made
|
||||
else:
|
||||
return existing
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
return self._domain
|
||||
|
||||
@property
|
||||
def domain_type(self):
|
||||
return self._domain_type
|
||||
|
||||
@property
|
||||
def energy_groups(self):
|
||||
return self._energy_groups
|
||||
|
||||
@property
|
||||
def num_groups(self):
|
||||
return self._num_groups
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
cv.check_type('MultiGroupXS name', name, basestring)
|
||||
self._name = name
|
||||
|
||||
@domain.setter
|
||||
def domain(self, domain):
|
||||
cv.check_type('MultiGroupXS domain', domain, DOMAINS)
|
||||
self._domain = domain
|
||||
if self._domain_type in ['material', 'cell', 'universe', 'mesh']:
|
||||
self._subdomain_offsets[domain.id] = 0
|
||||
|
||||
@energy_groups.setter
|
||||
def energy_groups(self, energy_groups):
|
||||
cv.check_type('MultiGroupXS energy groups', energy_groups,
|
||||
openmc.mgxs.EnergyGroups)
|
||||
self._energy_groups = energy_groups
|
||||
self._num_groups = energy_groups._num_groups
|
||||
|
||||
@domain_type.setter
|
||||
def domain_type(self, domain_type):
|
||||
cv.check_type('MultiGroupXS domain type', domain_type, DOMAIN_TYPES)
|
||||
self._domain_type = domain_type
|
||||
|
||||
def find_domain_offset(self):
|
||||
tally = self.tallies[self.tallies.keys()[0]]
|
||||
filter = tally.find_filter(self.domain_type, [self.domain.id])
|
||||
self._offset = filter.offset
|
||||
|
||||
def set_subdomain_offset(self, domain_id, offset):
|
||||
"""
|
||||
:param domain_id:
|
||||
:param offset:
|
||||
:return:
|
||||
"""
|
||||
|
||||
cv.check_type('subdomain id', domain_id, Integral)
|
||||
cv.check_type('subdomain offset', offset, Integral)
|
||||
self._subdomain_offsets[domain_id] = offset
|
||||
|
||||
@abc.abstractmethod
|
||||
def _create_tallies(self, scores, filters, keys, estimator):
|
||||
"""
|
||||
|
||||
:param scores:
|
||||
:param filters:
|
||||
:param keys:
|
||||
:param estimator:
|
||||
:return:
|
||||
"""
|
||||
|
||||
if self.energy_groups is None:
|
||||
raise ValueError('Unable to create Tallies without energy groups')
|
||||
elif self.domain is None:
|
||||
raise ValueError('Unable to create Tallies without a domain')
|
||||
elif self.domain_type is None:
|
||||
raise ValueError('Unable to create Tallies without a domain type')
|
||||
|
||||
cv.check_type('scores', scores, Iterable, basestring)
|
||||
cv.check_value('scores', scores, openmc.SCORE_TYPES)
|
||||
cv.check_type('filters', scores, Iterable, openmc.Filter)
|
||||
cv.check_type('keys', keys, Iterable, basestring)
|
||||
cv.check_value('# scores', len(scores), len(keys))
|
||||
cv.check_type('estimator', estimator, basestring)
|
||||
cv.check_value('estimator', estimator, ['analog', 'tracklength'])
|
||||
|
||||
# Create a domain Filter object
|
||||
domain_filter = openmc.Filter(self.domain_type, self.domain.id)
|
||||
|
||||
for score, key, filters in zip(scores, keys, filters):
|
||||
self.tallies[key] = openmc.Tally(name=self.name)
|
||||
self.tallies[key].add_score(score)
|
||||
self.tallies[key].estimator = estimator
|
||||
self.tallies[key].add_filter(domain_filter)
|
||||
|
||||
# Add all non-domain specific Filters (ie, energy) to the Tally
|
||||
for filter in filters:
|
||||
self.tallies[key].add_filter(filter)
|
||||
|
||||
def get_subdomain_offsets(self, subdomains='all'):
|
||||
"""
|
||||
|
||||
:param subdomains:
|
||||
:return:
|
||||
"""
|
||||
|
||||
if subdomains != 'all':
|
||||
cv.check_type('subdomains', subdomains, Iterable, Integral)
|
||||
|
||||
if subdomains == 'all':
|
||||
offsets = np.arange(self.xs.shape[1])
|
||||
else:
|
||||
offsets = np.zeros(len(subdomains), dtype=np.int64)
|
||||
|
||||
for i, subdomain in enumerate(subdomains):
|
||||
if subdomain in self._subdomain_offsets:
|
||||
offsets[i] = self._subdomain_offsets[subdomain]
|
||||
else:
|
||||
msg = 'Unable to get index for subdomain "{0}" since it ' \
|
||||
'is not a subdomain in cross-section'.format(subdomain)
|
||||
raise ValueError(msg)
|
||||
|
||||
return offsets
|
||||
|
||||
def get_subdomains(self, offsets='all'):
|
||||
|
||||
if offsets != 'all':
|
||||
cv.check_type('offsets', offsets, Iterable, Integral)
|
||||
|
||||
if offsets == 'all':
|
||||
offsets = self.get_subdomain_offsets()
|
||||
|
||||
subdomains = np.zeros(len(offsets), dtype=np.int64)
|
||||
keys = self._subdomain_offsets.keys()
|
||||
values = self._subdomain_offsets.values()
|
||||
|
||||
for i, offset in enumerate(offsets):
|
||||
if offset in values:
|
||||
subdomains[i] = keys[values.index(offset)]
|
||||
else:
|
||||
msg = 'Unable to get subdomain for offset "{0}" since it ' \
|
||||
'is not an offset in the cross-section'.format(offset)
|
||||
raise ValueError(msg)
|
||||
|
||||
return subdomains
|
||||
|
||||
def get_xs(self, groups='all', subdomains='all', metric='mean'):
|
||||
|
||||
if self.xs is None:
|
||||
msg = 'Unable to get cross-section since it has not been computed'
|
||||
raise ValueError(msg)
|
||||
|
||||
cv.check_value('metric', metric, ['mean', 'std. dev.', 'rel. err.'])
|
||||
if groups != 'all':
|
||||
cv.check_value('groups', groups, Iterable, Integral)
|
||||
if subdomains != 'all':
|
||||
cv.check_value('subdomains', subdomains, Iterable, Integral)
|
||||
|
||||
# FIXME: Make this use Tally.get_values()
|
||||
|
||||
def get_condensed_xs(self, coarse_groups):
|
||||
"""This routine takes in a collection of 2-tuples of energy groups"""
|
||||
|
||||
cv.check_value('coarse groups', coarse_groups, EnergyGroups)
|
||||
|
||||
# FIXME: this should use the Tally.slice(...) routine
|
||||
|
||||
def get_domain_vg_xs(self, subdomains='all'):
|
||||
|
||||
if self.domain_type != 'distribcell':
|
||||
msg = 'Unable to compute domain averaged "{0}" xs for "{1}"' \
|
||||
'"{2}" since it is not a distribcell'.format(self._xs_type,
|
||||
self._domain_type, self._domain.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if subdomains != 'all':
|
||||
cv.check_value('subdomains', subdomains, Iterable, Integral)
|
||||
|
||||
# FIXME: This should use tally arithmetic
|
||||
|
||||
def print_xs(self, subdomains='all'):
|
||||
|
||||
if subdomains != 'all':
|
||||
cv.check_value('subdomains', subdomains, Iterable, Integral)
|
||||
|
||||
string = 'Multi-Group XS\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.xs_type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tDomain Type', '=\t', self.domain_type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tDomain ID', '=\t', self.domain.id)
|
||||
|
||||
if subdomains == 'all':
|
||||
subdomains = self._subdomain_offsets.keys()
|
||||
|
||||
# Loop over all subdomains
|
||||
for subdomain in subdomains:
|
||||
|
||||
if self.domain_type == 'distribcell':
|
||||
string += '{0: <16}{1}{2}\n'.format('\tSubDomain', '=\t', subdomain)
|
||||
|
||||
string += '{0: <16}\n'.format('\tCross-Sections [cm^-1]:')
|
||||
|
||||
# Loop over energy groups ranges
|
||||
for group in range(1,self.num_groups+1):
|
||||
bounds = self._energy_groups.getGroupBounds(group)
|
||||
string += '{0: <12}Group {1} [{2: <10} - ' \
|
||||
'{3: <10}MeV]:\t'.format('', group, bounds[0], bounds[1])
|
||||
average = self.get_xs([group], [subdomain], 'mean')
|
||||
rel_err = self.get_xs([group], [subdomain], 'rel. err.')
|
||||
string += '{:.2e}+/-{:1.2e}%'.format(average[0,0,0], rel_err[0,0,0])
|
||||
string += '\n'
|
||||
string += '\n'
|
||||
|
||||
print(string)
|
||||
|
||||
def dump_to_file(self, filename='multigroupxs', directory='multigroupxs'):
|
||||
|
||||
cv.check_type('filename', filename, basestring)
|
||||
cv.check_type('directory', directory, basestring)
|
||||
|
||||
# Make directory if it does not exist
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
# Create an empty dictionary to store the data
|
||||
xs_results = dict()
|
||||
|
||||
# Store all of this MultiGroupXS' class attributes in the dictionary
|
||||
xs_results['name'] = self.name
|
||||
xs_results['xs type'] = self.xs_type
|
||||
xs_results['domain type'] = self.domain_type
|
||||
xs_results['domain'] = self.domain
|
||||
xs_results['energy groups'] = self.energy_groups
|
||||
xs_results['tallies'] = self.tallies
|
||||
xs_results['xs'] = self.xs
|
||||
xs_results['offset'] = self._offset
|
||||
xs_results['subdomain offsets'] = self._subdomain_offsets
|
||||
|
||||
# Pickle the MultiGroupXS results to a file
|
||||
filename = directory + '/' + filename + '.pkl'
|
||||
filename = filename.replace(' ', '-')
|
||||
pickle.dump(xs_results, open(filename, 'wb'))
|
||||
|
||||
def restore_from_file(self, filename='multigroupxs', directory='multigroupxs'):
|
||||
|
||||
cv.check_type('filename', filename, basestring)
|
||||
cv.check_type('directory', directory, basestring)
|
||||
|
||||
filename = directory + '/' + filename + '.pkl'
|
||||
filename = filename.replace(' ', '-')
|
||||
|
||||
# Check that the file exists
|
||||
if not os.path.exists(filename):
|
||||
msg = 'Unable to import from filename="{0}"'.format(filename)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Load the pickle file into a dictionary
|
||||
xs_results = pickle.load(open(filename, 'rb'))
|
||||
|
||||
# Store the MultiGroupXS class attributes
|
||||
self.name = xs_results['name']
|
||||
self.xs_type = xs_results['xs type']
|
||||
self.domain_type = xs_results['domain type']
|
||||
self.domain = xs_results['domain']
|
||||
self.energy_groups = xs_results['energy groups']
|
||||
self.tallies = xs_results['tallies']
|
||||
self.xs = xs_results['xs']
|
||||
self._offset = xs_results['offset']
|
||||
self._subdomain_offsets = xs_results['subdomain offsets']
|
||||
|
||||
def exportResults(self, subdomains='all', filename='multigroupxs',
|
||||
directory='multigroupxs', format='hdf5', append=True):
|
||||
|
||||
if subdomains != 'all':
|
||||
cv.check_type('submdomains', subdomains, Iterable, Integral)
|
||||
cv.check_type('filename', filename, basestring)
|
||||
cv.check_type('directory', directory, basestring)
|
||||
cv.check_values('format', format, ['hdf5', 'pickle'])
|
||||
cv.check_type('append', append, bool)
|
||||
|
||||
# Make directory if it does not exist
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
# FIXME: Use tally arithmetic!!!
|
||||
|
||||
def print_pdf(self, subdomains='all', filename='multigroupxs',
|
||||
directory='multigroupxs'):
|
||||
|
||||
if subdomains != 'all':
|
||||
cv.check_type('submdomains', subdomains, Iterable, Integral)
|
||||
cv.check_type('filename', filename, basestring)
|
||||
cv.check_type('directory', directory, basestring)
|
||||
|
||||
# Make directory if it does not exist
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
filename = filename.replace(' ', '-')
|
||||
|
||||
# Generate LaTeX file
|
||||
self.exportResults(subdomains, filename, '.', 'latex', False)
|
||||
|
||||
# Compile LaTeX to PDF
|
||||
FNULL = open(os.devnull, 'w')
|
||||
subprocess.check_call('pdflatex {0}.tex'.format(filename),
|
||||
shell=True, stdout=FNULL)
|
||||
|
||||
# Move PDF to requested directory and cleanup temporary LaTeX files
|
||||
if directory != '.':
|
||||
os.system('mv {0}.pdf {1}'.format(filename, directory))
|
||||
os.system('rm {0}.tex {0}.aux {0}.log'.format(filename))
|
||||
|
||||
|
||||
class TotalXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, groups=None):
|
||||
super(TotalXS, self).__init__(name, domain, domain_type, groups)
|
||||
self.xs_type = 'total'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'total']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(TotalXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['total'] / self.tallies['flux']
|
||||
|
||||
|
||||
class TransportXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, groups=None):
|
||||
super(TransportXS, self).__init__(name, domain, domain_type, groups)
|
||||
self.xs_type = 'transport'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'total', 'scatter-1']
|
||||
estimator = 'analog'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
energyout_filter = openmc.Filter('energyout', group_edges)
|
||||
filters = [[energy_filter], [energy_filter], [energyout_filter]]
|
||||
|
||||
# Initialize the Tallies
|
||||
super(TransportXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['total'] - self.tallies['scatter-1']
|
||||
self.xs /= self.tallies['flux']
|
||||
|
||||
|
||||
class AbsorptionXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, groups=None):
|
||||
super(AbsorptionXS, self).__init__(name, domain, domain_type, groups)
|
||||
self.xs_type = 'absorption'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'absorption']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(AbsorptionXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['absorption'] / self.tallies['flux']
|
||||
|
||||
|
||||
class CaptureXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, groups=None):
|
||||
super(CaptureXS, self).__init__(name, domain, domain_type, groups)
|
||||
self._xs_type = 'capture'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'absorption', 'fission']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter], [energy_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(CaptureXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['absorption'] - self.tallies['fission']
|
||||
self.xs /= self.tallies['flux']
|
||||
|
||||
|
||||
class FissionXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, energy_groups=None):
|
||||
super(FissionXS, self).__init__(name, domain, domain_type, energy_groups)
|
||||
self._xs_type = 'fission'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'fission']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self._energy_groups._group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(FissionXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['fission'] / self.tallies['flux']
|
||||
|
||||
|
||||
class NuFissionXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, groups=None):
|
||||
super(NuFissionXS, self).__init__(name, domain, domain_type, groups)
|
||||
self._xs_type = 'nu-fission'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'nu-fission']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(NuFissionXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['nu-fission'] / self.tallies['flux']
|
||||
|
||||
|
||||
class ScatterXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, energy_groups=None):
|
||||
super(ScatterXS, self).__init__(name, domain, domain_type, energy_groups)
|
||||
self._xs_type = 'scatter'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'scatter']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(ScatterXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['scatter'] / self.tallies['flux']
|
||||
|
||||
|
||||
class NuScatterXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, groups=None):
|
||||
super(NuScatterXS, self).__init__(name, domain, domain_type, groups)
|
||||
self._xs_type = 'nu-scatter'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'nu-scatter']
|
||||
estimator = 'analog'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(NuScatterXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['nu-scatter'] / self.tallies['flux']
|
||||
|
||||
|
||||
class ScatterMatrixXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, groups=None):
|
||||
super(ScatterMatrixXS, self).__init__(name, domain, domain_type, groups)
|
||||
self._xs_type = 'scatter matrix'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'scatter', 'scatter-1']
|
||||
estimator = 'analog'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
energyout_filter = openmc.Filter('energyout', group_edges)
|
||||
filters = [[energy_filter], [energy_filter, energyout_filter], [energyout_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(ScatterMatrixXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['scatter'] - self.tallies['scatter-1']
|
||||
self.xs /= self.tallies['flux']
|
||||
|
||||
def get_condensed_xs(self, coarse_groups):
|
||||
"""This routine takes in a collection of 2-tuples of energy groups"""
|
||||
|
||||
cv.check_value('coarse groups', coarse_groups, EnergyGroups)
|
||||
|
||||
# FIXME: this should use the Tally.slice(...) routine
|
||||
|
||||
# Error checking for the group bounds is done here
|
||||
new_groups = self.energy_groups.getCondensedGroups(coarse_groups)
|
||||
num_coarse_groups = new_groups._num_groups
|
||||
|
||||
def get_xs(self, in_groups='all', out_groups='all',
|
||||
subdomains='all', metric='mean'):
|
||||
|
||||
if self.xs is None:
|
||||
msg = 'Unable to get cross-section since it has not been computed'
|
||||
raise ValueError(msg)
|
||||
|
||||
cv.check_value('metric', metric, ['mean', 'std. dev.', 'rel. err.'])
|
||||
if in_groups != 'all':
|
||||
cv.check_value('in groups', in_groups, Iterable, Integral)
|
||||
if out_groups != 'all':
|
||||
cv.check_value('out groups', out_groups, Iterable, Integral)
|
||||
if subdomains != 'all':
|
||||
cv.check_value('subdomains', subdomains, Iterable, Integral)
|
||||
|
||||
# FIXME: Make this use Tally.get_values()
|
||||
|
||||
def print_xs(self, subdomains='all'):
|
||||
|
||||
if subdomains != 'all':
|
||||
cv.check_value('subdomains', subdomains, Iterable, Integral)
|
||||
|
||||
string = 'Multi-Group XS\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.xs_type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tDomain Type', '=\t', self.domain_type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tDomain ID', '=\t', self.domain.id)
|
||||
|
||||
string += '{0: <16}\n'.format('\tEnergy Groups:')
|
||||
|
||||
# Loop over energy groups ranges
|
||||
for group in range(1,self.num_groups+1):
|
||||
bounds = self.energy_groups.get_group_bounds(group)
|
||||
string += '{0: <12}Group {1} [{2: <10} - ' \
|
||||
'{3: <10}MeV]\n'.format('', group, bounds[0], bounds[1])
|
||||
|
||||
if subdomains == 'all':
|
||||
subdomains = self._subdomain_offsets.keys()
|
||||
|
||||
for subdomain in subdomains:
|
||||
|
||||
if self.domain_type == 'distribcell':
|
||||
string += '{0: <16}{1}{2}\n'.format('\tSubDomain', '=\t', subdomain)
|
||||
|
||||
string += '{0: <16}\n'.format('\tCross-Sections [cm^-1]:')
|
||||
|
||||
# Loop over energy groups ranges
|
||||
for in_group in range(1,self.num_groups+1):
|
||||
for out_group in range(1,self.num_groups+1):
|
||||
string += '{0: <12}Group {1} -> Group {2}:\t\t'.format('', in_group, out_group)
|
||||
average = self.get_xs([in_group], [out_group], [subdomain], 'mean')
|
||||
rel_err = self.get_xs([in_group], [out_group], [subdomain], 'rel. err.')
|
||||
string += '{:.2e}+/-{:1.2e}%'.format(average[0,0,0], rel_err[0,0,0])
|
||||
string += '\n'
|
||||
string += '\n'
|
||||
print(string)
|
||||
|
||||
|
||||
class NuScatterMatrixXS(ScatterMatrixXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, groups=None):
|
||||
super(NuScatterMatrixXS, self).__init__(name, domain, domain_type, groups)
|
||||
self.xs_type = 'nu-scatter matrix'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'nu-scatter', 'scatter-1']
|
||||
estimator = 'analog'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
energyout_filter = openmc.Filter('energyout', group_edges)
|
||||
filters = [[energy_filter], [energy_filter, energyout_filter], [energyout_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(ScatterMatrixXS, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
self.xs = self.tallies['nu-scatter'] - self.tallies['scatter-1']
|
||||
self.xs /= self.tallies['flux']
|
||||
|
||||
|
||||
class Chi(MultiGroupXS):
|
||||
|
||||
def __init__(self, name='', domain=None, domain_type=None, groups=None):
|
||||
super(Chi, self).__init__(name, domain, domain_type, groups)
|
||||
self._xs_type = 'chi'
|
||||
|
||||
def create_tallies(self):
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['nu-fission', 'nu-fission']
|
||||
estimator = 'analog'
|
||||
keys = ['nu-fission-in', 'nu-fission-out']
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self._energy_groups._group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
energyout_filter = openmc.Filter('energyout', group_edges)
|
||||
filters = [[energy_filter], [energyout_filter]]
|
||||
|
||||
# Intialize the Tallies
|
||||
super(Chi, self)._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
def compute_xs(self):
|
||||
|
||||
# Extract and clean the Tally data
|
||||
tally_data, zero_indices = super(Chi, self).getAllTallyData()
|
||||
nu_fission_in = tally_data['nu-fission-in']
|
||||
nu_fission_out = tally_data['nu-fission-out']
|
||||
|
||||
# Set any zero reaction rates to -1
|
||||
nu_fission_in[0, zero_indices['nu-fission-in']] = -1.
|
||||
|
||||
# FIXME - uncertainty propagation
|
||||
self._xs = infermc.error_prop.arithmetic.divide_by_scalar(nu_fission_out,
|
||||
nu_fission_in.sum(2)[0, :, np.newaxis, ...],
|
||||
corr, False)
|
||||
|
||||
# Compute the total across all groups per subdomain
|
||||
norm = self._xs.sum(2)[0, :, np.newaxis, ...]
|
||||
|
||||
# Set any zero norms (in non-fissionable domains) to -1
|
||||
norm_indices = norm == 0.
|
||||
norm[norm_indices] = -1.
|
||||
|
||||
# Normalize chi to 1.0
|
||||
# FIXME - uncertainty propagation
|
||||
self._xs = infermc.error_prop.arithmetic.divide_by_scalar(self._xs, norm,
|
||||
corr, False)
|
||||
|
||||
# For any region without flux or reaction rate, convert xs to zero
|
||||
self._xs[:, norm_indices] = 0.
|
||||
|
||||
# FIXME - uncertainty propagation - this is just a temporary fix
|
||||
self._xs[1, ...] = 0.
|
||||
|
||||
# Correct -0.0 to +0.0
|
||||
self._xs += 0.
|
||||
Loading…
Add table
Add a link
Reference in a new issue