diff --git a/openmc/deplete/coupled_operator.py b/openmc/deplete/coupled_operator.py index 54f8f9462b..86b3f10a39 100644 --- a/openmc/deplete/coupled_operator.py +++ b/openmc/deplete/coupled_operator.py @@ -164,6 +164,13 @@ class CoupledOperator(OpenMCOperator): ``None`` implies no limit on the depth. .. versionadded:: 0.12 + diff_volume_method : str + Specifies how the volumes of the new materials should be found. Default + is to 'divide equally' which divides the original material volume + equally between the new materials, 'match cell' sets the volume of the + material to volume of the cell they fill. + + .. versionadded:: 0.13.4 Attributes ---------- @@ -206,8 +213,8 @@ class CoupledOperator(OpenMCOperator): } def __init__(self, model, chain_file=None, prev_results=None, - diff_burnable_mats=False, normalization_mode="fission-q", - fission_q=None, + diff_burnable_mats=False, diff_volume_method="divide equally", + normalization_mode="fission-q", fission_q=None, fission_yield_mode="constant", fission_yield_opts=None, reaction_rate_mode="direct", reaction_rate_opts=None, reduce_chain=False, reduce_chain_level=None): @@ -257,15 +264,16 @@ class CoupledOperator(OpenMCOperator): } super().__init__( - model.materials, - cross_sections, - chain_file, - prev_results, - diff_burnable_mats, - fission_q, - helper_kwargs, - reduce_chain, - reduce_chain_level) + materials=model.materials, + cross_sections=cross_sections, + chain_file=chain_file, + prev_results=prev_results, + diff_burnable_mats=diff_burnable_mats, + diff_volume_method=diff_volume_method, + fission_q=fission_q, + helper_kwargs=helper_kwargs, + reduce_chain=reduce_chain, + reduce_chain_level=reduce_chain_level) def _differentiate_burnable_mats(self): """Assign distribmats for each burnable material""" @@ -278,19 +286,30 @@ class CoupledOperator(OpenMCOperator): [mat for mat in self.materials if mat.depletable and mat.num_instances > 1]) - for mat in distribmats: - if mat.volume is None: - raise RuntimeError("Volume not specified for depletable " - "material with ID={}.".format(mat.id)) - mat.volume /= mat.num_instances + if self.diff_volume_method == 'divide equally': + for mat in distribmats: + if mat.volume is None: + raise RuntimeError("Volume not specified for depletable " + f"material with ID={mat.id}.") + mat.volume /= mat.num_instances if distribmats: # Assign distribmats to cells for cell in self.geometry.get_all_material_cells().values(): if cell.fill in distribmats: mat = cell.fill - cell.fill = [mat.clone() - for i in range(cell.num_instances)] + if self.diff_volume_method == 'divide equally': + cell.fill = [mat.clone() for _ in range(cell.num_instances)] + elif self.diff_volume_method == 'match cell': + for _ in range(cell.num_instances): + cell.fill = mat.clone() + if not cell.volume: + raise ValueError( + f"Volume of cell ID={cell.id} not specified. " + "Set volumes of cells prior to using " + "diff_volume_method='match cell'." + ) + cell.fill.volume = cell.volume self.materials = openmc.Materials( self.model.geometry.get_all_materials().values() diff --git a/openmc/deplete/independent_operator.py b/openmc/deplete/independent_operator.py index aa71942dfd..d089ac373e 100644 --- a/openmc/deplete/independent_operator.py +++ b/openmc/deplete/independent_operator.py @@ -148,10 +148,10 @@ class IndependentOperator(OpenMCOperator): self.fluxes = fluxes super().__init__( - materials, - micros, - chain_file, - prev_results, + materials=materials, + cross_sections=micros, + chain_file=chain_file, + prev_results=prev_results, fission_q=fission_q, helper_kwargs=helper_kwargs, reduce_chain=reduce_chain, diff --git a/openmc/deplete/openmc_operator.py b/openmc/deplete/openmc_operator.py index 30203d23fb..766fbf133e 100644 --- a/openmc/deplete/openmc_operator.py +++ b/openmc/deplete/openmc_operator.py @@ -12,6 +12,7 @@ from typing import List, Tuple, Dict import numpy as np import openmc +from openmc.checkvalue import check_value from openmc.exceptions import DataError from openmc.mpi import comm from .abc import TransportOperator, OperatorResult @@ -45,7 +46,6 @@ class OpenMCOperator(TransportOperator): in the previous results. diff_burnable_mats : bool, optional Whether to differentiate burnable materials with multiple instances. - Volumes are divided equally from the original material volume. fission_q : dict, optional Dictionary of nuclides and their fission Q values [eV]. helper_kwargs : dict @@ -58,6 +58,14 @@ class OpenMCOperator(TransportOperator): if ``reduce_chain`` evaluates to true. The default value of ``None`` implies no limit on the depth. + diff_volume_method : str + Specifies how the volumes of the new materials should be found. Default + is to 'divide equally' which divides the original material volume + equally between the new materials, 'match cell' sets the volume of the + material to volume of the cell they fill. + + .. versionadded:: 0.13.4 + Attributes ---------- materials : openmc.Materials @@ -97,6 +105,7 @@ class OpenMCOperator(TransportOperator): chain_file=None, prev_results=None, diff_burnable_mats=False, + diff_volume_method='divide equally', fission_q=None, helper_kwargs=None, reduce_chain=False, @@ -116,6 +125,10 @@ class OpenMCOperator(TransportOperator): self.materials = materials self.cross_sections = cross_sections + check_value('diff volume method', diff_volume_method, + {'divide equally', 'match cell'}) + self.diff_volume_method = diff_volume_method + # Reduce the chain to only those nuclides present if reduce_chain: init_nuclides = set() diff --git a/tests/unit_tests/test_deplete_coupled_operator.py b/tests/unit_tests/test_deplete_coupled_operator.py index 6de0342b07..4fa1dac2ed 100644 --- a/tests/unit_tests/test_deplete_coupled_operator.py +++ b/tests/unit_tests/test_deplete_coupled_operator.py @@ -11,6 +11,7 @@ import numpy as np CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml" + @pytest.fixture(scope="module") def model(): fuel = openmc.Material(name="uo2") @@ -48,8 +49,72 @@ def model(): return openmc.Model(geometry, materials, settings) + +@pytest.fixture() +def model_with_volumes(): + mat1 = openmc.Material() + mat1.add_element("Ag", 1, percent_type="ao") + mat1.set_density("g/cm3", 10.49) + mat1.depletable = True + mat1.volume = 102 + + mat2 = openmc.Material() + mat2.add_element("Ag", 1, percent_type="ao") + mat2.set_density("g/cm3", 10.49) + + sph1 = openmc.Sphere(r=1.0) + sph2 = openmc.Sphere(r=2.0, x0=3) + sph3 = openmc.Sphere(r=5.0, boundary_type="vacuum") + + cell1 = openmc.Cell(region=-sph1, fill=mat1) + cell1.volume = 4.19 + cell2 = openmc.Cell(region=-sph2, fill=mat1) + cell2.volume = 33.51 + cell3 = openmc.Cell(region=-sph3 & +sph1 & +sph2, fill=mat2) + cell3.volume = 485.9 + + geometry = openmc.Geometry([cell1, cell2, cell3]) + + return openmc.Model(geometry) + + def test_operator_init(model): """The test uses a temporary dummy chain. This file will be removed at the end of the test, and only contains a depletion_chain node.""" CoupledOperator(model, CHAIN_PATH) + + +def test_diff_volume_method_match_cell(model_with_volumes): + """Tests the volumes assigned to the materials match the cell volumes""" + + operator = openmc.deplete.CoupledOperator( + model=model_with_volumes, + diff_burnable_mats=True, + diff_volume_method='match cell', + chain_file=CHAIN_PATH + ) + + all_cells = list(operator.geometry.get_all_cells().values()) + assert all_cells[0].fill.volume == 4.19 + assert all_cells[1].fill.volume == 33.51 + # mat2 is not depletable + assert all_cells[2].fill.volume is None + + +def test_diff_volume_method_divide_equally(model_with_volumes): + """Tests the volumes assigned to the materials are divided equally""" + + operator = openmc.deplete.CoupledOperator( + model=model_with_volumes, + diff_burnable_mats=True, + diff_volume_method='divide equally', + chain_file=CHAIN_PATH + ) + + all_cells = list(operator.geometry.get_all_cells().values()) + assert all_cells[0].fill[0].volume == 51 + assert all_cells[1].fill[0].volume == 51 + # mat2 is not depletable + assert all_cells[2].fill.volume is None +