{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Multigroup (Delayed) Cross Section Generation Part II: Advanced Features\n",
    "This IPython Notebook illustrates the use of the **`openmc.mgxs.Library`** class. The `Library` class is designed to automate the calculation of multi-group cross sections for use cases with one or more domains, cross section types, and/or nuclides. In particular, this Notebook illustrates the following features:\n",
    "\n",
    "* Calculation of multi-energy-group and multi-delayed-group cross sections for a **fuel assembly**\n",
    "* Automated creation, manipulation and storage of `MGXS` with **`openmc.mgxs.Library`**\n",
    "* Steady-state pin-by-pin **delayed neutron fractions (beta)** for each delayed group.\n",
    "* Generation of surface currents on the interfaces and surfaces of a Mesh."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generate Input Files"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import math\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "import openmc\n",
    "import openmc.mgxs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First we need to define materials that will be used in the problem: fuel, water, and cladding."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1.6 enriched fuel\n",
    "fuel = openmc.Material(name='1.6% Fuel')\n",
    "fuel.set_density('g/cm3', 10.31341)\n",
    "fuel.add_nuclide('U235', 3.7503e-4)\n",
    "fuel.add_nuclide('U238', 2.2625e-2)\n",
    "fuel.add_nuclide('O16', 4.6007e-2)\n",
    "\n",
    "# borated water\n",
    "water = openmc.Material(name='Borated Water')\n",
    "water.set_density('g/cm3', 0.740582)\n",
    "water.add_nuclide('H1', 4.9457e-2)\n",
    "water.add_nuclide('O16', 2.4732e-2)\n",
    "water.add_nuclide('B10', 8.0042e-6)\n",
    "\n",
    "# zircaloy\n",
    "zircaloy = openmc.Material(name='Zircaloy')\n",
    "zircaloy.set_density('g/cm3', 6.55)\n",
    "zircaloy.add_nuclide('Zr90', 7.2758e-3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With our three materials, we can now create a `Materials` object that can be exported to an actual XML file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a materials collection and export to XML\n",
    "materials = openmc.Materials((fuel, water, zircaloy))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's move on to the geometry. This problem will be a square array of fuel pins and control rod guide tubes for which we can use OpenMC's lattice/universe feature. The basic universe will have three regions for the fuel, the clad, and the surrounding coolant. The first step is to create the bounding surfaces for fuel and clad, as well as the outer bounding surfaces of the problem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create cylinders for the fuel and clad\n",
    "fuel_outer_radius = openmc.ZCylinder(r=0.39218)\n",
    "clad_outer_radius = openmc.ZCylinder(r=0.45720)\n",
    "\n",
    "# Create boundary planes to surround the geometry\n",
    "min_x = openmc.XPlane(x0=-10.71, boundary_type='reflective')\n",
    "max_x = openmc.XPlane(x0=+10.71, boundary_type='reflective')\n",
    "min_y = openmc.YPlane(y0=-10.71, boundary_type='reflective')\n",
    "max_y = openmc.YPlane(y0=+10.71, boundary_type='reflective')\n",
    "min_z = openmc.ZPlane(z0=-10., boundary_type='reflective')\n",
    "max_z = openmc.ZPlane(z0=+10., boundary_type='reflective')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With the surfaces defined, we can now construct a fuel pin cell from cells that are defined by intersections of half-spaces created by the surfaces."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a Universe to encapsulate a fuel pin\n",
    "fuel_pin_universe = openmc.Universe(name='1.6% Fuel Pin')\n",
    "\n",
    "# Create fuel Cell\n",
    "fuel_cell = openmc.Cell(name='1.6% Fuel')\n",
    "fuel_cell.fill = fuel\n",
    "fuel_cell.region = -fuel_outer_radius\n",
    "fuel_pin_universe.add_cell(fuel_cell)\n",
    "\n",
    "# Create a clad Cell\n",
    "clad_cell = openmc.Cell(name='1.6% Clad')\n",
    "clad_cell.fill = zircaloy\n",
    "clad_cell.region = +fuel_outer_radius & -clad_outer_radius\n",
    "fuel_pin_universe.add_cell(clad_cell)\n",
    "\n",
    "# Create a moderator Cell\n",
    "moderator_cell = openmc.Cell(name='1.6% Moderator')\n",
    "moderator_cell.fill = water\n",
    "moderator_cell.region = +clad_outer_radius\n",
    "fuel_pin_universe.add_cell(moderator_cell)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Likewise, we can construct a control rod guide tube with the same surfaces."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a Universe to encapsulate a control rod guide tube\n",
    "guide_tube_universe = openmc.Universe(name='Guide Tube')\n",
    "\n",
    "# Create guide tube Cell\n",
    "guide_tube_cell = openmc.Cell(name='Guide Tube Water')\n",
    "guide_tube_cell.fill = water\n",
    "guide_tube_cell.region = -fuel_outer_radius\n",
    "guide_tube_universe.add_cell(guide_tube_cell)\n",
    "\n",
    "# Create a clad Cell\n",
    "clad_cell = openmc.Cell(name='Guide Clad')\n",
    "clad_cell.fill = zircaloy\n",
    "clad_cell.region = +fuel_outer_radius & -clad_outer_radius\n",
    "guide_tube_universe.add_cell(clad_cell)\n",
    "\n",
    "# Create a moderator Cell\n",
    "moderator_cell = openmc.Cell(name='Guide Tube Moderator')\n",
    "moderator_cell.fill = water\n",
    "moderator_cell.region = +clad_outer_radius\n",
    "guide_tube_universe.add_cell(moderator_cell)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using the pin cell universe, we can construct a 17x17 rectangular lattice with a 1.26 cm pitch."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create fuel assembly Lattice\n",
    "assembly = openmc.RectLattice(name='1.6% Fuel Assembly')\n",
    "assembly.pitch = (1.26, 1.26)\n",
    "assembly.lower_left = [-1.26 * 17. / 2.0] * 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we create a NumPy array of fuel pin and guide tube universes for the lattice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create array indices for guide tube locations in lattice\n",
    "template_x = np.array([5, 8, 11, 3, 13, 2, 5, 8, 11, 14, 2, 5, 8,\n",
    "                       11, 14, 2, 5, 8, 11, 14, 3, 13, 5, 8, 11])\n",
    "template_y = np.array([2, 2, 2, 3, 3, 5, 5, 5, 5, 5, 8, 8, 8, 8,\n",
    "                       8, 11, 11, 11, 11, 11, 13, 13, 14, 14, 14])\n",
    "\n",
    "# Create universes array with the fuel pin and guide tube universes\n",
    "universes = np.tile(fuel_pin_universe, (17,17))\n",
    "universes[template_x, template_y] = guide_tube_universe\n",
    "\n",
    "# Store the array of universes in the lattice\n",
    "assembly.universes = universes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "OpenMC requires that there is a \"root\" universe. Let us create a root cell that is filled by the pin cell universe and then assign it to the root universe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create root Cell\n",
    "root_cell = openmc.Cell(name='root cell', fill=assembly)\n",
    "\n",
    "# Add boundary planes\n",
    "root_cell.region = +min_x & -max_x & +min_y & -max_y & +min_z & -max_z\n",
    "\n",
    "# Create root Universe\n",
    "root_universe = openmc.Universe(universe_id=0, name='root universe')\n",
    "root_universe.add_cell(root_cell)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now must create a geometry that is assigned a root universe and export it to XML."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create Geometry and export to XML\n",
    "geometry = openmc.Geometry(root_universe)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With the geometry and materials finished, we now just need to 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": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# OpenMC simulation parameters\n",
    "batches = 50\n",
    "inactive = 10\n",
    "particles = 2500\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': False}\n",
    "\n",
    "# Create an initial uniform spatial source distribution over fissionable zones\n",
    "bounds = [-10.71, -10.71, -10, 10.71, 10.71, 10.]\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": [
    "We'll tie the materials, geometry, and settings into a single model object and export the necessary XML files."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = openmc.Model(geometry, materials, settings)\n",
    "model.export_to_xml()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let us also create a plot to verify that our fuel assembly geometry was created successfully."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 6956 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot our geometry\n",
    "plot = openmc.Plot.from_geometry(geometry)\n",
    "plot.pixels = (250, 250)\n",
    "plot.color_by = 'material'\n",
    "openmc.plot_inline(plot)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we can see from the plot, we have a nice array of fuel and guide tube pin cells with fuel, cladding, and water!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create an MGXS Library"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we are ready to generate multi-group cross sections! First, let's define a 20-energy-group and 1-energy-group."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate a 20-group EnergyGroups object\n",
    "energy_groups = openmc.mgxs.EnergyGroups()\n",
    "energy_groups.group_edges = np.logspace(-3, 7.3, 21)\n",
    "\n",
    "# Instantiate a 1-group EnergyGroups object\n",
    "one_group = openmc.mgxs.EnergyGroups()\n",
    "one_group.group_edges = np.array([energy_groups.group_edges[0], energy_groups.group_edges[-1]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we will instantiate an `openmc.mgxs.Library` for the energy and delayed groups with our the fuel assembly geometry."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate a tally mesh\n",
    "mesh = openmc.RegularMesh(mesh_id=1)\n",
    "mesh.dimension = [17, 17, 1]\n",
    "mesh.lower_left = [-10.71, -10.71, -10000.]\n",
    "mesh.width = [1.26, 1.26, 20000.]\n",
    "\n",
    "# Initialize an 20-energy-group and 6-delayed-group MGXS Library\n",
    "mgxs_lib = openmc.mgxs.Library(geometry)\n",
    "mgxs_lib.energy_groups  = energy_groups\n",
    "mgxs_lib.num_delayed_groups = 6\n",
    "\n",
    "# Specify multi-group cross section types to compute\n",
    "mgxs_lib.mgxs_types = ['total', 'transport', 'nu-scatter matrix', 'kappa-fission', 'inverse-velocity', 'chi-prompt',\n",
    "                      'prompt-nu-fission', 'chi-delayed', 'delayed-nu-fission', 'beta']\n",
    "\n",
    "# Specify a \"mesh\" domain type for the cross section tally filters\n",
    "mgxs_lib.domain_type = 'mesh'\n",
    "\n",
    "# Specify the mesh domain over which to compute multi-group cross sections\n",
    "mgxs_lib.domains = [mesh]\n",
    "\n",
    "# Construct all tallies needed for the multi-group cross section library\n",
    "mgxs_lib.build_library()\n",
    "\n",
    "# Create a \"tallies.xml\" file for the MGXS Library\n",
    "tallies = openmc.Tallies()\n",
    "mgxs_lib.add_to_tallies_file(tallies, merge=True)\n",
    "\n",
    "# Instantiate a current tally\n",
    "mesh_filter = openmc.MeshSurfaceFilter(mesh)\n",
    "current_tally = openmc.Tally(name='current tally')\n",
    "current_tally.scores = ['current']\n",
    "current_tally.filters = [mesh_filter]\n",
    "\n",
    "# Add current tally to the tallies file\n",
    "tallies.append(current_tally)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we can run OpenMC to generate the cross sections."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "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": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/aparler/.conda/envs/openmc-env/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=1.\n",
      "  warn(msg, IDWarning)\n",
      "/home/aparler/.conda/envs/openmc-env/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=2.\n",
      "  warn(msg, IDWarning)\n",
      "/home/aparler/.conda/envs/openmc-env/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=5.\n",
      "  warn(msg, IDWarning)\n",
      "/home/aparler/.conda/envs/openmc-env/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=6.\n",
      "  warn(msg, IDWarning)\n",
      "/home/aparler/.conda/envs/openmc-env/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=17.\n",
      "  warn(msg, IDWarning)\n",
      "/home/aparler/.conda/envs/openmc-env/lib/python3.11/site-packages/openmc/mixin.py:70: IDWarning: Another Filter instance already exists with id=23.\n",
      "  warn(msg, IDWarning)\n"
     ]
    },
    {
     "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-06 20:56:04\n",
      "  OpenMP Threads | 4\n",
      "\n",
      " Reading settings XML file...\n",
      " Reading cross sections XML file...\n",
      " Reading materials XML file...\n",
      " Reading geometry XML file...\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 O16 from /opt/xdata/endfb-vii.1-hdf5/neutron/O16.h5\n",
      " Reading H1 from /opt/xdata/endfb-vii.1-hdf5/neutron/H1.h5\n",
      " Reading B10 from /opt/xdata/endfb-vii.1-hdf5/neutron/B10.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 U235\n",
      " Initializing source particles...\n",
      "\n",
      " ====================>     K EIGENVALUE SIMULATION     <====================\n",
      "\n",
      "  Bat./Gen.      k            Average k\n",
      "  =========   ========   ====================\n",
      "        1/1    0.99606\n",
      "        2/1    1.04749\n",
      "        3/1    1.02998\n",
      "        4/1    1.01909\n",
      "        5/1    1.01068\n",
      "        6/1    1.04796\n",
      "        7/1    1.02180\n",
      "        8/1    1.03041\n",
      "        9/1    0.99557\n",
      "       10/1    1.02019\n",
      "       11/1    1.00722\n",
      "       12/1    1.02203    1.01462 +/- 0.00741\n",
      "       13/1    1.01177    1.01367 +/- 0.00438\n",
      "       14/1    1.03241    1.01836 +/- 0.00562\n",
      "       15/1    1.03434    1.02155 +/- 0.00540\n",
      "       16/1    1.02384    1.02193 +/- 0.00442\n",
      "       17/1    1.02815    1.02282 +/- 0.00384\n",
      "       18/1    1.04619    1.02574 +/- 0.00443\n",
      "       19/1    1.03496    1.02677 +/- 0.00404\n",
      "       20/1    1.01787    1.02588 +/- 0.00372\n",
      "       21/1    1.04886    1.02797 +/- 0.00396\n",
      "       22/1    1.00158    1.02577 +/- 0.00423\n",
      "       23/1    1.01636    1.02504 +/- 0.00396\n",
      "       24/1    1.01508    1.02433 +/- 0.00373\n",
      "       25/1    0.95954    1.02001 +/- 0.00554\n",
      "       26/1    1.03097    1.02070 +/- 0.00523\n",
      "       27/1    1.03576    1.02158 +/- 0.00499\n",
      "       28/1    1.01183    1.02104 +/- 0.00474\n",
      "       29/1    1.03387    1.02172 +/- 0.00453\n",
      "       30/1    1.01176    1.02122 +/- 0.00433\n",
      "       31/1    1.03633    1.02194 +/- 0.00418\n",
      "       32/1    1.04652    1.02306 +/- 0.00414\n",
      "       33/1    0.98985    1.02161 +/- 0.00421\n",
      "       34/1    1.01121    1.02118 +/- 0.00405\n",
      "       35/1    1.03004    1.02153 +/- 0.00391\n",
      "       36/1    1.03398    1.02201 +/- 0.00378\n",
      "       37/1    1.04675    1.02293 +/- 0.00375\n",
      "       38/1    0.99813    1.02204 +/- 0.00372\n",
      "       39/1    1.05411    1.02315 +/- 0.00376\n",
      "       40/1    1.05007    1.02405 +/- 0.00374\n",
      "       41/1    0.99881    1.02323 +/- 0.00371\n",
      "       42/1    1.04085    1.02378 +/- 0.00363\n",
      "       43/1    1.01012    1.02337 +/- 0.00355\n",
      "       44/1    1.01510    1.02313 +/- 0.00345\n",
      "       45/1    1.03833    1.02356 +/- 0.00338\n",
      "       46/1    1.00385    1.02301 +/- 0.00333\n",
      "       47/1    1.02360    1.02303 +/- 0.00324\n",
      "       48/1    1.01066    1.02270 +/- 0.00317\n",
      "       49/1    0.98707    1.02179 +/- 0.00322\n",
      "       50/1    1.01133    1.02153 +/- 0.00315\n",
      " Creating state point statepoint.50.h5...\n",
      "\n",
      " =======================>     TIMING STATISTICS     <=======================\n",
      "\n",
      " Total time for initialization     = 3.6394e-01 seconds\n",
      "   Reading cross sections          = 3.4823e-01 seconds\n",
      " Total time in simulation          = 8.2881e+00 seconds\n",
      "   Time in transport only          = 8.1978e+00 seconds\n",
      "   Time in inactive batches        = 7.4927e-01 seconds\n",
      "   Time in active batches          = 7.5389e+00 seconds\n",
      "   Time synchronizing fission bank = 1.0994e-02 seconds\n",
      "     Sampling source sites         = 9.4419e-03 seconds\n",
      "     SEND/RECV source sites        = 1.5279e-03 seconds\n",
      "   Time accumulating tallies       = 3.5476e-02 seconds\n",
      "   Time writing statepoints        = 3.2617e-02 seconds\n",
      " Total time for finalization       = 1.6240e-06 seconds\n",
      " Total time elapsed                = 8.6614e+00 seconds\n",
      " Calculation Rate (inactive)       = 33365.8 particles/second\n",
      " Calculation Rate (active)         = 13264.6 particles/second\n",
      "\n",
      " ============================>     RESULTS     <============================\n",
      "\n",
      " k-effective (Collision)     = 1.02733 +/- 0.00284\n",
      " k-effective (Track-length)  = 1.02153 +/- 0.00315\n",
      " k-effective (Absorption)    = 1.02638 +/- 0.00281\n",
      " Combined k-effective        = 1.02616 +/- 0.00228\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": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the last statepoint file\n",
    "sp = openmc.StatePoint('statepoint.50.h5')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The statepoint is now ready to be analyzed by the `Library`. We simply have to load the tallies from the statepoint into the `Library` and our `MGXS` objects will compute the cross sections for us under-the-hood."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Initialize MGXS Library with OpenMC statepoint data\n",
    "mgxs_lib.load_from_statepoint(sp)\n",
    "\n",
    "# Extrack the current tally separately\n",
    "current_tally = sp.get_tally(name='current tally')\n",
    "\n",
    "# Close statepoint file now that we have the info we need\n",
    "sp.close()"
   ]
  },
  {
   "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` and `DelayedNuFissionXS` 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) $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "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 tr th {\n",
       "        text-align: left;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <th colspan=\"3\" halign=\"left\">mesh 1</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",
       "    <tr>\n",
       "      <th></th>\n",
       "      <th>x</th>\n",
       "      <th>y</th>\n",
       "      <th>z</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.000122</td>\n",
       "      <td>3.807030e-05</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.001547</td>\n",
       "      <td>4.775303e-04</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.000980</td>\n",
       "      <td>3.006970e-04</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.000770</td>\n",
       "      <td>2.340328e-04</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>5</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.000027</td>\n",
       "      <td>8.143610e-06</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>6</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.000002</td>\n",
       "      <td>6.498748e-07</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.000101</td>\n",
       "      <td>2.343214e-05</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.001282</td>\n",
       "      <td>2.937441e-04</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>3</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.000813</td>\n",
       "      <td>1.849319e-04</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>2</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>4</td>\n",
       "      <td>total</td>\n",
       "      <td>(((delayed-nu-fission / nu-fission) * (delayed...</td>\n",
       "      <td>0.000640</td>\n",
       "      <td>1.439161e-04</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  mesh 1       delayedgroup nuclide  \\\n",
       "       x  y  z                        \n",
       "0      1  1  1            1   total   \n",
       "1      1  1  1            2   total   \n",
       "2      1  1  1            3   total   \n",
       "3      1  1  1            4   total   \n",
       "4      1  1  1            5   total   \n",
       "5      1  1  1            6   total   \n",
       "6      2  1  1            1   total   \n",
       "7      2  1  1            2   total   \n",
       "8      2  1  1            3   total   \n",
       "9      2  1  1            4   total   \n",
       "\n",
       "                                               score      mean     std. dev.  \n",
       "                                                                              \n",
       "0  (((delayed-nu-fission / nu-fission) * (delayed...  0.000122  3.807030e-05  \n",
       "1  (((delayed-nu-fission / nu-fission) * (delayed...  0.001547  4.775303e-04  \n",
       "2  (((delayed-nu-fission / nu-fission) * (delayed...  0.000980  3.006970e-04  \n",
       "3  (((delayed-nu-fission / nu-fission) * (delayed...  0.000770  2.340328e-04  \n",
       "4  (((delayed-nu-fission / nu-fission) * (delayed...  0.000027  8.143610e-06  \n",
       "5  (((delayed-nu-fission / nu-fission) * (delayed...  0.000002  6.498748e-07  \n",
       "6  (((delayed-nu-fission / nu-fission) * (delayed...  0.000101  2.343214e-05  \n",
       "7  (((delayed-nu-fission / nu-fission) * (delayed...  0.001282  2.937441e-04  \n",
       "8  (((delayed-nu-fission / nu-fission) * (delayed...  0.000813  1.849319e-04  \n",
       "9  (((delayed-nu-fission / nu-fission) * (delayed...  0.000640  1.439161e-04  "
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Set the time constants for the delayed precursors (in seconds^-1)\n",
    "precursor_halflife = np.array([55.6, 24.5, 16.3, 2.37, 0.424, 0.195])\n",
    "precursor_lambda = math.log(2.0) / precursor_halflife\n",
    "\n",
    "beta = mgxs_lib.get_mgxs(mesh, 'beta')\n",
    "\n",
    "# Create a tally object with only the delayed group filter for the time constants\n",
    "beta_filters = [f for f in beta.xs_tally.filters if type(f) is not openmc.DelayedGroupFilter]\n",
    "lambda_tally = beta.xs_tally.summation(nuclides=beta.xs_tally.nuclides)\n",
    "for f in beta_filters:\n",
    "    lambda_tally = lambda_tally.summation(filter_type=type(f), remove_filter=True) * 0. + 1.\n",
    "\n",
    "# Set the mean of the lambda tally and reshape to account for nuclides and scores\n",
    "lambda_tally._mean = precursor_lambda\n",
    "lambda_tally._mean.shape = lambda_tally.std_dev.shape\n",
    "\n",
    "# Set a total nuclide and lambda score\n",
    "lambda_tally.nuclides = [openmc.Nuclide(name='total')]\n",
    "lambda_tally.scores = ['lambda']\n",
    "\n",
    "delayed_nu_fission = mgxs_lib.get_mgxs(mesh, 'delayed-nu-fission')\n",
    "\n",
    "# Use tally arithmetic to compute the precursor concentrations\n",
    "precursor_conc = beta.xs_tally.summation(filter_type=openmc.EnergyFilter, remove_filter=True) * \\\n",
    "    delayed_nu_fission.xs_tally.summation(filter_type=openmc.EnergyFilter, remove_filter=True) / lambda_tally\n",
    "\n",
    "# The difference is a derived tally which can generate Pandas DataFrames for inspection\n",
    "precursor_conc.get_pandas_dataframe().head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another useful feature of the Python API is the ability to extract the surface currents for the interfaces and surfaces of a mesh. We can inspect the currents for the mesh by getting the pandas dataframe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "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 tr th {\n",
       "        text-align: left;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <th colspan=\"4\" halign=\"left\">mesh 1</th>\n",
       "      <th>nuclide</th>\n",
       "      <th>score</th>\n",
       "      <th>mean</th>\n",
       "      <th>std. dev.</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <th>x</th>\n",
       "      <th>y</th>\n",
       "      <th>z</th>\n",
       "      <th>surf</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>x-min out</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.00000</td>\n",
       "      <td>0.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>x-min in</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.00000</td>\n",
       "      <td>0.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>x-max out</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.03147</td>\n",
       "      <td>0.000703</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>x-max in</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.03109</td>\n",
       "      <td>0.000648</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>y-min out</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.00000</td>\n",
       "      <td>0.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>y-min in</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.00000</td>\n",
       "      <td>0.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>y-max out</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.03183</td>\n",
       "      <td>0.000776</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>y-max in</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.03220</td>\n",
       "      <td>0.000812</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>z-min out</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.00000</td>\n",
       "      <td>0.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "      <td>z-min in</td>\n",
       "      <td>total</td>\n",
       "      <td>current</td>\n",
       "      <td>0.00000</td>\n",
       "      <td>0.000000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  mesh 1                  nuclide    score     mean std. dev.\n",
       "       x  y  z       surf                                    \n",
       "0      1  1  1  x-min out   total  current  0.00000  0.000000\n",
       "1      1  1  1   x-min in   total  current  0.00000  0.000000\n",
       "2      1  1  1  x-max out   total  current  0.03147  0.000703\n",
       "3      1  1  1   x-max in   total  current  0.03109  0.000648\n",
       "4      1  1  1  y-min out   total  current  0.00000  0.000000\n",
       "5      1  1  1   y-min in   total  current  0.00000  0.000000\n",
       "6      1  1  1  y-max out   total  current  0.03183  0.000776\n",
       "7      1  1  1   y-max in   total  current  0.03220  0.000812\n",
       "8      1  1  1  z-min out   total  current  0.00000  0.000000\n",
       "9      1  1  1   z-min in   total  current  0.00000  0.000000"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "current_tally.get_pandas_dataframe().head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cross Section Visualizations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition to inspecting the data in the tallies by getting the pandas dataframe, we can also plot the tally data on the domain mesh. Below is the delayed neutron fraction tallied in each mesh cell for each delayed group."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Beta - delayed group 6')"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 135248 characters]",
      "text/plain": [
       "<Figure size 1800x900 with 12 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Extract the energy-condensed delayed neutron fraction tally\n",
    "beta_by_group = beta.get_condensed_xs(one_group).xs_tally.summation(filter_type='energy', remove_filter=True)\n",
    "beta_by_group.mean.shape = (17, 17, 6)\n",
    "beta_by_group.mean[beta_by_group.mean == 0] = np.nan\n",
    "\n",
    "# Plot the betas\n",
    "plt.figure(figsize=(18,9))\n",
    "fig = plt.subplot(231)\n",
    "plt.imshow(beta_by_group.mean[:,:,0], interpolation='none', cmap='jet')\n",
    "plt.colorbar()\n",
    "plt.title('Beta - delayed group 1')\n",
    "\n",
    "fig = plt.subplot(232)\n",
    "plt.imshow(beta_by_group.mean[:,:,1], interpolation='none', cmap='jet')\n",
    "plt.colorbar()\n",
    "plt.title('Beta - delayed group 2')\n",
    "\n",
    "fig = plt.subplot(233)\n",
    "plt.imshow(beta_by_group.mean[:,:,2], interpolation='none', cmap='jet')\n",
    "plt.colorbar()\n",
    "plt.title('Beta - delayed group 3')\n",
    "\n",
    "fig = plt.subplot(234)\n",
    "plt.imshow(beta_by_group.mean[:,:,3], interpolation='none', cmap='jet')\n",
    "plt.colorbar()\n",
    "plt.title('Beta - delayed group 4')\n",
    "\n",
    "fig = plt.subplot(235)\n",
    "plt.imshow(beta_by_group.mean[:,:,4], interpolation='none', cmap='jet')\n",
    "plt.colorbar()\n",
    "plt.title('Beta - delayed group 5')\n",
    "\n",
    "fig = plt.subplot(236)\n",
    "plt.imshow(beta_by_group.mean[:,:,5], interpolation='none', cmap='jet')\n",
    "plt.colorbar()\n",
    "plt.title('Beta - delayed group 6')"
   ]
  }
 ],
 "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.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
