Repository: openmc-dev/openmc

Issue:
Depletion flux mode reaction rate reconstruction broken
<!--
If you are a user of OpenMC and are running into trouble with the code or are
seeking general user support, we highly recommend posting on the OpenMC
discourse forum first. GitHub issues should be used specifically for bug reports
and feature requests.

https://openmc.discourse.group/

-->

## Bug Description
<!--A clear and concise description of the problem (Note: A missing feature is not a bug).-->
The flux mode reaction rate reconstruction method causes OpenMC to crash after step 0, because other nuclides from the chain are not being loaded.

## Steps to Reproduce
<!--Steps to reproduce the behavior (input file, or modifications to an existing input file, etc.)-->
Go to `examples/pincell_depletion`, and define the depletion operator as:
```
chain_file = 'chain_simple.xml'
op = openmc.deplete.CoupledOperator(model, chain_file,
        reaction_rate_mode="flux", reaction_rate_opts=
        {'energies':openmc.mgxs.GROUP_STRUCTURES['SHEM-361']})
```

The simulation crashes with the message:

```
 k-effective (Collision)     = 1.16273 +/- 0.00361
 k-effective (Track-length)  = 1.15901 +/- 0.00440
 k-effective (Absorption)    = 1.16493 +/- 0.00321
 Combined k-effective        = 1.16348 +/- 0.00277
 Leakage Fraction            = 0.00000 +/- 0.00000

Traceback (most recent call last):
  File "/home/gavin/Code/openmc/openmc/lib/nuclide.py", line 115, in __getitem__
    _dll.openmc_get_nuclide_index(key.encode(), index)
  File "/home/gavin/Code/openmc/openmc/lib/error.py", line 33, in _error_handler
    raise exc.DataError(msg)
openmc.exceptions.DataError: No nuclide named 'I135' has been loaded.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/gavin/Code/openmc/examples/pincell_depletion/run_depletion.py", line 100, in <module>
    integrator.integrate()
  File "/home/gavin/Code/openmc/openmc/deplete/abc.py", line 790, in integrate
    n, res = self._get_bos_data_from_operator(i, source_rate, n)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/gavin/Code/openmc/openmc/deplete/abc.py", line 743, in _get_bos_data_from_operator
    res = self.operator(x, source_rate)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/gavin/Code/openmc/openmc/deplete/coupled_operator.py", line 457, in __call__
    rates = self._calculate_reaction_rates(source_rate)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/gavin/Code/openmc/openmc/deplete/openmc_operator.py", line 502, in _calculate_reaction_rates
    tally_rates = self._rate_helper.get_material_rates(
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/gavin/Code/openmc/openmc/deplete/helpers.py", line 391, in get_material_rates
    nuc = openmc.lib.nuclides[name]
          ~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/home/gavin/Code/openmc/openmc/lib/nuclide.py", line 118, in __getitem__
    raise KeyError(str(e))
KeyError: "No nuclide named 'I135' has been loaded."

```

## Environment
<!--Operating system, OpenMC version, how OpenMC was installed, nuclear data being used, etc.-->
"I use arch btw"

```
gpad% openmc -v
OpenMC version 0.13.4
Git SHA1: bf7a079b2c84d02f3636dd1bd185e1e326469189
Copyright (c) 2011-2023 MIT, UChicago Argonne LLC, and contributors
MIT/X license at <https://docs.openmc.org/en/latest/license.html>
Build type:            RelWithDebInfo
Compiler ID:           GNU 13.2.1
MPI enabled:           no
Parallel HDF5 enabled: no
PNG support:           yes
DAGMC support:         no
libMesh support:       no
MCPL support:          no
NCrystal support:      no
Coverage testing:      no
Profiling flags:       no

```


Discussion:

Resolving patch:
diff --git a/openmc/deplete/helpers.py b/openmc/deplete/helpers.py
--- a/openmc/deplete/helpers.py
+++ b/openmc/deplete/helpers.py
@@ -268,6 +268,11 @@ def nuclides(self, nuclides):
         if self._reactions_direct and self._nuclides_direct is None:
             self._rate_tally.nuclides = nuclides
 
+        # Make sure nuclide data is loaded
+        for nuclide in self.nuclides:
+            if nuclide not in openmc.lib.nuclides:
+                openmc.lib.load_nuclide(nuclide)
+
     def generate_tallies(self, materials, scores):
         """Produce multigroup flux spectrum tally
 


Test patch:
diff --git a/tests/unit_tests/test_deplete_activation.py b/tests/unit_tests/test_deplete_activation.py
--- a/tests/unit_tests/test_deplete_activation.py
+++ b/tests/unit_tests/test_deplete_activation.py
@@ -11,7 +11,7 @@
 @pytest.fixture
 def model():
     """Sphere of single nuclide"""
-    model = openmc.model.Model()
+    model = openmc.Model()
 
     w = openmc.Material(name='tungsten')
     w.add_nuclide('W186', 1.0)
@@ -161,3 +161,30 @@ def test_decay(run_in_tmpdir):
     # Ensure density goes down by a factor of 2 after each half-life
     assert atoms[1] / atoms[0] == pytest.approx(0.5)
     assert atoms[2] / atoms[1] == pytest.approx(0.25)
+
+
+def test_flux_rr_missing_nuclide(run_in_tmpdir, model):
+    # Create two-nuclide depletion chain -- since W184 is not in the model, this
+    # test ensures that FluxCollapseHelper loads missing nuclides appropriately
+    chain = openmc.deplete.Chain()
+    w184 = openmc.deplete.Nuclide('W184')
+    w184.add_reaction('(n,gamma)', None, 0.0, 1.0)
+    chain.add_nuclide(w184)
+    w186 = openmc.deplete.Nuclide('W186')
+    w186.add_reaction('(n,gamma)', None, 0.0, 1.0)
+    chain.add_nuclide(w186)
+    chain.export_to_xml('test_chain.xml')
+
+    # Create transport operator
+    op = openmc.deplete.CoupledOperator(
+        model, 'test_chain.xml',
+        normalization_mode="source-rate",
+        reaction_rate_mode="flux",
+        reaction_rate_opts={'energies': [0.0, 20.0e6]},
+    )
+
+    # Deplete with two decay steps
+    integrator = openmc.deplete.PredictorIntegrator(
+        op, [100.0], source_rates=[10.0]
+    )
+    integrator.integrate()