Remove initial dilute nuclides in MicroXS (#2579)

This commit is contained in:
Paul Romano 2023-06-29 04:06:35 -05:00 committed by GitHub
parent b6c621f9b4
commit 36f229eb01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 101 deletions

View file

@ -5,14 +5,13 @@ nuclide names as row indices and reaction names as column indices.
"""
import tempfile
from copy import deepcopy
from pandas import DataFrame, read_csv, Series
import numpy as np
from openmc.checkvalue import check_type, check_value, check_iterable_type
from openmc.exceptions import DataError
from openmc import StatePoint, Materials
from openmc import StatePoint
import openmc
from .chain import Chain, REACTIONS
from .coupled_operator import _find_cross_sections, _get_nuclides_with_data
@ -31,9 +30,10 @@ class MicroXS(DataFrame):
@classmethod
def from_model(cls,
model,
reaction_domain,
domain,
nuclides=None,
reactions=None,
chain_file=None,
dilute_initial=1.0e3,
energy_bounds=(0, 20e6),
run_kwargs=None):
"""Generate a one-group cross-section dataframe using OpenMC.
@ -44,17 +44,19 @@ class MicroXS(DataFrame):
----------
model : openmc.Model
OpenMC model object. Must contain geometry, materials, and settings.
reaction_domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh
domain : openmc.Material or openmc.Cell or openmc.Universe
Domain in which to tally reaction rates.
nuclides : list of str
Nuclides to get cross sections for. If not specified, all burnable
nuclides from the depletion chain file are used.
reactions : list of str
Reactions to get cross sections for. If not specified, all neutron
reactions listed in the depletion chain file are used.
chain_file : str, optional
Path to the depletion chain XML file that will be used in depletion
simulation. Used to determine cross sections for materials not
present in the inital composition. Defaults to
``openmc.config['chain_file']``.
dilute_initial : float, optional
Initial atom density [atoms/cm^3] to add for nuclides that
are zero in initial condition to ensure they exist in the cross
section data. Only done for nuclides with reaction rates.
energy_bound : 2-tuple of float, optional
Bounds for the energy group.
run_kwargs : dict, optional
@ -66,29 +68,40 @@ class MicroXS(DataFrame):
Cross section data in [b]
"""
# Set up the reaction tallies
# Save any original tallies on the model
original_tallies = model.tallies
original_materials = deepcopy(model.materials)
xs = {}
reactions, burnable_nucs, diluted_materials = cls._add_dilute_nuclides(
chain_file, model, dilute_initial)
model.materials = diluted_materials
# Determine what reactions and nuclides are available in chain
if chain_file is None:
chain_file = openmc.config.get('chain_file')
if chain_file is None:
raise DataError(
"No depletion chain specified and could not find depletion "
"chain in openmc.config['chain_file']"
)
chain = Chain.from_xml(chain_file)
if reactions is None:
reactions = chain.reactions
if not nuclides:
cross_sections = _find_cross_sections(model)
nuclides_with_data = _get_nuclides_with_data(cross_sections)
nuclides = [nuc.name for nuc in chain.nuclides
if nuc.name in nuclides_with_data]
# Set up the reaction rate and flux tallies
energy_filter = openmc.EnergyFilter(energy_bounds)
if isinstance(reaction_domain, openmc.Material):
domain_filter = openmc.MaterialFilter([reaction_domain])
elif isinstance(reaction_domain, openmc.Cell):
domain_filter = openmc.CellFilter([reaction_domain])
elif isinstance(reaction_domain, openmc.Universe):
domain_filter = openmc.UniverseFilter([reaction_domain])
if isinstance(domain, openmc.Material):
domain_filter = openmc.MaterialFilter([domain])
elif isinstance(domain, openmc.Cell):
domain_filter = openmc.CellFilter([domain])
elif isinstance(domain, openmc.Universe):
domain_filter = openmc.UniverseFilter([domain])
else:
raise ValueError(f"Unsupported domain type: {type(reaction_domain)}")
raise ValueError(f"Unsupported domain type: {type(domain)}")
# TODO: Right now, we use all nuclides from the material but it probably
# should be based on the burnable nuclides
rr_tally = openmc.Tally(name='MicroXS RR')
rr_tally.filters = [domain_filter, energy_filter]
rr_tally.nuclides = reaction_domain.get_nuclides()
rr_tally.nuclides = nuclides
rr_tally.multiply_density = False
rr_tally.scores = reactions
@ -126,68 +139,8 @@ class MicroXS(DataFrame):
# Revert to the original tallies and materials
model.tallies = original_tallies
model.materials = original_materials
return cls(series)
@classmethod
def _add_dilute_nuclides(cls, chain_file, model, dilute_initial):
"""
Add nuclides not present in burnable materials that have neutron data
and are present in the depletion chain to those materials. This allows
us to tally those specific nuclides for reactions to create one-group
cross sections.
Parameters
----------
chain_file : str
Path to the depletion chain XML file that will be used in depletion
simulation. Used to determine cross sections for materials not
present in the inital composition.
model : openmc.Model
Model object
dilute_initial : float
Initial atom density [atoms/cm^3] to add for nuclides that
are zero in initial condition to ensure they exist in the cross
section data. Only done for nuclides with reaction rates.
Returns
-------
reactions : list of str
List of reaction names
diluted_materials : openmc.Materials
:class:`openmc.Materials` object with nuclides added to burnable
materials.
"""
if chain_file is None:
chain_file = openmc.config.get('chain_file')
if chain_file is None:
raise DataError(
"No depletion chain specified and could not find depletion "
"chain in openmc.config['chain_file']"
)
chain = Chain.from_xml(chain_file)
reactions = chain.reactions
cross_sections = _find_cross_sections(model)
nuclides_with_data = _get_nuclides_with_data(cross_sections)
burnable_nucs = [nuc.name for nuc in chain.nuclides
if nuc.name in nuclides_with_data]
diluted_materials = Materials()
for material in model.materials:
if material.depletable:
nuc_densities = material.get_nuclide_atom_densities()
dilute_density = 1.0e-24 * dilute_initial
material.set_density('sum')
for nuc, density in nuc_densities.items():
material.remove_nuclide(nuc)
material.add_nuclide(nuc, density)
for burn_nuc in burnable_nucs:
if burn_nuc not in nuc_densities:
material.add_nuclide(burn_nuc,
dilute_density)
diluted_materials.append(material)
return reactions, burnable_nucs, diluted_materials
return cls(series).rename_axis('nuclide')
@classmethod
def from_array(cls, nuclides, reactions, data):

View file

@ -40,14 +40,17 @@ def model():
settings = openmc.Settings()
settings.particles = 1000
settings.inactive = 10
settings.batches = 50
settings.inactive = 5
settings.batches = 10
return openmc.Model(geometry, materials, settings)
def test_from_model(model):
test_xs = MicroXS.from_model(model, model.materials[0], CHAIN_FILE)
fuel = model.materials[0]
nuclides = ['U234', 'U235', 'U238', 'U236', 'O16', 'O17', 'I135', 'Xe135',
'Xe136', 'Cs135', 'Gd157', 'Gd156']
test_xs = MicroXS.from_model(model, fuel, nuclides, chain_file=CHAIN_FILE)
if config['update']:
test_xs.to_csv('test_reference.csv')

View file

@ -1,13 +1,13 @@
nuclide,"(n,gamma)",fission
U234,20.548033586079335,0.4951725071956495
U235,10.593745111766133,48.86980740247932
U238,0.8607296097035912,0.10623994948321437
U236,8.697176401063281,0.32148140073986475
O16,7.503456435273737e-05,0.0
O17,0.0004107265933745623,0.0
I135,6.896228129273278,0.0
Xe135,229100.9245987756,0.0
Xe136,0.02336047367105298,0.0
Cs135,2.055822714073886,0.0
Gd157,12927.465334134899,0.0
Gd156,3.500756543915523,0.0
U234,21.418670317831197,0.5014588470882195
U235,10.343944102483244,47.46718472611891
U238,0.8741166723597251,0.10829568455139126
U236,9.083486784689326,0.3325287927011428
O16,7.548646353912453e-05,0.0
O17,0.0004018486221310307,0.0
I135,6.6912565089429235,0.0
Xe135,223998.64185667288,0.0
Xe136,0.022934362666193576,0.0
Cs135,2.28453952223533,0.0
Gd157,12582.079620036275,0.0
Gd156,2.9421127515332417,0.0

1 nuclide (n,gamma) fission
2 U234 20.548033586079335 21.418670317831197 0.4951725071956495 0.5014588470882195
3 U235 10.593745111766133 10.343944102483244 48.86980740247932 47.46718472611891
4 U238 0.8607296097035912 0.8741166723597251 0.10623994948321437 0.10829568455139126
5 U236 8.697176401063281 9.083486784689326 0.32148140073986475 0.3325287927011428
6 O16 7.503456435273737e-05 7.548646353912453e-05 0.0
7 O17 0.0004107265933745623 0.0004018486221310307 0.0
8 I135 6.896228129273278 6.6912565089429235 0.0
9 Xe135 229100.9245987756 223998.64185667288 0.0
10 Xe136 0.02336047367105298 0.022934362666193576 0.0
11 Cs135 2.055822714073886 2.28453952223533 0.0
12 Gd157 12927.465334134899 12582.079620036275 0.0
13 Gd156 3.500756543915523 2.9421127515332417 0.0