Repository: openmc-dev/openmc

Issue:
Incorrect value read from openmc.deplete.ResultsList for fisrst simulation time step 
I were testing deplete functionality both with old and new *.hdf5 cross-sections version and  when try to get `results = od.ResultsList('depletion_results.h5')
                                          results.get_atoms('1','nuclide_name')`
concentration for nuclide initially not pointed in materials.xml for first time step I received the same value 4.68084739e+02. 


Discussion:
Interesting. Can you provide the full output from `results.get_atoms('1', 'nuclide_name')`? Depending on the problem [very small time step, very low power, trace nuclide], it could be that that nuclide isn't changing with time. 
@drewejohnson I use one of the depletion_test as a pattern and benchmark data. My input:`# Power and timesteps
dt1 = 15.*24*60*60  # 15 days
dt2 = 1.5*30*24*60*60  # 1.5 months
N = floor(dt2/dt1)
dt = np.full(N, dt1)
qv = 108 #MWt/m^3 or Wt/cm^3
H = 23.6 #cm
numfuelpin = 308 # number of fuelled pin
Q = sqrt(3.0)/2.0*H*H*qv #*l
power = Q/numfuelpin
openmc.deplete.integrator.predictor(op, dt, power)` with a `power ~ 170 W/cm`.
Output for 'U235' initially was in materials.xml:
`results.get_atoms('1','U235')
(array([      0., 1296000., 2592000., 3888000.]), array([4.08965636e+20, 4.00814666e+20, 3.92765161e+20, 3.84836867e+20]))`
Output for 'Pu239' initially wasn't in materials.xml, but was in chain.xml and cross_sections.xml:
`>>> results.get_atoms('1','Pu239')
(array([      0., 1296000., 2592000., 3888000.]), array([4.68084739e+02, 2.11624708e+18, 4.82005499e+18, 7.41003374e+18]))
`
One thing the [`operator`](https://docs.openmc.org/en/latest/pythonapi/generated/openmc.deplete.Operator.html#openmc.deplete.Operator) does prior to running the transport simulation is set isotopes with zero concentration, like `Pu239`, to some very small quantity. This is done according to the `dilute_initial` attribute. The reasoning for this is to ensure that 1) useful reaction rates are obtained and 2) mitigate some potential divide by zero and round off errors through the depletion process. The values are small enough to be _functionally_ zero, compared to all other isotopes, 400 vs. 2x10^18 and greater for U235.
@drewejohnson ,yes I understand that made for some numerical reason, but is it right for users to get in fisrst time step incorrect value by calling `get_atoms` function?
@drewejohnson I mean is it should be on public code side rather than on private?
`dilute_initial` could probably be moved to give the user more control like you suggest

Resolving patch:
diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py
--- a/openmc/deplete/abc.py
+++ b/openmc/deplete/abc.py
@@ -10,11 +10,12 @@
 from abc import ABC, abstractmethod
 from xml.etree import ElementTree as ET
 from warnings import warn
+from numbers import Real
 
 from numpy import nonzero, empty
 
 from openmc.data import DataLibrary, JOULE_PER_EV
-from openmc.checkvalue import check_type
+from openmc.checkvalue import check_type, check_greater_than
 from .chain import Chain
 
 OperatorResult = namedtuple('OperatorResult', ['k', 'rates'])
@@ -55,17 +56,21 @@ class TransportOperator(ABC):
     fission_q : dict, optional
         Dictionary of nuclides and their fission Q values [eV]. If not given,
         values will be pulled from the ``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 decay chain.
+        Only done for nuclides with reaction rates.
+        Defaults to 1.0e3.
 
     Attributes
     ----------
     dilute_initial : float
-        Initial atom density to add for nuclides that are zero in initial
-        condition to ensure they exist in the decay chain.  Only done for
-        nuclides with reaction rates. Defaults to 1.0e3.
-
+        Initial atom density [atoms/cm^3] to add for nuclides that are zero
+        in initial condition to ensure they exist in the decay chain.
+        Only done for nuclides with reaction rates.
     """
-    def __init__(self, chain_file=None, fission_q=None):
-        self.dilute_initial = 1.0e3
+    def __init__(self, chain_file=None, fission_q=None, dilute_initial=1.0e3):
+        self.dilute_initial = dilute_initial
         self.output_dir = '.'
 
         # Read depletion chain
@@ -89,6 +94,17 @@ def __init__(self, chain_file=None, fission_q=None):
                      FutureWarning)
         self.chain = Chain.from_xml(chain_file, fission_q)
 
+    @property
+    def dilute_initial(self):
+        """Initial atom density for nuclides with zero initial concentration"""
+        return self._dilute_initial
+
+    @dilute_initial.setter
+    def dilute_initial(self, value):
+        check_type("dilute_initial", value, Real)
+        check_greater_than("dilute_initial", value, 0.0, equality=True)
+        self._dilute_initial = value
+
     @abstractmethod
     def __call__(self, vec, print_out=True):
         """Runs a simulation.
diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py
--- a/openmc/deplete/operator.py
+++ b/openmc/deplete/operator.py
@@ -76,6 +76,11 @@ class Operator(TransportOperator):
     fission_q : dict, optional
         Dictionary of nuclides and their fission Q values [eV]. If not given,
         values will be pulled from the ``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 decay chain.
+        Only done for nuclides with reaction rates.
+        Defaults to 1.0e3.
 
     Attributes
     ----------
@@ -84,9 +89,9 @@ class Operator(TransportOperator):
     settings : openmc.Settings
         OpenMC settings object
     dilute_initial : float
-        Initial atom density to add for nuclides that are zero in initial
-        condition to ensure they exist in the decay chain. Only done for
-        nuclides with reaction rates. Defaults to 1.0e3.
+        Initial atom density [atoms/cm^3] to add for nuclides that
+        are zero in initial condition to ensure they exist in the decay
+        chain. Only done for nuclides with reaction rates.
     output_dir : pathlib.Path
         Path to output directory to save results.
     round_number : bool
@@ -110,11 +115,11 @@ class Operator(TransportOperator):
         Results from a previous depletion calculation
     diff_burnable_mats : bool
         Whether to differentiate burnable materials with multiple instances
-
     """
     def __init__(self, geometry, settings, chain_file=None, prev_results=None,
-                 diff_burnable_mats=False, fission_q=None):
-        super().__init__(chain_file, fission_q)
+                 diff_burnable_mats=False, fission_q=None,
+                 dilute_initial=1.0e3):
+        super().__init__(chain_file, fission_q, dilute_initial)
         self.round_number = False
         self.settings = settings
         self.geometry = geometry
diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py
--- a/openmc/deplete/results_list.py
+++ b/openmc/deplete/results_list.py
@@ -26,7 +26,15 @@ def __init__(self, filename):
                 self.append(Results.from_hdf5(fh, i))
 
     def get_atoms(self, mat, nuc):
-        """Get nuclide concentration over time from a single material
+        """Get number of nuclides over time from a single material
+
+        .. note::
+
+            Initial values for some isotopes that do not appear in
+            initial concentrations may be non-zero, depending on the
+            value of :class:`openmc.deplete.Operator` ``dilute_initial``.
+            The :class:`openmc.deplete.Operator` adds isotopes according
+            to this setting, which can be set to zero.
 
         Parameters
         ----------
@@ -56,6 +64,14 @@ def get_atoms(self, mat, nuc):
     def get_reaction_rate(self, mat, nuc, rx):
         """Get reaction rate in a single material/nuclide over time
 
+        .. note::
+
+            Initial values for some isotopes that do not appear in
+            initial concentrations may be non-zero, depending on the
+            value of :class:`openmc.deplete.Operator` ``dilute_initial``
+            The :class:`openmc.deplete.Operator` adds isotopes according
+            to this setting, which can be set to zero.
+
         Parameters
         ----------
         mat : str


Test patch: