Incorporated ability to transfer from Library to MGXS_Library. Didnt test yet.

This commit is contained in:
Adam Nelson 2016-05-01 10:23:20 -04:00
parent d2018de8ec
commit e1f70b40d4
2 changed files with 260 additions and 92 deletions

View file

@ -4,6 +4,8 @@ import copy
import pickle
from numbers import Integral
from collections import OrderedDict
import numpy as np
import warnings.warn as warn
import openmc
import openmc.mgxs
@ -712,3 +714,169 @@ class Library(object):
# Load and return pickled Library object
return pickle.load(open(full_filename, 'rb'))
def write_mg_library(self, xs_type='micro', domain_names=None, xs_ids=None,
filename='mg_cross_sections', directory='./'):
"""Create a cross-section data library file for the Multi-Group
mode of OpenMC.
Parameters
----------
xs_type: {'macro', 'micro'}
Provide the macro or micro cross section in units of cm^-1 or
barns. Defaults to 'macro'. If the Library object is not tallied by
nuclide this will be set to 'macro' regardless
domain_names : Iterable of str
List of names to apply to the xsdata entries in the
resultant mgxs data file. Defaults to "set1", "set2", ...
xs_ids : str or Iterable of str
Cross section set identifier (i.e., "71c") for all
data sets (if only str) or for each individual one
(if iterable of str). Defaults to '1g'
filename : str
Filename for the pickle file. Defaults to 'mg_cross_sections'.
directory : str
Directory for the pickle file. Defaults to './' (the
current working directory).
See also
--------
Library.dump_to_file(mgxs_lib, filename, directory)
"""
# Check data types provided
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
if not self.by_nuclide:
xs_type = 'macro'
if domain_names is not None:
cv.check_iterable_type('domain_names', filename, basestring)
if xs_ids is not None:
if isinstance(xs_ids, basestring):
# If we only have a string lets convert it now to a list
# of strings.
xs_ids = [xs_ids for i in range(len(self.domains))]
else:
cv.check_iterable_type('xs_ids', xs_ids, basestring)
else:
xs_ids = ['.1g']
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)
full_filename = os.path.join(directory, filename + '.xml')
full_filename = full_filename.replace(' ', '-')
# Initialize file
mgxs_file = openmc.MGXSLibrary(self.energy_groups)
# Set the scattering order as isotropic until
# support for higher orders are included
order = 0
# Build XSdata objects
xsdatas = []
for i in range(len(self.domains)):
id = self.domains[i].id
if not self.by_nuclide:
# Use k instead of i simply because k will be used for
# the nuclide index in the else part of this conditional
# and using k allows us to use the same code.
k = i
# Build & add metadata to XSdata object
# (Use i here because k in nuclides will add chars to this)
if domain_names is None:
name = 'set' + str(i + 1)
else:
name = domain_names[i]
name += xs_ids[k]
xsdata = openmc.XSdata(name, self.energy_groups)
xsdata.order = order
# Now get xs data itself
if 'total' in self.mgxs_types:
xsdata.set_total(self.all_mgxs[id]['total'],
xs_type=xs_type, subdomains=(k + 1,))
if 'absorption' in self.mgxs_types:
xsdata.set_absorption(self.all_mgxs[id]['absorption'],
xs_type=xs_type, subdomains=(k + 1,))
if 'fission' in self.mgxs_types:
xsdata.set_fission(self.all_mgxs[id]['fission'],
xs_type=xs_type, subdomains=(k + 1,))
if 'kappa-fission' in self.mgxs_types:
xsdata.set_k_fission(self.all_mgxs[id]['kappa-fission'],
xs_type=xs_type, subdomains=(k + 1,))
if 'chi' in self.mgxs_types:
xsdata.set_chi(self.all_mgxs[id]['chi'],
xs_type=xs_type, subdomains=(k + 1,))
if 'nu-fission' in self.mgxs_types:
xsdata.set_nu_fission(self.all_mgxs[id]['nu-fission'],
xs_type=xs_type, subdomains=(k + 1,))
# multiplicity requires scatter and nu-scatter
if (('scatter' in self.mgxs_types) and ('nu-scatter' in
self.mgxs_types)):
xsdata.set_multiplicity(self.all_mgxs[id]['nu-scatter'],
self.all_mgxs[id]['scatter'],
xs_type=xs_type,
subdomains=(k + 1,))
using_multiplicity = True
else:
using_multiplicity = False
if using_multiplicity:
xsdata.set_scatter(self.all_mgxs[id]['scatter'],
xs_type=xs_type,
subdomains=(k + 1,))
else:
if 'nu-scatter' in self.mgxs_types:
xsdata.set_scatter(self.all_mgxs[id]['nu-scatter'],
xs_type=xs_type,
subdomains=(k + 1,))
# Since we are not using multiplicity, then
# scattering multiplication (nu-scatter) must be
# accounted for approximately by using an adjusted
# absorption cross section.
if self.total is not None:
xsdata.absorption = \
np.subtract(xsdata.total,
np.sum(xsdata.scatter[0, :, :],
axis=1))
else:
# Total isnt included so we cant do the above
# approximation w/out changing absorption instead.
# That can be done with:
# SigA' = SigA - (nuSigS - SigS)
# Doing so would mean essentially duplicating
# set_scatter from MGXSLibrary to obtain the
# SigS, which would be big and ugly once
# angle filters are available.
# Instead, raise a warning about the
# lack of neutron balance and then use scatter
# instead of nu-scatter
if 'scatter' in self.mgxs_types:
msg = "To properly use the 'nu-scatter' " + \
"MGXS type and maintain neutron " + \
"balance, a 'total' MGXS type " + \
"should be provided."
warn(msg)
xsdata.set_scatter(
self.all_mgxs[id]['scatter'],
xs_type=xs_type, subdomains=(k + 1,))
else:
# Welp, cant do that either. Quit while ahead.
msg = "Total X/S must be provided if using" + \
" nu-scatter as the scattering data"
raise ValueError(msg)
xsdatas.append(xsdata)
else:
pass
# Add XSdatas to file
# Finally, write the file
mgxs_file.export_to_xml(full_filename)

View file

@ -739,6 +739,98 @@ class XSdata(object):
'required, "{1}"'.format(fission.shape, shape)
raise ValueError(msg)
def set_nu_fission(self, nu_fission, **kwargs):
# The NuFissionXS class does not have the capability to produce
# a fission matrix and therefore if this path is pursued, we know
# chi must be used.
if isinstance(nu_fission, openmc.mgxs.NuFissionXS):
# Make sure passed MGXS object contains correct group structure
if self.energy_groups != nu_fission.energy_groups:
msg = 'Group structure of provided NuFissionXS does not match'\
' group structure of XSdata object'
raise ValueError(msg)
# Get openmc.mgxs.get_xs() arguments from kwargs
# nuclides, xs_type, and value will have sane defaults but can be
# overridden by kwards
if 'nuclides' in kwargs:
nuclides = kwargs['nuclides']
else:
nuclides = 'sum'
if 'xs_type' in kwargs:
xs_type = kwargs['xs_type']
else:
xs_type = 'macro'
if 'value' in kwargs:
value = kwargs['value']
else:
value = 'mean'
# subdomains is required from the kwargs as this is specific to
# this XSdata object.
if 'subdomains' in kwargs:
subdomains = kwargs['subdomains']
else:
msg = "Argument 'subdomains' is required"
raise ValueError(msg)
if self._representation is 'isotropic':
self._nu_fission = nu_fission.get_xs(subdomains=subdomains,
nuclides=nuclides,
xs_type=xs_type,
value=value)
elif self._representation is 'angle':
# Not yet implemented as MGXS do not yet support this
pass
self._use_chi = True
else:
# nu_fission can be given as a vector or a matrix
# Vector is used when chi also exists.
# Matrix is used when chi does not exist.
# We have to check that the correct form is given, but only if
# chi already has been set. If not, we just check that this is OK
# and set the use_chi flag accordingly
# First lets set our dimensions here since they get used repeatedly
# throughout this code.
if self._representation is 'isotropic':
shape_vec = (self._energy_groups.num_groups,)
shape_mat = (self._energy_groups.num_groups,
self._energy_groups.num_groups)
elif self._representation is 'angle':
shape_vec = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups)
shape_mat = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups,
self._energy_groups.num_groups)
# Begin by checking the case when chi has already been given and
# thus the rules for filling in nu_fission are set.
if self._use_chi is not None:
if self._use_chi:
shape = shape_vec
else:
shape = shape_mat
if nu_fission.shape != shape:
msg = "Invalid Shape of Nu_fission!"
raise ValueError(msg)
else:
# Get shape of nu_fission to determine if we need chi or not
if nu_fission.shape == shape_vec:
self._use_chi = True
elif nu_fission.shape == shape_mat:
self._use_chi = False
else:
msg = "Invalid Shape of Nu_fission!"
raise ValueError(msg)
# check we have a numpy list
check_type("nu_fission", nu_fission, np.ndarray,
expected_iter_type=Real)
self._nu_fission = np.copy(nu_fission)
if np.sum(self._nu_fission) > 0.0:
self._fissionable = True
def set_k_fission(self, k_fission, **kwargs):
if isinstance(k_fission, openmc.mgxs.KappaFissionXS):
# Make sure passed MGXS object contains correct group structure
@ -986,98 +1078,6 @@ class XSdata(object):
' required, "{1}"'.format(multiplicity.shape, shape)
raise ValueError(msg)
def set_nu_fission(self, nu_fission, **kwargs):
# The NuFissionXS class does not have the capability to produce
# a fission matrix and therefore if this path is pursued, we know
# chi must be used.
if isinstance(nu_fission, openmc.mgxs.NuFissionXS):
# Make sure passed MGXS object contains correct group structure
if self.energy_groups != nu_fission.energy_groups:
msg = 'Group structure of provided NuFissionXS does not match'\
' group structure of XSdata object'
raise ValueError(msg)
# Get openmc.mgxs.get_xs() arguments from kwargs
# nuclides, xs_type, and value will have sane defaults but can be
# overridden by kwards
if 'nuclides' in kwargs:
nuclides = kwargs['nuclides']
else:
nuclides = 'sum'
if 'xs_type' in kwargs:
xs_type = kwargs['xs_type']
else:
xs_type = 'macro'
if 'value' in kwargs:
value = kwargs['value']
else:
value = 'mean'
# subdomains is required from the kwargs as this is specific to
# this XSdata object.
if 'subdomains' in kwargs:
subdomains = kwargs['subdomains']
else:
msg = "Argument 'subdomains' is required"
raise ValueError(msg)
if self._representation is 'isotropic':
self._nu_fission = nu_fission.get_xs(subdomains=subdomains,
nuclides=nuclides,
xs_type=xs_type,
value=value)
elif self._representation is 'angle':
# Not yet implemented as MGXS do not yet support this
pass
self._use_chi = True
else:
# nu_fission can be given as a vector or a matrix
# Vector is used when chi also exists.
# Matrix is used when chi does not exist.
# We have to check that the correct form is given, but only if
# chi already has been set. If not, we just check that this is OK
# and set the use_chi flag accordingly
# First lets set our dimensions here since they get used repeatedly
# throughout this code.
if self._representation is 'isotropic':
shape_vec = (self._energy_groups.num_groups,)
shape_mat = (self._energy_groups.num_groups,
self._energy_groups.num_groups)
elif self._representation is 'angle':
shape_vec = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups)
shape_mat = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups,
self._energy_groups.num_groups)
# Begin by checking the case when chi has already been given and
# thus the rules for filling in nu_fission are set.
if self._use_chi is not None:
if self._use_chi:
shape = shape_vec
else:
shape = shape_mat
if nu_fission.shape != shape:
msg = "Invalid Shape of Nu_fission!"
raise ValueError(msg)
else:
# Get shape of nu_fission to determine if we need chi or not
if nu_fission.shape == shape_vec:
self._use_chi = True
elif nu_fission.shape == shape_mat:
self._use_chi = False
else:
msg = "Invalid Shape of Nu_fission!"
raise ValueError(msg)
# check we have a numpy list
check_type("nu_fission", nu_fission, np.ndarray,
expected_iter_type=Real)
self._nu_fission = np.copy(nu_fission)
if np.sum(self._nu_fission) > 0.0:
self._fissionable = True
def _get_xsdata_xml(self):
element = ET.Element("xsdata")
element.set("name", self._name)