We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Feature: Easier indexing through ResultsList to find depletion step index
If a user wanted to perform a restart simulation not necessarily at the end point in time, there isn't a super simple way to do it from the API. Currently you would have to manually search through the `Result` instances and compare their time data one at a time. 

This isn't a monumental task, and I think we could easily provide a method that would allow users to find the index corresponding to a specific point in time. Supporting similar `time_units` like we do for the `Operator` (days, seconds, MWd/kgHM) would also be super nice. 

Looking at #1708, this would also be useful when exporting `material.xml` files either during the depletion run or after the fact. Either way, the user will need a way to link a depletion step to a point in calendar time for restarting or other analysis.

Other things to consider would be some tolerance on the search criteria. If I ask for the step where burnup was 12 MWd/kgHM, (for example), but the closest point is 12.1 MWd/kgHM, should that step be returned? Or error out? 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `openmc/deplete/results_list.py`
Content:
```
1 import h5py
2 import numpy as np
3 
4 from .results import Results, VERSION_RESULTS
5 from openmc.checkvalue import check_filetype_version, check_value
6 
7 
8 __all__ = ["ResultsList"]
9 
10 
11 class ResultsList(list):
12     """A list of openmc.deplete.Results objects
13 
14     It is recommended to use :meth:`from_hdf5` over
15     direct creation.
16     """
17 
18     @classmethod
19     def from_hdf5(cls, filename):
20         """Load in depletion results from a previous file
21 
22         Parameters
23         ----------
24         filename : str
25             Path to depletion result file
26 
27         Returns
28         -------
29         new : ResultsList
30             New instance of depletion results
31         """
32         with h5py.File(str(filename), "r") as fh:
33             check_filetype_version(fh, 'depletion results', VERSION_RESULTS[0])
34             new = cls()
35 
36             # Get number of results stored
37             n = fh["number"][...].shape[0]
38 
39             for i in range(n):
40                 new.append(Results.from_hdf5(fh, i))
41         return new
42 
43     def get_atoms(self, mat, nuc, nuc_units="atoms", time_units="s"):
44         """Get number of nuclides over time from a single material
45 
46         .. note::
47             Initial values for some isotopes that do not appear in
48             initial concentrations may be non-zero, depending on the
49             value of :class:`openmc.deplete.Operator` ``dilute_initial``.
50             The :class:`openmc.deplete.Operator` adds isotopes according
51             to this setting, which can be set to zero.
52 
53         Parameters
54         ----------
55         mat : str
56             Material name to evaluate
57         nuc : str
58             Nuclide name to evaluate
59         nuc_units : {"atoms", "atom/b-cm", "atom/cm3"}, optional
60             Units for the returned concentration. Default is ``"atoms"``
61 
62             .. versionadded:: 0.12
63         time_units : {"s", "min", "h", "d"}, optional
64             Units for the returned time array. Default is ``"s"`` to
65             return the value in seconds.
66 
67             .. versionadded:: 0.12
68 
69         Returns
70         -------
71         times : numpy.ndarray
72             Array of times in units of ``time_units``
73         concentrations : numpy.ndarray
74             Concentration of specified nuclide in units of ``nuc_units``
75 
76         """
77         check_value("time_units", time_units, {"s", "d", "min", "h"})
78         check_value("nuc_units", nuc_units,
79                     {"atoms", "atom/b-cm", "atom/cm3"})
80 
81         times = np.empty_like(self, dtype=float)
82         concentrations = np.empty_like(self, dtype=float)
83 
84         # Evaluate value in each region
85         for i, result in enumerate(self):
86             times[i] = result.time[0]
87             concentrations[i] = result[0, mat, nuc]
88 
89         # Unit conversions
90         if time_units == "d":
91             times /= (60 * 60 * 24)
92         elif time_units == "h":
93             times /= (60 * 60)
94         elif time_units == "min":
95             times /= 60
96 
97         if nuc_units != "atoms":
98             # Divide by volume to get density
99             concentrations /= self[0].volume[mat]
100             if nuc_units == "atom/b-cm":
101                 # 1 barn = 1e-24 cm^2
102                 concentrations *= 1e-24
103 
104         return times, concentrations
105 
106     def get_reaction_rate(self, mat, nuc, rx):
107         """Get reaction rate in a single material/nuclide over time
108 
109         .. note::
110 
111             Initial values for some isotopes that do not appear in
112             initial concentrations may be non-zero, depending on the
113             value of :class:`openmc.deplete.Operator` ``dilute_initial``
114             The :class:`openmc.deplete.Operator` adds isotopes according
115             to this setting, which can be set to zero.
116 
117         Parameters
118         ----------
119         mat : str
120             Material name to evaluate
121         nuc : str
122             Nuclide name to evaluate
123         rx : str
124             Reaction rate to evaluate
125 
126         Returns
127         -------
128         times : numpy.ndarray
129             Array of times in [s]
130         rates : numpy.ndarray
131             Array of reaction rates
132 
133         """
134         times = np.empty_like(self, dtype=float)
135         rates = np.empty_like(self, dtype=float)
136 
137         # Evaluate value in each region
138         for i, result in enumerate(self):
139             times[i] = result.time[0]
140             rates[i] = result.rates[0].get(mat, nuc, rx) * result[0, mat, nuc]
141 
142         return times, rates
143 
144     def get_eigenvalue(self):
145         """Evaluates the eigenvalue from a results list.
146 
147         Returns
148         -------
149         times : numpy.ndarray
150             Array of times in [s]
151         eigenvalues : numpy.ndarray
152             k-eigenvalue at each time. Column 0
153             contains the eigenvalue, while column
154             1 contains the associated uncertainty
155 
156         """
157         times = np.empty_like(self, dtype=float)
158         eigenvalues = np.empty((len(self), 2), dtype=float)
159 
160         # Get time/eigenvalue at each point
161         for i, result in enumerate(self):
162             times[i] = result.time[0]
163             eigenvalues[i] = result.k[0]
164 
165         return times, eigenvalues
166 
167     def get_depletion_time(self):
168         """Return an array of the average time to deplete a material
169 
170         .. note::
171 
172             Will have one fewer row than number of other methods,
173             like :meth:`get_eigenvalues`, because no depletion
174             is performed at the final transport stage
175 
176         Returns
177         -------
178         times : numpy.ndarray
179             Vector of average time to deplete a single material
180             across all processes and materials.
181 
182         """
183         times = np.empty(len(self) - 1)
184         # Need special logic because the predictor
185         # writes EOS values for step i as BOS values
186         # for step i+1
187         # The first proc_time may be zero
188         if self[0].proc_time > 0.0:
189             items = self[:-1]
190         else:
191             items = self[1:]
192         for ix, res in enumerate(items):
193             times[ix] = res.proc_time
194         return times
195
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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
@@ -1,8 +1,12 @@
+import numbers
+import bisect
+import math
+
 import h5py
 import numpy as np
 
 from .results import Results, VERSION_RESULTS
-from openmc.checkvalue import check_filetype_version, check_value
+from openmc.checkvalue import check_filetype_version, check_value, check_type
 
 
 __all__ = ["ResultsList"]
@@ -192,3 +196,103 @@
         for ix, res in enumerate(items):
             times[ix] = res.proc_time
         return times
+
+    def get_times(self, time_units="d") -> np.ndarray:
+        """Return the points in time that define the depletion schedule
+
+
+        .. versionadded:: 0.12.1
+
+        Parameters
+        ----------
+        time_units : {"s", "d", "h", "min"}, optional
+            Return the vector in these units. Default is to
+            convert to days
+
+        Returns
+        -------
+        numpy.ndarray
+            1-D vector of time points
+
+        """
+        check_type("time_units", time_units, str)
+
+        times = np.fromiter(
+            (r.time[0] for r in self),
+            dtype=self[0].time.dtype,
+            count=len(self),
+        )
+
+        if time_units == "d":
+            times /= (60 * 60 * 24)
+        elif time_units == "h":
+            times /= (60 * 60)
+        elif time_units == "min":
+            times /= 60
+        elif time_units != "s":
+            raise ValueError(
+                'Unable to set "time_units" to {} since it is not '
+                'in ("s", "d", "min", "h")'.format(time_units)
+            )
+        return times
+
+    def get_step_where(
+        self, time, time_units="d", atol=1e-6, rtol=1e-3
+    ) -> int:
+        """Return the index closest to a given point in time
+
+        In the event ``time`` lies exactly between two points, the
+        lower index will be returned. It is possible that the index
+        will be at most one past the point in time requested, but only
+        according to tolerances requested.
+
+        Passing ``atol=math.inf`` and ``rtol=math.inf`` will return
+        the closest index to the requested point.
+
+
+        .. versionadded:: 0.12.1
+
+        Parameters
+        ----------
+        time : float
+            Desired point in time
+        time_units : {"s", "d", "min", "h"}, optional
+            Units on ``time``. Default: days
+        atol : float, optional
+            Absolute tolerance (in ``time_units``) if ``time`` is not
+            found.
+        rtol : float, optional
+            Relative tolerance if ``time`` is not found.
+
+        Returns
+        -------
+        int
+
+        """
+        check_type("time", time, numbers.Real)
+        check_type("atol", atol, numbers.Real)
+        check_type("rtol", rtol, numbers.Real)
+
+        times = self.get_times(time_units)
+
+        if times[0] < time < times[-1]:
+            ix = bisect.bisect_left(times, time)
+            if ix == times.size:
+                ix -= 1
+            # Bisection will place us either directly on the point
+            # or one-past the first value less than time
+            elif time - times[ix - 1] <= times[ix] - time:
+                ix -= 1
+        elif times[0] >= time:
+            ix = 0
+        elif time >= times[-1]:
+            ix = times.size - 1
+
+        if math.isclose(time, times[ix], rel_tol=rtol, abs_tol=atol):
+            return ix
+
+        raise ValueError(
+            "A value of {} {} was not found given absolute and "
+            "relative tolerances {} and {}.".format(
+                time, time_units, atol, rtol)
+        )

{"golden_diff": "diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py\n--- a/openmc/deplete/results_list.py\n+++ b/openmc/deplete/results_list.py\n@@ -1,8 +1,12 @@\n+import numbers\n+import bisect\n+import math\n+\n import h5py\n import numpy as np\n \n from .results import Results, VERSION_RESULTS\n-from openmc.checkvalue import check_filetype_version, check_value\n+from openmc.checkvalue import check_filetype_version, check_value, check_type\n \n \n __all__ = [\"ResultsList\"]\n@@ -192,3 +196,103 @@\n         for ix, res in enumerate(items):\n             times[ix] = res.proc_time\n         return times\n+\n+    def get_times(self, time_units=\"d\") -> np.ndarray:\n+        \"\"\"Return the points in time that define the depletion schedule\n+\n+\n+        .. versionadded:: 0.12.1\n+\n+        Parameters\n+        ----------\n+        time_units : {\"s\", \"d\", \"h\", \"min\"}, optional\n+            Return the vector in these units. Default is to\n+            convert to days\n+\n+        Returns\n+        -------\n+        numpy.ndarray\n+            1-D vector of time points\n+\n+        \"\"\"\n+        check_type(\"time_units\", time_units, str)\n+\n+        times = np.fromiter(\n+            (r.time[0] for r in self),\n+            dtype=self[0].time.dtype,\n+            count=len(self),\n+        )\n+\n+        if time_units == \"d\":\n+            times /= (60 * 60 * 24)\n+        elif time_units == \"h\":\n+            times /= (60 * 60)\n+        elif time_units == \"min\":\n+            times /= 60\n+        elif time_units != \"s\":\n+            raise ValueError(\n+                'Unable to set \"time_units\" to {} since it is not '\n+                'in (\"s\", \"d\", \"min\", \"h\")'.format(time_units)\n+            )\n+        return times\n+\n+    def get_step_where(\n+        self, time, time_units=\"d\", atol=1e-6, rtol=1e-3\n+    ) -> int:\n+        \"\"\"Return the index closest to a given point in time\n+\n+        In the event ``time`` lies exactly between two points, the\n+        lower index will be returned. It is possible that the index\n+        will be at most one past the point in time requested, but only\n+        according to tolerances requested.\n+\n+        Passing ``atol=math.inf`` and ``rtol=math.inf`` will return\n+        the closest index to the requested point.\n+\n+\n+        .. versionadded:: 0.12.1\n+\n+        Parameters\n+        ----------\n+        time : float\n+            Desired point in time\n+        time_units : {\"s\", \"d\", \"min\", \"h\"}, optional\n+            Units on ``time``. Default: days\n+        atol : float, optional\n+            Absolute tolerance (in ``time_units``) if ``time`` is not\n+            found.\n+        rtol : float, optional\n+            Relative tolerance if ``time`` is not found.\n+\n+        Returns\n+        -------\n+        int\n+\n+        \"\"\"\n+        check_type(\"time\", time, numbers.Real)\n+        check_type(\"atol\", atol, numbers.Real)\n+        check_type(\"rtol\", rtol, numbers.Real)\n+\n+        times = self.get_times(time_units)\n+\n+        if times[0] < time < times[-1]:\n+            ix = bisect.bisect_left(times, time)\n+            if ix == times.size:\n+                ix -= 1\n+            # Bisection will place us either directly on the point\n+            # or one-past the first value less than time\n+            elif time - times[ix - 1] <= times[ix] - time:\n+                ix -= 1\n+        elif times[0] >= time:\n+            ix = 0\n+        elif time >= times[-1]:\n+            ix = times.size - 1\n+\n+        if math.isclose(time, times[ix], rel_tol=rtol, abs_tol=atol):\n+            return ix\n+\n+        raise ValueError(\n+            \"A value of {} {} was not found given absolute and \"\n+            \"relative tolerances {} and {}.\".format(\n+                time, time_units, atol, rtol)\n+        )\n", "issue": "Feature: Easier indexing through ResultsList to find depletion step index\nIf a user wanted to perform a restart simulation not necessarily at the end point in time, there isn't a super simple way to do it from the API. Currently you would have to manually search through the `Result` instances and compare their time data one at a time. \r\n\r\nThis isn't a monumental task, and I think we could easily provide a method that would allow users to find the index corresponding to a specific point in time. Supporting similar `time_units` like we do for the `Operator` (days, seconds, MWd/kgHM) would also be super nice. \r\n\r\nLooking at #1708, this would also be useful when exporting `material.xml` files either during the depletion run or after the fact. Either way, the user will need a way to link a depletion step to a point in calendar time for restarting or other analysis.\r\n\r\nOther things to consider would be some tolerance on the search criteria. If I ask for the step where burnup was 12 MWd/kgHM, (for example), but the closest point is 12.1 MWd/kgHM, should that step be returned? Or error out? \n", "before_files": [{"content": "import h5py\nimport numpy as np\n\nfrom .results import Results, VERSION_RESULTS\nfrom openmc.checkvalue import check_filetype_version, check_value\n\n\n__all__ = [\"ResultsList\"]\n\n\nclass ResultsList(list):\n    \"\"\"A list of openmc.deplete.Results objects\n\n    It is recommended to use :meth:`from_hdf5` over\n    direct creation.\n    \"\"\"\n\n    @classmethod\n    def from_hdf5(cls, filename):\n        \"\"\"Load in depletion results from a previous file\n\n        Parameters\n        ----------\n        filename : str\n            Path to depletion result file\n\n        Returns\n        -------\n        new : ResultsList\n            New instance of depletion results\n        \"\"\"\n        with h5py.File(str(filename), \"r\") as fh:\n            check_filetype_version(fh, 'depletion results', VERSION_RESULTS[0])\n            new = cls()\n\n            # Get number of results stored\n            n = fh[\"number\"][...].shape[0]\n\n            for i in range(n):\n                new.append(Results.from_hdf5(fh, i))\n        return new\n\n    def get_atoms(self, mat, nuc, nuc_units=\"atoms\", time_units=\"s\"):\n        \"\"\"Get number of nuclides over time from a single material\n\n        .. note::\n            Initial values for some isotopes that do not appear in\n            initial concentrations may be non-zero, depending on the\n            value of :class:`openmc.deplete.Operator` ``dilute_initial``.\n            The :class:`openmc.deplete.Operator` adds isotopes according\n            to this setting, which can be set to zero.\n\n        Parameters\n        ----------\n        mat : str\n            Material name to evaluate\n        nuc : str\n            Nuclide name to evaluate\n        nuc_units : {\"atoms\", \"atom/b-cm\", \"atom/cm3\"}, optional\n            Units for the returned concentration. Default is ``\"atoms\"``\n\n            .. versionadded:: 0.12\n        time_units : {\"s\", \"min\", \"h\", \"d\"}, optional\n            Units for the returned time array. Default is ``\"s\"`` to\n            return the value in seconds.\n\n            .. versionadded:: 0.12\n\n        Returns\n        -------\n        times : numpy.ndarray\n            Array of times in units of ``time_units``\n        concentrations : numpy.ndarray\n            Concentration of specified nuclide in units of ``nuc_units``\n\n        \"\"\"\n        check_value(\"time_units\", time_units, {\"s\", \"d\", \"min\", \"h\"})\n        check_value(\"nuc_units\", nuc_units,\n                    {\"atoms\", \"atom/b-cm\", \"atom/cm3\"})\n\n        times = np.empty_like(self, dtype=float)\n        concentrations = np.empty_like(self, dtype=float)\n\n        # Evaluate value in each region\n        for i, result in enumerate(self):\n            times[i] = result.time[0]\n            concentrations[i] = result[0, mat, nuc]\n\n        # Unit conversions\n        if time_units == \"d\":\n            times /= (60 * 60 * 24)\n        elif time_units == \"h\":\n            times /= (60 * 60)\n        elif time_units == \"min\":\n            times /= 60\n\n        if nuc_units != \"atoms\":\n            # Divide by volume to get density\n            concentrations /= self[0].volume[mat]\n            if nuc_units == \"atom/b-cm\":\n                # 1 barn = 1e-24 cm^2\n                concentrations *= 1e-24\n\n        return times, concentrations\n\n    def get_reaction_rate(self, mat, nuc, rx):\n        \"\"\"Get reaction rate in a single material/nuclide over time\n\n        .. note::\n\n            Initial values for some isotopes that do not appear in\n            initial concentrations may be non-zero, depending on the\n            value of :class:`openmc.deplete.Operator` ``dilute_initial``\n            The :class:`openmc.deplete.Operator` adds isotopes according\n            to this setting, which can be set to zero.\n\n        Parameters\n        ----------\n        mat : str\n            Material name to evaluate\n        nuc : str\n            Nuclide name to evaluate\n        rx : str\n            Reaction rate to evaluate\n\n        Returns\n        -------\n        times : numpy.ndarray\n            Array of times in [s]\n        rates : numpy.ndarray\n            Array of reaction rates\n\n        \"\"\"\n        times = np.empty_like(self, dtype=float)\n        rates = np.empty_like(self, dtype=float)\n\n        # Evaluate value in each region\n        for i, result in enumerate(self):\n            times[i] = result.time[0]\n            rates[i] = result.rates[0].get(mat, nuc, rx) * result[0, mat, nuc]\n\n        return times, rates\n\n    def get_eigenvalue(self):\n        \"\"\"Evaluates the eigenvalue from a results list.\n\n        Returns\n        -------\n        times : numpy.ndarray\n            Array of times in [s]\n        eigenvalues : numpy.ndarray\n            k-eigenvalue at each time. Column 0\n            contains the eigenvalue, while column\n            1 contains the associated uncertainty\n\n        \"\"\"\n        times = np.empty_like(self, dtype=float)\n        eigenvalues = np.empty((len(self), 2), dtype=float)\n\n        # Get time/eigenvalue at each point\n        for i, result in enumerate(self):\n            times[i] = result.time[0]\n            eigenvalues[i] = result.k[0]\n\n        return times, eigenvalues\n\n    def get_depletion_time(self):\n        \"\"\"Return an array of the average time to deplete a material\n\n        .. note::\n\n            Will have one fewer row than number of other methods,\n            like :meth:`get_eigenvalues`, because no depletion\n            is performed at the final transport stage\n\n        Returns\n        -------\n        times : numpy.ndarray\n            Vector of average time to deplete a single material\n            across all processes and materials.\n\n        \"\"\"\n        times = np.empty(len(self) - 1)\n        # Need special logic because the predictor\n        # writes EOS values for step i as BOS values\n        # for step i+1\n        # The first proc_time may be zero\n        if self[0].proc_time > 0.0:\n            items = self[:-1]\n        else:\n            items = self[1:]\n        for ix, res in enumerate(items):\n            times[ix] = res.proc_time\n        return times\n", "path": "openmc/deplete/results_list.py"}], "after_files": [{"content": "import numbers\nimport bisect\nimport math\n\nimport h5py\nimport numpy as np\n\nfrom .results import Results, VERSION_RESULTS\nfrom openmc.checkvalue import check_filetype_version, check_value, check_type\n\n\n__all__ = [\"ResultsList\"]\n\n\nclass ResultsList(list):\n    \"\"\"A list of openmc.deplete.Results objects\n\n    It is recommended to use :meth:`from_hdf5` over\n    direct creation.\n    \"\"\"\n\n    @classmethod\n    def from_hdf5(cls, filename):\n        \"\"\"Load in depletion results from a previous file\n\n        Parameters\n        ----------\n        filename : str\n            Path to depletion result file\n\n        Returns\n        -------\n        new : ResultsList\n            New instance of depletion results\n        \"\"\"\n        with h5py.File(str(filename), \"r\") as fh:\n            check_filetype_version(fh, 'depletion results', VERSION_RESULTS[0])\n            new = cls()\n\n            # Get number of results stored\n            n = fh[\"number\"][...].shape[0]\n\n            for i in range(n):\n                new.append(Results.from_hdf5(fh, i))\n        return new\n\n    def get_atoms(self, mat, nuc, nuc_units=\"atoms\", time_units=\"s\"):\n        \"\"\"Get number of nuclides over time from a single material\n\n        .. note::\n            Initial values for some isotopes that do not appear in\n            initial concentrations may be non-zero, depending on the\n            value of :class:`openmc.deplete.Operator` ``dilute_initial``.\n            The :class:`openmc.deplete.Operator` adds isotopes according\n            to this setting, which can be set to zero.\n\n        Parameters\n        ----------\n        mat : str\n            Material name to evaluate\n        nuc : str\n            Nuclide name to evaluate\n        nuc_units : {\"atoms\", \"atom/b-cm\", \"atom/cm3\"}, optional\n            Units for the returned concentration. Default is ``\"atoms\"``\n\n            .. versionadded:: 0.12\n        time_units : {\"s\", \"min\", \"h\", \"d\"}, optional\n            Units for the returned time array. Default is ``\"s\"`` to\n            return the value in seconds.\n\n            .. versionadded:: 0.12\n\n        Returns\n        -------\n        times : numpy.ndarray\n            Array of times in units of ``time_units``\n        concentrations : numpy.ndarray\n            Concentration of specified nuclide in units of ``nuc_units``\n\n        \"\"\"\n        check_value(\"time_units\", time_units, {\"s\", \"d\", \"min\", \"h\"})\n        check_value(\"nuc_units\", nuc_units,\n                    {\"atoms\", \"atom/b-cm\", \"atom/cm3\"})\n\n        times = np.empty_like(self, dtype=float)\n        concentrations = np.empty_like(self, dtype=float)\n\n        # Evaluate value in each region\n        for i, result in enumerate(self):\n            times[i] = result.time[0]\n            concentrations[i] = result[0, mat, nuc]\n\n        # Unit conversions\n        if time_units == \"d\":\n            times /= (60 * 60 * 24)\n        elif time_units == \"h\":\n            times /= (60 * 60)\n        elif time_units == \"min\":\n            times /= 60\n\n        if nuc_units != \"atoms\":\n            # Divide by volume to get density\n            concentrations /= self[0].volume[mat]\n            if nuc_units == \"atom/b-cm\":\n                # 1 barn = 1e-24 cm^2\n                concentrations *= 1e-24\n\n        return times, concentrations\n\n    def get_reaction_rate(self, mat, nuc, rx):\n        \"\"\"Get reaction rate in a single material/nuclide over time\n\n        .. note::\n\n            Initial values for some isotopes that do not appear in\n            initial concentrations may be non-zero, depending on the\n            value of :class:`openmc.deplete.Operator` ``dilute_initial``\n            The :class:`openmc.deplete.Operator` adds isotopes according\n            to this setting, which can be set to zero.\n\n        Parameters\n        ----------\n        mat : str\n            Material name to evaluate\n        nuc : str\n            Nuclide name to evaluate\n        rx : str\n            Reaction rate to evaluate\n\n        Returns\n        -------\n        times : numpy.ndarray\n            Array of times in [s]\n        rates : numpy.ndarray\n            Array of reaction rates\n\n        \"\"\"\n        times = np.empty_like(self, dtype=float)\n        rates = np.empty_like(self, dtype=float)\n\n        # Evaluate value in each region\n        for i, result in enumerate(self):\n            times[i] = result.time[0]\n            rates[i] = result.rates[0].get(mat, nuc, rx) * result[0, mat, nuc]\n\n        return times, rates\n\n    def get_eigenvalue(self):\n        \"\"\"Evaluates the eigenvalue from a results list.\n\n        Returns\n        -------\n        times : numpy.ndarray\n            Array of times in [s]\n        eigenvalues : numpy.ndarray\n            k-eigenvalue at each time. Column 0\n            contains the eigenvalue, while column\n            1 contains the associated uncertainty\n\n        \"\"\"\n        times = np.empty_like(self, dtype=float)\n        eigenvalues = np.empty((len(self), 2), dtype=float)\n\n        # Get time/eigenvalue at each point\n        for i, result in enumerate(self):\n            times[i] = result.time[0]\n            eigenvalues[i] = result.k[0]\n\n        return times, eigenvalues\n\n    def get_depletion_time(self):\n        \"\"\"Return an array of the average time to deplete a material\n\n        .. note::\n\n            Will have one fewer row than number of other methods,\n            like :meth:`get_eigenvalues`, because no depletion\n            is performed at the final transport stage\n\n        Returns\n        -------\n        times : numpy.ndarray\n            Vector of average time to deplete a single material\n            across all processes and materials.\n\n        \"\"\"\n        times = np.empty(len(self) - 1)\n        # Need special logic because the predictor\n        # writes EOS values for step i as BOS values\n        # for step i+1\n        # The first proc_time may be zero\n        if self[0].proc_time > 0.0:\n            items = self[:-1]\n        else:\n            items = self[1:]\n        for ix, res in enumerate(items):\n            times[ix] = res.proc_time\n        return times\n\n    def get_times(self, time_units=\"d\") -> np.ndarray:\n        \"\"\"Return the points in time that define the depletion schedule\n\n\n        .. versionadded:: 0.12.1\n\n        Parameters\n        ----------\n        time_units : {\"s\", \"d\", \"h\", \"min\"}, optional\n            Return the vector in these units. Default is to\n            convert to days\n\n        Returns\n        -------\n        numpy.ndarray\n            1-D vector of time points\n\n        \"\"\"\n        check_type(\"time_units\", time_units, str)\n\n        times = np.fromiter(\n            (r.time[0] for r in self),\n            dtype=self[0].time.dtype,\n            count=len(self),\n        )\n\n        if time_units == \"d\":\n            times /= (60 * 60 * 24)\n        elif time_units == \"h\":\n            times /= (60 * 60)\n        elif time_units == \"min\":\n            times /= 60\n        elif time_units != \"s\":\n            raise ValueError(\n                'Unable to set \"time_units\" to {} since it is not '\n                'in (\"s\", \"d\", \"min\", \"h\")'.format(time_units)\n            )\n        return times\n\n    def get_step_where(\n        self, time, time_units=\"d\", atol=1e-6, rtol=1e-3\n    ) -> int:\n        \"\"\"Return the index closest to a given point in time\n\n        In the event ``time`` lies exactly between two points, the\n        lower index will be returned. It is possible that the index\n        will be at most one past the point in time requested, but only\n        according to tolerances requested.\n\n        Passing ``atol=math.inf`` and ``rtol=math.inf`` will return\n        the closest index to the requested point.\n\n\n        .. versionadded:: 0.12.1\n\n        Parameters\n        ----------\n        time : float\n            Desired point in time\n        time_units : {\"s\", \"d\", \"min\", \"h\"}, optional\n            Units on ``time``. Default: days\n        atol : float, optional\n            Absolute tolerance (in ``time_units``) if ``time`` is not\n            found.\n        rtol : float, optional\n            Relative tolerance if ``time`` is not found.\n\n        Returns\n        -------\n        int\n\n        \"\"\"\n        check_type(\"time\", time, numbers.Real)\n        check_type(\"atol\", atol, numbers.Real)\n        check_type(\"rtol\", rtol, numbers.Real)\n\n        times = self.get_times(time_units)\n\n        if times[0] < time < times[-1]:\n            ix = bisect.bisect_left(times, time)\n            if ix == times.size:\n                ix -= 1\n            # Bisection will place us either directly on the point\n            # or one-past the first value less than time\n            elif time - times[ix - 1] <= times[ix] - time:\n                ix -= 1\n        elif times[0] >= time:\n            ix = 0\n        elif time >= times[-1]:\n            ix = times.size - 1\n\n        if math.isclose(time, times[ix], rel_tol=rtol, abs_tol=atol):\n            return ix\n\n        raise ValueError(\n            \"A value of {} {} was not found given absolute and \"\n            \"relative tolerances {} and {}.\".format(\n                time, time_units, atol, rtol)\n        )\n", "path": "openmc/deplete/results_list.py"}]}