{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Lattice computations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmc\n",
    "from IPython.display import Image"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this section, we will build one of the assemblies from the BEAVRS benchmark.  This is a PWR assembly with fuel pins, guide tubes, and borosilicate glass burnable poisons.  A diagram of the assembly is plotted below.  To make it a little easier (and improve our statistics!) we will only build one quarter of it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 6416 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image('assembly_diagram.png')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Materials\n",
    "\n",
    "Again we have UO2, Zr, and H2O.  We also have borosilicate glass (pyrex).  Note the shortcut for defining enriched uranium.  Also note that we can use `add_nuclide` and `add_element` directly with a string.  We do not have to create a `Nuclide` or `Element` object first."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "uo2 = openmc.Material(name='uo2')\n",
    "uo2.add_element('U', 1.0, enrichment=3.0)\n",
    "uo2.add_nuclide('O16', 2.0)\n",
    "uo2.set_density('g/cm3', 10.0)\n",
    "\n",
    "zirconium = openmc.Material(name='zirconium')\n",
    "zirconium.add_element('Zr', 1.0)\n",
    "zirconium.set_density('g/cm3', 6.55)\n",
    "\n",
    "water = openmc.Material(name='water')\n",
    "water.add_nuclide('H1', 2)\n",
    "water.add_nuclide('O16', 1)\n",
    "water.set_density('g/cm3', 0.701)\n",
    "water.add_s_alpha_beta('c_H_in_H2O')\n",
    "\n",
    "pyrex = openmc.Material(name='pyrex')\n",
    "pyrex.add_element('B', 0.49)\n",
    "pyrex.add_element('O', 4.7)\n",
    "pyrex.add_element('Al', 0.17)\n",
    "pyrex.add_element('Si', 1.8)\n",
    "pyrex.set_density('g/cm3', 2.26)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "mf = openmc.Materials((uo2, zirconium, water, pyrex))\n",
    "mf.export_to_xml()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Later in this example, we will make a bunch of geometry plots.  By default, every region is colored randomly and the results are not always pretty.  Since we know our materials, let's define a dictonary of colors to use when plotting our geometry."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "colors = {}\n",
    "colors[water] = (100, 200, 200)\n",
    "colors[zirconium] = (150, 150, 150)\n",
    "colors[pyrex] = (100, 255, 100)\n",
    "colors[uo2] = (255, 50, 50)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fuel pin\n",
    "\n",
    "This is similar to the pincell example, but we don't have boundary conditions.  This `fuel_pin` universe extends to infinity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "pitch = 1.26\n",
    "\n",
    "fuel_or = openmc.ZCylinder(r=0.39)\n",
    "clad_ir = openmc.ZCylinder(r=0.40)\n",
    "clad_or = openmc.ZCylinder(r=0.46)\n",
    "\n",
    "fuel = openmc.Cell(1, 'fuel')\n",
    "fuel.fill = uo2\n",
    "fuel.region = -fuel_or\n",
    "\n",
    "gap = openmc.Cell(2, 'air gap')\n",
    "gap.fill = None\n",
    "gap.region = +fuel_or & -clad_ir\n",
    "\n",
    "clad = openmc.Cell(3, 'clad')\n",
    "clad.fill = zirconium\n",
    "clad.region = +clad_ir & -clad_or\n",
    "\n",
    "moderator = openmc.Cell(4, 'moderator')\n",
    "moderator.fill = water\n",
    "moderator.region = +clad_or\n",
    "\n",
    "fuel_pin = openmc.Universe()\n",
    "fuel_pin.add_cells((fuel, gap, clad, moderator))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When building a complex geometry, it is helpful to plot each universe as you go along.  Let's plot this pincell now"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We need a cell to contain the fuel_pin universe.\n",
    "main = openmc.Cell()\n",
    "main.fill = fuel_pin\n",
    "\n",
    "root = openmc.Universe()\n",
    "root.add_cell(main)\n",
    "\n",
    "g = openmc.Geometry()\n",
    "g.root_universe = root\n",
    "g.export_to_xml()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 4768 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "p = openmc.Plot()\n",
    "p.width = [pitch, pitch]\n",
    "p.pixels = [400, 400]\n",
    "p.color_by = 'material'\n",
    "p.colors = colors\n",
    "\n",
    "openmc.plot_inline(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Guide tube"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "clad_ir = openmc.ZCylinder(r=0.56)\n",
    "clad_or = openmc.ZCylinder(r=0.60)\n",
    "\n",
    "inner = openmc.Cell()\n",
    "inner.fill = water\n",
    "inner.region = -clad_ir\n",
    "\n",
    "clad = openmc.Cell()\n",
    "clad.fill = zirconium\n",
    "clad.region = +clad_ir & -clad_or\n",
    "\n",
    "outer = openmc.Cell()\n",
    "outer.fill = water\n",
    "outer.region = +clad_or\n",
    "\n",
    "guide_tube = openmc.Universe()\n",
    "guide_tube.add_cells((inner, clad, outer))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "main = openmc.Cell()\n",
    "main.fill = guide_tube\n",
    "\n",
    "root = openmc.Universe()\n",
    "root.add_cell(main)\n",
    "\n",
    "g = openmc.Geometry()\n",
    "g.root_universe = root\n",
    "g.export_to_xml()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 4492 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "p = openmc.Plot()\n",
    "p.width = [pitch, pitch]\n",
    "p.pixels = [400, 400]\n",
    "p.color_by = 'material'\n",
    "p.colors = colors\n",
    "\n",
    "openmc.plot_inline(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pyrex burnable poison"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 7244 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(\"pyrex_diagram.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define the cylinders which bound each radial zone.\n",
    "radii = [0.21, 0.23, 0.24, 0.43, 0.44, 0.48, 0.56, 0.60]\n",
    "cyls = [openmc.ZCylinder(r=R) for R in radii]\n",
    "\n",
    "# Initialize a list of cells.\n",
    "bp_cells = []\n",
    "\n",
    "# Define the inner void zone first.\n",
    "c = openmc.Cell()\n",
    "c.region = -cyls[0]\n",
    "c.fill = None\n",
    "bp_cells.append(c)\n",
    "\n",
    "# Now all the sandwiched layers.\n",
    "mats = [zirconium, None, pyrex, None, zirconium, water, zirconium]\n",
    "for i in range(len(mats)):\n",
    "    c = openmc.Cell()\n",
    "    c.region = +cyls[i] & -cyls[i+1]\n",
    "    c.fill = mats[i]\n",
    "    bp_cells.append(c)\n",
    "\n",
    "# And the outer moderator region.\n",
    "c = openmc.Cell()\n",
    "c.region = +cyls[-1]\n",
    "c.fill = water\n",
    "bp_cells.append(c)\n",
    "\n",
    "# Make a universe containing these cells\n",
    "burn = openmc.Universe()\n",
    "burn.add_cells(bp_cells)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "main = openmc.Cell()\n",
    "main.fill = burn\n",
    "\n",
    "root = openmc.Universe()\n",
    "root.add_cell(main)\n",
    "\n",
    "g = openmc.Geometry()\n",
    "g.root_universe = root\n",
    "g.export_to_xml()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 8508 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "p = openmc.Plot()\n",
    "p.width = [pitch, pitch]\n",
    "p.pixels = [400, 400]\n",
    "p.color_by = 'material'\n",
    "p.colors = colors\n",
    "\n",
    "openmc.plot_inline(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lattices in OpenMC\n",
    "\n",
    "OpenMC has `RectLattice` and `HexLattice` objects.  This demo will use a `RectLattice`.  Let's look at a simple one before we do the quarter assembly.\n",
    "\n",
    "First, we also need to define a universe that is all water."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "moderator = openmc.Cell()\n",
    "moderator.fill = water\n",
    "\n",
    "all_water = openmc.Universe()\n",
    "all_water.add_cell(moderator)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "lattice = openmc.RectLattice()\n",
    "\n",
    "# First we specify the dimension---the number of lattice tiles in x and y.\n",
    "lattice.dimension = [2, 2]\n",
    "\n",
    "# Next are the xy-coordiantes of the lower-left corner of the lattice.\n",
    "lattice.lower_left = [0.0, 0.0]\n",
    "\n",
    "# Next is the pitch---the size of the lattice tiles in each direction.\n",
    "lattice.pitch = [pitch]*2\n",
    "\n",
    "# Now we specify what is actually inside the lattice.  This 2D lattice will be a\n",
    "# list of lists like\n",
    "# lattice.universes = [ [a1, a2, ...], [b1, b2, ...], ...]\n",
    "# The inner lists specify columns from left to right.  The outer lists specify\n",
    "# rows from top to bottom.\n",
    "lattice.universes = [\n",
    "    [fuel_pin, fuel_pin],\n",
    "    [guide_tube, fuel_pin]\n",
    "#\n",
    "]\n",
    "\n",
    "# We also need to specify what is outside of the lattice.  In this case, it is\n",
    "# the infinite water universe.\n",
    "lattice.outer = all_water"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "main = openmc.Cell()\n",
    "main.fill = lattice\n",
    "\n",
    "root = openmc.Universe()\n",
    "root.add_cell(main)\n",
    "\n",
    "g = openmc.Geometry()\n",
    "g.root_universe = root\n",
    "g.export_to_xml()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 5508 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "p = openmc.Plot()\n",
    "p.origin = (pitch, pitch, 0)\n",
    "p.width = (3*pitch, 3*pitch)\n",
    "p.pixels = (400, 400)\n",
    "p.color_by = 'material'\n",
    "p.colors = colors\n",
    "\n",
    "openmc.plot_inline(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## What exactly does `outer` mean?\n",
    "\n",
    "We are taking that one universe and tiling it infinitely outside the lattice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 9180 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Try a different outer universe to see what I mean\n",
    "lattice.outer = guide_tube\n",
    "\n",
    "g.export_to_xml()\n",
    "\n",
    "openmc.plot_inline(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The BEAVRS assembly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 6416 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image('assembly_diagram.png')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "lattice = openmc.RectLattice()\n",
    "\n",
    "lattice.dimension = [9, 9]\n",
    "lattice.pitch = [pitch]*2\n",
    "lattice.outer = all_water\n",
    "\n",
    "# I want (x0, y0) = (0, 0) to be the center of the instrument tube so that means\n",
    "# the lower-left will be -half a pin pitch in x and y.\n",
    "lattice.lower_left = [-pitch/2.0]*2\n",
    "\n",
    "# Most of the lattice positions are fuel pins so rather than type all of those\n",
    "# out, I will use a Python list comprehension to start with a 9x9 array of fuel.\n",
    "lattice.universes = [[fuel_pin for i in range(9)] for j in range(9)]\n",
    "\n",
    "# Then I will replace some fuel pins with guide tubes.  First index is the row,\n",
    "# starting from the top, and the second is the column (like a matrix).\n",
    "lattice.universes[2][0] = guide_tube\n",
    "lattice.universes[2][3] = guide_tube\n",
    "lattice.universes[5][0] = guide_tube\n",
    "lattice.universes[5][3] = guide_tube\n",
    "lattice.universes[5][6] = guide_tube\n",
    "lattice.universes[8][0] = guide_tube\n",
    "lattice.universes[8][3] = guide_tube\n",
    "lattice.universes[8][6] = guide_tube\n",
    "\n",
    "# And the burnable poison rod.\n",
    "#lattice.universes[3][5] = burn\n",
    "lattice.universes[2][6] = burn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we just have to add the boundary conditions and root universe to finish the geometry."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "height = 100 # Finite height is not strictly necessary but may avoid floating-point errors\n",
    "assembly_pitch = 21.5\n",
    "x0 = openmc.XPlane(x0=0.0, boundary_type='reflective')\n",
    "x1 = openmc.XPlane(x0=assembly_pitch/2.0, boundary_type='reflective')\n",
    "y0 = openmc.YPlane(y0=0.0, boundary_type='reflective')\n",
    "y1 = openmc.YPlane(y0=assembly_pitch/2.0, boundary_type='reflective')\n",
    "z0 = openmc.ZPlane(z0=-height/2.0, boundary_type='reflective')\n",
    "z1 = openmc.ZPlane(z0=height/2.0, boundary_type='reflective')\n",
    "\n",
    "main = openmc.Cell()\n",
    "main.region = +x0 & -x1 & +y0 & -y1 & +z0 & -z1\n",
    "main.fill = lattice"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "root = openmc.Universe()\n",
    "root.add_cell(main)\n",
    "\n",
    "g = openmc.Geometry()\n",
    "g.root_universe = root\n",
    "g.export_to_xml()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 27908 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "p = openmc.Plot()\n",
    "p.origin = (0.25*assembly_pitch, 0.25*assembly_pitch, 0)\n",
    "p.width = (0.55*assembly_pitch, 0.55*assembly_pitch)\n",
    "p.pixels = (700, 700)\n",
    "p.color_by = 'material'\n",
    "p.colors = colors\n",
    "\n",
    "openmc.plot_inline(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "settings = openmc.Settings()\n",
    "settings.source = openmc.IndependentSource(space=openmc.stats.Box((0.1, 0.1, 0), (0.49*assembly_pitch, 0.49*assembly_pitch, 0)))\n",
    "settings.batches = 50\n",
    "settings.inactive = 10\n",
    "settings.particles = 1000\n",
    "settings.export_to_xml()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "scrolled": false
   },
   "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-10-19 22:47:40\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 U234 from /opt/xdata/endfb-vii.1-hdf5/neutron/U234.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 U236 from /opt/xdata/endfb-vii.1-hdf5/neutron/U236.h5\n",
      " Reading O16 from /opt/xdata/endfb-vii.1-hdf5/neutron/O16.h5\n",
      " Reading Zr90 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr90.h5\n",
      " Reading Zr91 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr91.h5\n",
      " Reading Zr92 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr92.h5\n",
      " Reading Zr94 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr94.h5\n",
      " Reading Zr96 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr96.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 B11 from /opt/xdata/endfb-vii.1-hdf5/neutron/B11.h5\n",
      " Reading O17 from /opt/xdata/endfb-vii.1-hdf5/neutron/O17.h5\n",
      " Reading Al27 from /opt/xdata/endfb-vii.1-hdf5/neutron/Al27.h5\n",
      " Reading Si28 from /opt/xdata/endfb-vii.1-hdf5/neutron/Si28.h5\n",
      " Reading Si29 from /opt/xdata/endfb-vii.1-hdf5/neutron/Si29.h5\n",
      " Reading Si30 from /opt/xdata/endfb-vii.1-hdf5/neutron/Si30.h5\n",
      " Reading c_H_in_H2O from /opt/xdata/endfb-vii.1-hdf5/neutron/c_H_in_H2O.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    1.35290\n",
      "        2/1    1.29703\n",
      "        3/1    1.34523\n",
      "        4/1    1.32638\n",
      "        5/1    1.34864\n",
      "        6/1    1.46246\n",
      "        7/1    1.27655\n",
      "        8/1    1.35373\n",
      "        9/1    1.36660\n",
      "       10/1    1.29825\n",
      "       11/1    1.40694\n",
      "       12/1    1.30211    1.35453 +/- 0.05241\n",
      "       13/1    1.27009    1.32638 +/- 0.04133\n",
      "       14/1    1.34940    1.33214 +/- 0.02978\n",
      "       15/1    1.27348    1.32040 +/- 0.02588\n",
      "       16/1    1.33208    1.32235 +/- 0.02122\n",
      "       17/1    1.33995    1.32486 +/- 0.01811\n",
      "       18/1    1.31032    1.32305 +/- 0.01579\n",
      "       19/1    1.31485    1.32214 +/- 0.01395\n",
      "       20/1    1.40171    1.33009 +/- 0.01480\n",
      "       21/1    1.38497    1.33508 +/- 0.01429\n",
      "       22/1    1.30876    1.33289 +/- 0.01323\n",
      "       23/1    1.29851    1.33024 +/- 0.01245\n",
      "       24/1    1.39786    1.33507 +/- 0.01250\n",
      "       25/1    1.26751    1.33057 +/- 0.01248\n",
      "       26/1    1.35265    1.33195 +/- 0.01175\n",
      "       27/1    1.31182    1.33077 +/- 0.01110\n",
      "       28/1    1.34023    1.33129 +/- 0.01048\n",
      "       29/1    1.42655    1.33631 +/- 0.01111\n",
      "       30/1    1.26505    1.33274 +/- 0.01113\n",
      "       31/1    1.41047    1.33644 +/- 0.01121\n",
      "       32/1    1.39542    1.33912 +/- 0.01102\n",
      "       33/1    1.33338    1.33887 +/- 0.01053\n",
      "       34/1    1.26051    1.33561 +/- 0.01060\n",
      "       35/1    1.22201    1.33107 +/- 0.01114\n",
      "       36/1    1.27395    1.32887 +/- 0.01092\n",
      "       37/1    1.47329    1.33422 +/- 0.01179\n",
      "       38/1    1.37005    1.33550 +/- 0.01144\n",
      "       39/1    1.31772    1.33488 +/- 0.01105\n",
      "       40/1    1.26834    1.33267 +/- 0.01090\n",
      "       41/1    1.34448    1.33305 +/- 0.01055\n",
      "       42/1    1.29664    1.33191 +/- 0.01028\n",
      "       43/1    1.33513    1.33201 +/- 0.00997\n",
      "       44/1    1.28174    1.33053 +/- 0.00978\n",
      "       45/1    1.34897    1.33106 +/- 0.00951\n",
      "       46/1    1.39485    1.33283 +/- 0.00941\n",
      "       47/1    1.31665    1.33239 +/- 0.00916\n",
      "       48/1    1.24309    1.33004 +/- 0.00922\n",
      "       49/1    1.26441    1.32836 +/- 0.00914\n",
      "       50/1    1.30363    1.32774 +/- 0.00893\n",
      " Creating state point statepoint.50.h5...\n",
      "\n",
      " =======================>     TIMING STATISTICS     <=======================\n",
      "\n",
      " Total time for initialization     = 1.7103e+00 seconds\n",
      "   Reading cross sections          = 1.6833e+00 seconds\n",
      " Total time in simulation          = 2.7935e+00 seconds\n",
      "   Time in transport only          = 2.7625e+00 seconds\n",
      "   Time in inactive batches        = 4.1086e-01 seconds\n",
      "   Time in active batches          = 2.3827e+00 seconds\n",
      "   Time synchronizing fission bank = 3.8710e-03 seconds\n",
      "     Sampling source sites         = 3.4089e-03 seconds\n",
      "     SEND/RECV source sites        = 4.4113e-04 seconds\n",
      "   Time accumulating tallies       = 1.2799e-02 seconds\n",
      "   Time writing statepoints        = 7.3130e-03 seconds\n",
      " Total time for finalization       = 7.2897e-04 seconds\n",
      " Total time elapsed                = 4.5103e+00 seconds\n",
      " Calculation Rate (inactive)       = 24339.4 particles/second\n",
      " Calculation Rate (active)         = 16787.9 particles/second\n",
      "\n",
      " ============================>     RESULTS     <============================\n",
      "\n",
      " k-effective (Collision)     = 1.33237 +/- 0.00615\n",
      " k-effective (Track-length)  = 1.32774 +/- 0.00893\n",
      " k-effective (Absorption)    = 1.33247 +/- 0.00479\n",
      " Combined k-effective        = 1.33354 +/- 0.00432\n",
      " Leakage Fraction            = 0.00000 +/- 0.00000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "openmc.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tallies\n",
    "\n",
    "Okay, that was cool, but $k_\\text{eff}$ isn't everyting.  We also want to know reaction rates so we can compute the power distribution, depletion rate, etc.  If we want pin-by-pin reaction rates, we have two options.  First, we can use a tally mesh.  This will lay a rectangular grid over the geometry and tally the reaction rates in each mesh bin."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "tallies = openmc.Tallies()\n",
    "\n",
    "mesh = openmc.RegularMesh()\n",
    "#mesh.dimension = lattice.dimension # Could set dimension from the lattice\n",
    "mesh.dimension = [3, 3] # But we will use 3x3 for this example\n",
    "mesh.lower_left = lattice.lower_left\n",
    "mesh.width = lattice.pitch\n",
    "\n",
    "mesh_filt = openmc.MeshFilter(mesh)\n",
    "\n",
    "t = openmc.Tally(1)\n",
    "t.filters = [mesh_filt]\n",
    "t.scores = ['total', 'fission']\n",
    "t.nuclides = ['total', 'U235']\n",
    "tallies.append(t)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or we can use something called a \"distribcell\" filter.  Note that the bin specifies a cell id."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "dist_filt = openmc.DistribcellFilter(fuel.id)\n",
    "t = openmc.Tally(2)\n",
    "t.filters = [dist_filt]\n",
    "t.scores = ['total', 'fission']\n",
    "t.nuclides = ['total', 'U235']\n",
    "tallies.append(t)\n",
    "\n",
    "tallies.export_to_xml()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "scrolled": true
   },
   "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-10-19 22:47:45\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 U234 from /opt/xdata/endfb-vii.1-hdf5/neutron/U234.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 U236 from /opt/xdata/endfb-vii.1-hdf5/neutron/U236.h5\n",
      " Reading O16 from /opt/xdata/endfb-vii.1-hdf5/neutron/O16.h5\n",
      " Reading Zr90 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr90.h5\n",
      " Reading Zr91 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr91.h5\n",
      " Reading Zr92 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr92.h5\n",
      " Reading Zr94 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr94.h5\n",
      " Reading Zr96 from /opt/xdata/endfb-vii.1-hdf5/neutron/Zr96.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 B11 from /opt/xdata/endfb-vii.1-hdf5/neutron/B11.h5\n",
      " Reading O17 from /opt/xdata/endfb-vii.1-hdf5/neutron/O17.h5\n",
      " Reading Al27 from /opt/xdata/endfb-vii.1-hdf5/neutron/Al27.h5\n",
      " Reading Si28 from /opt/xdata/endfb-vii.1-hdf5/neutron/Si28.h5\n",
      " Reading Si29 from /opt/xdata/endfb-vii.1-hdf5/neutron/Si29.h5\n",
      " Reading Si30 from /opt/xdata/endfb-vii.1-hdf5/neutron/Si30.h5\n",
      " Reading c_H_in_H2O from /opt/xdata/endfb-vii.1-hdf5/neutron/c_H_in_H2O.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    1.35290\n",
      "        2/1    1.29703\n",
      "        3/1    1.34523\n",
      "        4/1    1.32638\n",
      "        5/1    1.34864\n",
      "        6/1    1.46246\n",
      "        7/1    1.27655\n",
      "        8/1    1.35373\n",
      "        9/1    1.36660\n",
      "       10/1    1.29825\n",
      "       11/1    1.40694\n",
      "       12/1    1.30211    1.35453 +/- 0.05241\n",
      "       13/1    1.27009    1.32638 +/- 0.04133\n",
      "       14/1    1.34940    1.33214 +/- 0.02978\n",
      "       15/1    1.27348    1.32040 +/- 0.02588\n",
      "       16/1    1.33208    1.32235 +/- 0.02122\n",
      "       17/1    1.33995    1.32486 +/- 0.01811\n",
      "       18/1    1.31032    1.32305 +/- 0.01579\n",
      "       19/1    1.31485    1.32214 +/- 0.01395\n",
      "       20/1    1.40171    1.33009 +/- 0.01480\n",
      "       21/1    1.38497    1.33508 +/- 0.01429\n",
      "       22/1    1.30876    1.33289 +/- 0.01323\n",
      "       23/1    1.29851    1.33024 +/- 0.01245\n",
      "       24/1    1.39786    1.33507 +/- 0.01250\n",
      "       25/1    1.26751    1.33057 +/- 0.01248\n",
      "       26/1    1.35265    1.33195 +/- 0.01175\n",
      "       27/1    1.31182    1.33077 +/- 0.01110\n",
      "       28/1    1.34023    1.33129 +/- 0.01048\n",
      "       29/1    1.42655    1.33631 +/- 0.01111\n",
      "       30/1    1.26505    1.33274 +/- 0.01113\n",
      "       31/1    1.41047    1.33644 +/- 0.01121\n",
      "       32/1    1.39542    1.33912 +/- 0.01102\n",
      "       33/1    1.33338    1.33887 +/- 0.01053\n",
      "       34/1    1.26051    1.33561 +/- 0.01060\n",
      "       35/1    1.22201    1.33107 +/- 0.01114\n",
      "       36/1    1.27395    1.32887 +/- 0.01092\n",
      "       37/1    1.47329    1.33422 +/- 0.01179\n",
      "       38/1    1.37005    1.33550 +/- 0.01144\n",
      "       39/1    1.31772    1.33488 +/- 0.01105\n",
      "       40/1    1.26834    1.33267 +/- 0.01090\n",
      "       41/1    1.34448    1.33305 +/- 0.01055\n",
      "       42/1    1.29664    1.33191 +/- 0.01028\n",
      "       43/1    1.33513    1.33201 +/- 0.00997\n",
      "       44/1    1.28174    1.33053 +/- 0.00978\n",
      "       45/1    1.34897    1.33106 +/- 0.00951\n",
      "       46/1    1.39485    1.33283 +/- 0.00941\n",
      "       47/1    1.31665    1.33239 +/- 0.00916\n",
      "       48/1    1.24309    1.33004 +/- 0.00922\n",
      "       49/1    1.26441    1.32836 +/- 0.00914\n",
      "       50/1    1.30363    1.32774 +/- 0.00893\n",
      " Creating state point statepoint.50.h5...\n",
      "\n",
      " =======================>     TIMING STATISTICS     <=======================\n",
      "\n",
      " Total time for initialization     = 1.5860e+00 seconds\n",
      "   Reading cross sections          = 1.5596e+00 seconds\n",
      " Total time in simulation          = 2.7953e+00 seconds\n",
      "   Time in transport only          = 2.7587e+00 seconds\n",
      "   Time in inactive batches        = 4.0746e-01 seconds\n",
      "   Time in active batches          = 2.3879e+00 seconds\n",
      "   Time synchronizing fission bank = 3.9311e-03 seconds\n",
      "     Sampling source sites         = 3.4452e-03 seconds\n",
      "     SEND/RECV source sites        = 4.6194e-04 seconds\n",
      "   Time accumulating tallies       = 1.8771e-02 seconds\n",
      "   Time writing statepoints        = 7.6215e-03 seconds\n",
      " Total time for finalization       = 7.3739e-04 seconds\n",
      " Total time elapsed                = 4.3878e+00 seconds\n",
      " Calculation Rate (inactive)       = 24542.4 particles/second\n",
      " Calculation Rate (active)         = 16751.2 particles/second\n",
      "\n",
      " ============================>     RESULTS     <============================\n",
      "\n",
      " k-effective (Collision)     = 1.33237 +/- 0.00615\n",
      " k-effective (Track-length)  = 1.32774 +/- 0.00893\n",
      " k-effective (Absorption)    = 1.33247 +/- 0.00479\n",
      " Combined k-effective        = 1.33354 +/- 0.00432\n",
      " Leakage Fraction            = 0.00000 +/- 0.00000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "openmc.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " ============================>     TALLY 1     <============================\n",
      "\n",
      " Mesh Index (1, 1)\n",
      "   Total Material\n",
      "     Total Reaction Rate                  0.131468 +/- 0.0036769\n",
      "     Fission Rate                         0 +/- 0\n",
      "   U235\n",
      "     Total Reaction Rate                  0 +/- 0\n",
      "     Fission Rate                         0 +/- 0\n",
      " Mesh Index (2, 1)\n",
      "   Total Material\n",
      "     Total Reaction Rate                  0.212182 +/- 0.00352323\n",
      "     Fission Rate                         0.00412647 +/- 0.000185732\n",
      "   U235\n",
      "     Total Reaction Rate                  0.00536945 +/- 0.000226616\n",
      "...\n",
      "     Fission Rate                         0.00680248 +/- 0.000316935\n",
      " Distributed Cell u11->c22->l10(7,8)->u1->c1\n",
      "   Total Material\n",
      "     Total Reaction Rate                  0.0827467 +/- 0.0014009\n",
      "     Fission Rate                         0.00781763 +/- 0.000310541\n",
      "   U235\n",
      "     Total Reaction Rate                  0.010213 +/- 0.000379642\n",
      "     Fission Rate                         0.00743933 +/- 0.000311658\n",
      " Distributed Cell u11->c22->l10(8,8)->u1->c1\n",
      "   Total Material\n",
      "     Total Reaction Rate                  0.0825296 +/- 0.00125014\n",
      "     Fission Rate                         0.00740099 +/- 0.000264317\n",
      "   U235\n",
      "     Total Reaction Rate                  0.00969887 +/- 0.000320549\n",
      "     Fission Rate                         0.00701317 +/- 0.000264028\n"
     ]
    }
   ],
   "source": [
    "!cat tallies.out | head -n 15\n",
    "print('...')\n",
    "!cat tallies.out | tail -n 15"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice the Distributed Cell description showing the path from the root universe all the way down to the fuel cell. This path can become a lot more complicated as we build more complex geometries: for instance, we could take our BEAVRS assembly and place it into a lattice as well."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 1723104 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(\"core_diagram.png\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Distributed materials\n",
    "\n",
    "In a depletion problem, every fuel pin might need its own unique material.  We have a feature called \"distributed materials\" which makes this easier."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "many_uo2_mats = []\n",
    "for i in range(72):\n",
    "    m = openmc.Material(name='uo2')\n",
    "    m.add_element('U', 1.0, enrichment=3.0)\n",
    "    m.add_nuclide('O16', 2.0)\n",
    "    m.set_density('g/cm3', 10.0)\n",
    "    many_uo2_mats.append(m)\n",
    "\n",
    "mf += many_uo2_mats\n",
    "mf.export_to_xml()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 38636 characters]",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fuel.fill = many_uo2_mats\n",
    "g.export_to_xml()\n",
    "openmc.plot_inline(p)"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Raw Cell Format",
  "kernelspec": {
   "display_name": "Python 3",
   "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": 1
}
