diff --git a/docs/source/usersguide/depletion.rst b/docs/source/usersguide/depletion.rst index 29bbcd1ed..821ff338b 100644 --- a/docs/source/usersguide/depletion.rst +++ b/docs/source/usersguide/depletion.rst @@ -216,7 +216,7 @@ and a path to a depletion chain file:: materials = openmc.Materials() ... - micro_xs = openmc.deplete.MicroXS() + micro_xs = openmc.deplete.MicroXS ... op = openmc.deplete.IndependentOperator(materials, micro_xs, chain_file) @@ -267,7 +267,9 @@ Users can generate the one-group microscopic cross sections needed by model = openmc.Model.from_xml() - micro_xs = openmc.deplete.MicroXS.from_model(model, model.materials[0]) + micro_xs = openmc.deplete.MicroXS.from_model(model, + model.materials[0], + chain_file) The :meth:`~openmc.deplete.MicroXS.from_model()` method will produce a :class:`~openmc.deplete.MicroXS` object with microscopic cross section data in diff --git a/openmc/deplete/microxs.py b/openmc/deplete/microxs.py index 89ad5e03c..09de090bf 100644 --- a/openmc/deplete/microxs.py +++ b/openmc/deplete/microxs.py @@ -8,13 +8,16 @@ import tempfile from pathlib import Path from pandas import DataFrame, read_csv, concat -from numpy import ndarray +import numpy as np from openmc.checkvalue import check_type, check_value, check_iterable_type from openmc.mgxs import EnergyGroups, ArbitraryXS, FissionXS -from openmc import Tallies, StatePoint +from openmc.data import DataLibrary +from openmc import Tallies, StatePoint, Materials -from .chain import REACTIONS + +from .chain import Chain, REACTIONS +from .coupled_operator import _find_cross_sections _valid_rxns = list(REACTIONS) _valid_rxns.append('fission') @@ -29,13 +32,8 @@ class MicroXS(DataFrame): def from_model(cls, model, reaction_domain, - reactions=['(n,gamma)', - '(n,2n)', - '(n,p)', - '(n,a)', - '(n,3n)', - '(n,4n)', - 'fission'], + chain_file, + dilute_initial=1.0e3, energy_bounds=(0, 20e6)): """Generate a one-group cross-section dataframe using OpenMC. Note that the ``openmc`` executable must be compiled. @@ -46,6 +44,14 @@ class MicroXS(DataFrame): OpenMC model object. Must contain geometry, materials, and settings. reaction_domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain in which to tally reaction rates. + 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. + 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. reactions : list of str, optional Reaction names to tally energy_bound : 2-tuple of float, optional @@ -63,6 +69,12 @@ class MicroXS(DataFrame): original_tallies = model.tallies tallies = Tallies() xs = {} + reactions, diluted_materials = cls._add_dilute_nuclides(chain_file, + model, + dilute_initial) + diluted_materials.export_to_xml('diluted_materials.xml') + model.materials = diluted_materials + for rxn in reactions: if rxn == 'fission': xs[rxn] = FissionXS(domain=reaction_domain, groups=groups, by_nuclide=True) @@ -94,6 +106,56 @@ class MicroXS(DataFrame): return micro_xs + @classmethod + def _add_dilute_nuclides(cls, chain_file, model, dilute_initial): + chain = Chain.from_xml(chain_file) + reactions = chain.reactions + cross_sections = _find_cross_sections(model) + nuclides_with_data = cls._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.E-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, diluted_materials + + @staticmethod + def _get_nuclides_with_data(cross_sections): + """Loads cross_sections.xml file to find nuclides with neutron data + + Parameters + ---------- + cross_sections : str + Path to cross_sections.xml file + + Returns + ------- + nuclides : set of str + Set of nuclide names that have cross secton data + + """ + nuclides = set() + data_lib = DataLibrary.from_xml(cross_sections) + for library in data_lib.libraries: + if library['type'] != 'neutron': + continue + for name in library['materials']: + if name not in nuclides: + nuclides.add(name) + + return nuclides + @classmethod def from_array(cls, nuclides, reactions, data): """ @@ -162,6 +224,6 @@ class MicroXS(DataFrame): def _validate_micro_xs_inputs(nuclides, reactions, data): check_iterable_type('nuclides', nuclides, str) check_iterable_type('reactions', reactions, str) - check_type('data', data, ndarray, expected_iter_type=float) + check_type('data', data, np.ndarray, expected_iter_type=float) for reaction in reactions: check_value('reactions', reaction, _valid_rxns) diff --git a/tests/micro_xs_simple.csv b/tests/micro_xs_simple.csv index bc43600ba..787ce74c3 100644 --- a/tests/micro_xs_simple.csv +++ b/tests/micro_xs_simple.csv @@ -1,7 +1,13 @@ nuclide,"(n,gamma)",fission -U234,23.518634203050645,0.49531930067650976 -U235,10.621118186344665,49.10955932965905 -U238,0.8652742788116043,0.10579281644765708 -U236,9.095623870006154,0.32315392339237936 -O16,0.0029535689693132015,0.0 -O17,0.05980775766572907,0.0 +U234,22.231989822002465,0.49620744663749855 +U235,10.479008971197121,48.41787337164604 +U238,0.8673334105437324,0.10467880588762352 +U236,8.65171044607122,0.31948392400019293 +O16,7.497851000107524e-05,0.0 +O17,0.0004079227797153371,0.0 +I135,6.842395323713927,0.0 +Xe135,227463.86426990604,0.0 +Xe136,0.02317896034753588,0.0 +Cs135,2.1721665580713623,0.0 +Gd157,12786.09939237018,0.0 +Gd156,3.4006085445846983,0.0 diff --git a/tests/regression_tests/deplete_no_transport/test_reference_fission_q.h5 b/tests/regression_tests/deplete_no_transport/test_reference_fission_q.h5 index 6c1b3de84..82bf11567 100644 Binary files a/tests/regression_tests/deplete_no_transport/test_reference_fission_q.h5 and b/tests/regression_tests/deplete_no_transport/test_reference_fission_q.h5 differ diff --git a/tests/regression_tests/deplete_no_transport/test_reference_source_rate.h5 b/tests/regression_tests/deplete_no_transport/test_reference_source_rate.h5 index ff75648b8..88ed73b17 100644 Binary files a/tests/regression_tests/deplete_no_transport/test_reference_source_rate.h5 and b/tests/regression_tests/deplete_no_transport/test_reference_source_rate.h5 differ diff --git a/tests/regression_tests/microxs/test.py b/tests/regression_tests/microxs/test.py index 89fdfcaeb..dbfe3357b 100644 --- a/tests/regression_tests/microxs/test.py +++ b/tests/regression_tests/microxs/test.py @@ -1,10 +1,14 @@ """Test one-group cross section generation""" +from pathlib import Path + import numpy as np import pytest import openmc from openmc.deplete import MicroXS +CHAIN_FILE = Path(__file__).parents[2] / "chain_simple.xml" + @pytest.fixture(scope="module") def model(): fuel = openmc.Material(name="uo2") @@ -24,8 +28,6 @@ def model(): radii = [0.42, 0.45] fuel.volume = np.pi * radii[0] ** 2 - clad.volume = np.pi * (radii[1]**2 - radii[0]**2) - water.volume = 1.24**2 - (np.pi * radii[1]**2) materials = openmc.Materials([fuel, clad, water]) @@ -45,6 +47,6 @@ def model(): def test_from_model(model): ref_xs = MicroXS.from_csv('test_reference.csv') - test_xs = MicroXS.from_model(model, model.materials[0]) + test_xs = MicroXS.from_model(model, model.materials[0], CHAIN_FILE) np.testing.assert_allclose(test_xs, ref_xs, rtol=1e-11) diff --git a/tests/regression_tests/microxs/test_reference.csv b/tests/regression_tests/microxs/test_reference.csv index 0c47409b0..787ce74c3 100644 --- a/tests/regression_tests/microxs/test_reference.csv +++ b/tests/regression_tests/microxs/test_reference.csv @@ -1,7 +1,13 @@ -nuclide,"(n,gamma)","(n,2n)","(n,p)","(n,a)","(n,3n)","(n,4n)",fission -U234,23.518634203050674,0.0008255330840536984,0.0,0.0,9.404554397521455e-07,0.0,0.49531930067650964 -U235,10.621118186344795,0.004359401013759254,0.0,0.0,7.2974901306692395e-06,0.0,49.10955932965902 -U238,0.8652742788116055,0.005661917442209096,0.0,0.0,4.92273921631416e-05,0.0,0.10579281644765708 -U236,9.095623870006163,0.0024373322002926834,0.0,0.0,1.966889146690413e-05,0.0,0.3231539233923791 -O16,7.511380881289377e-05,0.0,1.3764104470470622e-05,0.002862620940027927,0.0,0.0,0.0 -O17,0.00041221042693945085,1.9826700699084533e-05,7.764409141772239e-06,0.05938517754333328,0.0,0.0,0.0 +nuclide,"(n,gamma)",fission +U234,22.231989822002465,0.49620744663749855 +U235,10.479008971197121,48.41787337164604 +U238,0.8673334105437324,0.10467880588762352 +U236,8.65171044607122,0.31948392400019293 +O16,7.497851000107524e-05,0.0 +O17,0.0004079227797153371,0.0 +I135,6.842395323713927,0.0 +Xe135,227463.86426990604,0.0 +Xe136,0.02317896034753588,0.0 +Cs135,2.1721665580713623,0.0 +Gd157,12786.09939237018,0.0 +Gd156,3.4006085445846983,0.0