From e431d49bec3d326f255bbde73239d09cc4921ff9 Mon Sep 17 00:00:00 2001 From: Lorenzo Chierici Date: Mon, 20 Apr 2026 14:13:53 +0200 Subject: [PATCH] Add reactivity control to coupled transport-depletion analyses (#2693) Co-authored-by: Andrew Johnson Co-authored-by: Paul Romano --- docs/source/io_formats/depletion_results.rst | 4 +- openmc/deplete/abc.py | 143 +++++++++++++++++- openmc/deplete/coupled_operator.py | 2 +- openmc/deplete/independent_operator.py | 2 +- openmc/deplete/keff_search_control.py | 128 ++++++++++++++++ openmc/deplete/stepresult.py | 23 ++- .../__init__.py | 0 .../ref_depletion_with_refuel.h5 | Bin 0 -> 28640 bytes .../ref_depletion_with_rotation.h5 | Bin 0 -> 28640 bytes .../ref_depletion_with_translation.h5 | Bin 0 -> 28640 bytes .../deplete_with_keff_search_control/test.py | 140 +++++++++++++++++ .../test_deplete_keff_search_control.py | 116 ++++++++++++++ 12 files changed, 548 insertions(+), 10 deletions(-) create mode 100644 openmc/deplete/keff_search_control.py create mode 100644 tests/regression_tests/deplete_with_keff_search_control/__init__.py create mode 100644 tests/regression_tests/deplete_with_keff_search_control/ref_depletion_with_refuel.h5 create mode 100644 tests/regression_tests/deplete_with_keff_search_control/ref_depletion_with_rotation.h5 create mode 100644 tests/regression_tests/deplete_with_keff_search_control/ref_depletion_with_translation.h5 create mode 100644 tests/regression_tests/deplete_with_keff_search_control/test.py create mode 100644 tests/unit_tests/test_deplete_keff_search_control.py diff --git a/docs/source/io_formats/depletion_results.rst b/docs/source/io_formats/depletion_results.rst index 7fb088268..856993db6 100644 --- a/docs/source/io_formats/depletion_results.rst +++ b/docs/source/io_formats/depletion_results.rst @@ -4,7 +4,7 @@ Depletion Results File Format ============================= -The current version of the depletion results file format is 1.2. +The current version of the depletion results file format is 1.3. **/** @@ -29,6 +29,8 @@ The current version of the depletion results file format is 1.2. - **depletion time** (*double[]*) -- Average process time in [s] spent depleting a material across all burnable materials and, if applicable, MPI processes. + - **keff_search_root** (*double[]*) -- Root of the keff search at the + end of the timestep, if applicable. **/materials//** diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 7736b8c13..80d8474ec 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -31,6 +31,7 @@ from .results import Results, _SECONDS_PER_MINUTE, _SECONDS_PER_HOUR, \ from .pool import deplete from .reaction_rates import ReactionRates from .transfer_rates import TransferRates, ExternalSourceRates +from .keff_search_control import _KeffSearchControl __all__ = [ @@ -159,7 +160,7 @@ class TransportOperator(ABC): self.prev_res = prev_results @abstractmethod - def __call__(self, vec, source_rate): + def __call__(self, vec, source_rate) -> OperatorResult: """Runs a simulation. Parameters @@ -686,6 +687,7 @@ class Integrator(ABC): self.transfer_rates = None self.external_source_rates = None + self._keff_search_control = None if isinstance(solver, str): # Delay importing of cram module, which requires this file @@ -839,6 +841,37 @@ class Integrator(ABC): return (self.operator.prev_res[-1].time[0], len(self.operator.prev_res) - 1) + def _restore_keff_search_control(self, res: StepResult): + """Restore keff search control from restart results.""" + keff_search_root = res.keff_search_root + if keff_search_root is None: + raise ValueError( + "Cannot restore keff search control from restart " + "results because no stored keff_search_root is " + "available." + ) + self._keff_search_control.function(keff_search_root) + return keff_search_root + + def _get_bos_data(self, step_index, source_rate, bos_conc): + """Get beginning-of-step concentrations, rates, and control state.""" + if step_index > 0 or self.operator.prev_res is None: + if self._keff_search_control is not None and source_rate != 0.0: + keff_search_root = self._keff_search_control.run(bos_conc) + else: + keff_search_root = None + bos_conc, res = self._get_bos_data_from_operator( + step_index, source_rate, bos_conc) + else: + bos_conc, res = self._get_bos_data_from_restart( + source_rate, bos_conc) + if self._keff_search_control is not None and source_rate != 0.0: + keff_search_root = self._restore_keff_search_control(self.operator.prev_res[-1]) + else: + keff_search_root = None + + return bos_conc, res, keff_search_root + def integrate( self, final_step: bool = True, @@ -877,11 +910,8 @@ class Integrator(ABC): if output and comm.rank == 0: print(f"[openmc.deplete] t={t} s, dt={dt} s, source={source_rate}") - # Solve transport equation (or obtain result from restart) - if i > 0 or self.operator.prev_res is None: - n, res = self._get_bos_data_from_operator(i, source_rate, n) - else: - n, res = self._get_bos_data_from_restart(source_rate, n) + # Get beginning-of-step data from operator or restart results + n, res, keff_search_root = self._get_bos_data(i, source_rate, n) # Solve Bateman equations over time interval proc_time, n_end = self(n, res.rates, dt, source_rate, i) @@ -895,6 +925,7 @@ class Integrator(ABC): self._i_res + i, proc_time, write_rates=write_rates, + keff_search_root=keff_search_root, path=path ) @@ -908,6 +939,10 @@ class Integrator(ABC): # solve) if output and final_step and comm.rank == 0: print(f"[openmc.deplete] t={t} (final operator evaluation)") + if self._keff_search_control is not None and source_rate != 0.0: + keff_search_root = self._keff_search_control.run(n) + else: + keff_search_root = None res_final = self.operator(n, source_rate if final_step else 0.0) StepResult.save( self.operator, @@ -918,6 +953,7 @@ class Integrator(ABC): self._i_res + len(self), proc_time, write_rates=write_rates, + keff_search_root=keff_search_root, path=path ) self.operator.write_bos_data(len(self) + self._i_res) @@ -1050,6 +1086,101 @@ class Integrator(ABC): self.transfer_rates.set_redox(material, buffer, oxidation_states, timesteps) + def add_keff_search_control( + self, + function: Callable, + x0: float, + x1: float, + bracket: Sequence[float], + **search_kwargs + ): + """Add keff search to the integrator scheme. + + This method causes OpenMC to perform a keff search during depletion to + maintain a target keff by adjusting a model parameter through the + provided function. + + .. important:: + The function **must** modify the model through ``openmc.lib`` (e.g., + ``openmc.lib.cells``, ``openmc.lib.materials``) and **NOT** through + ``openmc.Model``. The function is called within a + :class:`openmc.lib.TemporarySession` context where only the C API + (``openmc.lib``) is available for modifications. + + Parameters + ---------- + function : Callable + Function that takes a single float argument and modifies the model + through :mod:`openmc.lib`. + x0 : float + Initial lower bound for the keff search. + x1 : float + Initial upper bound for the keff search. + bracket : sequence of float + Bracket interval [x_min, x_max] that constrains the allowed parameter + values during the keff search. This is a required parameter + that defines the absolute bounds for the search. The bracket must contain + exactly 2 elements with bracket[0] < bracket[1]. These values are passed + directly to the ``x_min`` and ``x_max`` optional arguments in + :meth:`openmc.Model.keff_search`, which enforce hard limits on the + parameter range. If the keff search converges to a value outside this + bracket, it will be clamped to the nearest bracket bound with a warning. + **search_kwargs + Additional keyword arguments passed to + :meth:`openmc.Model.keff_search`. Common options include: + + * ``target`` : float, optional + Target keff value to search for. Defaults to 1.0. + * ``k_tol`` : float, optional + Stopping criterion on the function value. Defaults to 1e-4. + * ``sigma_final`` : float, optional + Maximum accepted k-effective uncertainty. Defaults to 3e-4. + * ``maxiter`` : int, optional + Maximum number of iterations. Defaults to 50. + + See :meth:`openmc.Model.keff_search` for a complete list of + available options. + + Examples + -------- + Add keff search that adjusts a control rod position: + + >>> def adjust_rod_position(position): + ... openmc.lib.cells[rod_cell.id].translation = [0, 0, position] + >>> integrator.add_keff_search_control( + ... adjust_rod_position, + ... x0=0.0, + ... x1=5.0, + ... bracket=[-10,10], + ... target=1.0, + ... k_tol=1e-4 + ... ) + + Add keff search that adjusts the U235 density: + + >>> def set_u235_density(u235_density): + ... # Get the material from openmc.lib + ... lib_mat = openmc.lib.materials[material_id] + ... # Get current nuclides and densities + ... nuclides = lib_mat.nuclides + ... densities = lib_mat.densities + ... u235_idx = nuclides.index('U235') + ... densities[u235_idx] = u235_density + ... lib_mat.set_densities(nuclides, densities) + >>> integrator.add_keff_search_control( + ... set_u235_density, + ... x0=5.0e-4, + ... x1=1.0e-3, + ... bracket=[1.0e-4, 2.0e-3], + ... target=1.0 + ... ) + + .. versionadded:: 0.15.4 + + """ + self._keff_search_control = _KeffSearchControl( + self.operator, function, x0, x1, bracket, **search_kwargs) + @add_params class SIIntegrator(Integrator): r"""Abstract class for the Stochastic Implicit Euler integrators diff --git a/openmc/deplete/coupled_operator.py b/openmc/deplete/coupled_operator.py index 34bb28b49..a21d57d46 100644 --- a/openmc/deplete/coupled_operator.py +++ b/openmc/deplete/coupled_operator.py @@ -399,7 +399,7 @@ class CoupledOperator(OpenMCOperator): self.materials.export_to_xml(nuclides_to_ignore=self._decay_nucs) - def __call__(self, vec, source_rate): + def __call__(self, vec, source_rate) -> OperatorResult: """Runs a simulation. Simulation will abort under the following circumstances: diff --git a/openmc/deplete/independent_operator.py b/openmc/deplete/independent_operator.py index c192907cf..c12863956 100644 --- a/openmc/deplete/independent_operator.py +++ b/openmc/deplete/independent_operator.py @@ -384,7 +384,7 @@ class IndependentOperator(OpenMCOperator): # Return number density vector return super().initial_condition(self.materials) - def __call__(self, vec, source_rate): + def __call__(self, vec, source_rate) -> OperatorResult: """Obtain the reaction rates Parameters diff --git a/openmc/deplete/keff_search_control.py b/openmc/deplete/keff_search_control.py new file mode 100644 index 000000000..49f7cc4df --- /dev/null +++ b/openmc/deplete/keff_search_control.py @@ -0,0 +1,128 @@ +from typing import Callable +from warnings import warn + +import openmc.lib + + +class _KeffSearchControl: + """Controller for keff search during depletion calculations. + + This class performs keff searches to maintain a target keff by adjusting a + model parameter through a provided function. + + Parameters + ---------- + operator : openmc.deplete.Operator + Depletion operator instance + function : Callable + Function that modifies the model based on a parameter value + x0 : float + Initial lower bound for the keff search + x1 : float + Initial upper bound for the keff search + bracket : list[float] + Absolute bracketing interval lower and upper. If the keff search + solution lies off these limits the closest limit will be set as new + result. + **search_kwargs : dict, optional + Additional keyword arguments to pass to :meth:`openmc.Model.keff_search` + + """ + def __init__(self, operator, function: Callable, x0: float, x1: float, bracket: list[float], **search_kwargs): + if len(bracket) != 2: + raise ValueError(f"bracket must have exactly 2 elements, got {len(bracket)}") + if bracket[0] >= bracket[1]: + raise ValueError(f"bracket[0] must be < bracket[1], got {bracket}") + self.x0 = x0 + self.x1 = x1 + self.operator = operator + self.function = function + self.search_kwargs = search_kwargs + self.search_kwargs['x_min'] = bracket[0] + self.search_kwargs['x_max'] = bracket[1] + + def run(self, x): + """Perform keff search and update the atom density vector. + + Parameters + ---------- + x : list of numpy.ndarray + Current atom density vector (atoms per material) + + Returns + ------- + root : float + Parameter value that achieves target keff + """ + root = self._search_for_keff() + self._update_vec(x) + return root + + def _search_for_keff(self) -> float: + """Perform the keff search using the model's keff_search method. + + Returns + ------- + float + Parameter value that achieves target keff + + Raises + ------ + ValueError + If the keff search fails to converge + """ + with openmc.lib.TemporarySession(self.operator.model): + # Only pass the first 3 required args plus explicitly provided kwargs + result = self.operator.model.keff_search( + self.function, self.x0, self.x1, **self.search_kwargs + ) + if not result.converged: + raise ValueError( + f"Search for keff failed to converge. " + f"Termination reason: {result.flag}" + ) + + root = result.root + + # Check if root is outside the bracket bounds and give a warning + if root < self.search_kwargs['x_min']: + warn(f"keff search result ({root:.6f}) is below the lower bracket " + f"bound ({self.search_kwargs['x_min']:.6f}).", UserWarning) + elif root > self.search_kwargs['x_max']: + warn(f"keff search result ({root:.6f}) is above the upper bracket " + f"bound ({self.search_kwargs['x_max']:.6f}).", UserWarning) + + # Restore the number of initial batches + openmc.lib.settings.set_batches(self.operator.model.settings.batches) + + return root + + def _update_vec(self, x): + """Update the atom density vector from openmc.lib.materials and AtomNumber object. + + The depletion vector ``x`` is rank-local, matching the materials owned + by ``self.operator.number`` on the current MPI rank. We therefore only + update entries for locally owned materials using the compositions + currently stored in ``openmc.lib.materials``. + + Parameters + ---------- + x : list of numpy.ndarray + Atom density vector to update (atoms per material) + + """ + number = self.operator.number + + for mat_idx, mat in enumerate(number.materials): + lib_material = openmc.lib.materials[int(mat)] + nuclides = lib_material.nuclides + densities = 1e24 * lib_material.densities + volume = number.get_mat_volume(mat) + + for nuc_idx, nuc in enumerate(number.burnable_nuclides): + if nuc in nuclides: + lib_nuc_idx = nuclides.index(nuc) + atom_density = densities[lib_nuc_idx] + else: + atom_density = number.get_atom_density(mat, nuc) + x[mat_idx][nuc_idx] = atom_density * volume diff --git a/openmc/deplete/stepresult.py b/openmc/deplete/stepresult.py index 6da7f7adc..9f638a058 100644 --- a/openmc/deplete/stepresult.py +++ b/openmc/deplete/stepresult.py @@ -17,7 +17,7 @@ from openmc.mpi import MPI, comm from .reaction_rates import ReactionRates -VERSION_RESULTS = (1, 2) +VERSION_RESULTS = (1, 3) __all__ = ["StepResult"] @@ -58,6 +58,8 @@ class StepResult: proc_time : int Average time spent depleting a material across all materials and processes + keff_search_root : float + The root returned by the keff search control. """ def __init__(self): @@ -74,6 +76,7 @@ class StepResult: self.name_list = None self.data = None + self.keff_search_root = None def __repr__(self): t = self.time[0] @@ -368,6 +371,10 @@ class StepResult: "depletion time", (1,), maxshape=(None,), dtype="float64") + handle.create_dataset( + "keff_search_root", (1,), maxshape=(None,), + dtype="float64") + def _to_hdf5(self, handle, index, parallel=False, write_rates: bool = False): """Converts results object into an hdf5 object. @@ -400,6 +407,7 @@ class StepResult: time_dset = handle["/time"] source_rate_dset = handle["/source_rate"] proc_time_dset = handle["/depletion time"] + keff_search_root_dset = handle["/keff_search_root"] # Get number of results stored number_shape = list(number_dset.shape) @@ -433,6 +441,10 @@ class StepResult: proc_shape[0] = new_shape proc_time_dset.resize(proc_shape) + keff_search_root_shape = list(keff_search_root_dset.shape) + keff_search_root_shape[0] = new_shape + keff_search_root_dset.resize(keff_search_root_shape) + # If nothing to write, just return if len(self.index_mat) == 0: return @@ -452,6 +464,7 @@ class StepResult: proc_time_dset[index] = ( self.proc_time / (comm.size * self.n_hdf5_mats) ) + keff_search_root_dset[index] = self.keff_search_root @classmethod def from_hdf5(cls, handle, step): @@ -500,6 +513,10 @@ class StepResult: if step < proc_time_dset.shape[0]: results.proc_time = proc_time_dset[step] + if "keff_search_root" in handle: + keff_search_root_dset = handle["/keff_search_root"] + results.keff_search_root = keff_search_root_dset[step] + if results.proc_time is None: results.proc_time = np.array([np.nan]) @@ -554,6 +571,7 @@ class StepResult: step_ind, proc_time=None, write_rates: bool = False, + keff_search_root=None, path: PathLike = "depletion_results.h5" ): """Creates and writes depletion results to disk @@ -578,6 +596,8 @@ class StepResult: processes. write_rates : bool, optional Whether reaction rates should be written to the results file. + keff_search_root : float + The root returned by the keff search control. path : PathLike Path to file to write. Defaults to 'depletion_results.h5'. @@ -605,6 +625,7 @@ class StepResult: results.proc_time = proc_time if results.proc_time is not None: results.proc_time = comm.reduce(proc_time, op=MPI.SUM) + results.keff_search_root = keff_search_root if not Path(path).is_file(): Path(path).parent.mkdir(parents=True, exist_ok=True) diff --git a/tests/regression_tests/deplete_with_keff_search_control/__init__.py b/tests/regression_tests/deplete_with_keff_search_control/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/regression_tests/deplete_with_keff_search_control/ref_depletion_with_refuel.h5 b/tests/regression_tests/deplete_with_keff_search_control/ref_depletion_with_refuel.h5 new file mode 100644 index 0000000000000000000000000000000000000000..a335e3b47800c98e4e001dfc2159f884a31c6bf0 GIT binary patch literal 28640 zcmeHOO-x)>6uxf;+Mz`St7b%vFB%O*(?X$icDzQYm}pDXwyeq<;bBLI88SZvyTX{5 zx-hb2iMuX|x?st|g-ezgO`M%BYGQ0mSXtkF=X`~E%ms(x(J7o+%z5|TbMLw5d-pr{ zyf=5g8Xdm;#Hp@RN`Ddw)uK9rOME@xPbE(^D5Krw3yb9)mUm-xVfiEKwj|eg@c4F| zDpt7u!}mue>Ynoo#Y{e@IubsOf)@DF%zu;lmSi67#kVDj2 z0Q_)xe*g?v|6{0VP*NPwk$^rMCrQE%8tl+K8Z>+4x?}$jJF2ik~-A*LrS$a@8^AC*inB zaVchuOMmHP$p6^Ew^Dz+Nf(HtTrJ{=B7nHG!u}!dq<9@f<+i2bRrxiZXH#d$qRWP5 zUybSx9F1|Yev#*5pIhR%1~0MSVSS^1zmoRV7HkKq_bD0)cE-6p&-3VDLrc8Ap|{56 z%Cn?^e0cS_>hUUER?kztO+syO8RMxgZhw&#np|LC4db=-9RGB%uck}+89P^=`N%8S z*kiS8rhS&TO+Uuejx*zor_1$f2|pMJ5j7Do@x$hIgicZMBfLL0%8wSFuxbBT;{DZSJ>duLu*g~nnE2s@^0cV< z5zY%6w`b5PK`z5mAu)Rk4fuDCw{MdMf?78fi z@PmO6Q4;|ZKNff$p;J`+aBQ;Rvb{$6!9PP7pU*d6V|(nF@PmO6Q4;|ZKbCnNp;J`+ za9<@03v91Ze&9W(Y5z#}kUf_j6MirdB5ERF;>TKdwR%+iSa_Z6EVI2v`GI+w_|ePf zL|k@H_)&eIrBsc8i650Ws`aq$4(k0bMt`f=*MGq;ygk)-VUVRh6ZQ^Ot83@`gA-T2 z)M~mM93U9?NI^^85A$=iQUo9R-K$k0Y;5xT3)l@mNA>a?k~UpH&*3uiNjeRoREqae z=!N%HhlWw(98aJZjxVFEH_!Vd^p?527P_yjerN*k^~3XNG%m*21535iT42X5oHvP6 z#g-m*Xybl3FAls=ANNnf>N?Z;lve+jP~9Gj$AYd2Wd|e1V=c?Uf~BRY+w_o$pfUJz zw=N^R+KC6PV2h^v@H{ba0S+kF??3&L8;`{+m&h;eyB>~xpO4Vny@!5;cqHGY=`}eX zbpNc-xpSwyn)#ARq_`0)l`bAP5Kof`B0Kh!HS7KXmLduWo)0fFAPyaJRdL=LNif zlIMglVp0|a1OY)n5D)|e0YRWS5!i3unEIc2C}^6lTlzdAoI{L=R4P}h~o7ZMY5LshON|8auynit4>%Rh5_rCP75Q|-cc hY=1Ch|6Ka^<(~WZhR%L@BlX@F1sWH2A$QnU&VN6C&0qik literal 0 HcmV?d00001 diff --git a/tests/regression_tests/deplete_with_keff_search_control/ref_depletion_with_rotation.h5 b/tests/regression_tests/deplete_with_keff_search_control/ref_depletion_with_rotation.h5 new file mode 100644 index 0000000000000000000000000000000000000000..c8b4f67ff25dcfec4cd6d02aeea9443077d6feed GIT binary patch literal 28640 zcmeHOO>7%Q6rQz{)@cnS6p*PvtcpOXK;SfK>JxEA0!FH6C`~aEYSv9Q)Zo~Wf1pH4Z zo<&1NRMc7Bvv#qRE#y^q#G#SblB9Nnn89>Vkl+i-QN{YI%jq9cQ`wwd{%pqf260X& zoDMyXM88RU#!+xAnnk-*$(2j2XgC_K4)})oZ!q6t%%etpTcn7E#()+gUHk&C76HqpsU`tH-Is0{5dhptSq0W_YPS zBnbH90QiyQ{Q)pw{g0xeMM-hMjRbV8A0-JpXfS>AXwmF}>yG{3k5^Pt05Q}NCx3~k zY<|+7bG+Y{zxWV|s)Sc#sH5sBr{8QLSDALQlpb@4O(fO8aW!z?7eB8i#!i3!$;G5D zKk>&+ic2AFT>9NjhMdFn&Pv^Vl`arRxmv^lMF4SWnf*iDN$@%d%56i1Yw~M6&n8Zg zMT-r~z8cgWI2hw%{UXoBKDWqm4PIiu!}>=3ZYAxj9oP<3{0SNfcG6s)<9T$mp+#Qb z&|Bql^=VQ-KD_cw?RZr#t7oa+B%#*1jPcY-Zhwvy+FW2?_2aeq9RH-ZuV%`HX)|A$ zzGfFq?6IzEx_y?nO((|Gk27h;(_(xi&M*)n>LQ?vGYY)qUf|(g#ToF!A17MnN9Tk2 z*5}6@#}$k9gdYrqh`I>q_+j!oLZ_hk;ol!y{;xX@PmO6Q5OLnKjwKIp;J)&NSb89VtcLfgMWt5KA&&A#P--R;RgdDqAmhDek}1i zLZ_hkVZBHe=Gk7W{J?um-To0fP4+ByO!&b-h^UKzjvuSNwdz6fWBz5bv&8mViZ_}Uf(~TR^wubJup-!tp#@6 z!1pF$s@T+{4qe<2--`n;)Q6ofVck02_bHvuFQIOGC>-*-MwID|7!GwTc?*`7rrYKY z8SxsuleKvn;MI0KXayTI-3Q+j0~g?ca`XPvFS+4RxO$%aa(#D)W8dcj^tSQP4-t>p z1)5%)<3ab&@O^iVb+P;P5E{L*>HiiU+-Q8$_EBT|`Unh<`Kylr9)Ny2Q?`rQOb-7O z$X6zE*-0BkMV}}rWoJLO^Rt;;g`3J*_v=2TN`*>s!X7U=!+1k({{Mh9KDDQ&#!Ggl zIB|WvSSXZX*CmU4U_Ygj{=AR(Z+VKQ;ZCt_*Rz}d8&3EIe<4$P2y0rZc`8C5_oGH> zOTps*d7%pWkl0tZ00vc&f`A|(2nYg#fFK|U2m*qDAn=e8&^fQsUI1fa-|2^X^O`Zt-CZ{Lrf`A|(2nYg#fFRHY2sE27j5f#@)+AqO zgS#qI5CjAPK|l}?1ojqzX7hz~gM49K@`b&kAP5Kof`B0K2qN%9X7JY!{*4cx zyK^P}=a1hEfAvLvW%=7bhG#FF>MH+qZ|J?Te?I)>yYf(tYq7tQUU}8_$1Yuaqpw8c!!BeU`~LwQ6w!D9 literal 0 HcmV?d00001 diff --git a/tests/regression_tests/deplete_with_keff_search_control/ref_depletion_with_translation.h5 b/tests/regression_tests/deplete_with_keff_search_control/ref_depletion_with_translation.h5 new file mode 100644 index 0000000000000000000000000000000000000000..35285321bc2a30f35f474f4b123ac68750eb1b0c GIT binary patch literal 28640 zcmeHOL2MgE6rHt`#%&BFq@YlNSb?ZgfvD4@sc+ee1dNJk1SFVCvnJV4gJVZ_5>ig# zRwN{H3_?goTzZUvL&O2)2)A;}4c~wRDgq(pMA_Z{UlVUUF^;pToBTcO-4M3l$=d6#}Wz%VVCySTD=$ePw_zLK5A)UW4m8u2Q~5 zL&a3wUEPaLv6L<3RaeZVk>6rbyFtugIw(l+1?9M6eRb09A5$~goHKu8&hZCvPd#pj zZdan;v@_=_xE9T#Q(DN)msrtoH9Q^g4fEe(zV$MX>hW!rA_DuugNrye2!L;C_87ZD zlGhJ_=;sTpOJW6D8<%gses#p-6ztxj{b8!_gWVVRwhP=g0H{XY;`%i>uF=!&yOmDru)_T)4k+!us~KLZj|l?7 zH~@ZFygvX2tp71oG$|<#c#(jP%?^^Vg9bA&k0#9?x$fBigLuUi1rS4>aPya#%I2q? zCD;31`AYzixJvpphU!qK-F}OO+`_DzrF5IaY{F85ooeuIAbws;j-UJJ{i~KPKMBT7 zic1k~T>8^XhTOyS&r1FF0$m`Ea=3ekJXzZP*S};t?7NcG6s4;(2tjp;cbr(A(g0 zT^>`I7t0$=5B%wCBjPcYNZhw*$T3ldX4dS)^9RH}luV&^8vu1u__FboF zVvqG))9tgoZMre0cAQBwo;KqnafX2qQ4;}OoKfH<_W}CeP zIIh^NC;VU_MASq;#}AX&5jus%kKq2;EI-jQ&!of8e59cxAf(;tj&r4Y+Q`62Zm~=wZM)W_}(N+ z72A5$;UxFN_u{|{^-=drSg%g^eM-CgOQ_c#iAMacF=hHAMk8%yf5Fny^xC{3V}7H5 zvbQfoyxNTitzd(ud+&Q<-~t>_Zrp$RB{v$0RxXoYp6}jp?E6xP-qs)bKH|~)8cnao z@u2%>1im}Ry4e4Eh>T5a`@e$+H|yWDL)6&4K0?D|`Pu`32cVzL%sa(wCWrqCCcCF|JG(`8r~FJc0GIfzu|^o@E0 zpBJj24~czs2VhVYDF_Gxf`A|(2nYg#fFK|U2m<#R0p0UMSDktF@pAz5kpBm}-O@ZS z;Qf<4Cj=3bvLGM`2m*qDARq_`0{JGY