{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Multigroup (Delayed) Cross Section Generation Part I: Introduction\n",
    "This IPython Notebook introduces the use of the `openmc.mgxs` module to calculate multi-energy-group and multi-delayed-group cross sections for an infinite homogeneous medium. In particular, this Notebook introduces the the following features:\n",
    "\n",
    "* Creation of multi-delayed-group cross sections for an **infinite homogeneous medium**\n",
    "* Calculation of delayed neutron precursor concentrations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction to Multi-Delayed-Group Cross Sections (MDGXS)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Many Monte Carlo particle transport codes, including OpenMC, use continuous-energy nuclear cross section data. However, most deterministic neutron transport codes use *multi-group cross sections* defined over discretized energy bins or *energy groups*. Furthermore, kinetics calculations typically separate out parameters that involve delayed neutrons into prompt and delayed components and further subdivide delayed components by delayed groups. An example is the energy spectrum for prompt and delayed neutrons for U-235 and Pu-239 computed for a light water reactor spectrum."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 31720 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 1,
     "metadata": {
      "image/png": {
       "width": 350
      }
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.display import Image\n",
    "Image(filename='images/mdgxs.png', width=350)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A variety of tools employing different methodologies have been developed over the years to compute multi-group cross sections for certain applications, including NJOY (LANL), MC$^2$-3 (ANL), and Serpent (VTT). The `openmc.mgxs` Python module is designed to leverage OpenMC's tally system to calculate multi-group cross sections with arbitrary energy discretizations and different delayed group models (e.g. 6, 7, or 8 delayed group models) for fine-mesh heterogeneous deterministic neutron transport applications.\n",
    "\n",
    "Before proceeding to illustrate how one may use the `openmc.mgxs` module, it is worthwhile to define the general equations used to calculate multi-energy-group and multi-delayed-group cross sections. This is only intended as a brief overview of the methodology used by `openmc.mgxs` - we refer the interested reader to the large body of literature on the subject for a more comprehensive understanding of this complex topic."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Introductory Notation\n",
    "The continuous real-valued microscopic cross section may be denoted $\\sigma_{n,x}(\\mathbf{r}, E)$ for position vector $\\mathbf{r}$, energy $E$, nuclide $n$ and interaction type $x$. Similarly, the scalar neutron flux may be denoted by $\\Phi(\\mathbf{r},E)$ for position $\\mathbf{r}$ and energy $E$. **Note**: Although nuclear cross sections are dependent on the temperature $T$ of the interacting medium, the temperature variable is neglected here for brevity."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Spatial and Energy Discretization\n",
    "The energy domain for critical systems such as thermal reactors spans more than 10 orders of magnitude of neutron energies from 10$^{-5}$ - 10$^7$ eV. The multi-group approximation discretization divides this energy range into one or more energy groups. In particular, for $G$ total groups, we denote an energy group index $g$ such that $g \\in \\{1, 2, ..., G\\}$. The energy group indices are defined such that the smaller group the higher the energy, and vice versa. The integration over neutron energies across a discrete energy group is commonly referred to as **energy condensation**.\n",
    "\n",
    "The delayed neutrons created from fissions are created from > 30 delayed neutron precursors. Modeling each of the delayed neutron precursors is possible, but this approach has not recieved much attention due to large uncertainties in certain precursors. Therefore, the delayed neutrons are often combined into \"delayed groups\" that have a set time constant, $\\lambda_d$. Some cross section libraries use the same group time constants for all nuclides (e.g. JEFF 3.1) while other libraries use different time constants for all nuclides (e.g. ENDF/B-VII.1). Multi-delayed-group cross sections can either be created with the entire delayed group set, a subset of delayed groups, or integrated over all delayed groups.\n",
    "\n",
    "Multi-group cross sections are computed for discretized spatial zones in the geometry of interest. The spatial zones may be defined on a structured and regular fuel assembly or pin cell mesh, an arbitrary unstructured mesh or the constructive solid geometry used by OpenMC. For a geometry with $K$ distinct spatial zones, we designate each spatial zone an index $k$ such that $k \\in \\{1, 2, ..., K\\}$. The volume of each spatial zone is denoted by $V_{k}$. The integration over discrete spatial zones is commonly referred to as **spatial homogenization**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### General Scalar-Flux Weighted MDGXS\n",
    "The multi-group cross sections computed by `openmc.mgxs` are defined as a *scalar flux-weighted average* of the microscopic cross sections across each discrete energy group. This formulation is employed in order to preserve the reaction rates within each energy group and spatial zone. In particular, spatial homogenization and energy condensation are used to compute the general multi-group cross section. For instance, the delayed-nu-fission multi-energy-group and multi-delayed-group cross section, $\\nu_d \\sigma_{f,x,k,g}$, can be computed as follows:\n",
    "\n",
    "$$\\nu_d \\sigma_{n,x,k,g} = \\frac{\\int_{E_{g}}^{E_{g-1}}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r} \\nu_d \\sigma_{f,x}(\\mathbf{r},E')\\Phi(\\mathbf{r},E')}{\\int_{E_{g}}^{E_{g-1}}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r}\\Phi(\\mathbf{r},E')}$$\n",
    "\n",
    "This scalar flux-weighted average microscopic cross section is computed by `openmc.mgxs` for only the delayed-nu-fission and delayed neutron fraction reaction type at the moment. These double integrals are stochastically computed with OpenMC's tally system - in particular, [filters](../usersguide/tallies.rst#filters) on the energy range and spatial zone (material, cell, universe, or mesh) define the bounds of integration for both numerator and denominator."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Multi-Group Prompt and Delayed Fission Spectrum\n",
    "The energy spectrum of neutrons emitted from fission is denoted by $\\chi_{n}(\\mathbf{r},E' \\rightarrow E'')$ for incoming and outgoing energies $E'$ and $E''$, respectively. Unlike the multi-group cross sections $\\sigma_{n,x,k,g}$ considered up to this point, the fission spectrum is a probability distribution and must sum to unity. The outgoing energy is typically much less dependent on the incoming energy for fission than for scattering interactions. As a result, it is common practice to integrate over the incoming neutron energy when computing the multi-group fission spectrum. The fission spectrum may be simplified as $\\chi_{n}(\\mathbf{r},E)$ with outgoing energy $E$.\n",
    "\n",
    "Computing the cumulative energy spectrum of emitted neutrons, $\\chi_{n}(\\mathbf{r},E)$, has been presented in the `mgxs-part-i.ipynb` notebook. Here, we will present the energy spectrum of prompt and delayed emission neutrons, $\\chi_{n,p}(\\mathbf{r},E)$ and $\\chi_{n,d}(\\mathbf{r},E)$, respectively. Unlike the multi-group cross sections defined up to this point, the multi-group fission spectrum is weighted by the fission production rate rather than the scalar flux. This formulation is intended to preserve the total fission production rate in the multi-group deterministic calculation. In order to mathematically define the multi-group fission spectrum, we denote the microscopic fission cross section as $\\sigma_{n,f}(\\mathbf{r},E)$ and the average number of neutrons emitted from fission interactions with nuclide $n$ as $\\nu_{n,p}(\\mathbf{r},E)$ and $\\nu_{n,d}(\\mathbf{r},E)$ for prompt and delayed neutrons, respectively. The multi-group fission spectrum $\\chi_{n,k,g,d}$ is then the probability of fission neutrons emitted into energy group $g$ and delayed group $d$. There are not prompt groups, so inserting $p$ in place of $d$ just denotes all prompt neutrons. \n",
    "\n",
    "Similar to before, spatial homogenization and energy condensation are used to find the multi-energy-group and multi-delayed-group fission spectrum $\\chi_{n,k,g,d}$ as follows:\n",
    "\n",
    "$$\\chi_{n,k,g',d} = \\frac{\\int_{E_{g'}}^{E_{g'-1}}\\mathrm{d}E''\\int_{0}^{\\infty}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r}\\chi_{n,d}(\\mathbf{r},E'\\rightarrow E'')\\nu_{n,d}(\\mathbf{r},E')\\sigma_{n,f}(\\mathbf{r},E')\\Phi(\\mathbf{r},E')}{\\int_{0}^{\\infty}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r}\\nu_{n,d}(\\mathbf{r},E')\\sigma_{n,f}(\\mathbf{r},E')\\Phi(\\mathbf{r},E')}$$\n",
    "\n",
    "The fission production-weighted multi-energy-group and multi-delayed-group fission spectrum for delayed neutrons is computed using OpenMC tallies with energy in, energy out, and delayed group filters. Alternatively, the delayed group filter can be omitted to compute the fission spectrum integrated over all delayed groups.\n",
    "\n",
    "This concludes our brief overview on the methodology to compute multi-energy-group and multi-delayed-group cross sections. The following sections detail more concretely how users may employ the `openmc.mgxs` module to power simulation workflows requiring multi-group cross sections for downstream deterministic calculations."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generate Input Files"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import openmc\n",
    "import openmc.mgxs as mgxs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First we need to define materials that will be used in the problem. Let's create a material for the homogeneous medium."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate a Material and register the Nuclides\n",
    "inf_medium = openmc.Material(name='moderator')\n",
    "inf_medium.set_density('g/cc', 5.)\n",
    "inf_medium.add_nuclide('H1',  0.03)\n",
    "inf_medium.add_nuclide('O16', 0.015)\n",
    "inf_medium.add_nuclide('U235', 0.0001)\n",
    "inf_medium.add_nuclide('U238', 0.007)\n",
    "inf_medium.add_nuclide('Pu239', 0.00003)\n",
    "inf_medium.add_nuclide('Zr90', 0.002)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With our material, we can now create a `Materials` object that can be exported to an actual XML file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate a Materials collection and export to XML\n",
    "materials = openmc.Materials([inf_medium])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's move on to the geometry. This problem will be a simple square cell with reflective boundary conditions to simulate an infinite homogeneous medium. The first step is to create the outer bounding surfaces of the problem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate boundary Planes\n",
    "min_x = openmc.XPlane(boundary_type='reflective', x0=-0.63)\n",
    "max_x = openmc.XPlane(boundary_type='reflective', x0=0.63)\n",
    "min_y = openmc.YPlane(boundary_type='reflective', y0=-0.63)\n",
    "max_y = openmc.YPlane(boundary_type='reflective', y0=0.63)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With the surfaces defined, we can now create a cell that is defined by intersections of half-spaces created by the surfaces."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate a Cell\n",
    "cell = openmc.Cell(cell_id=1, name='cell')\n",
    "\n",
    "# Register bounding Surfaces with the Cell\n",
    "cell.region = +min_x & -max_x & +min_y & -max_y\n",
    "\n",
    "# Fill the Cell with the Material\n",
    "cell.fill = inf_medium"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now must create a geometry and export it to XML."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create Geometry and set root Universe\n",
    "geometry = openmc.Geometry([cell])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we must define simulation parameters. In this case, we will use 10 inactive batches and 40 active batches each with 2500 particles."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# OpenMC simulation parameters\n",
    "batches = 50\n",
    "inactive = 10\n",
    "particles = 5000\n",
    "\n",
    "# Instantiate a Settings object\n",
    "settings = openmc.Settings()\n",
    "settings.batches = batches\n",
    "settings.inactive = inactive\n",
    "settings.particles = particles\n",
    "settings.output = {'tallies': True}\n",
    "\n",
    "# Create an initial uniform spatial source distribution over fissionable zones\n",
    "bounds = [-0.63, -0.63, -0.63, 0.63, 0.63, 0.63]\n",
    "uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)\n",
    "settings.source = openmc.IndependentSource(space=uniform_dist)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we are ready to generate multi-group cross sections! First, let's define a 100-energy-group structure and 1-energy-group structure using the built-in `EnergyGroups` class. We will also create a 6-delayed-group list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate a 100-group EnergyGroups object\n",
    "energy_groups = mgxs.EnergyGroups()\n",
    "energy_groups.group_edges = np.logspace(-3, 7.3, 101)\n",
    "\n",
    "# Instantiate a 1-group EnergyGroups object\n",
    "one_group = mgxs.EnergyGroups()\n",
    "one_group.group_edges = np.array([energy_groups.group_edges[0], energy_groups.group_edges[-1]])\n",
    "\n",
    "delayed_groups = list(range(1,7))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now use the `EnergyGroups` object and delayed group list, along with our previously created materials and geometry, to instantiate some `MGXS` objects from the `openmc.mgxs` module. In particular, the following are subclasses of the generic and abstract `MGXS` class:\n",
    "\n",
    "* `TotalXS`\n",
    "* `TransportXS`\n",
    "* `AbsorptionXS`\n",
    "* `CaptureXS`\n",
    "* `FissionXS`\n",
    "* `NuFissionMatrixXS`\n",
    "* `KappaFissionXS`\n",
    "* `ScatterXS`\n",
    "* `ScatterMatrixXS`\n",
    "* `Chi`\n",
    "* `InverseVelocity`\n",
    "\n",
    "A separate abstract `MDGXS` class is used for cross-sections and parameters that involve delayed neutrons. The subclasses of `MDGXS` include:\n",
    "\n",
    "* `DelayedNuFissionXS`\n",
    "* `ChiDelayed`\n",
    "* `Beta`\n",
    "* `DecayRate`\n",
    "\n",
    "These classes provide us with an interface to generate the tally inputs as well as perform post-processing of OpenMC's tally data to compute the respective multi-group cross sections. \n",
    "\n",
    "In this case, let's create the multi-group chi-prompt, chi-delayed, and prompt-nu-fission cross sections with our 100-energy-group structure and multi-group delayed-nu-fission and beta cross sections with our 100-energy-group and 6-delayed-group structures. \n",
    "\n",
    "The prompt chi and nu-fission data can actually be gathered using the `Chi` and `FissionXS` classes, respectively, by passing in a value of `True` for the optional `prompt` parameter upon initialization."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate a few different sections\n",
    "chi_prompt = mgxs.Chi(domain=cell, energy_groups=energy_groups, by_nuclide=True, prompt=True)\n",
    "prompt_nu_fission = mgxs.FissionXS(domain=cell, energy_groups=energy_groups, by_nuclide=True, nu=True, prompt=True)\n",
    "chi_delayed = mgxs.ChiDelayed(domain=cell, energy_groups=energy_groups, by_nuclide=True)\n",
    "delayed_nu_fission = mgxs.DelayedNuFissionXS(domain=cell, energy_groups=energy_groups, delayed_groups=delayed_groups, by_nuclide=True)\n",
    "beta = mgxs.Beta(domain=cell, energy_groups=energy_groups, delayed_groups=delayed_groups, by_nuclide=True)\n",
    "decay_rate = mgxs.DecayRate(domain=cell, energy_groups=one_group, delayed_groups=delayed_groups, by_nuclide=True)\n",
    "\n",
    "chi_prompt.nuclides = ['U235', 'Pu239']\n",
    "prompt_nu_fission.nuclides = ['U235', 'Pu239']\n",
    "chi_delayed.nuclides = ['U235', 'Pu239']\n",
    "delayed_nu_fission.nuclides = ['U235', 'Pu239']\n",
    "beta.nuclides = ['U235', 'Pu239']\n",
    "decay_rate.nuclides = ['U235', 'Pu239']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Each multi-group cross section object stores its tallies in a Python dictionary called `tallies`. We can inspect the tallies in the dictionary for our `Decay Rate` object as follows. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('delayed-nu-fission',\n",
       "              Tally\n",
       "              \tID             =\t1\n",
       "              \tName           =\t\n",
       "              \tFilters        =\tCellFilter, DelayedGroupFilter\n",
       "              \tNuclides       =\tU235 Pu239\n",
       "              \tScores         =\t['delayed-nu-fission']\n",
       "              \tEstimator      =\ttracklength),\n",
       "             ('decay-rate',\n",
       "              Tally\n",
       "              \tID             =\t2\n",
       "              \tName           =\t\n",
       "              \tFilters        =\tCellFilter, DelayedGroupFilter\n",
       "              \tNuclides       =\tU235 Pu239\n",
       "              \tScores         =\t['decay-rate']\n",
       "              \tEstimator      =\ttracklength)])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "decay_rate.tallies"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `Beta` object includes tracklength tallies for the 'nu-fission' and 'delayed-nu-fission' scores in the 100-energy-group and 6-delayed-group structure in cell 1. Now that each `MGXS` and `MDGXS` object contains the tallies that it needs, we must add these tallies to a `Tallies` object to generate the \"tallies.xml\" input file for OpenMC."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/data/home/jupyter-aparler/.conda/envs/nuclear/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=3.\n",
      "  warn(msg, IDWarning)\n",
      "/data/home/jupyter-aparler/.conda/envs/nuclear/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=5.\n",
      "  warn(msg, IDWarning)\n",
      "/data/home/jupyter-aparler/.conda/envs/nuclear/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=4.\n",
      "  warn(msg, IDWarning)\n",
      "/data/home/jupyter-aparler/.conda/envs/nuclear/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=7.\n",
      "  warn(msg, IDWarning)\n",
      "/data/home/jupyter-aparler/.conda/envs/nuclear/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=13.\n",
      "  warn(msg, IDWarning)\n"
     ]
    }
   ],
   "source": [
    "# Instantiate an empty Tallies object\n",
    "tallies = openmc.Tallies()\n",
    "\n",
    "# Add chi-prompt tallies to the tallies file\n",
    "tallies += chi_prompt.tallies.values()\n",
    "\n",
    "# Add prompt-nu-fission tallies to the tallies file\n",
    "tallies += prompt_nu_fission.tallies.values()\n",
    "\n",
    "# Add chi-delayed tallies to the tallies file\n",
    "tallies += chi_delayed.tallies.values()\n",
    "\n",
    "# Add delayed-nu-fission tallies to the tallies file\n",
    "tallies += delayed_nu_fission.tallies.values()\n",
    "\n",
    "# Add beta tallies to the tallies file\n",
    "tallies += beta.tallies.values()\n",
    "\n",
    "# Add decay rate tallies to the tallies file\n",
    "tallies += decay_rate.tallies.values()\n",
    "\n",
    "# Export to \"tallies.xml\"\n",
    "tallies.export_to_xml()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we a have a complete set of inputs, so we can go ahead and run our simulation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tie geometry, materials, settings, and tallies together into a model object\n",
    "model = openmc.Model(geometry=geometry,\n",
    "                     materials=materials,\n",
    "                     settings=settings,\n",
    "                     tallies=tallies)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                                %%%%%%%%%%%%%%%\n",
      "                           %%%%%%%%%%%%%%%%%%%%%%%%\n",
      "                        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
      "                      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
      "                    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
      "                   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
      "                                    %%%%%%%%%%%%%%%%%%%%%%%%\n",
      "                                     %%%%%%%%%%%%%%%%%%%%%%%%\n",
      "                 ###############      %%%%%%%%%%%%%%%%%%%%%%%%\n",
      "                ##################     %%%%%%%%%%%%%%%%%%%%%%%\n",
      "                ###################     %%%%%%%%%%%%%%%%%%%%%%%\n",
      "                ####################     %%%%%%%%%%%%%%%%%%%%%%\n",
      "                #####################     %%%%%%%%%%%%%%%%%%%%%\n",
      "                ######################     %%%%%%%%%%%%%%%%%%%%\n",
      "                #######################     %%%%%%%%%%%%%%%%%%\n",
      "                 #######################     %%%%%%%%%%%%%%%%%\n",
      "                 ######################     %%%%%%%%%%%%%%%%%\n",
      "                  ####################     %%%%%%%%%%%%%%%%%\n",
      "                    #################     %%%%%%%%%%%%%%%%%\n",
      "                     ###############     %%%%%%%%%%%%%%%%\n",
      "                       ############     %%%%%%%%%%%%%%%\n",
      "                          ########     %%%%%%%%%%%%%%\n",
      "                                      %%%%%%%%%%%\n",
      "\n",
      "                 | The OpenMC Monte Carlo Code\n",
      "       Copyright | 2011-2023 MIT, UChicago Argonne LLC, and contributors\n",
      "         License | https://docs.openmc.org/en/latest/license.html\n",
      "         Version | 0.13.3\n",
      "        Git SHA1 | 50e39a4e20dc9e0f3d7ccf07333f6a5e6c797c8c\n",
      "       Date/Time | 2023-11-07 11:17:14\n",
      "  OpenMP Threads | 32\n",
      "\n",
      " Reading settings XML file...\n",
      " Reading cross sections XML file...\n",
      " Reading materials XML file...\n",
      " Reading geometry XML file...\n",
      " Reading H1 from /opt/xdata/endfb-vii.1-hdf5/neutron/H1.h5\n",
      " Reading O16 from /opt/xdata/endfb-vii.1-hdf5/neutron/O16.h5\n",
      " Reading U235 from /opt/xdata/endfb-vii.1-hdf5/neutron/U235.h5\n",
      " Reading U238 from /opt/xdata/endfb-vii.1-hdf5/neutron/U238.h5\n",
      " Reading Pu239 from /opt/xdata/endfb-vii.1-hdf5/neutron/Pu239.h5\n",
      " Reading Zr90 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr90.h5\n",
      " Minimum neutron data temperature: 294 K\n",
      " Maximum neutron data temperature: 294 K\n",
      " Reading tallies XML file...\n",
      " Preparing distributed cell instances...\n",
      " Reading plot XML file...\n",
      " Writing summary.h5 file...\n",
      " Maximum neutron transport energy: 20000000 eV for H1\n",
      " Initializing source particles...\n",
      "\n",
      " ====================>     K EIGENVALUE SIMULATION     <====================\n",
      "\n",
      "  Bat./Gen.      k            Average k\n",
      "  =========   ========   ====================\n",
      "        1/1    1.24970\n",
      "        2/1    1.21100\n",
      "        3/1    1.19756\n",
      "        4/1    1.25555\n",
      "        5/1    1.24295\n",
      "        6/1    1.21186\n",
      "        7/1    1.25633\n",
      "        8/1    1.24522\n",
      "        9/1    1.21656\n",
      "       10/1    1.26028\n",
      "       11/1    1.20898\n",
      "       12/1    1.23185    1.22041 +/- 0.01143\n",
      "       13/1    1.25350    1.23144 +/- 0.01285\n",
      "       14/1    1.22167    1.22900 +/- 0.00941\n",
      "       15/1    1.24549    1.23230 +/- 0.00800\n",
      "       16/1    1.24976    1.23521 +/- 0.00715\n",
      "       17/1    1.18269    1.22771 +/- 0.00963\n",
      "       18/1    1.23822    1.22902 +/- 0.00845\n",
      "       19/1    1.23413    1.22959 +/- 0.00747\n",
      "       20/1    1.21361    1.22799 +/- 0.00687\n",
      "       21/1    1.24244    1.22930 +/- 0.00635\n",
      "       22/1    1.21414    1.22804 +/- 0.00593\n",
      "       23/1    1.21809    1.22727 +/- 0.00551\n",
      "       24/1    1.19780    1.22517 +/- 0.00552\n",
      "       25/1    1.24190    1.22628 +/- 0.00526\n",
      "       26/1    1.24078    1.22719 +/- 0.00500\n",
      "       27/1    1.21557    1.22651 +/- 0.00475\n",
      "       28/1    1.26431    1.22861 +/- 0.00494\n",
      "       29/1    1.27196    1.23089 +/- 0.00520\n",
      "       30/1    1.24033    1.23136 +/- 0.00496\n",
      "       31/1    1.24532    1.23203 +/- 0.00476\n",
      "       32/1    1.22646    1.23177 +/- 0.00455\n",
      "       33/1    1.23791    1.23204 +/- 0.00436\n",
      "       34/1    1.21230    1.23122 +/- 0.00425\n",
      "       35/1    1.22857    1.23111 +/- 0.00408\n",
      "       36/1    1.22386    1.23083 +/- 0.00393\n",
      "       37/1    1.25504    1.23173 +/- 0.00388\n",
      "       38/1    1.24488    1.23220 +/- 0.00377\n",
      "       39/1    1.24251    1.23255 +/- 0.00366\n",
      "       40/1    1.19482    1.23130 +/- 0.00375\n",
      "       41/1    1.20078    1.23031 +/- 0.00376\n",
      "       42/1    1.24233    1.23069 +/- 0.00366\n",
      "       43/1    1.29614    1.23267 +/- 0.00406\n",
      "       44/1    1.23726    1.23281 +/- 0.00394\n",
      "       45/1    1.24222    1.23307 +/- 0.00384\n",
      "       46/1    1.24097    1.23329 +/- 0.00374\n",
      "       47/1    1.27425    1.23440 +/- 0.00380\n",
      "       48/1    1.25510    1.23495 +/- 0.00374\n",
      "       49/1    1.23654    1.23499 +/- 0.00364\n",
      "       50/1    1.23369    1.23495 +/- 0.00355\n",
      " Creating state point statepoint.50.h5...\n",
      "\n",
      " =======================>     TIMING STATISTICS     <=======================\n",
      "\n",
      " Total time for initialization     = 8.7385e-01 seconds\n",
      "   Reading cross sections          = 8.6672e-01 seconds\n",
      " Total time in simulation          = 1.3402e+02 seconds\n",
      "   Time in transport only          = 1.3397e+02 seconds\n",
      "   Time in inactive batches        = 4.7414e+00 seconds\n",
      "   Time in active batches          = 1.2927e+02 seconds\n",
      "   Time synchronizing fission bank = 2.1579e-02 seconds\n",
      "     Sampling source sites         = 1.9879e-02 seconds\n",
      "     SEND/RECV source sites        = 1.6605e-03 seconds\n",
      "   Time accumulating tallies       = 1.0377e-02 seconds\n",
      "   Time writing statepoints        = 4.2323e-03 seconds\n",
      " Total time for finalization       = 1.3320e-02 seconds\n",
      " Total time elapsed                = 1.3492e+02 seconds\n",
      " Calculation Rate (inactive)       = 10545.3 particles/second\n",
      " Calculation Rate (active)         = 1547.09 particles/second\n",
      "\n",
      " ============================>     RESULTS     <============================\n",
      "\n",
      " k-effective (Collision)     = 1.23445 +/- 0.00332\n",
      " k-effective (Track-length)  = 1.23495 +/- 0.00355\n",
      " k-effective (Absorption)    = 1.23293 +/- 0.00238\n",
      " Combined k-effective        = 1.23332 +/- 0.00230\n",
      " Leakage Fraction            = 0.00000 +/- 0.00000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Run OpenMC\n",
    "statepoint_filename = model.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tally Data Processing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Our simulation ran successfully and created statepoint and summary output files. We begin our analysis by instantiating a `StatePoint` object. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the last statepoint file\n",
    "sp = openmc.StatePoint(statepoint_filename)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition to the statepoint file, our simulation also created a summary file which encapsulates information about the materials and geometry. By default, a `Summary` object is automatically linked when a `StatePoint` is loaded. This is necessary for the `openmc.mgxs` module to properly process the tally data."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The statepoint is now ready to be analyzed by our multi-group cross sections. We simply have to load the tallies from the `StatePoint` into each object as follows and our `MGXS` objects will compute the cross sections for us under-the-hood."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the tallies from the statepoint into each MGXS object\n",
    "chi_prompt.load_from_statepoint(sp)\n",
    "prompt_nu_fission.load_from_statepoint(sp)\n",
    "chi_delayed.load_from_statepoint(sp)\n",
    "delayed_nu_fission.load_from_statepoint(sp)\n",
    "beta.load_from_statepoint(sp)\n",
    "decay_rate.load_from_statepoint(sp)\n",
    "# Close statepoint file now that we have the info we need\n",
    "sp.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Voila! Our multi-group cross sections are now ready to rock 'n roll!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extracting and Storing MGXS Data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's first inspect our delayed-nu-fission section by printing it to the screen after condensing the cross section down to one group."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[5.14603413e-06, 1.16498857e-06]],\n",
       "\n",
       "       [[2.65622446e-05, 7.58694377e-06]],\n",
       "\n",
       "       [[2.53586263e-05, 5.74154841e-06]],\n",
       "\n",
       "       [[5.68561321e-05, 1.04823410e-05]],\n",
       "\n",
       "       [[2.33102114e-05, 5.45999869e-06]],\n",
       "\n",
       "       [[9.76456653e-06, 1.65254173e-06]]])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "delayed_nu_fission.get_condensed_xs(one_group).get_xs()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since the `openmc.mgxs` module uses [tally arithmetic](tally-arithmetic.ipynb) under-the-hood, the cross section is stored as a \"derived\" `Tally` object. This means that it can be queried and manipulated using all of the same methods supported for the `Tally` class in the OpenMC Python API. For example, we can construct a [Pandas](https://pandas.pydata.org/) `DataFrame` of the multi-group cross section data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>cell</th>\n",
       "      <th>delayedgroup</th>\n",
       "      <th>group in</th>\n",
       "      <th>nuclide</th>\n",
       "      <th>mean</th>\n",
       "      <th>std. dev.</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>198</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>U235</td>\n",
       "      <td>9.479196e-08</td>\n",
       "      <td>5.863577e-08</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>199</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>1.600306e-08</td>\n",
       "      <td>9.891470e-09</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>398</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>U235</td>\n",
       "      <td>4.892869e-07</td>\n",
       "      <td>3.026598e-07</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>399</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>1.042193e-07</td>\n",
       "      <td>6.441782e-08</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>598</th>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>1</td>\n",
       "      <td>U235</td>\n",
       "      <td>4.671158e-07</td>\n",
       "      <td>2.889454e-07</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>599</th>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>1</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>7.886975e-08</td>\n",
       "      <td>4.874928e-08</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>798</th>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "      <td>U235</td>\n",
       "      <td>1.047312e-06</td>\n",
       "      <td>6.478393e-07</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>799</th>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>1.439925e-07</td>\n",
       "      <td>8.900152e-08</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>998</th>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>U235</td>\n",
       "      <td>4.293832e-07</td>\n",
       "      <td>2.656050e-07</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>999</th>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>1</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>7.500220e-08</td>\n",
       "      <td>4.635875e-08</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     cell  delayedgroup  group in nuclide          mean     std. dev.\n",
       "198     1             1         1    U235  9.479196e-08  5.863577e-08\n",
       "199     1             1         1   Pu239  1.600306e-08  9.891470e-09\n",
       "398     1             2         1    U235  4.892869e-07  3.026598e-07\n",
       "399     1             2         1   Pu239  1.042193e-07  6.441782e-08\n",
       "598     1             3         1    U235  4.671158e-07  2.889454e-07\n",
       "599     1             3         1   Pu239  7.886975e-08  4.874928e-08\n",
       "798     1             4         1    U235  1.047312e-06  6.478393e-07\n",
       "799     1             4         1   Pu239  1.439925e-07  8.900152e-08\n",
       "998     1             5         1    U235  4.293832e-07  2.656050e-07\n",
       "999     1             5         1   Pu239  7.500220e-08  4.635875e-08"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = delayed_nu_fission.get_pandas_dataframe()\n",
    "df.head(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>cell</th>\n",
       "      <th>delayedgroup</th>\n",
       "      <th>nuclide</th>\n",
       "      <th>mean</th>\n",
       "      <th>std. dev.</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>U235</td>\n",
       "      <td>0.013336</td>\n",
       "      <td>0.000061</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>0.013271</td>\n",
       "      <td>0.000059</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>U235</td>\n",
       "      <td>0.032739</td>\n",
       "      <td>0.000150</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>0.030881</td>\n",
       "      <td>0.000136</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>U235</td>\n",
       "      <td>0.120780</td>\n",
       "      <td>0.000552</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>0.113370</td>\n",
       "      <td>0.000501</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>U235</td>\n",
       "      <td>0.302780</td>\n",
       "      <td>0.001383</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>0.292500</td>\n",
       "      <td>0.001292</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>U235</td>\n",
       "      <td>0.849490</td>\n",
       "      <td>0.003880</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>0.857490</td>\n",
       "      <td>0.003787</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>1</td>\n",
       "      <td>6</td>\n",
       "      <td>U235</td>\n",
       "      <td>2.853000</td>\n",
       "      <td>0.013029</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>1</td>\n",
       "      <td>6</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>2.729700</td>\n",
       "      <td>0.012056</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    cell  delayedgroup nuclide      mean  std. dev.\n",
       "0      1             1    U235  0.013336   0.000061\n",
       "1      1             1   Pu239  0.013271   0.000059\n",
       "2      1             2    U235  0.032739   0.000150\n",
       "3      1             2   Pu239  0.030881   0.000136\n",
       "4      1             3    U235  0.120780   0.000552\n",
       "5      1             3   Pu239  0.113370   0.000501\n",
       "6      1             4    U235  0.302780   0.001383\n",
       "7      1             4   Pu239  0.292500   0.001292\n",
       "8      1             5    U235  0.849490   0.003880\n",
       "9      1             5   Pu239  0.857490   0.003787\n",
       "10     1             6    U235  2.853000   0.013029\n",
       "11     1             6   Pu239  2.729700   0.012056"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = decay_rate.get_pandas_dataframe()\n",
    "df.head(12)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Each multi-group cross section object can be easily exported to a variety of file formats, including CSV, Excel, and LaTeX for storage or data processing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "beta.export_xs_data(filename='beta', format='excel')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following code snippet shows how to export the chi-prompt and chi-delayed `MGXS` to the same HDF5 binary data store."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "chi_prompt.build_hdf5_store(filename='mdgxs', append=True)\n",
    "chi_delayed.build_hdf5_store(filename='mdgxs', append=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using Tally Arithmetic to Compute the Delayed Neutron Precursor Concentrations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we illustrate how one can leverage OpenMC's [tally arithmetic](tally-arithmetic.ipynb) data processing feature with `MGXS` objects. The `openmc.mgxs` module uses tally arithmetic to compute multi-group cross sections with automated uncertainty propagation. Each `MGXS` object includes an `xs_tally` attribute which is a \"derived\" `Tally` based on the tallies needed to compute the cross section type of interest. These derived tallies can be used in subsequent tally arithmetic operations. For example, we can use tally artithmetic to compute the delayed neutron precursor concentrations using the `Beta`, `DelayedNuFissionXS`, and `DecayRate` objects. The delayed neutron precursor concentrations are modeled using the following equations:\n",
    "\n",
    "$$\\frac{\\partial}{\\partial t} C_{k,d} (t) = \\int_{0}^{\\infty}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r} \\beta_{k,d} (t) \\nu_d \\sigma_{f,x}(\\mathbf{r},E',t)\\Phi(\\mathbf{r},E',t) - \\lambda_{d} C_{k,d} (t) $$\n",
    "\n",
    "$$C_{k,d} (t=0) = \\frac{1}{\\lambda_{d}} \\int_{0}^{\\infty}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r} \\beta_{k,d} (t=0) \\nu_d \\sigma_{f,x}(\\mathbf{r},E',t=0)\\Phi(\\mathbf{r},E',t=0) $$\n",
    "\n",
    "First, let's investigate the decay rates for U235 and Pu235. The fraction of the delayed neutron precursors remaining as a function of time after fission for each delayed group and fissioning isotope have been plotted below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x7f0aa9759e20>"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 92328 characters]",
      "text/plain": [
       "<Figure size 576x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Get the decay rate data\n",
    "dr_tally = decay_rate.xs_tally\n",
    "dr_u235 = dr_tally.get_values(nuclides=['U235']).flatten()\n",
    "dr_pu239 = dr_tally.get_values(nuclides=['Pu239']).flatten()\n",
    "\n",
    "# Compute the exponential decay of the precursors\n",
    "time = np.logspace(-3,3)\n",
    "dr_u235_points = np.exp(-np.outer(dr_u235, time))\n",
    "dr_pu239_points = np.exp(-np.outer(dr_pu239, time))\n",
    "\n",
    "# Create a plot of the fraction of the precursors remaining as a f(time)\n",
    "colors = ['b', 'g', 'r', 'c', 'm', 'k']\n",
    "legend = []\n",
    "fig = plt.figure(figsize=(8,6))\n",
    "for g,c in enumerate(colors):\n",
    "    plt.semilogx(time, dr_u235_points [g,:], color=c, linestyle='--', linewidth=3)\n",
    "    plt.semilogx(time, dr_pu239_points[g,:], color=c, linestyle=':' , linewidth=3)\n",
    "    legend.append('U-235 $t_{1/2}$ = ' + '{0:1.2f} seconds'.format(np.log(2) / dr_u235[g]))\n",
    "    legend.append('Pu-239 $t_{1/2}$ = ' + '{0:1.2f} seconds'.format(np.log(2) / dr_pu239[g]))\n",
    "\n",
    "plt.title('Delayed Neutron Precursor Decay Rates')\n",
    "plt.xlabel('Time (s)')\n",
    "plt.ylabel('Fraction Remaining')\n",
    "plt.legend(legend, loc=1, bbox_to_anchor=(1.55, 0.95))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's compute the initial concentration of the delayed neutron precursors:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>cell</th>\n",
       "      <th>delayedgroup</th>\n",
       "      <th>nuclide</th>\n",
       "      <th>score</th>\n",
       "      <th>mean</th>\n",
       "      <th>std. dev.</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>U235</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>8.785604e-08</td>\n",
       "      <td>4.659397e-10</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>7.154286e-09</td>\n",
       "      <td>3.857161e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>U235</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>9.534897e-07</td>\n",
       "      <td>5.056780e-09</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>1.303974e-07</td>\n",
       "      <td>7.030245e-10</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>U235</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>2.355637e-07</td>\n",
       "      <td>1.249299e-09</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>2.034167e-08</td>\n",
       "      <td>1.096700e-10</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>U235</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>4.723668e-07</td>\n",
       "      <td>2.505171e-09</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>2.627951e-08</td>\n",
       "      <td>1.416833e-10</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>U235</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>2.829997e-08</td>\n",
       "      <td>1.500873e-10</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>2.432107e-09</td>\n",
       "      <td>1.311246e-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>1</td>\n",
       "      <td>6</td>\n",
       "      <td>U235</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>1.478618e-09</td>\n",
       "      <td>7.841770e-12</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>1</td>\n",
       "      <td>6</td>\n",
       "      <td>Pu239</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>6.998687e-11</td>\n",
       "      <td>3.773271e-13</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    cell  delayedgroup nuclide  \\\n",
       "0      1             1    U235   \n",
       "1      1             1   Pu239   \n",
       "2      1             2    U235   \n",
       "3      1             2   Pu239   \n",
       "4      1             3    U235   \n",
       "5      1             3   Pu239   \n",
       "6      1             4    U235   \n",
       "7      1             4   Pu239   \n",
       "8      1             5    U235   \n",
       "9      1             5   Pu239   \n",
       "10     1             6    U235   \n",
       "11     1             6   Pu239   \n",
       "\n",
       "                                                score     mean  std. dev.  \n",
       "0   (((delayed-nu-fission / nu-fission) * (delayed... 8.79e-08   4.66e-10  \n",
       "1   (((delayed-nu-fission / nu-fission) * (delayed... 7.15e-09   3.86e-11  \n",
       "2   (((delayed-nu-fission / nu-fission) * (delayed... 9.53e-07   5.06e-09  \n",
       "3   (((delayed-nu-fission / nu-fission) * (delayed... 1.30e-07   7.03e-10  \n",
       "4   (((delayed-nu-fission / nu-fission) * (delayed... 2.36e-07   1.25e-09  \n",
       "5   (((delayed-nu-fission / nu-fission) * (delayed... 2.03e-08   1.10e-10  \n",
       "6   (((delayed-nu-fission / nu-fission) * (delayed... 4.72e-07   2.51e-09  \n",
       "7   (((delayed-nu-fission / nu-fission) * (delayed... 2.63e-08   1.42e-10  \n",
       "8   (((delayed-nu-fission / nu-fission) * (delayed... 2.83e-08   1.50e-10  \n",
       "9   (((delayed-nu-fission / nu-fission) * (delayed... 2.43e-09   1.31e-11  \n",
       "10  (((delayed-nu-fission / nu-fission) * (delayed... 1.48e-09   7.84e-12  \n",
       "11  (((delayed-nu-fission / nu-fission) * (delayed... 7.00e-11   3.77e-13  "
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Use tally arithmetic to compute the precursor concentrations\n",
    "precursor_conc = beta.get_condensed_xs(one_group).xs_tally.summation(filter_type=openmc.EnergyFilter, remove_filter=True) * \\\n",
    "    delayed_nu_fission.get_condensed_xs(one_group).xs_tally.summation(filter_type=openmc.EnergyFilter, remove_filter=True) / \\\n",
    "    decay_rate.xs_tally.summation()\n",
    "\n",
    "# Get the Pandas DataFrames for inspection\n",
    "precursor_conc.get_pandas_dataframe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can plot the delayed neutron fractions for each nuclide."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Beta (U-235) : 0.006504 +/- 0.000007\n",
      "Beta (Pu-239): 0.002245 +/- 0.000003\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(0.0, 7.0)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 15464 characters]",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "energy_filter = [f for f in beta.xs_tally.filters if type(f) is openmc.EnergyFilter]\n",
    "beta_integrated = beta.get_condensed_xs(one_group).xs_tally.summation(filter_type=openmc.EnergyFilter, remove_filter=True)\n",
    "beta_u235  = beta_integrated.get_values(nuclides=['U235'])\n",
    "beta_pu239 = beta_integrated.get_values(nuclides=['Pu239'])\n",
    "\n",
    "# Reshape the betas\n",
    "beta_u235.shape = (beta_u235.shape[0])\n",
    "beta_pu239.shape = (beta_pu239.shape[0])\n",
    "\n",
    "df = beta_integrated.summation(filter_type=openmc.DelayedGroupFilter, remove_filter=True).get_pandas_dataframe()\n",
    "print('Beta (U-235) : {:.6f} +/- {:.6f}'.format(df[df['nuclide'] == 'U235']['mean'][0], df[df['nuclide'] == 'U235']['std. dev.'][0]))\n",
    "print('Beta (Pu-239): {:.6f} +/- {:.6f}'.format(df[df['nuclide'] == 'Pu239']['mean'][1], df[df['nuclide'] == 'Pu239']['std. dev.'][1]))\n",
    "\n",
    "beta_u235 = np.append(beta_u235[0], beta_u235)\n",
    "beta_pu239 = np.append(beta_pu239[0], beta_pu239)\n",
    "\n",
    "# Create a step plot for the MGXS\n",
    "plt.plot(np.arange(0.5, 7.5, 1), beta_u235, drawstyle='steps', color='b', linewidth=3)\n",
    "plt.plot(np.arange(0.5, 7.5, 1), beta_pu239, drawstyle='steps', color='g', linewidth=3)\n",
    "\n",
    "plt.title('Delayed Neutron Fraction (beta)')\n",
    "plt.xlabel('Delayed Group')\n",
    "plt.ylabel('Beta(fraction total neutrons)')\n",
    "plt.legend(['U-235', 'Pu-239'])\n",
    "plt.xlim([0,7])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also plot the energy spectrum for fission emission of prompt and delayed neutrons."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1000.0, 20000000.0)"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 26956 characters]",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "chi_d_u235  = np.squeeze(chi_delayed.get_xs(nuclides=['U235'], order_groups='decreasing'))\n",
    "chi_d_pu239 = np.squeeze(chi_delayed.get_xs(nuclides=['Pu239'], order_groups='decreasing'))\n",
    "chi_p_u235  = np.squeeze(chi_prompt.get_xs(nuclides=['U235'], order_groups='decreasing'))\n",
    "chi_p_pu239 = np.squeeze(chi_prompt.get_xs(nuclides=['Pu239'], order_groups='decreasing'))\n",
    "\n",
    "chi_d_u235  = np.append(chi_d_u235 , chi_d_u235[0])\n",
    "chi_d_pu239 = np.append(chi_d_pu239, chi_d_pu239[0])\n",
    "chi_p_u235  = np.append(chi_p_u235 , chi_p_u235[0])\n",
    "chi_p_pu239 = np.append(chi_p_pu239, chi_p_pu239[0])\n",
    "\n",
    "# Create a step plot for the MGXS\n",
    "plt.semilogx(energy_groups.group_edges, chi_d_u235 , drawstyle='steps', color='b', linestyle='--', linewidth=3)\n",
    "plt.semilogx(energy_groups.group_edges, chi_d_pu239, drawstyle='steps', color='g', linestyle='--', linewidth=3)\n",
    "plt.semilogx(energy_groups.group_edges, chi_p_u235 , drawstyle='steps', color='b', linestyle=':', linewidth=3)\n",
    "plt.semilogx(energy_groups.group_edges, chi_p_pu239, drawstyle='steps', color='g', linestyle=':', linewidth=3)\n",
    "\n",
    "plt.title('Energy Spectrum for Fission Neutrons')\n",
    "plt.xlabel('Energy (eV)')\n",
    "plt.ylabel('Fraction on emitted neutrons')\n",
    "plt.legend(['U-235 delayed', 'Pu-239 delayed', 'U-235 prompt', 'Pu-239 prompt'],loc=2)\n",
    "plt.xlim(1.0e3, 20.0e6)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
