adding back files to be reviewed

This commit is contained in:
Paul Romano 2019-10-28 11:55:45 -05:00
parent ae28233110
commit bc09d1ef55
1244 changed files with 301904 additions and 0 deletions

View file

@ -0,0 +1,569 @@
.. _methods_cmfd:
================================================================
Nonlinear Diffusion Acceleration - Coarse Mesh Finite Difference
================================================================
This page section discusses how nonlinear diffusion acceleration (NDA) using
coarse mesh finite difference (CMFD) is implemented into OpenMC. Before we get
into the theory, general notation for this section is discussed.
Note that the methods discussed in this section are written specifically for
continuous-energy mode but equivalent apply to the multi-group mode if the
particle's energy is replaced with the particle's group
--------
Notation
--------
Before deriving NDA relationships, notation is explained. If a parameter has a
:math:`\overline{\cdot}`, it is surface area-averaged and if it has a
:math:`\overline{\overline\cdot}`, it is volume-averaged. When describing a
specific cell in the geometry, indices :math:`(i,j,k)` are used which correspond
to directions :math:`(x,y,z)`. In most cases, the same operation is performed in
all three directions. To compactly write this, an arbitrary direction set
:math:`(u,v,w)` that corresponds to cell indices :math:`(l,m,n)` is used. Note
that :math:`u` and :math:`l` do not have to correspond to :math:`x` and
:math:`i`. However, if :math:`u` and :math:`l` correspond to :math:`y` and
:math:`j`, :math:`v` and :math:`w` correspond to :math:`x` and :math:`z`
directions. An example of this is shown in the following expression:
.. math::
:label: not1
\sum\limits_{u\in(x,y,z)}\left\langle\overline{J}^{u,g}_{l+1/2,m,n}
\Delta_m^v\Delta_n^w\right\rangle
Here, :math:`u` takes on each direction one at a time. The parameter :math:`J`
is surface area-averaged over the transverse indices :math:`m` and :math:`n`
located at :math:`l+1/2`. Usually, spatial indices are listed as subscripts and
the direction as a superscript. Energy group indices represented by :math:`g`
and :math:`h` are also listed as superscripts here. The group :math:`g` is the
group of interest and, if present, :math:`h` is all groups. Finally, any
parameter surrounded by :math:`\left\langle\cdot\right\rangle` represents a
tally quantity that can be edited from a Monte Carlo (MC) solution.
------
Theory
------
NDA is a diffusion model that has equivalent physics to a transport model. There
are many different methods that can be classified as NDA. The CMFD method is a
type of NDA that represents second order multigroup diffusion equations on a
coarse spatial mesh. Whether a transport model or diffusion model is used to
represent the distribution of neutrons, these models must satisfy the *neutron
balance equation*. This balance is represented by the following formula for a
specific energy group :math:`g` in cell :math:`(l,m,n)`:
.. math::
:label: eq_neut_bal
\sum\limits_{u\in(x,y,z)}\left(\left\langle\overline{J}^{u,g}_{l+1/2,m,n}
\Delta_m^v\Delta_n^w\right\rangle -
\left\langle\overline{J}^{u,g}_{l-1/2,m,n}
\Delta_m^v\Delta_n^w\right\rangle\right)
+
\left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g
\overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle
= \\
\sum\limits_{h=1}^G\left\langle
\overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow
g}\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w
\right\rangle
+
\frac{1}{k_{eff}}\sum\limits_{h=1}^G
\left\langle\overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow
g}\overline{\overline\phi}_{l,m,n}^h
\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle.
In eq. :eq:`eq_neut_bal` the parameters are defined as:
* :math:`\left\langle\overline{J}^{u,g}_{l\pm
1/2,m,n}\Delta_m^v\Delta_n^w\right\rangle` --- surface area-integrated net
current over surface :math:`(l\pm 1/2,m,n)` with surface normal in direction
:math:`u` in energy group :math:`g`. By dividing this quantity by the transverse
area, :math:`\Delta_m^v\Delta_n^w`, the surface area-averaged net current can
be computed.
* :math:`\left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g
\overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle`
--- volume-integrated total reaction rate over energy group :math:`g`.
* :math:`\left\langle\overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow
g}
\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle`
--- volume-integrated scattering production rate of neutrons that begin with
energy in group :math:`h` and exit reaction in group :math:`g`. This reaction
rate also includes the energy transfer of reactions (except fission) that
produce multiple neutrons such as (n, 2n); hence, the need for :math:`\nu_s`
to represent neutron multiplicity.
* :math:`k_{eff}` --- core multiplication factor.
* :math:`\left\langle\overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow
g}\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle`
--- volume-integrated fission production rate of neutrons from fissions in
group :math:`h` that exit in group :math:`g`.
Each quantity in :math:`\left\langle\cdot\right\rangle` represents a scalar value that
is obtained from an MC tally. A good verification step when using an MC code is
to make sure that tallies satisfy this balance equation within statistics. No
NDA acceleration can be performed if the balance equation is not satisfied.
There are three major steps to consider when performing NDA: (1) calculation of
macroscopic cross sections and nonlinear parameters, (2) solving an eigenvalue
problem with a system of linear equations, and (3) modifying MC source
distribution to align with the NDA solution on a chosen mesh. This process is
illustrated as a flow chart below. After a batch of neutrons
is simulated, NDA can take place. Each of the steps described above is described
in detail in the following sections.
.. figure:: ../_images/cmfd_flow.png
:align: center
:figclass: align-center
Flow chart of NDA process. Note "XS" is used for cross section and "DC" is
used for diffusion coefficient.
Calculation of Macroscopic Cross Sections
-----------------------------------------
A diffusion model needs macroscopic cross sections and diffusion coefficients to
solve for multigroup fluxes. Cross sections are derived by conserving reaction
rates predicted by MC tallies. From Eq. :eq:`eq_neut_bal`, total, scattering
production and fission production macroscopic cross sections are needed. They are
defined from MC tallies as follows:
.. math::
:label: xs1
\overline{\overline\Sigma}_{t_{l,m,n}}^g \equiv
\frac{\left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g
\overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}
{\left\langle\overline{\overline\phi}_{l,m,n}^g
\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle},
.. math::
:label: xs2
\overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow g} \equiv
\frac{\left\langle\overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow
g}\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}
{\left\langle\overline{\overline\phi}_{l,m,n}^h
\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}
and
.. math::
:label: xs3
\overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow g} \equiv
\frac{\left\langle\overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow
g}\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}
{\left\langle\overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}.
In order to fully conserve neutron balance, leakage rates also need to be
preserved. In standard diffusion theory, leakage rates are represented by
diffusion coefficients. Unfortunately, it is not easy in MC to calculate a
single diffusion coefficient for a cell that describes leakage out of each
surface. Luckily, it does not matter what definition of diffusion coefficient is
used because nonlinear equivalence parameters will correct for this
inconsistency. However, depending on the diffusion coefficient definition
chosen, different convergence properties of NDA equations are observed.
Here, we introduce a diffusion coefficient that is derived for a coarse energy
transport reaction rate. This definition can easily be constructed from
MC tallies provided that angular moments of scattering reaction rates can
be obtained. The diffusion coefficient is defined as follows:
.. math::
:label: eq_transD
\overline{\overline D}_{l,m,n}^g =
\frac{\left\langle\overline{\overline\phi}_{l,m,n}^g
\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle}{3
\left\langle\overline{\overline\Sigma}_{tr_{l,m,n}}^g
\overline{\overline\phi}_{l,m,n}^g
\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle},
where
.. math::
:label: xs4
\left\langle\overline{\overline\Sigma}_{tr_{l,m,n}}^g
\overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle
=
\left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g
\overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle
\\ -
\left\langle\overline{\overline{\nu_s\Sigma}}_{s1_{l,m,n}}^g
\overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle.
Note that the transport reaction rate is calculated from the total reaction rate
reduced by the :math:`P_1` scattering production reaction rate. Equation :eq:`eq_transD`
does not represent the best definition of diffusion coefficients from MC;
however, it is very simple and usually fits into MC tally frameworks
easily. Different methods to calculate more accurate diffusion coefficients can
found in [Herman]_.
CMFD Equations
--------------
The first part of this section is devoted to discussing second-order finite
volume discretization of multigroup diffusion equations. This will be followed
up by the formulation of CMFD equations that are used in this NDA
scheme. When performing second-order finite volume discretization of the
diffusion equation, we need information that relates current to flux. In this
numerical scheme, each cell is coupled only to its direct neighbors. Therefore,
only two types of coupling exist: (1) cell-to-cell coupling and (2)
cell-to-boundary coupling. The derivation of this procedure is referred to as
finite difference diffusion equations and can be found in literature such
as [Hebert]_. These current/flux relationships are as follows:
* cell-to-cell coupling
.. math::
:label: eq_cell_cell
\overline{J}^{u,g}_{l\pm1/2,m,n} = -\frac{2\overline{\overline
D}_{l\pm1,m,n}^g\overline{\overline
D}_{l,m,n}^g}{\overline{\overline D}_{l\pm1,m,n}^g\Delta_l^u +
\overline{\overline
D}_{l,m,n}^g\Delta_{l\pm1}^u}
\left(\pm\overline{\overline{\phi}}_{l\pm1,m,n}^g\mp
\overline{\overline{\phi}}_{l,m,n}^g\right),
* cell-to-boundary coupling
.. math::
:label: eq_cell_bound
\overline{J}^{u,g}_{l\pm1/2,m,n} = \pm\frac{2\overline{\overline
D}_{l,m,n}^g\left(1 -
\beta_{l\pm1/2,m,n}^{u,g}\right)}{4\overline{\overline
D}_{l,m,n}^g\left(1 + \beta_{l\pm1/2,m,n}^{u,g}\right) + \left(1 -
\beta_{l\pm1/2,m,n}^{u,g}\right)\Delta_l^u}\overline{\overline{\phi}}_{l,m,n}^{g}.
In Eqs. :eq:`eq_cell_cell` and :eq:`eq_cell_bound`, the :math:`\pm` refers to
left (:math:`-x`) or right (:math:`+x`) surface in the :math:`x` direction,
back (:math:`-y`) or front (:math:`+y`) surface in the :math:`y` direction and
bottom (:math:`-z`) or top (:math:`+z`) surface in the :math:`z` direction. For
cell-to-boundary coupling, a general albedo, :math:`\beta_{l\pm1/2,m,n}^{u,g}`,
is used. The albedo is defined as the ratio of incoming (:math:`-` superscript)
to outgoing (:math:`+` superscript) partial current on any surface represented
as
.. math::
:label: eq_albedo
\beta_{l\pm1/2,m,n}^{u,g} =
\frac{\overline{J}^{u,g-}_{l\pm1/2,m,n}}{\overline{J}^{u,g+}_{l\pm1/2,m,n}}.
Common boundary conditions are: vacuum (:math:`\beta=0`), reflective
(:math:`\beta=1`) and zero flux (:math:`\beta=-1`). Both eq. :eq:`eq_cell_cell`
and eq. :eq:`eq_cell_bound` can be written in this generic form,
.. math::
:label: eq_dtilde
\overline{J}^{u,g}_{l\pm1/2,m,n} = \widetilde{D}_{l,m,n}^{u,g} \left(\dots\right).
The parameter :math:`\widetilde{D}_{l,m,n}^{u,g}` represents the linear
coupling term between current and flux. These current relationships can be
sustituted into eq. :eq:`eq_neut_bal` to produce a linear system of multigroup
diffusion equations for each spatial cell and energy group. However, a solution
to these equations is not consistent with a higher order transport solution
unless equivalence factors are present. This is because both the diffusion
approximation, governed by Fick's Law, and spatial trunction error will produce
differences. Therefore, a nonlinear parameter,
:math:`\widehat{D}_{l,m,n}^{u,g}`, is added to eqs. :eq:`eq_cell_cell` and
:eq:`eq_cell_bound`. These equations are, respectively,
.. math::
:label: eq_dhat_cell
\overline{J}^{u,g}_{l\pm1/2,m,n} = -\widetilde{D}_{l,m,n}^{u,g}
\left(\pm\overline{\overline{\phi}}_{l\pm1,m,n}^g\mp
\overline{\overline{\phi}}_{l,m,n}^g\right) + \widehat{D}_{l,m,n}^{u,g}
\left(\overline{\overline{\phi}}_{l\pm1,m,n}^g +
\overline{\overline{\phi}}_{l,m,n}^g\right)
and
.. math::
:label: eq_dhat_bound
\overline{J}^{u,g}_{l\pm1/2,m,n} = \pm\widetilde{D}_{l,m,n}^{u,g}
\overline{\overline{\phi}}_{l,m,n}^{g} + \widehat{D}_{l,m,n}^{u,g}
\overline{\overline{\phi}}_{l,m,n}^{g}.
The only unknown in each of these equations is the equivalence parameter. The
current, linear coupling term and flux can either be obtained or derived from
MC tallies. Thus, it is called nonlinear because it is dependent on the flux
which is updated on the next iteration.
Equations :eq:`eq_dhat_cell` and :eq:`eq_dhat_bound` can be substituted into
eq. :eq:`eq_neut_bal` to create a linear system of equations that is consistent
with transport physics. One example of this equation is written for an
interior cell,
.. math::
:label: eq_cmfd_sys
\sum_{u\in
x,y,x}\frac{1}{\Delta_l^u}\left[\left(-\tilde{D}_{l-1/2,m,n}^{u,g} -
\hat{D}_{l-1/2,m,n}^{u,g}\right)\overline{\overline{\phi}}_{l-1,m,n}^g\right.
\\ + \left(\tilde{D}_{l-1/2,m,n}^{u,g} +
\tilde{D}_{l+1/2,m,n}^{u,g} - \hat{D}_{l-1/2,m,n}^{u,g} +
\hat{D}_{l+1/2,m,n}^{u,g}\right)\overline{\overline{\phi}}_{l,m,n}^g
\\ +
\left. \left(-\tilde{D}_{l+1/2,m,n}^{u,g} +
\hat{D}_{l+1/2,m,n}^{u,g}\right)\overline{\overline{\phi}}_{l+1,m,n}^g
\right] \\ +
\overline{\overline\Sigma}_{t_{l,m,n}}^g\overline{\overline{\phi}}_{l,m,n}^g
- \sum\limits_{h=1}^G\overline{\overline{\nu_s\Sigma}}^{h\rightarrow
g}_{s_{l,m,n}}\overline{\overline{\phi}}_{l,m,n}^h =
\frac{1}{k}\sum\limits_{h=1}^G\overline{\overline{\nu_f\Sigma}}^{h\rightarrow
g}_{f_{l,m,n}}\overline{\overline{\phi}}_{l,m,n}^h.
It should be noted that before substitution, eq. :eq:`eq_neut_bal` was divided
by the volume of the cell, :math:`\Delta_l^u\Delta_m^v\Delta_n^w`. Equation
:eq:`eq_cmfd_sys` can be represented in operator form as
.. math::
:label: eq_CMFDopers
\mathbb{M}\mathbf{\Phi} = \frac{1}{k}\mathbb{F}\mathbf{\Phi},
where :math:`\mathbb{M}` is the neutron loss matrix operator,
:math:`\mathbb{F}` is the neutron production matrix operator,
:math:`\mathbf{\Phi}` is the multigroup flux vector and :math:`k` is the
eigenvalue. This generalized eigenvalue problem is solved to obtain fundamental
mode multigroup fluxes and eigenvalue. In order to produce consistent results
with transport theory from these equations, the neutron balance equation must
have been satisfied by MC tallies. The desire is that CMFD equations will
produce a more accurate source than MC after each fission source generation.
CMFD Feedback
-------------
Now that a more accurate representation of the expected source distribution is
estimated from CMFD, it needs to be communicated back to MC. The first step
in this process is to generate a probability mass function that provides
information about how probable it is for a neutron to be born in a given cell
and energy group. This is represented as
.. math::
:label: eq_cmfd_psrc
p_{l,m,n}^g =
\frac{\sum_{h=1}^{G}\overline{\overline{\nu_f\Sigma}}^{h\rightarrow
g}_{f_{l,m,n}}\overline{\overline{\phi}}_{l,m,n}^h\Delta_l^u\Delta_m^v
\Delta_n^w}{\sum_n\sum_m\sum_l\sum_{h=1}^{G}\overline{
\overline{\nu_f\Sigma}}^{h\rightarrow
g}_{f_{l,m,n}}\overline{\overline{\phi}}_{l,m,n}^h\Delta_l^u\Delta_m^v
\Delta_n^w}.
This equation can be multiplied by the number of source neutrons to obtain an
estimate of the expected number of neutrons to be born in a given cell and
energy group. This distribution can be compared to the MC source distribution
to generate weight adjusted factors defined as
.. math::
:label: eq_waf
f_{l,m,n}^g = \frac{Np_{l,m,n}^g}{\sum\limits_s w_s};\quad s\in
\left(g,l,m,n\right).
The MC source distribution is represented on the same coarse mesh as
CMFD by summing all neutrons' weights, :math:`w_s`, in a given cell and
energy group. MC source weights can then be modified by this weight
adjustment factor so that it matches the CMFD solution on the coarse
mesh,
.. math::
:label: src_mod
w^\prime_s = w_s\times f_{l,m,n}^g;\quad s\in \left(g,l,m,n\right).
It should be noted that heterogeneous information about local coordinates and
energy remain constant throughout this modification process.
------------------------
Implementation in OpenMC
------------------------
The section describes how CMFD was implemented in OpenMC. Before the simulation
begins, a user sets up a CMFD input file that contains the following basic
information:
* CMFD mesh (space and energy),
* boundary conditions at edge of mesh (albedos),
* acceleration region (subset of mesh, optional),
* fission source generation (FSG)/batch that CMFD should begin, and
* whether CMFD feedback should be applied.
It should be noted that for more difficult simulations (e.g., light water
reactors), there are other options available to users such as tally resetting
parameters, effective down-scatter usage, tally estimator, etc. For more
information please see the :class:`openmc.cmfd.CMFDRun` class.
Of the options described above, the optional acceleration subset region is an
uncommon feature. Because OpenMC only has a structured Cartesian mesh, mesh
cells may overlay regions that don't contain fissionable material and may be so
far from the core that the neutron flux is very low. If these regions were
included in the CMFD solution, bad estimates of diffusion parameters may result
and affect CMFD feedback. To deal with this, a user can carve out an active
acceleration region from their structured Cartesian mesh. This is illustrated
in diagram below. When placing a CMFD mesh over a geometry, the boundary
conditions must be known at the global edges of the mesh. If the geometry is
complex like the one below, one may have to cover the whole geometry including
the reactor pressure vessel because we know that there is a zero incoming
current boundary condition at the outer edge of the pressure vessel. This is
not viable in practice because neutrons in simulations may not reach mesh cells
that are near the pressure vessel. To circumvent this, one can shrink the mesh
to cover just the core region as shown in the diagram. However, one must still
estimate the boundary conditions at the global boundaries, but at these
locations, they are not readily known. In OpenMC, one can carve out the active
core region from the entire structured Cartesian mesh. This is shown in the
diagram below by the darkened region over the core. The albedo boundary
conditions at the active core/reflector boundary can be tallied indirectly
during the MC simulation with incoming and outgoing partial currents. This
allows the user to not have to worry about neutrons producing adequate tallies
in mesh cells far away from the core.
.. figure:: ../_images/meshfig.png
:align: center
:figclass: align-center
Diagram of CMFD acceleration mesh
During an MC simulation, CMFD tallies are accumulated. The basic tallies needed
are listed in Table :ref:`tab_tally`. Each tally is performed on a spatial and
energy mesh basis. The surface area-integrated net current is tallied on every
surface of the mesh. OpenMC tally objects are created by the CMFD code
internally, and cross sections are calculated at each CMFD feedback iteration.
The first CMFD iteration, controlled by the user, occurs just after tallies are
communicated to the master processor. Once tallies are collapsed, cross
sections, diffusion coefficients and equivalence parameters are calculated. This
is performed only on the acceleration region if that option has been activated
by the user. Once all diffusion parameters are calculated, CMFD matrices are
formed where energy groups are the inner most iteration index. In OpenMC,
compressed row storage sparse matrices are used due to the sparsity of CMFD
operators. An example of this sparsity is shown for the 3-D BEAVRS model in
figures :num:`fig-loss` and :num:`fig-prod` [BEAVRS]_. These matrices represent
an assembly radial mesh, 24 cell mesh in the axial direction and two energy
groups. The loss matrix is 99.92% sparse and the production matrix is 99.99%
sparse. Although the loss matrix looks like it is tridiagonal, it is really a
seven banded matrix with a block diagonal matrix for scattering. The production
matrix is a :math:`2\times 2` block diagonal; however, zeros are present because
no fission neutrons appear with energies in the thermal group.
.. _tab_tally:
.. table:: OpenMC CMFD tally list
+--------------------------------------------------------------------------------------------+----------------+---------------------------+
+--------------------------------------------------------------------------------------------+----------------+---------------------------+
| tally | score | filter |
+============================================================================================+================+===========================+
| \ :math:`\left\langle\overline{\overline\phi}_{l,m,n}^g | flux | mesh, energy |
| \Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | |
+--------------------------------------------------------------------------------------------+----------------+---------------------------+
| \ :math:`\left\langle\overline{\overline\Sigma}_{t_{l,m,n}}^g | total | mesh, energy |
| \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | |
+--------------------------------------------------------------------------------------------+----------------+---------------------------+
| \ :math:`\left\langle\overline{\overline{\nu_s\Sigma}}_{s1_{l,m,n}}^g | nu-scatter-1 | mesh, energy |
| \overline{\overline\phi}_{l,m,n}^g\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | |
+--------------------------------------------------------------------------------------------+----------------+---------------------------+
| \ :math:`\left\langle\overline{\overline{\nu_s\Sigma}}_{s_{l,m,n}}^{h\rightarrow g} | nu-scatter | mesh, energy, energyout |
| \overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | |
+--------------------------------------------------------------------------------------------+----------------+---------------------------+
| \ :math:`\left\langle\overline{\overline{\nu_f\Sigma}}_{f_{l,m,n}}^{h\rightarrow g} | nu-fission | mesh, energy, energyout |
| \overline{\overline\phi}_{l,m,n}^h\Delta_l^u\Delta_m^v\Delta_n^w\right\rangle` | | |
+--------------------------------------------------------------------------------------------+----------------+---------------------------+
| \ :math:`\left\langle\overline{J}^{u,g}_{l\pm 1/2,m,n}\Delta_m^v\Delta_n^w\right\rangle` | current | mesh, energy |
+--------------------------------------------------------------------------------------------+----------------+---------------------------+
.. _fig-loss:
.. figure:: ../_images/loss.png
:scale: 50
Sparsity of Neutron Loss Operator
.. _fig-prod:
.. figure:: ../_images/prod.png
:scale: 50
Sparsity of Neutron Production Operator
To solve the eigenvalue problem with these matrices, different source iteration
and linear solvers can be used. The most common source iteration solver used is
standard power iteration as described in [Gill]_. To accelerate these source
iterations, a Wielandt shift scheme can be used as discussed in [Park]_. PETSc
solvers were first implemented to perform the linear solution in parallel that
occurs once per source iteration. When using PETSc, different types of parallel
linear solvers and preconditioners can be used. By default, OpenMC uses an
incomplete LU preconditioner and a GMRES Krylov solver. After some initial
studies of parallelization with PETSc, it was observed that because CMFD
matrices are very sparse, solution times do not scale well. An additional
Gauss-Seidel linear solver with Chebyshev acceleration was added that is
similar to the one used for CMFD in CASMO [Rhodes]_ and [Smith]_. This solver
was implemented with a custom section for two energy groups. Because energy
group is the inner most index, a block diagonal is formed when using more than
one group. For two groups, it is easy to invert this diagonal analytically
inside the Gauss-Seidel iterative solver. For more than two groups, this
analytic inversion can still be performed, but with more computational effort.
A standard Gauss-Seidel solver is used for more than two groups.
Besides a power iteration, a Jacobian-free Newton-Krylov method was also
implemented to obtain eigenvalue and multigroup fluxes as described in [Gill]_
and [Knoll]_. This method is not the primary one used, but has gotten recent
attention due to its coupling advantages to other physics such as thermal
hydraulics. Once multigroup fluxes are obtained, a normalized fission source is
calculated in the code using eq. :eq:`eq_cmfd_psrc` directly.
The next step in the process is to compute weight adjustment factors. These are
calculated by taking the ratio of the expected number of neutrons from the CMFD
source distribution to the current number of neutrons in each mesh. It is
straightforward to compute the CMFD number of neutrons because it is the
product between the total starting initial weight of neutrons and the CMFD
normalized fission source distribution. To compute the number of neutrons from
the current MC source, OpenMC sums the statistical
weights of neutrons from the source bank on a given spatial and energy mesh.
Once weight adjustment factors were calculated, each neutron's statistical
weight in the source bank was modified according to its location and energy.
Examples of CMFD simulations using OpenMC can be found in [HermanThesis]_.
.. only:: html
.. rubric:: References
.. [BEAVRS] Nick Horelik, Bryan Herman. *Benchmark for Evaluation And Verification of Reactor
Simulations*. Massachusetts Institute of Technology, https://crpg.mit.edu/research/beavrs
, 2013.
.. [Gill] Daniel F. Gill. *Newton-Krylov methods for the solution of the k-eigenvalue problem in
multigroup neutronics calculations*. Ph.D. thesis, Pennsylvania State University, 2010.
.. [Hebert] Alain Hebert. *Applied reactor physics*. Presses Internationales Polytechnique,
Montreal, 2009.
.. [Herman] Bryan R. Herman, Benoit Forget, Kord Smith, and Brian N. Aviles. Improved
diffusion coefficients generated from Monte Carlo codes. In *Proceedings of M&C
2013*, Sun Valley, ID, USA, May 5 - 9, 2013.
.. [HermanThesis] Bryan R. Herman. *Monte Carlo and Thermal Hydraulic Coupling using
Low-Order Nonlinear Diffusion Acceleration*. Sc.D. thesis,
Massachusetts Institute of Technology, 2014.
.. [Knoll] D.A. Knoll, H. Park, and C. Newman. *Acceleration of k-eigenvalue/criticality
calculations using the Jacobian-free Newton-Krylov method*. Nuclear Science and
Engineering, 167:133140, 2011.
.. [Park] H. Park, D.A. Knoll, and C.K. Newman. *Nonlinear acceleration of transport
criticality problems*. Nuclear Science and Engineering, 172:5265, 2012.
.. [Rhodes] Joel Rhodes and Malte Edenius. *CASMO-4 --- A Fuel Assembly Burnup Program.
Users Manual*. Studsvik of America, ssp-09/443-u rev 0, proprietary edition, 2001.
.. [Smith] Kord S Smith and Joel D Rhodes III. *Full-core, 2-D, LWR core calculations with
CASMO-4E*. In Proceedings of PHYSOR 2002, Seoul, Korea, October 7 - 10, 2002.

View file

@ -0,0 +1,280 @@
.. _methods_cross_sections:
=============================
Cross Section Representations
=============================
----------------------
Continuous-Energy Data
----------------------
In OpenMC, the data governing the interaction of neutrons with various nuclei
for continous-energy problems are represented using an HDF5 format that can be
produced by converting files in the ACE format, which is used by MCNP_ and
Serpent_. ACE-format data can be generated with the NJOY_ nuclear data
processing system, which converts raw `ENDF/B data`_ into linearly-interpolable
data as required by most Monte Carlo codes. Since ACE-format data can be
converted into OpenMC's HDF5 format, it is possible to perform direct comparison
of OpenMC with other codes using the same underlying nuclear data library.
The ACE format contains continuous-energy cross sections for the following types
of reactions: elastic scattering, fission (or first-chance fission,
second-chance fission, etc.), inelastic scattering, :math:`(n,xn)`,
:math:`(n,\gamma)`, and various other absorption reactions. For those reactions
with one or more neutrons in the exit channel, secondary angle and energy
distributions may be provided. In addition, fissionable nuclides have total,
prompt, and/or delayed :math:`\nu` as a function of energy and neutron precursor
distributions. Many nuclides also have probability tables to be used for
accurate treatment of self-shielding in the unresolved resonance range. For
bound scatterers, separate tables with :math:`S(\alpha,\beta,T)` scattering law
data can be used.
Energy Grid Methods
-------------------
The method by which continuous-energy cross sections for each nuclide in a
problem are stored as a function of energy can have a substantial effect on the
performance of a Monte Carlo simulation. Since the ACE format is based on
linearly-interpolable cross sections, each nuclide has cross sections tabulated
over a wide range of energies. Some nuclides may only have a few points
tabulated (e.g. H-1) whereas other nuclides may have hundreds or thousands of
points tabulated (e.g. U-238).
At each collision, it is necessary to sample the probability of having a
particular type of interaction whether it be elastic scattering, :math:`(n,2n)`,
level inelastic scattering, etc. This requires looking up the microscopic cross
sections for these reactions for each nuclide within the target material. Since
each nuclide has a unique energy grid, it would be necessary to search for the
appropriate index for each nuclide at every collision. This can become a very
time-consuming process, especially if there are many nuclides in a problem as
there would be for burnup calculations. Thus, there is a strong motive to
implement a method of reducing the number of energy grid searches in order to
speed up the calculation.
Logarithmic Mapping
+++++++++++++++++++
To speed up energy grid searches, OpenMC uses a `logarithmic mapping technique`_
to limit the range of energies that must be searched for each nuclide. The
entire energy range is divided up into equal-lethargy segments, and the bounding
energies of each segment are mapped to bounding indices on each of the nuclide
energy grids. By default, OpenMC uses 8000 equal-lethargy segments as
recommended by Brown.
Other Methods
+++++++++++++
A good survey of other energy grid techniques, including unionized energy grids,
can be found in a paper by Leppanen_.
.. _windowed_multipole:
Windowed Multipole Representation
---------------------------------
In addition to the usual pointwise representation of cross sections, OpenMC
offers support for a data format called windowed multipole (WMP). This data
format requires less memory than pointwise cross sections, and it allows
on-the-fly Doppler broadening to arbitrary temperature.
The multipole method was introduced by Hwang_ and the faster windowed multipole
method by Josey_. In the multipole format, cross section resonances are
represented by poles, :math:`p_j`, and residues, :math:`r_j`, in the complex
plane. The 0K cross sections in the resolved resonance region can be computed
by summing up a contribution from each pole:
.. math::
\sigma(E, T=0\text{K}) = \frac{1}{E} \sum_j \text{Re} \left[
\frac{i r_j}{\sqrt{E} - p_j} \right]
Assuming free-gas thermal motion, cross sections in the multipole form can be
analytically Doppler broadened to give the form:
.. math::
\sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[r_j
\sqrt{\pi} W_i(z) - \frac{r_j}{\sqrt{\pi}} C \left(\frac{p_j}{\sqrt{\xi}},
\frac{u}{2 \sqrt{\xi}}\right)\right]
.. math::
W_i(z) = \frac{i}{\pi} \int_{-\infty}^\infty dt \frac{e^{-t^2}}{z - t}
.. math::
C \left(\frac{p_j}{\sqrt{\xi}},\frac{u}{2 \sqrt{\xi}}\right) =
2p_j \int_0^\infty du' \frac{e^{-(u + u')^2/4\xi}}{p_j^2 - u'^2}
.. math::
z = \frac{\sqrt{E} - p_j}{2 \sqrt{\xi}}
.. math::
\xi = \frac{k_B T}{4 A}
.. math::
u = \sqrt{E}
where :math:`T` is the temperature of the resonant scatterer, :math:`k_B` is the
Boltzmann constant, :math:`A` is the mass of the target nucleus. For
:math:`E \gg k_b T/A`, the :math:`C` integral is approximately zero, simplifying
the cross section to:
.. math::
\sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[i r_j
\sqrt{\pi} W_i(z)\right]
The :math:`W_i` integral simplifies down to an analytic form. We define the
Faddeeva function, :math:`W` as:
.. math::
W(z) = e^{-z^2} \text{Erfc}(-iz)
Through this, the integral transforms as follows:
.. math::
\text{Im} (z) > 0 : W_i(z) = W(z)
.. math::
\text{Im} (z) < 0 : W_i(z) = -W(z^*)^*
There are freely available algorithms_ to evaluate the Faddeeva function. For
many nuclides, the Faddeeva function needs to be evaluated thousands of times to
calculate a cross section. To mitigate that computational cost, the WMP method
only evaluates poles within a certain energy "window" around the incident
neutron energy and accounts for the effect of resonances outside that window
with a polynomial fit. This polynomial fit is then broadened exactly. This
exact broadening can make up for the removal of the :math:`C` integral, as
typically at low energies, only curve fits are used.
Note that the implementation of WMP in OpenMC currently assumes that inelastic
scattering does not occur in the resolved resonance region. This is usually,
but not always the case. Future library versions may eliminate this issue.
The data format used by OpenMC to represent windowed multipole data is specified
in :ref:`io_data_wmp` with a publicly available `WMP library`_.
.. _temperature_treatment:
Temperature Treatment
---------------------
At the beginning of a simulation, OpenMC collects a list of all temperatures
that are present in a model. It then uses this list to determine what cross
sections to load. The data that is loaded depends on what temperature method has
been selected. There are three methods available:
:Nearest: Cross sections are loaded only if they are within a specified
tolerance of the actual temperatures in the model.
:Interpolation: Cross sections are loaded at temperatures that bound the actual
temperatures in the model. During transport, cross sections for
each material are calculated using statistical linear-linear
interpolation between bounding temperature. Suppose cross
sections are available at temperatures :math:`T_1, T_2, ...,
T_n` and a material is assigned a temperature :math:`T` where
:math:`T_i < T < T_{i+1}`. Statistical interpolation is applied
as follows: a uniformly-distributed random number of the unit
interval, :math:`\xi`, is sampled. If :math:`\xi < (T -
T_i)/(T_{i+1} - T_i)`, then cross sections at temperature
:math:`T_{i+1}` are used. Otherwise, cross sections at
:math:`T_i` are used. This procedure is applied for pointwise
cross sections in the resolved resonance range, unresolved
resonance probability tables, and :math:`S(\alpha,\beta)`
thermal scattering tables.
:Multipole: Resolved resonance cross sections are calculated on-the-fly using
techniques/data described in :ref:`windowed_multipole`. Cross
section data is loaded for a single temperature and is used in the
unresolved resonance and fast energy ranges.
----------------
Multi-Group Data
----------------
The data governing the interaction of particles with various nuclei or materials
are represented using a multi-group library format specific to the OpenMC code.
The format is described in the :ref:`mgxs_lib_spec`. The data itself can be
prepared via traditional paths or directly from a continuous-energy OpenMC
calculation by use of the Python API as is shown in the
:ref:`notebook_mg_mode_part_i` example notebook. This multi-group library
consists of meta-data (such as the energy group structure) and multiple `xsdata`
objects which contains the required microscopic or macroscopic multi-group data.
At a minimum, the library must contain the absorption cross section
(:math:`\sigma_{a,g}`) and a scattering matrix. If the problem is an eigenvalue
problem then all fissionable materials must also contain either a fission
production matrix cross section (:math:`\nu\sigma_{f,g\rightarrow g'}`), or both
the fission spectrum data (:math:`\chi_{g'}`) and a fission production cross
section (:math:`\nu\sigma_{f,g}`), or, . The library must also contain the
fission cross section (:math:`\sigma_{f,g}`) or the fission energy release cross
section (:math:`\kappa\sigma_{f,g}`) if the associated tallies are required by
the model using the library.
After a scattering collision, the outgoing particle experiences a change in both
energy and angle. The probability of a particle resulting in a given outgoing
energy group (`g'`) given a certain incoming energy group (`g`) is provided by
the scattering matrix data. The angular information can be expressed either via
Legendre expansion of the particle's change-in-angle (:math:`\mu`), a tabular
representation of the probability distribution function of :math:`\mu`, or a
histogram representation of the same PDF. The formats used to represent these
are described in the :ref:`mgxs_lib_spec`.
Unlike the continuous-energy mode, the multi-group mode does not explicitly
track particles produced from scattering multiplication (i.e., :math:`(n,xn)`)
reactions. These are instead accounted for by adjusting the weight of the
particle after the collision such that the correct total weight is maintained.
The weight adjustment factor is optionally provided by the `multiplicity` data
which is required to be provided in the form of a group-wise matrix. This data
is provided as a group-wise matrix since the probability of producing multiple
particles in a scattering reaction depends on both the incoming energy, `g`, and
the sampled outgoing energy, `g'`. This data represents the average number of
particles emitted from a scattering reaction, given a scattering reaction has
occurred:
.. math::
multiplicity_{g \rightarrow g'} = \frac{\nu_{scatter}\sigma_{s,g \rightarrow g'}}{
\sigma_{s,g \rightarrow g'}}
If this scattering multiplication information is not provided in the library
then no weight adjustment will be performed. This is equivalent to neglecting
any additional particles produced in scattering multiplication reactions.
However, this assumption will result in a loss of accuracy since the total
particle population would not be conserved. This reduction in accuracy due to
the loss in particle conservation can be mitigated by reducing the absorption
cross section as needed to maintain particle conservation. This adjustment can
be done when generating the library, or by OpenMC. To have OpenMC perform the
adjustment, the total cross section (:math:`\sigma_{t,g}`) must be provided.
With this information, OpenMC will then adjust the absorption cross section as
follows:
.. math::
\sigma_{a,g} = \sigma_{t,g} - \sum_{g'}\nu_{scatter}\sigma_{s,g \rightarrow g'}
The above method is the same as is usually done with most deterministic solvers.
Note that this method is less accurate than using the scattering multiplication
weight adjustment since simply reducing the absorption cross section does not
include any information about the outgoing energy of the particles produced in
these reactions.
All of the data discussed in this section can be provided to the code
independent of the particle's direction of motion (i.e., isotropic), or the data
can be provided as a tabular distribution of the polar and azimuthal particle
direction angles. The isotropic representation is the most commonly used,
however inaccuracies are to be expected especially near material interfaces
where a material has a very large cross sections relative to the other material
(as can be expected in the resonance range). The angular representation can be
used to minimize this error.
Finally, the above options for representing the physics do not have to be
consistent across the problem. The number of groups and the structure, however,
does have to be consistent across the data sets. That is to say that each
microscopic or macroscopic data set does not have to apply the same scattering
expansion, treatment of multiplicity or angular representation of the cross
sections. This allows flexibility for the model to use highly anisotropic
scattering information in the water while the fuel can be simulated with linear
or even isotropic scattering.
.. _logarithmic mapping technique:
https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf
.. _Hwang: http://www.ans.org/pubs/journals/nse/a_16381
.. _Josey: https://doi.org/10.1016/j.jcp.2015.08.013
.. _WMP Library: https://github.com/mit-crpg/WMP_Library
.. _MCNP: http://mcnp.lanl.gov
.. _Serpent: http://montecarlo.vtt.fi
.. _NJOY: http://t2.lanl.gov/codes.shtml
.. _ENDF/B data: http://www.nndc.bnl.gov/endf
.. _Leppanen: https://doi.org/10.1016/j.anucene.2009.03.019
.. _algorithms: http://ab-initio.mit.edu/wiki/index.php/Faddeeva_Package

View file

@ -0,0 +1,161 @@
.. _methods_eigenvalue:
=======================
Eigenvalue Calculations
=======================
An eigenvalue calculation, also referred to as a criticality calculation, is a
transport simulation wherein the source of neutrons includes a fissionable
material. Some common eigenvalue calculations include the simulation of nuclear
reactors, spent fuel pools, nuclear weapons, and other fissile systems. The
reason they are called *eigenvalue* calculations is that the transport equation
becomes an eigenvalue equation if a fissionable source is present since then the
source of neutrons will depend on the flux of neutrons itself. Eigenvalue
simulations using Monte Carlo methods are becoming increasingly common with the
advent of high-performance computing.
This section will explore the theory behind and implementation of eigenvalue
calculations in a Monte Carlo code.
.. _method-successive-generations:
--------------------------------
Method of Successive Generations
--------------------------------
The method used to converge on the fission source distribution in an eigenvalue
calculation, known as the method of successive generations, was first introduced
by [Lieberoth]_. In this method, a finite number of neutron histories,
:math:`N`, are tracked through their lifetime iteratively. If fission occurs,
rather than tracking the resulting fission neutrons, the spatial coordinates of
the fission site, the sampled outgoing energy and direction of the fission
neutron, and the weight of the neutron are stored for use in the subsequent
generation. In OpenMC, the array used for storing the fission site information
is called the *fission bank*. At the end of each fission generation, :math:`N`
source sites for the next generation must be randomly sampled from the :math:`M`
fission sites that were stored to ensure that the neutron population does not
grow exponentially. The sampled source sites are stored in an array called the
*source bank* and can be retrieved during the subsequent generation.
It's important to recognize that in the method of successive generations, we
must start with some assumption on how the fission source sites are distributed
since the distribution is not known *a priori*. Typically, a user will make a
guess as to what the distribution is -- this guess could be a uniform
distribution over some region of the geometry or simply a point
source. Fortunately, regardless of the choice of initial source distribution,
the method is guaranteed to converge to the true source distribution. Until the
source distribution converges, tallies should not be scored to since they will
otherwise include contributions from an unconverged source distribution.
The method by which the fission source iterations are parallelized can have a
large impact on the achievable parallel scaling. This topic is discussed at length
in :ref:`fission-bank-algorithms`.
-------------------------
Source Convergence Issues
-------------------------
Diagnosing Convergence with Shannon Entropy
-------------------------------------------
As discussed earlier, it is necessary to converge both :math:`k_{eff}` and the
source distribution before any tallies can begin. Moreover, the convergence rate
of the source distribution is in general slower than that of
:math:`k_{eff}`. One should thus examine not only the convergence of
:math:`k_{eff}` but also the convergence of the source distribution in order to
make decisions on when to start active batches.
However, the representation of the source distribution makes it a bit more
difficult to analyze its convergence. Since :math:`k_{eff}` is a scalar
quantity, it is easy to simply look at a line plot of :math:`k_{eff}` versus the
number of batches and this should give the user some idea about whether it has
converged. On the other hand, the source distribution at any given batch is a
finite set of coordinates in Euclidean space. In order to analyze the
convergence, we would either need to use a method for assessing convergence of
an N-dimensional quantity or transform our set of coordinates into a scalar
metric. The latter approach has been developed considerably over the last decade
and a method now commonly used in Monte Carlo eigenvalue calculations is to use
a metric called the `Shannon entropy`_, a concept borrowed from information
theory.
To compute the Shannon entropy of the source distribution, we first need to
discretize the source distribution rather than having a set of coordinates in
Euclidean space. This can be done by superimposing a structured mesh over the
geometry (containing at least all fissionable materials). Then, the fraction of
source sites that are present in each mesh element is counted:
.. math::
:label: fraction-source
S_i = \frac{\text{Source sites in $i$-th mesh element}}{\text{Total number of
source sites}}
The Shannon entropy is then computed as
.. math::
:label: shannon-entropy
H = - \sum_{i=1}^N S_i \log_2 S_i
where :math:`N` is the number of mesh elements. With equation
:eq:`shannon-entropy`, we now have a scalar metric that we can use to assess the
convergence of the source distribution by observing line plots of the Shannon
entropy versus the number of batches.
In recent years, researchers have started looking at ways of automatically
assessing source convergence to relieve the burden on the user of having to look
at plots of :math:`k_{eff}` and the Shannon entropy. A number of methods have
been proposed (see e.g. [Romano]_, [Ueki]_), but each of these is not without
problems.
---------------------------
Uniform Fission Site Method
---------------------------
Generally speaking, the variance of a Monte Carlo tally will be inversely
proportional to the number of events that score to the tally. In a reactor
problem, this implies that regions with low relative power density will have
higher variance that regions with high relative power density. One method to
circumvent the uneven distribution of relative errors is the uniform fission
site (UFS) method introduced by [Sutton]_. In this method, the portion of the
problem containing fissionable material is subdivided into a number of cells
(typically using a structured mesh). Rather than producing
.. math::
m = \frac{w}{k} \frac{\nu\Sigma_f}{\Sigma_t}
fission sites at each collision where :math:`w` is the weight of the neutron,
:math:`k` is the previous-generation estimate of the neutron multiplication
factor, :math:`\nu\Sigma_f` is the neutron production cross section, and
:math:`\Sigma_t` is the total cross section, in the UFS method we produce
.. math::
m_{UFS} = \frac{w}{k} \frac{\nu\Sigma_f}{\Sigma_t} \frac{v_i}{s_i}
fission sites at each collision where :math:`v_i` is the fraction of the total
volume occupied by cell :math:`i` and :math:`s_i` is the fraction of the fission
source contained in cell :math:`i`. To ensure that no bias is introduced, the
weight of each fission site stored in the fission bank is :math:`s_i/v_i` rather
than unity. By ensuring that the expected number of fission sites in each mesh
cell is constant, the collision density across all cells, and hence the variance
of tallies, is more uniform than it would be otherwise.
.. _Shannon entropy: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-06-3737.pdf
.. [Lieberoth] J. Lieberoth, "A Monte Carlo Technique to Solve the Static
Eigenvalue Problem of the Boltzmann Transport Equation," *Nukleonik*, **11**,
213-219 (1968).
.. [Romano] Paul K. Romano, "Application of the Stochastic Oscillator to Assess
Source Convergence in Monte Carlo Criticality Calculations,"
*Proc. International Conference on Mathematics, Computational Methods, and
Reactor Physics*, Saratoga Springs, New York (2009).
.. [Sutton] Daniel J. Kelly, Thomas M. Sutton, and Stephen C. Wilson, "MC21
Analysis of the Nuclear Energy Agency Monte Carlo Performance Benchmark
Problem," *Proc. PHYSOR 2012*, Knoxville, Tennessee, Apr. 15--20 (2012).
.. [Ueki] Taro Ueki, "On-the-Fly Judgments of Monte Carlo Fission Source
Convergence," *Trans. Am. Nucl. Soc.*, **98**, 512 (2008).

View file

@ -0,0 +1,149 @@
.. _methods_heating:
=============================
Heating and Energy Deposition
=============================
As particles traverse a problem, some portion of their energy is deposited at
collision sites. This energy is deposited when charged particles, including
electrons and recoil nuclei, undergo electromagnetic interactions with
surrounding electons and ions. The information describing how much energy
is deposited for a specific reaction is referred to as
"heating numbers" and can be computed using a program like NJOY with the
``heatr`` module.
These heating rate is the product of reaction-specific coefficients and
a reaction cross section
.. math::
H(E) = \phi(E)\sum_i\rho_i\sum_rk_{i, r}(E),
and has units energy per time, typically eV / s.
Here, :math:`k_{i, r}` are the KERMA (Kinetic Energy Release in Materials)
[Mack97]_ coefficients for reaction :math:`r` of isotope :math:`i`.
The KERMA coefficients have units energy :math:`\times` cross-section, e.g.
eV-barn, and can be used much like a reaction cross section for the purpose
of tallying energy deposition.
KERMA coefficients can be computed using the energy-balance method with
a nuclear data processing code like NJOY, which performs the following
iteration over all reactions :math:`r` for all isotopes :math:`i`
requested
.. math::
k_{i, r}(E) = \left(E + Q_{i, r} - \bar{E}_{i, r, n}
- \bar{E}_{i, r, \gamma}\right)\sigma_{i, r}(E),
removing the energy of neutral particles (neutrons and photons) that are
transported away from the reaction site :math:`\bar{E}`, and the reaction
:math:`Q` value.
-------
Fission
-------
During a fission event, there are potentially many secondary particles, and all
must be considered. The total energy released in a fission event is typically
broken up into the following categories:
- :math:`E_{fr}` - kinetic energy of fission fragments
- :math:`E_{n,p}` - energy of prompt fission neutrons
- :math:`E_{n,d}` - energy of delayed fission neutrons
- :math:`E_{\gamma,p}` - energy of prompt fission photons
- :math:`E_{\gamma,d}` - energy of delayed fission photons
- :math:`E_{\beta}` - energy of released :math:`\beta` particles
- :math:`E_{\nu}` - energy of neutrinos
These components are defined in MF=1,MT=458 data in a standard ENDF/B-6 formatted
file. All these quantities may depend upon incident neutron energy,
but this dependence is not shown to make the following demonstrations cleaner.
As neutrinos scarcely interact with matter, the recoverable energy from
fission is defined as
.. math::
E_r\equiv E_{fr} + E_{n,p} + E_{n, d} + E_{\gamma, p}
+ E_{\gamma, d} + E_{\beta}
Furthermore, the energy of the secondary neutrons and photons is given as
:math:`E_{n, p}` and :math:`E_{\gamma, p}`, respectively.
NJOY computes the fission KERMA coefficient using this energy-balance method to be
.. math::
k_{i, f}(E) = \left[E + Q(E) - \bar{E}(E)\right]\sigma_{i, f}(E)
= \left[E_{fr} + E_{\gamma, p}\right]\sigma_{i, j}(E)
.. note::
The energy from delayed neutrons and photons and beta particles is intentionally
left out from the NJOY calculations.
---------------------
OpenMC Implementation
---------------------
For fissile isotopes, OpenMC makes modifications to the heating reaction to
include all relevant components of fission energy release. These modifications
are made to the total heating reaction, MT=301. Breaking the total heating
KERMA into a fission and non-fission section, one can write
.. math::
k_i(E) = k_{i, nf}(E) + \left[E_{fr}(E) + E_{\gamma, p}\right]\sigma_{i, f}(E)
OpenMC seeks to modify the total heating data to include energy from
:math:`\beta` particles and, conditionally, delayed photons. This conditional
inclusion depends on the simulation mode: neutron transport, or coupled
neutron-photon transport. The heating due to fission is removed using MT=318
data, and then re-built using the desired components of fission energy release
from MF=1,MT=458 data.
Neutron Transport
-----------------
For this case, OpenMC instructs ``heatr`` to produce heating coefficients
assuming that energy from photons, :math:`E_{\gamma, p}` and
:math:`E_{\gamma, d}`, is deposited at the fission site.
Let :math:`N901` represent the total heating number returned from this ``heatr``
run with :math:`N918` reflecting fission heating computed from NJOY.
:math:`M901` represent the following modification
.. math::
M901_{i}(E)\equiv N901_{i}(E) - N918_{i}(E)
+ \left[E_{i, fr} + E_{i, \beta} + E_{i, \gamma, p}
+ E_{i, \gamma, d}\right]\sigma_{i, f}(E).
This modified heating data is stored as the MT=901 reaction and will be scored
if ``heating-local`` is included in :attr:`openmc.Tally.scores`.
Coupled neutron-photon transport
--------------------------------
Here, OpenMC instructs ``heatr`` to assume that energy from photons is not
deposited locally. However, the definitions provided in the NJOY manual
indicate that, regardless of this mode, the prompt photon energy is still
included in :math:`k_{i, f}`, and therefore must be manually removed.
Let :math:`N301` represent the total heating number returned from this
``heatr`` run and :math:`M301` be
.. math::
M301_{i}(E)\equiv N301_{i}(E) - N318_{i}(E)
+ \left[E_{i, fr}(E) + E_{i, \beta}(E)\right]\sigma_{i, f}(E).
This modified heating data is stored as the MT=301 reaction and will be scored
if ``heating`` is included in :attr:`openmc.Tally.scores`.
----------
References
----------
.. [Mack97] Abdou, M.A., Maynard, C.W., and Wright, R.Q. MACK: computer
program to calculate neutron energy release parameters (fluence-to-kerma
factors) and multigroup neutron reaction cross sections from nuclear data
in ENDF Format. Oak Ridge National Laboratory report ORNL-TM-3994.

View file

@ -0,0 +1,967 @@
.. _methods_geometry:
========
Geometry
========
---------------------------
Constructive Solid Geometry
---------------------------
OpenMC uses a technique known as `constructive solid geometry`_ (CSG) to build
arbitrarily complex three-dimensional models in Euclidean space. In a CSG model,
every unique object is described as the union and/or intersection of
*half-spaces* created by bounding `surfaces`_. Every surface divides all of
space into exactly two half-spaces. We can mathematically define a surface as a
collection of points that satisfy an equation of the form :math:`f(x,y,z) = 0`
where :math:`f(x,y,z)` is a given function. All coordinates for which
:math:`f(x,y,z) < 0` are referred to as the negative half-space (or simply the
*negative side*) and coordinates for which :math:`f(x,y,z) > 0` are referred to
as the positive half-space.
Let us take the example of a sphere centered at the point :math:`(x_0,y_0,z_0)`
with radius :math:`R`. One would normally write the equation of the sphere as
.. math::
:label: sphere-equation
(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = R^2
By subtracting the right-hand term from both sides of equation
:eq:`sphere-equation`, we can then write the surface equation for the sphere:
.. math::
:label: surface-equation-sphere
f(x,y,z) = (x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 - R^2 = 0
One can confirm that any point inside this sphere will correspond to
:math:`f(x,y,z) < 0` and any point outside the sphere will correspond to
:math:`f(x,y,z) > 0`.
In OpenMC, every surface defined by the user is assigned an integer to uniquely
identify it. We can then refer to either of the two half-spaces created by a
surface by a combination of the unique ID of the surface and a positive/negative
sign. Figure :num:`fig-halfspace` shows an example of an ellipse with unique ID 1
dividing space into two half-spaces.
.. _fig-halfspace:
.. figure:: ../_images/halfspace.svg
:align: center
:figclass: align-center
Example of an ellipse and its associated half-spaces.
References to half-spaces created by surfaces are used to define regions of
space of uniform composition, which are then assigned to *cells*. OpenMC allows
regions to be defined using union, intersection, and complement operators. As in
MCNP_, the intersection operator is implicit as doesn't need to be written in a
region specification. A defined region is then associated with a material
composition in a cell. Figure :num:`fig-union` shows an example of a cell region
defined as the intersection of an ellipse and two planes.
.. _fig-union:
.. figure:: ../_images/union.svg
:align: center
:figclass: align-center
The shaded region represents a cell bounded by three surfaces.
The ability to form regions based on bounding quadratic surfaces enables OpenMC
to model arbitrarily complex three-dimensional objects. In practice, one is
limited only by the different surface types available in OpenMC. The following
table lists the available surface types, the identifier used to specify them in
input files, the corresponding surface equation, and the input parameters needed
to fully define the surface.
.. table:: Surface types available in OpenMC.
+----------------------+------------+------------------------------+-------------------------+
| Surface | Identifier | Equation | Parameters |
+======================+============+==============================+=========================+
| Plane perpendicular | x-plane | :math:`x - x_0 = 0` | :math:`x_0` |
| to :math:`x`-axis | | | |
+----------------------+------------+------------------------------+-------------------------+
| Plane perpendicular | y-plane | :math:`y - y_0 = 0` | :math:`y_0` |
| to :math:`y`-axis | | | |
+----------------------+------------+------------------------------+-------------------------+
| Plane perpendicular | z-plane | :math:`z - z_0 = 0` | :math:`z_0` |
| to :math:`z`-axis | | | |
+----------------------+------------+------------------------------+-------------------------+
| Arbitrary plane | plane | :math:`Ax + By + Cz = D` | :math:`A\;B\;C\;D` |
+----------------------+------------+------------------------------+-------------------------+
| Infinite cylinder | x-cylinder | :math:`(y-y_0)^2 + (z-z_0)^2 | :math:`y_0\;z_0\;R` |
| parallel to | | = R^2` | |
| :math:`x`-axis | | | |
+----------------------+------------+------------------------------+-------------------------+
| Infinite cylinder | y-cylinder | :math:`(x-x_0)^2 + (z-z_0)^2 | :math:`x_0\;z_0\;R` |
| parallel to | | = R^2` | |
| :math:`y`-axis | | | |
+----------------------+------------+------------------------------+-------------------------+
| Infinite cylinder | z-cylinder | :math:`(x-x_0)^2 + (y-y_0)^2 | :math:`x_0\;y_0\;R` |
| parallel to | | = R^2` | |
| :math:`z`-axis | | | |
+----------------------+------------+------------------------------+-------------------------+
| Sphere | sphere | :math:`(x-x_0)^2 + (y-y_0)^2 | :math:`x_0 \; y_0 \; |
| | | + (z-z_0)^2 = R^2` | z_0 \; R` |
+----------------------+------------+------------------------------+-------------------------+
| Cone parallel to the | x-cone | :math:`(y-y_0)^2 + (z-z_0)^2 | :math:`x_0 \; y_0 \; |
| :math:`x`-axis | | = R^2(x-x_0)^2` | z_0 \; R^2` |
+----------------------+------------+------------------------------+-------------------------+
| Cone parallel to the | y-cone | :math:`(x-x_0)^2 + (z-z_0)^2 | :math:`x_0 \; y_0 \; |
| :math:`y`-axis | | = R^2(y-y_0)^2` | z_0 \; R^2` |
+----------------------+------------+------------------------------+-------------------------+
| Cone parallel to the | z-cone | :math:`(x-x_0)^2 + (y-y_0)^2 | :math:`x_0 \; y_0 \; |
| :math:`z`-axis | | = R^2(z-z_0)^2` | z_0 \; R^2` |
+----------------------+------------+------------------------------+-------------------------+
| General quadric | quadric | :math:`Ax^2 + By^2 + Cz^2 + | :math:`A \; B \; C \; D |
| surface | | Dxy + Eyz + Fxz + Gx + Hy + | \; E \; F \; G \; H \; |
| | | Jz + K` | J \; K` |
+----------------------+------------+------------------------------+-------------------------+
.. _universes:
Universes
---------
OpenMC supports universe-based geometry similar to the likes of MCNP_ and
Serpent_. This capability enables user to model any identical repeated
structures once and then fill them in various spots in the geometry. A
prototypical example of a repeated structure would be a fuel pin within a fuel
assembly or a fuel assembly within a core.
Each cell in OpenMC can either be filled with a normal material or with a
universe. If the cell is filled with a universe, only the region of the universe
that is within the defined boundaries of the parent cell will be present in the
geometry. That is to say, even though a collection of cells in a universe may
extend to infinity, not all of the universe will be "visible" in the geometry
since it will be truncated by the boundaries of the cell that contains it.
When a cell is filled with a universe, it is possible to specify that the
universe filling the cell should be rotated and translated. This is done through
the ``rotation`` and ``translation`` attributes on a cell (note though that
these can only be specified on a cell that is filled with another universe, not
a material).
It is not necessary to use or assign universes in a geometry if there are no
repeated structures. Any cell in the geometry that is not assigned to a
specified universe is automatically part of the *base universe* whose
coordinates are just the normal coordinates in Euclidean space.
Lattices
--------
Often times, repeated structures in a geometry occur in a regular pattern such
as a rectangular or hexagonal lattice. In such a case, it would be cumbersome
for a user to have to define the boundaries of each of the cells to be filled
with a universe. Thus, OpenMC provides a lattice capability similar to that used
in MCNP_ and Serpent_.
The implementation of lattices is similar in principle to universes --- instead
of a cell being filled with a universe, the user can specify that it is filled
with a finite lattice. The lattice is then defined by a two-dimensional array of
universes that are to fill each position in the lattice. A good example of the
use of lattices and universes can be seen in the OpenMC model for the `Monte
Carlo Performance benchmark`_.
------------------------------------------
Computing the Distance to Nearest Boundary
------------------------------------------
One of the most basic algorithms in any Monte Carlo code is determining the
distance to the nearest surface within a cell. Since each cell is defined by
the surfaces that bound it, if we compute the distance to all surfaces bounding
a cell, we can determine the nearest one.
With the possibility of a particle having coordinates on multiple levels
(universes) in a geometry, we must exercise care when calculating the distance
to the nearest surface. Each different level of geometry has a set of boundaries
with which the particle's direction of travel may intersect. Thus, it is
necessary to check the distance to the surfaces bounding the cell in each
level. This should be done starting the highest (most global) level going down
to the lowest (most local) level. That ensures that if two surfaces on different
levels are coincident, by default the one on the higher level will be selected
as the nearest surface. Although they are not explicitly defined, it is also
necessary to check the distance to surfaces representing lattice boundaries if a
lattice exists on a given level.
The following procedure is used to calculate the distance to each bounding
surface. Suppose we have a particle at :math:`(x_0,y_0,z_0)` traveling in the
direction :math:`u_0,v_0,w_0`. To find the distance :math:`d` to a surface
:math:`f(x,y,z) = 0`, we need to solve the equation:
.. math::
:label: dist-to-boundary-1
f(x_0 + du_0, y_0 + dv_0, z_0 + dw_0) = 0
If no solutions to equation :eq:`dist-to-boundary-1` exist or the only solutions
are complex, then the particle's direction of travel will not intersect the
surface. If the solution to equation :eq:`dist-to-boundary-1` is negative, this
means that the surface is "behind" the particle, i.e. if the particle continues
traveling in its current direction, it will not hit the surface. The complete
derivation for different types of surfaces used in OpenMC will be presented in
the following sections.
Since :math:`f(x,y,z)` in general is quadratic in :math:`x`, :math:`y`, and
:math:`z`, this implies that :math:`f(x_0 + du_0, y + dv_0, z + dw_0)` is
quadratic in :math:`d`. Thus we expect at most two real solutions to
:eq:`dist-to-boundary-1`. If no solutions to :eq:`dist-to-boundary-1` exist or
the only solutions are complex, then the particle's direction of travel will not
intersect the surface. If the solution to :eq:`dist-to-boundary-1` is negative,
this means that the surface is "behind" the particle, i.e. if the particle
continues traveling in its current direction, it will not hit the surface.
Once a distance has been computed to a surface, we need to check if it is closer
than previously-computed distances to surfaces. Unfortunately, we cannot just
use the minimum function because some of the calculated distances, which should
be the same in theory (e.g. coincident surfaces), may be slightly different due
to the use of floating-point arithmetic. Consequently, we should first check for
floating-point equality of the current distance calculated and the minimum found
thus far. This is done by checking if
.. math::
:label: fp-distance
\frac{| d - d_{min} |}{d_{min}} < \epsilon
where :math:`d` is the distance to a surface just calculated, :math:`d_{min}` is
the minimum distance found thus far, and :math:`\epsilon` is a small number. In
OpenMC, this parameter is set to :math:`\epsilon = 10^{-14}` since all floating
calculations are done on 8-byte floating point numbers.
Plane Perpendicular to an Axis
------------------------------
The equation for a plane perpendicular to, for example, the x-axis is simply
:math:`x - x_0 = 0`. As such, we need to solve :math:`x + du - x_0 = 0`. The
solution for the distance is
.. math::
:label: dist-xplane
d = \frac{x_0 - x}{u}
Note that if the particle's direction of flight is parallel to the x-axis,
i.e. :math:`u = 0`, the distance to the surface will be infinity. While the
example here was for a plane perpendicular to the x-axis, the same formula can
be applied for the surfaces :math:`y = y_0` and :math:`z = z_0`.
Generic Plane
-------------
The equation for a generic plane is :math:`Ax + By + Cz = D`. Thus, we need to
solve the equation :math:`A(x + du) + B(y + dv) + C(z + dw) = D`. The solution
to this equation for the distance is
.. math::
:label: dist-plane
d = \frac{D - Ax - By - Cz}{Au + Bv + Cw}
Again, we need to check whether the denominator is zero. If so, this means that
the particle's direction of flight is parallel to the plane and it will
therefore never hit the plane.
.. _cylinder_distance:
Cylinder Parallel to an Axis
----------------------------
The equation for a cylinder parallel to, for example, the x-axis is :math:`(y -
y_0)^2 + (z - z_0)^2 = R^2`. Thus, we need to solve :math:`(y + dv - y_0)^2 +
(z + dw - z_0)^2 = R^2`. Let us define :math:`\bar{y} = y - y_0` and
:math:`\bar{z} = z - z_0`. We then have
.. math::
:label: dist-xcylinder-1
(\bar{y} + dv)^2 + (\bar{z} + dw)^2 = R^2
Expanding equation :eq:`dist-xcylinder-1` and rearranging terms, we obtain
.. math::
:label: dist-xcylinder-2
(v^2 + w^2) d^2 + 2 (\bar{y}v + \bar{z}w) d + (\bar{y}^2 + \bar{z}^2 - R^2)
= 0
This is a quadratic equation for :math:`d`. To simplify notation, let us define
:math:`a = v^2 + w^2`, :math:`k = \bar{y}v + \bar{z}w`, and :math:`c =
\bar{y}^2 + \bar{z}^2 - R^2`. Thus, the distance is just the solution to
:math:`ad^2 + 2kd + c = 0`:
.. math::
:label: dist-xcylinder-3
d = \frac{-k \pm \sqrt{k^2 - ac}}{a}
A few conditions must be checked for. If :math:`a = 0`, this means the particle
is parallel to the cylinder and will thus never intersect it. Also, if
:math:`k^2 - ac < 0`, this means that both solutions to the quadratic are
complex. In physical terms, this means that the ray along which the particle is
traveling does not make any intersections with the cylinder.
If we do have intersections and :math:`c < 0`, this means that the particle is
inside the cylinder. Thus, one solution should be positive and one should be
negative. Clearly, the positive distance will occur when the sign on the
square root of the discriminant is positive since :math:`a > 0`.
If we have intersections and :math:`c > 0` this means that the particle is
outside the cylinder. Thus, the solutions to the quadratic are either both
positive or both negative. If they are both positive, the smaller (closer) one
will be the solution with a negative sign on the square root of the
discriminant.
The same equations and logic here can be used for cylinders that are parallel to
the y- or z-axis with appropriate substitution of constants.
Sphere
------
The equation for a sphere is :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 =
R^2`. Thus, we need to solve the equation
.. math::
:label: dist-sphere-1
(x + du - x_0)^2 + (y + dv - y_0)^2 + (z + dw - z_0)^2 = R^2
Let us define :math:`\bar{x} = x - x_0`, :math:`\bar{y} = y - y_0`, and
:math:`\bar{z} = z - z_0`. We then have
.. math::
:label: dist-sphere-2
(\bar{x} + du)^2 + (\bar{y} + dv)^2 + (\bar{z} - dw)^2 = R^2
Expanding equation :eq:`dist-sphere-2` and rearranging terms, we obtain
.. math::
:label: dist-sphere-3
d^2 + 2 (\bar{x}u + \bar{y}v + \bar{z}w) d + (\bar{x}^2 + \bar{y}^2 +
\bar{z}^2 - R^2) = 0
This is a quadratic equation for :math:`d`. To simplify notation, let us define
:math:`k = \bar{x}u + \bar{y}v + \bar{z}w` and :math:`c = \bar{x}^2 +
\bar{y}^2 + \bar{z}^2 - R^2`. Thus, the distance is just the solution to
:math:`d^2 + 2kd + c = 0`:
.. math::
:label: dist-sphere-4
d = -k \pm \sqrt{k^2 - c}
If the discriminant :math:`k^2 - c < 0`, this means that both solutions to the
quadratic are complex. In physical terms, this means that the ray along which
the particle is traveling does not make any intersections with the sphere.
If we do have intersections and :math:`c < 0`, this means that the particle is
inside the sphere. Thus, one solution should be positive and one should be
negative. The positive distance will occur when the sign on the square root of
the discriminant is positive. If we have intersections but :math:`c > 0` this
means that the particle is outside the sphere. The solutions to the quadratic
will then be either both positive or both negative. If they are both positive,
the smaller (closer) one will be the solution with a negative sign on the square
root of the discriminant.
Cone Parallel to an Axis
------------------------
The equation for a cone parallel to, for example, the x-axis is :math:`(y -
y_0)^2 + (z - z_0)^2 = R^2(x - x_0)^2`. Thus, we need to solve :math:`(y + dv -
y_0)^2 + (z + dw - z_0)^2 = R^2(x + du - x_0)^2`. Let us define :math:`\bar{x} =
x - x_0`, :math:`\bar{y} = y - y_0`, and :math:`\bar{z} = z - z_0`. We then have
.. math::
:label: dist-xcone-1
(\bar{y} + dv)^2 + (\bar{z} + dw)^2 = R^2(\bar{x} + du)^2
Expanding equation :eq:`dist-xcone-1` and rearranging terms, we obtain
.. math::
:label: dist-xcone-2
(v^2 + w^2 - R^2u^2) d^2 + 2 (\bar{y}v + \bar{z}w - R^2\bar{x}u) d +
(\bar{y}^2 + \bar{z}^2 - R^2\bar{x}^2) = 0
Defining the terms
.. math::
:label: dist-xcone-terms
a = v^2 + w^2 - R^2u^2
k = \bar{y}v + \bar{z}w - R^2\bar{x}u
c = \bar{y}^2 + \bar{z}^2 - R^2\bar{x}^2
we then have the simple quadratic equation :math:`ad^2 + 2kd + c = 0` which can
be solved as described in :ref:`cylinder_distance`.
General Quadric
---------------
The equation for a general quadric surface is :math:`Ax^2 + By^2 + Cz^2 + Dxy +
Eyz + Fxz + Gx + Hy + Jz + K = 0`. Thus, we need to solve the equation
.. math::
:label: dist-quadric-1
A(x+du)^2 + B(y+dv)^2 + C(z+dw)^2 + D(x+du)(y+dv) + E(y+dv)(z+dw) + \\
F(x+du)(z+dw) + G(x+du) + H(y+dv) + J(z+dw) + K = 0
Expanding equation :eq:`dist-quadric-1` and rearranging terms, we obtain
.. math::
:label: dist-quadric-2
d^2(uv + vw + uw) + 2d(Aux + Bvy + Cwx + (D(uv + vx) + E(vz + wy) + \\
F(wx + uz))/2) + (x(Ax + Dy) + y(By + Ez) + z(Cz + Fx)) = 0
Defining the terms
.. math::
:label: dist-quadric-terms
a = uv + vw + uw
k = Aux + Bvy + Cwx + (D(uv + vx) + E(vz + wy) + F(wx + uz))/2
c = x(Ax + Dy) + y(By + Ez) + z(Cz + Fx)
we then have the simple quadratic equation :math:`ad^2 + 2kd + c = 0` which can
be solved as described in :ref:`cylinder_distance`.
.. _find-cell:
----------------------------
Finding a Cell Given a Point
----------------------------
Another basic algorithm is to determine which cell contains a given point in the
global coordinate system, i.e. if the particle's position is :math:`(x,y,z)`,
what cell is it currently in. This is done in the following manner in
OpenMC. With the possibility of multiple levels of coordinates, we must perform
a recursive search for the cell. First, we start in the highest (most global)
universe, which we call the base universe, and loop over each cell within
that universe. For each cell, we check whether the specified point is inside the
cell using the algorithm described in :ref:`cell-contains`. If the cell is
filled with a normal material, the search is done and we have identified the
cell containing the point. If the cell is filled with another universe, we then
search all cells within that universe to see if any of them contain the
specified point. If the cell is filled with a lattice, the position within the
lattice is determined, and then whatever universe fills that lattice position is
recursively searched. The search ends once a cell containing a normal material
is found that contains the specified point.
.. _cell-contains:
----------------------
Finding a Lattice Tile
----------------------
If a particle is inside a lattice, its position inside the lattice must be
determined before assigning it to a cell. Throughout this section, the
volumetric units of the lattice will be referred to as "tiles". Tiles are
identified by thier indices, and the process of discovering which tile contains
the particle is referred to as "indexing".
Rectilinear Lattice Indexing
----------------------------
Indices are assigned to tiles in a rectilinear lattice based on the tile's
position along the :math:`x`, :math:`y`, and :math:`z` axes. Figure
:num:`fig-rect-lat` maps the indices for a 2D lattice. The indices, (1, 1),
map to the lower-left tile. (5, 1) and (5, 5) map to the lower-right and
upper-right tiles, respectively.
.. _fig-rect-lat:
.. figure:: ../_images/rect_lat.svg
:align: center
:figclass: align-center
:width: 400px
Rectilinear lattice tile indices.
In general, a lattice tile is specified by the three indices,
:math:`(i_x, i_y, i_z)`. If a particle's current coordinates are
:math:`(x, y, z)` then the indices can be determined from these formulas:
.. math::
:label: rect_indexing
i_x = \left \lceil \frac{x - x_0}{p_0} \right \rceil
i_y = \left \lceil \frac{y - y_0}{p_1} \right \rceil
i_z = \left \lceil \frac{z - z_0}{p_2} \right \rceil
where :math:`(x_0, y_0, z_0)` are the coordinates to the lower-left-bottom
corner of the lattice, and :math:`p_0, p_1, p_2` are the pitches along the
:math:`x`, :math:`y`, and :math:`z` axes, respectively.
.. _hexagonal_indexing:
Hexagonal Lattice Indexing
--------------------------
A skewed coordinate system is used for indexing hexagonal lattice tiles.
Rather than a :math:`y`-axis, another axis is used that is rotated 30 degrees
counter-clockwise from the :math:`y`-axis. This axis is referred to as the
:math:`\alpha`-axis. Figure :num:`fig-hex-lat` shows how 2D hexagonal tiles
are mapped with the :math:`(x, \alpha)` basis. In this system, (0, 0) maps to
the center tile, (0, 2) to the top tile, and (2, -1) to the middle tile on the
right side.
.. _fig-hex-lat:
.. figure:: ../_images/hex_lat.svg
:align: center
:figclass: align-center
:width: 400px
Hexagonal lattice tile indices.
Unfortunately, the indices cannot be determined with one simple formula as
before. Indexing requires a two-step process, a coarse step which determines a
set of four tiles that contains the particle and a fine step that determines
which of those four tiles actually contains the particle.
In the first step, indices are found using these formulas:
.. math::
:label: hex_indexing
\alpha = -\frac{x}{\sqrt{3}} + y
i_x^* = \left \lfloor \frac{x}{p_0 \sqrt{3} / 2} \right \rfloor
i_\alpha^* = \left \lfloor \frac{\alpha}{p_0} \right \rfloor
where :math:`p_0` is the lattice pitch (in the :math:`x`-:math:`y` plane). The
true index of the particle could be :math:`(i_x^*, i_\alpha^*)`,
:math:`(i_x^* + 1, i_\alpha^*)`, :math:`(i_x^*, i_\alpha^* + 1)`, or
:math:`(i_x^* + 1, i_\alpha^* + 1)`.
The second step selects the correct tile from that neighborhood of 4. OpenMC
does this by calculating the distance between the particle and the centers of
each of the 4 tiles, and then picking the closest tile. This works because
regular hexagonal tiles form a Voronoi tessellation which means that all of the
points within a tile are closest to the center of that same tile.
Indexing along the :math:`z`-axis uses the same method from rectilinear
lattices, i.e.
.. math::
:label: hex_indexing_z
i_z = \left \lceil \frac{z - z_0}{p_2} \right \rceil
----------------------------------------
Determining if a Coordinate is in a Cell
----------------------------------------
To determine which cell a particle is in given its coordinates, we need to be
able to check whether a given cell contains a point. The algorithm for
determining if a cell contains a point is as follows. For each surface that
bounds a cell, we determine the particle's sense with respect to the surface. As
explained earlier, if we have a point :math:`(x_0,y_0,z_0)` and a surface
:math:`f(x,y,z) = 0`, the point is said to have negative sense if
:math:`f(x_0,y_0,z_0) < 0` and positive sense if :math:`f(x_0,y_0,z_0) > 0`. If
for all surfaces, the sense of the particle with respect to the surface matches
the specified sense that defines the half-space within the cell, then the point
is inside the cell. Note that this algorithm works only for *simple cells*
defined as intersections of half-spaces.
It may help to illustrate this algorithm using a simple example. Let's say we
have a cell defined as
.. code-block:: xml
<surface id="1" type="sphere" coeffs="0 0 0 10" />
<surface id="2" type="x-plane" coeffs="-3" />
<surface id="3" type="y-plane" coeffs="2" />
<cell id="1" surfaces="-1 2 -3" />
This means that the cell is defined as the intersection of the negative half
space of a sphere, the positive half-space of an x-plane, and the negative
half-space of a y-plane. Said another way, any point inside this cell must
satisfy the following equations
.. math::
:label: cell-contains-example
x^2 + y^2 + z^2 - 10^2 < 0 \\
x - (-3) > 0 \\
y - 2 < 0
In order to determine if a point is inside the cell, we would substitute its
coordinates into equation :eq:`cell-contains-example`. If the inequalities are
satisfied, than the point is indeed inside the cell.
--------------------------
Handling Surface Crossings
--------------------------
A particle will cross a surface if the distance to the nearest surface is closer
than the distance sampled to the next collision. A number of things happen when
a particle hits a surface. First, we need to check if a non-transmissive
boundary condition has been applied to the surface. If a vacuum boundary
condition has been applied, the particle is killed and any surface current
tallies are scored to as needed. If a reflective boundary condition has been
applied to the surface, surface current tallies are scored to and then the
particle's direction is changed according to the procedure in :ref:`reflection`.
Note that the white boundary condition can be considered as the special case of
reflective boundary condition, where the same processing method will be applied to
deal with the surface current tallies scoring, except for determining the
changes of particle's direction according to the procedures in :ref:`white`.
Next, we need to determine what cell is beyond the surface in the direction of
travel of the particle so that we can evaluate cross sections based on its
material properties. At initialization, a list of neighboring cells is created
for each surface in the problem as described in :ref:`neighbor-lists`. The
algorithm outlined in :ref:`find-cell` is used to find a cell containing the
particle with one minor modification; rather than searching all cells in the
base universe, only the list of neighboring cells is searched. If this search is
unsuccessful, then a search is done over every cell in the base universe.
.. _neighbor-lists:
-----------------------
Building Neighbor Lists
-----------------------
After the geometry has been loaded and stored in memory from an input file,
OpenMC builds a list for each surface containing any cells that are bounded by
that surface in order to speed up processing of surface crossings. The algorithm
to build these lists is as follows. First, we loop over all cells in the
geometry and count up how many times each surface appears in a specification as
bounding a negative half-space and bounding a positive half-space. Two arrays
are then allocated for each surface, one that lists each cell that contains the
negative half-space of the surface and one that lists each cell that contains
the positive half-space of the surface. Another loop is performed over all cells
and the neighbor lists are populated for each surface.
.. _reflection:
------------------------------
Reflective Boundary Conditions
------------------------------
If the velocity of a particle is :math:`\mathbf{v}` and it crosses a surface of
the form :math:`f(x,y,z) = 0` with a reflective boundary condition, it can be
shown based on geometric arguments that the velocity vector will then become
.. math::
:label: reflection-v
\mathbf{v'} = \mathbf{v} - 2 (\mathbf{v} \cdot \hat{\mathbf{n}})
\hat{\mathbf{n}}
where :math:`\hat{\mathbf{n}}` is a unit vector normal to the surface at the
point of the surface crossing. The rationale for this can be understood by
noting that :math:`(\mathbf{v} \cdot \hat{\mathbf{n}}) \hat{\mathbf{n}}` is the
projection of the velocity vector onto the normal vector. By subtracting two
times this projection, the velocity is reflected with respect to the surface
normal. Since the magnitude of the velocity of the particle will not change as
it undergoes reflection, we can work with the direction of the particle instead,
simplifying equation :eq:`reflection-v` to
.. math::
:label: reflection-omega
\mathbf{\Omega'} = \mathbf{\Omega} - 2 (\mathbf{\Omega} \cdot
\hat{\mathbf{n}}) \hat{\mathbf{n}}
where :math:`\mathbf{v} = || \mathbf{v} || \mathbf{\Omega}`. The direction of
the surface normal will be the gradient of the surface at the point of crossing,
i.e. :math:`\mathbf{n} = \nabla f(x,y,z)`. Substituting this into equation
:eq:`reflection-omega`, we get
.. math::
:label: reflection-omega-2
\mathbf{\Omega'} = \mathbf{\Omega} - \frac{2 ( \mathbf{\Omega} \cdot \nabla
f )}{|| \nabla f ||^2} \nabla f
If we write the initial and final directions in terms of their vector
components, :math:`\mathbf{\Omega} = (u,v,w)` and :math:`\mathbf{\Omega'} = (u',
v', w')`, this allows us to represent equation :eq:`reflection-omega` as a
series of equations:
.. math::
:label: reflection-system
u' = u - \frac{2 ( \mathbf{\Omega} \cdot \nabla f )}{|| \nabla f ||^2}
\frac{\partial f}{\partial x} \\
v' = v - \frac{2 ( \mathbf{\Omega} \cdot \nabla f )}{|| \nabla f ||^2}
\frac{\partial f}{\partial y} \\
w' = w - \frac{2 ( \mathbf{\Omega} \cdot \nabla f )}{|| \nabla f ||^2}
\frac{\partial f}{\partial z}
One can then use equation :eq:`reflection-system` to develop equations for
transforming a particle's direction given the equation of the surface.
Plane Perpendicular to an Axis
------------------------------
For a plane that is perpendicular to an axis, the rule for reflection is almost
so simple that no derivation is needed at all. Nevertheless, we will proceed
with the derivation to confirm that the rules of geometry agree with our
intuition. The gradient of the surface :math:`f(x,y,z) = x - x_0 = 0` is simply
:math:`\nabla f = (1, 0, 0)`. Note that this vector is already normalized,
i.e. :math:`|| \nabla f || = 1`. The second two equations in
:eq:`reflection-system` tell us that :math:`v` and :math:`w` do not change and
the first tell us that
.. math::
:label: reflection-xplane
u' = u - 2u = -u
We see that reflection for a plane perpendicular to an axis only entails
negating the directional cosine for that axis.
Generic Plane
-------------
A generic plane has the form :math:`f(x,y,z) = Ax + By + Cz - D = 0`. Thus, the
gradient to the surface is simply :math:`\nabla f = (A,B,C)` whose norm squared
is :math:`A^2 + B^2 + C^2`. This implies that
.. math::
:label: reflection-plane-constant
\frac{2 (\mathbf{\Omega} \cdot \nabla f)}{|| \nabla f ||^2} = \frac{2(Au +
Bv + Cw)}{A^2 + B^2 + C^2}
Substituting equation :eq:`reflection-plane-constant` into equation
:eq:`reflection-system` gives us the form of the solution. For example, the
x-component of the reflected direction will be
.. math::
:label: reflection-plane
u' = u - \frac{2A(Au + Bv + Cw)}{A^2 + B^2 + C^2}
Cylinder Parallel to an Axis
----------------------------
A cylinder parallel to, for example, the x-axis has the form :math:`f(x,y,z) =
(y - y_0)^2 + (z - z_0)^2 - R^2 = 0`. Thus, the gradient to the surface is
.. math::
:label: reflection-cylinder-grad
\nabla f = 2 \left ( \begin{array}{c} 0 \\ y - y_0 \\ z - z_0 \end{array}
\right ) = 2 \left ( \begin{array}{c} 0 \\ \bar{y} \\ \bar{z} \end{array}
\right )
where we have introduced the constants :math:`\bar{y}` and
:math:`\bar{z}`. Taking the square of the norm of the gradient, we find that
.. math::
:label: reflection-cylinder-norm
|| \nabla f ||^2 = 4 \bar{y}^2 + 4 \bar{z}^2 = 4 R^2
This implies that
.. math::
:label: reflection-cylinder-constant
\frac{2 (\mathbf{\Omega} \cdot \nabla f)}{|| \nabla f ||^2} =
\frac{\bar{y}v + \bar{z}w}{R^2}
Substituting equations :eq:`reflection-cylinder-constant` and
:eq:`reflection-cylinder-grad` into equation :eq:`reflection-system` gives us
the form of the solution. In this case, the x-component will not change. The y-
and z-components of the reflected direction will be
.. math::
:label: reflection-cylinder
v' = v - \frac{2 ( \bar{y}v + \bar{z}w ) \bar{y}}{R^2} \\
w' = w - \frac{2 ( \bar{y}v + \bar{z}w ) \bar{z}}{R^2}
Sphere
------
The surface equation for a sphere has the form :math:`f(x,y,z) = (x - x_0)^2 +
(y - y_0)^2 + (z - z_0)^2 - R^2 = 0`. Thus, the gradient to the surface is
.. math::
:label: reflection-sphere-grad
\nabla f = 2 \left ( \begin{array}{c} x - x_0 \\ y - y_0 \\ z - z_0
\end{array} \right ) = 2 \left ( \begin{array}{c} \bar{x} \\ \bar{y} \\
\bar{z} \end{array} \right )
where we have introduced the constants :math:`\bar{x}, \bar{y}, \bar{z}`. Taking
the square of the norm of the gradient, we find that
.. math::
:label: reflection-sphere-norm
|| \nabla f ||^2 = 4 \bar{x}^2 + 4 \bar{y}^2 + 4 \bar{z}^2 = 4 R^2
This implies that
.. math::
:label: reflection-sphere-constant
\frac{2 (\mathbf{\Omega} \cdot \nabla f)}{|| \nabla f ||^2} =
\frac{\bar{x}u + \bar{y}v + \bar{z}w}{R^2}
Substituting equations :eq:`reflection-sphere-constant` and
:eq:`reflection-sphere-grad` into equation :eq:`reflection-system` gives us the
form of the solution:
.. math::
:label: reflection-sphere
u' = u - \frac{2 ( \bar{x}u + \bar{y}v + \bar{z}w ) \bar{x} }{R^2} \\
v' = v - \frac{2 ( \bar{x}u + \bar{y}v + \bar{z}w ) \bar{y} }{R^2} \\
w' = w - \frac{2 ( \bar{x}u + \bar{y}v + \bar{z}w ) \bar{z} }{R^2}
Cone Parallel to an Axis
------------------------
A cone parallel to, for example, the z-axis has the form :math:`f(x,y,z) = (x -
x_0)^2 + (y - y_0)^2 - R^2(z - z_0)^2 = 0`. Thus, the gradient to the surface is
.. math::
:label: reflection-cone-grad
\nabla f = 2 \left ( \begin{array}{c} x - x_0 \\ y - y_0 \\ -R^2(z - z_0)
\end{array} \right ) = 2 \left ( \begin{array}{c} \bar{x} \\ \bar{y} \\
-R^2\bar{z} \end{array} \right )
where we have introduced the constants :math:`\bar{x}`, :math:`\bar{y}`, and
:math:`\bar{z}`. Taking the square of the norm of the gradient, we find that
.. math::
:label: reflection-cone-norm
|| \nabla f ||^2 = 4 \bar{x}^2 + \bar{y}^2 + 4 R^4 \bar{z}^2 \\ = 4 R^2
\bar{z}^2 + 4 R^4 \bar{z}^2 \\ = 4 R^2 (1 + R^2) \bar{z}^2
This implies that
.. math::
:label: reflection-cone-constant
\frac{2 (\mathbf{\Omega} \cdot \nabla f)}{|| \nabla f ||^2} =
\frac{\bar{x}u + \bar{y}v - R^2\bar{z}w}{R^2 (1 + R^2) \bar{z}^2}
Substituting equations :eq:`reflection-cone-constant` and
:eq:`reflection-cone-grad` into equation :eq:`reflection-system` gives us the
form of the solution:
.. math::
:label: reflection-cone
u' = u - \frac{2 (\bar{x}u + \bar{y}v - R^2\bar{z}w) \bar{x}}{R^2 (1 + R^2)
\bar{z}^2}
v' = v - \frac{2 (\bar{x}u + \bar{y}v - R^2\bar{z}w) \bar{y}}{R^2 (1 + R^2)
\bar{z}^2}
w' = w + \frac{2 (\bar{x}u + \bar{y}v - R^2\bar{z}w)}{R^2 (1 + R^2) \bar{z}}
General Quadric
---------------
A general quadric surface has the form :math:`f(x,y,z) = Ax^2 + By^2 + Cz^2 +
Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0`. Thus, the gradient to the surface is
.. math::
:label: reflection-quadric-grad
\nabla f = \left ( \begin{array}{c} 2Ax + Dy + Fz + G \\ 2By + Dx + Ez + H
\\ 2Cz + Ey + Fx + J \end{array} \right ).
.. _white:
-------------------------
White Boundary Conditions
-------------------------
The `white boundary condition <https://doi.org/10.1016/j.anucene.2019.05.006>`_
is usually applied in deterministic codes, where the particle will hit the
surface and travel back with isotropic angular distribution. The change in
particle's direction is sampled from a cosine distribution instead of uniform.
Figure :num:`fig-cosine-dist` shows an example of cosine-distribution reflection
on the arbitrary surface relative to the surface normal.
.. _fig-cosine-dist:
.. figure:: ../_images/cosine-dist.png
:align: center
:figclass: align-center
Cosine-distribution reflection on an arbitrary surface.
The probability density function (pdf) for the reflected direction can be
expressed as follows,
.. math::
:label: white-reflection-pdf
f(\mu, \phi) d\mu d\phi = \frac{\mu}{\pi} d\mu d\phi = 2\mu d\mu \frac{d\phi}{2\pi}
where :math:`\mu = \cos \theta` is the cosine of the polar angle between
reflected direction and the normal to the surface; and :math:`\theta` is the
azimuthal angle in :math:`[0,2\pi]`. We can separate the multivariate
probability density into two separate univariate density functions, one for
the cosine of the polar angle,
.. math::
:label: white-reflection-cosine
f(\mu) = 2\mu
and one for the azimuthal angle,
.. math::
:label: white-reflection-uniform
f(\phi) = \frac{1}{2\pi}.
Each of these density functions can be sampled by analytical inversion of the
cumulative distribution distribution, resulting in the following sampling
scheme:
.. math::
:label: white-reflection-sqrt-prn
\mu = \sqrt{\xi_1} \\
\phi = 2\pi\xi_2
where :math:`\xi_1` and :math:`\xi_2` are uniform random numbers on
:math:`[0,1)`. With the sampled values of :math:`\mu` and :math:`\phi`, the
final reflected direction vector can be computed via rotation of the surface
normal using the equations from :ref:`transform-coordinates`. The white boundary
condition can be applied to any kind of surface, as long as the normal to the
surface is known as in :ref:`reflection`.
.. _constructive solid geometry: https://en.wikipedia.org/wiki/Constructive_solid_geometry
.. _surfaces: https://en.wikipedia.org/wiki/Surface
.. _MCNP: http://mcnp.lanl.gov
.. _Serpent: http://montecarlo.vtt.fi
.. _Monte Carlo Performance benchmark: https://github.com/mit-crpg/benchmarks/tree/master/mc-performance/openmc

View file

@ -0,0 +1,21 @@
.. _methods:
======================
Theory and Methodology
======================
.. toctree::
:numbered:
:maxdepth: 3
introduction
geometry
cross_sections
random_numbers
neutron_physics
photon_physics
tallies
eigenvalue
parallelization
cmfd
energy_deposition

View file

@ -0,0 +1,147 @@
.. _methods_introduction:
============
Introduction
============
The physical process by which a population of particles evolves over time is
governed by a number of `probability distributions`_. For instance, given a
particle traveling through some material, there is a probability distribution
for the distance it will travel until its next collision (an exponential
distribution). Then, when it collides with a nucleus, there is an associated
probability of undergoing each possible reaction with that nucleus. While the
behavior of any single particle is unpredictable, the average behavior of a
large population of particles originating from the same source is well defined.
If the probability distributions that govern the transport of a particle are
known, the process of single particles randomly streaming and colliding with
nuclei can be simulated directly with computers using a technique known as
`Monte Carlo`_ simulation. If enough particles are simulated this way, the
average behavior can be determined to within arbitrarily small statistical
error, a fact guaranteed by the `central limit theorem`_. To be more precise,
the central limit theorem tells us that the variance of the sample mean of some
physical parameter being estimated with Monte Carlo will be inversely
proportional to the number of realizations, i.e. the number of particles we
simulate:
.. math::
\sigma^2 \propto \frac{1}{N}.
where :math:`\sigma^2` is the variance of the sample mean and :math:`N` is the
number of realizations.
------------------------
Overview of Program Flow
------------------------
OpenMC performs a Monte Carlo simulation one particle at a time -- at no point
is more than one particle being tracked on a single program instance. Before any
particles are tracked, the problem must be initialized. This involves the
following steps:
- Read input files and building data structures for the geometry, materials,
tallies, and other associated variables.
- Initialize the pseudorandom number generator.
- Read the contiuous-energy or multi-group cross section data specified in
the problem.
- If using a special energy grid treatment such as a union energy grid or
lethargy bins, that must be initialized as well in a continuous-energy
problem.
- In a multi-group problem, individual nuclide cross section information is
combined to produce material-specific cross section data.
- In a fixed source problem, source sites are sampled from the specified
source. In an eigenvalue problem, source sites are sampled from some
initial source distribution or from a source file. The source sites
consist of coordinates, a direction, and an energy.
Once initialization is complete, the actual transport simulation can
proceed. The life of a single particle will proceed as follows:
1. The particle's properties are initialized from a source site previously
sampled.
2. Based on the particle's coordinates, the current cell in which the particle
resides is determined.
3. The energy-dependent cross sections for the material that the particle is
currently in are determined. Note that this includes the total
cross section, which is not pre-calculated.
4. The distance to the nearest boundary of the particle's cell is determined
based on the bounding surfaces to the cell.
5. The distance to the next collision is sampled. If the total material
cross section is :math:`\Sigma_t`, this can be shown to be
.. math::
d = -\frac{\ln \xi}{\Sigma_t}
where :math:`\xi` is a `pseudorandom number`_ sampled from a uniform
distribution on :math:`[0,1)`.
6. If the distance to the nearest boundary is less than the distance to the next
collision, the particle is moved forward to this boundary. Then, the process
is repeated from step 2. If the distance to collision is closer than the
distance to the nearest boundary, then the particle will undergo a collision.
7. The material at the collision site may consist of multiple nuclides. First,
the nuclide with which the collision will happen is sampled based on the
total cross sections. If the total cross section of material :math:`i` is
:math:`\Sigma_{t,i}`, then the probability that any nuclide is sampled is
.. math::
P(i) = \frac{\Sigma_{t,i}}{\Sigma_t}.
Note that the above selection of collided nuclide only applies to
continuous-energy simulations as multi-group simulations use nuclide
data which has already been combined in to material-specific data.
8. Once the specific nuclide is sampled, the random samples a reaction for
that nuclide based on the microscopic cross sections. If the microscopic
cross section for some reaction :math:`x` is :math:`\sigma_x` and the total
microscopic cross section for the nuclide is :math:`\sigma_t`, then the
probability that reaction :math:`x` will occur is
.. math::
P(x) = \frac{\sigma_x}{\sigma_t}.
Since multi-group simulations use material-specific data, the above is
performed with those material multi-group cross sections (i.e.,
macroscopic cross sections for the material) instead of microscopic
cross sections for the nuclide).
9. If the sampled reaction is elastic or inelastic scattering, the outgoing
energy and angle is sampled from the appropriate distribution. In
continuous-energy simulation, reactions of type :math:`(n,xn)` are treated
as scattering and any additional particles which may be created are added
to a secondary particle bank to be tracked later. In a multi-group
simulation, this secondary bank is not used but the particle weight is
increased accordingly. The original particle then continues from step 3.
If the reaction is absorption or fission, the particle dies and if
necessary, fission sites are created and stored in the fission bank.
After all particles have been simulated, there are a few final tasks that must
be performed before the run is finished. This include the following:
- With the accumulated sum and sum of squares for each tally, the sample mean
and its variance is calculated.
- All tallies and other results are written to disk.
- If requested, a source file is written to disk.
- Dynamically-allocated memory should be freed.
.. _probability distributions: https://en.wikipedia.org/wiki/Probability_distribution
.. _Monte Carlo: https://en.wikipedia.org/wiki/Monte_Carlo_method
.. _central limit theorem: https://en.wikipedia.org/wiki/Central_limit_theorem
.. _pseudorandom number: https://en.wikipedia.org/wiki/Pseudorandom_number_generator

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,650 @@
.. _methods_parallel:
===============
Parallelization
===============
Due to the computationally-intensive nature of Monte Carlo methods, there has
been an ever-present interest in parallelizing such simulations. Even in the
`first paper`_ on the Monte Carlo method, John Metropolis and Stanislaw Ulam
recognized that solving the Boltzmann equation with the Monte Carlo method could
be done in parallel very easily whereas the deterministic counterparts for
solving the Boltzmann equation did not offer such a natural means of
parallelism. With the introduction of `vector computers`_ in the early 1970s,
general-purpose parallel computing became a reality. In 1972, Troubetzkoy et
al. designed a Monte Carlo code to be run on the first vector computer, the
ILLIAC-IV [Troubetzkoy]_. The general principles from that work were later
refined and extended greatly through the `work of Forrest Brown`_ in the
1980s. However, as Brown's work shows, the `single-instruction multiple-data`_
(SIMD) parallel model inherent to vector processing does not lend itself to the
parallelism on particles in Monte Carlo simulations. Troubetzkoy et
al. recognized this, remarking that "the order and the nature of these physical
events have little, if any, correlation from history to history," and thus
following independent particle histories simultaneously using a SIMD model is
difficult.
The difficulties with vector processing of Monte Carlo codes led to the adoption
of the `single program multiple data`_ (SPMD) technique for parallelization. In
this model, each different process tracks a particle independently of other
processes, and between fission source generations the processes communicate data
through a `message-passing interface`_. This means of parallelism was enabled by
the introduction of message-passing standards in the late 1980s and early 1990s
such as PVM_ and MPI_. The SPMD model proved much easier to use in practice and
took advantage of the inherent parallelism on particles rather than
instruction-level parallelism. As a result, it has since become ubiquitous for
Monte Carlo simulations of transport phenomena.
Thanks to the particle-level parallelism using SPMD techniques, extremely high
parallel efficiencies could be achieved in Monte Carlo codes. Until the last
decade, even the most demanding problems did not require transmitting large
amounts of data between processors, and thus the total amount of time spent on
communication was not significant compared to the amount of time spent on
computation. However, today's computing power has created a demand for
increasingly large and complex problems, requiring a greater number of particles
to obtain decent statistics (and convergence in the case of criticality
calculations). This results in a correspondingly higher amount of communication,
potentially degrading the parallel efficiency. Thus, while Monte Carlo
simulations may seem `embarrassingly parallel`_, obtaining good parallel scaling
with large numbers of processors can be quite difficult to achieve in practice.
.. _fission-bank-algorithms:
-----------------------
Fission Bank Algorithms
-----------------------
Master-Slave Algorithm
----------------------
Monte Carlo particle transport codes commonly implement a SPMD model by having
one master process that controls the scheduling of work and the remaining
processes wait to receive work from the master, process the work, and then send
their results to the master at the end of the simulation (or a source iteration
in the case of an eigenvalue calculation). This idea is illustrated in
:ref:`figure-master-slave`.
.. _figure-master-slave:
.. figure:: ../_images/master-slave.png
:align: center
:figclass: align-center
Communication pattern in master-slave algorithm.
Eigenvalue calculations are slightly more difficult to parallelize than fixed
source calculations since it is necessary to converge on the fission source
distribution and eigenvalue before tallying. In the
:ref:`method-successive-generations`, to ensure that the results are
reproducible, one must guarantee that the process by which fission sites are
randomly sampled does not depend on the number of processors. What is typically
done is the following:
1. Each compute node sends_ its fission bank sites to a master process;
2. The master process sorts or orders the fission sites based on a unique
identifier;
3. The master process samples :math:`N` fission sites from the ordered array
of :math:`M` sites; and
4. The master process broadcasts_ all the fission sites to the compute
nodes.
The first and last steps of this process are the major sources of communication
overhead between cycles. Since the master process must receive :math:`M` fission
sites from the compute nodes, the first step is necessarily serial. This step
can be completed in :math:`O(M)` time. The broadcast step can benefit from
parallelization through a tree-based algorithm. Despite this, the communication
overhead is still considerable.
To see why this is the case, it is instructive to look at a hypothetical
example. Suppose that a calculation is run with :math:`N = 10,000,000` neutrons
across 64 compute nodes. On average, :math:`M = 10,000,000` fission sites will
be produced. If the data for each fission site consists of a spatial location
(three 8 byte real numbers) and a unique identifier (one 4 byte integer), the
memory required per site is 28 bytes. To broadcast 10,000,000 source sites to 64
nodes will thus require transferring 17.92 GB of data. Since each compute node
does not need to keep every source site in memory, one could modify the
algorithm from a broadcast to a scatter_. However, for practical reasons
(e.g. work self-scheduling), this is normally not done in production Monte Carlo
codes.
.. _nearest-neighbors-algorithm:
Nearest Neighbors Algorithm
---------------------------
To reduce the amount of communication required in a fission bank synchronization
algorithm, it is desirable to move away from the typical master-slave algorithm
to an algorithm whereby the compute nodes communicate with one another only as
needed. This concept is illustrated in :ref:`figure-nearest-neighbor`.
.. _figure-nearest-neighbor:
.. figure:: ../_images/nearest-neighbor.png
:align: center
:figclass: align-center
Communication pattern in nearest neighbor algorithm.
Since the source sites for each cycle are sampled from the fission sites banked
from the previous cycle, it is a common occurrence for a fission site to be
banked on one compute node and sent back to the master only to get sent back to
the same compute node as a source site. As a result, much of the communication
inherent in the algorithm described previously is entirely unnecessary. By
keeping the fission sites local, having each compute node sample fission sites,
and sending sites between nodes only as needed, one can cut down on most of the
communication. One algorithm to achieve this is as follows:
1. An exclusive scan is performed on the number of sites banked, and the
total number of fission bank sites is broadcasted to all compute nodes. By
picturing the fission bank as one large array distributed across multiple
nodes, one can see that this step enables each compute node to determine the
starting index of fission bank sites in this array. Let us call the starting
and ending indices on the :math:`i`-th node :math:`a_i` and :math:`b_i`,
respectively;
2. Each compute node samples sites at random from the fission bank using the
same starting seed. A separate array on each compute node is created that
consists of sites that were sampled local to that node, i.e. if the index of
the sampled site is between :math:`a_i` and :math:`b_i`, it is set aside;
3. If any node sampled more than :math:`N/p` fission sites where :math:`p`
is the number of compute nodes, the extra sites are put in a separate array
and sent to all other compute nodes. This can be done efficiently using the
allgather_ collective operation;
4. The extra sites are divided among those compute nodes that sampled fewer
than :math:`N/p` fission sites.
However, even this algorithm exhibits more communication than necessary since
the allgather will send fission bank sites to nodes that don't necessarily
need any extra sites.
One alternative is to replace the allgather with a series of sends. If
:math:`a_i` is less than :math:`iN/p`, then send :math:`iN/p - a_i` sites to the
left adjacent node. Similarly, if :math:`a_i` is greater than :math:`iN/p`, then
receive :math:`a_i - iN/p` from the left adjacent node. This idea is applied to
the fission bank sites at the end of each node's array as well. If :math:`b_i`
is less than :math:`(i+1)N/p`, then receive :math:`(i+1)N/p - b_i` sites from
the right adjacent node. If :math:`b_i` is greater than :math:`(i+1)N/p`, then
send :math:`b_i - (i+1)N/p` sites to the right adjacent node. Thus, each compute
node sends/receives only two messages under normal circumstances.
The following example illustrates how this algorithm works. Let us suppose we
are simulating :math:`N = 1000` neutrons across four compute nodes. For this
example, it is instructive to look at the state of the fission bank and source
bank at several points in the algorithm:
1. The beginning of a cycle where each node has :math:`N/p` source sites;
2. The end of a cycle where each node has accumulated fission sites;
3. After sampling, where each node has some amount of source sites usually
not equal to :math:`N/p`;
4. After redistribution, each node again has :math:`N/p` source sites for
the next cycle;
At the end of each cycle, each compute node needs 250 fission bank sites to
continue on the next cycle. Let us suppose that :math:`p_0` produces 270 fission
banks sites, :math:`p_1` produces 230, :math:`p_2` produces 290, and :math:`p_3`
produces 250. After each node samples from its fission bank sites, let's assume
that :math:`p_0` has 260 source sites, :math:`p_1` has 215, :math:`p_2` has 280,
and :math:`p_3` has 245. Note that the total number of sampled sites is 1000 as
needed. For each node to have the same number of source sites, :math:`p_0` needs
to send its right-most 10 sites to :math:`p_1`, and :math:`p_2` needs to send
its left-most 25 sites to :math:`p_1` and its right-most 5 sites to
:math:`p_3`. A schematic of this example is shown in
:ref:`figure-neighbor-example`. The data local to each node is given a different
hatching, and the cross-hatched regions represent source sites that are
communicated between adjacent nodes.
.. _figure-neighbor-example:
.. figure:: ../_images/nearest-neighbor-example.png
:align: center
:figclass: align-center
Example of nearest neighbor algorithm.
.. _master-slave-cost:
Cost of Master-Slave Algorithm
------------------------------
While the prior considerations may make it readily apparent that the novel
algorithm should outperform the traditional algorithm, it is instructive to look
at the total communication cost of the novel algorithm relative to the
traditional algorithm. This is especially so because the novel algorithm does
not have a constant communication cost due to stochastic fluctuations. Let us
begin by looking at the cost of communication in the traditional algorithm
As discussed earlier, the traditional algorithm is composed of a series of sends
and typically a broadcast. To estimate the communication cost of the algorithm,
we can apply a simple model that captures the essential features. In this model,
we assume that the time that it takes to send a message between two nodes is
given by :math:`\alpha + (sN)\beta`, where :math:`\alpha` is the time it takes
to initiate the communication (commonly called the latency_), :math:`\beta` is
the transfer time per unit of data (commonly called the bandwidth_), :math:`N`
is the number of fission sites, and :math:`s` is the size in bytes of each
fission site.
The first step of the traditional algorithm is to send :math:`p` messages to the
master node, each of size :math:`sN/p`. Thus, the total time to send these
messages is
.. math::
:label: t-send
t_{\text{send}} = p\alpha + sN\beta.
Generally, the best parallel performance is achieved in a weak scaling scheme
where the total number of histories is proportional to the number of
processors. However, we see that when :math:`N` is proportional to :math:`p`,
the time to send these messages increases proportionally with :math:`p`.
Estimating the time of the broadcast is complicated by the fact that different
MPI implementations may use different algorithms to perform collective
communications. Worse yet, a single implementation may use a different algorithm
depending on how many nodes are communicating and the size of the message. Using
multiple algorithms allows one to minimize latency for small messages and
minimize bandwidth for long messages.
We will focus here on the implementation of broadcast in the MPICH2_
implementation. For short messages, MPICH2 uses a `binomial tree`_ algorithm. In
this algorithm, the root process sends the data to one node in the first step,
and then in the subsequent, both the root and the other node can send the data
to other nodes. Thus, it takes a total of :math:`\lceil \log_2 p \rceil` steps
to complete the communication. The time to complete the communication is
.. math::
:label: t-short
t_{\text{short}} = \lceil \log_2 p \rceil \left ( \alpha + sN\beta \right ).
This algorithm works well for short messages since the latency term scales
logarithmically with the number of nodes. However, for long messages, an
algorithm that has lower bandwidth has been proposed by Barnett_ and implemented
in MPICH2. Rather than using a binomial tree, the broadcast is divided into a
scatter and an allgather. The time to complete the scatter is :math:` \log_2 p
\: \alpha + \frac{p-1}{p} N\beta` using a binomial tree algorithm. The allgather
is performed using a ring algorithm that completes in :math:`p-1) \alpha +
\frac{p-1}{p} N\beta`. Thus, together the time to complete the broadcast is
.. math::
:label: t-broadcast
t_{\text{long}} = \left ( \log_2 p + p - 1 \right ) \alpha + 2 \frac{p-1}{p}
sN\beta.
The fission bank data will generally exceed the threshold for switching from
short to long messages (typically 8 kilobytes), and thus we will use the
equation for long messages. Adding equations :eq:`t-send` and :eq:`t-broadcast`,
the total cost of the series of sends and the broadcast is
.. math::
:label: t-old
t_{\text{old}} = \left ( \log_2 p + 2p - 1 \right ) \alpha + \frac{3p-2}{p}
sN\beta.
Cost of Nearest Neighbor Algorithm
----------------------------------
With the communication cost of the traditional fission bank algorithm
quantified, we now proceed to discuss the communicatin cost of the proposed
algorithm. Comparing the cost of communication of this algorithm with the
traditional algorithm is not trivial due to fact that the cost will be a
function of how many fission sites are sampled on each node. If each node
samples exactly :math:`N/p` sites, there will not be communication between nodes
at all. However, if any one node samples more or less than :math:`N/p` sites,
the deviation will result in communication between logically adjacent nodes. To
determine the expected deviation, one can analyze the process based on the
fundamentals of the Monte Carlo process.
The steady-state neutron transport equation for a multiplying medium can be
written in the form of an eigenvalue problem,
.. math::
:label: NTE
S(\mathbf{r})= \frac{1}{k} \int F(\mathbf{r}' \rightarrow
\mathbf{r})S(\mathbf{r}')\: d\mathbf{r},
where :math:`\mathbf{r}` is the spatial coordinates of the neutron,
:math:`S(\mathbf{r})` is the source distribution defined as the expected number
of neutrons born from fission per unit phase-space volume at :math:`\mathbf{r}`,
:math:`F( \mathbf{r}' \rightarrow \mathbf{r})` is the expected number of
neutrons born from fission per unit phase space volume at :math:`\mathbf{r}`
caused by a neutron at :math:`\mathbf{r}`, and :math:`k` is the eigenvalue. The
fundamental eigenvalue of equation :eq:`NTE` is known as :math:`k_{eff}`, but
for simplicity we will simply refer to it as :math:`k`.
In a Monte Carlo criticality simulation, the power iteration method is applied
iteratively to obtain stochastic realizations of the source distribution and
estimates of the :math:`k`-eigenvalue. Let us define :math:`\hat{S}^{(m)}` to be
the realization of the source distribution at cycle :math:`m` and
:math:`\hat{\epsilon}^{(m)}` be the noise arising from the stochastic nature of
the tracking process. We can write the stochastic realization in terms of the
fundamental source distribution and the noise component as (see `Brissenden and
Garlick`_):
.. math::
:label: source
\hat{S}^{(m)}(\mathbf{r})= N S(\mathbf{r}) + \sqrt{N}
\hat{\epsilon}^{(m)}(\mathbf{r}),
where :math:`N` is the number of particle histories per cycle. Without loss of
generality, we shall drop the superscript notation indicating the cycle as it is
understood that the stochastic realization is at a particular cycle. The
expected value of the stochastic source distribution is simply
.. math::
:label: expected-value-source
E \left[ \hat{S}(\mathbf{r})\right] = N S (\mathbf{r})
since :math:`E \left[ \hat{\epsilon}(\mathbf{r})\right] = 0`. The noise in the
source distribution is due only to :math:`\hat{\epsilon}(\mathbf{r})` and thus
the variance of the source distribution will be
.. math::
:label: var-source
\text{Var} \left[ \hat{S}(\mathbf{r})\right] = N \text{Var} \left[
\hat{\epsilon}(\mathbf{r}) \right].
Lastly, the stochastic and true eigenvalues can be written as integrals over all
phase space of the stochastic and true source distributions, respectively, as
.. math::
:label: k-to-source
\hat{k} = \frac{1}{N} \int \hat{S}(\mathbf{r}) \: d\mathbf{r} \quad
\text{and} \quad k = \int S(\mathbf{r}) \: d\mathbf{r},
noting that :math:`S(\mathbf{r})` is :math:`O(1)`. One should note that the
expected value :math:`k` calculated by Monte Carlo power iteration (i.e. the
method of successive generations) will be biased from the true fundamental
eigenvalue of equation :eq:`NTE` by :math:`O(1/N)` (see `Brissenden and
Garlick`_), but we will assume henceforth that the number of particle histories
per cycle is sufficiently large to neglect this bias.
With this formalism, we now have a framework within which we can determine the
properties of the distribution of expected number of fission sites. The explicit
form of the source distribution can be written as
.. math::
:label: source-explicit
\hat{S}(\mathbf{r}) = \sum_{i=1}^{M} w_i \delta( \mathbf{r} - \mathbf{r}_i )
where :math:`\mathbf{r}_i` is the spatial location of the :math:`i`-th fission
site, :math:`w_i` is the statistical weight of the fission site at
:math:`\mathbf{r}_i`, and :math:`M` is the total number of fission sites. It is
clear that the total weight of the fission sites is simply the integral of the
source distribution. Integrating equation :eq:`source` over all space, we obtain
.. math::
:label: source-integrated
\int \hat{S}(\mathbf{r}) \: d\mathbf{r} = N \int S(\mathbf{r}) \:
d\mathbf{r} + \sqrt{N} \int \hat{\epsilon}(\mathbf{r}) \: d\mathbf{r} .
Substituting the expressions for the stochastic and true eigenvalues from
equation :eq:`k-to-source`, we can relate the stochastic eigenvalue to the
integral of the noise component of the source distribution as
.. math::
:label: noise-integeral
N\hat{k} = Nk + \sqrt{N} \int \hat{\epsilon}(\mathbf{r}) \: d\mathbf{r}.
Since the expected value of :math:`\hat{\epsilon}` is zero, the expected value
of its integral will also be zero. We thus see that the variance of the integral
of the source distribution, i.e. the variance of the total weight of fission
sites produced, is directly proportional to the variance of the integral of the
noise component. Let us call this term :math:`\sigma^2` for simplicity:
.. math::
:label: variance-sigma2
\text{Var} \left[ \int \hat{S}(\mathbf{r}) \right ] = N \sigma^2.
The actual value of :math:`\sigma^2` will depend on the physical nature of the
problem, whether variance reduction techniques are employed, etc. For instance,
one could surmise that for a highly scattering problem, :math:`\sigma^2` would
be smaller than for a highly absorbing problem since more collisions will lead
to a more precise estimate of the source distribution. Similarly, using implicit
capture should in theory reduce the value of :math:`\sigma^2`.
Let us now consider the case where the :math:`N` total histories are divided up
evenly across :math:`p` compute nodes. Since each node simulates :math:`N/p`
histories, we can write the source distribution as
.. math::
:label: source-node
\hat{S}_i(\mathbf{r})= \frac{N}{p} S(\mathbf{r}) + \sqrt{\frac{N}{p}}
\hat{\epsilon}_i(\mathbf{r}) \quad \text{for} \quad i = 1, \dots, p
Integrating over all space and simplifying, we can obtain an expression for the
eigenvalue on the :math:`i`-th node:
.. math::
:label: k-i-hat
\hat{k}_i = k + \sqrt{\frac{p}{N}} \int \hat{\epsilon}_i(\mathbf{r}) \:
d\mathbf{r}.
It is easy to show from this expression that the stochastic realization of the
global eigenvalue is merely the average of these local eigenvalues:
.. math::
:label: average-k-as-sum
\hat{k} = \frac{1}{p} \sum_{i=1}^p \hat{k}_i.
As was mentioned earlier, at the end of each cycle one must sample :math:`N`
sites from the :math:`M` sites that were created. Thus, the source for the next
cycle can be seen as the fission source from the current cycle divided by the
stochastic realization of the eigenvalue since it is clear from equation
:eq:`k-to-source` that :math:`\hat{k} = M/N`. Similarly, the number of sites
sampled on each compute node that will be used for the next cycle is
.. math::
:label: sites-per-node
M_i = \frac{1}{\hat{k}} \int \hat{S}_i(\mathbf{r}) \: d\mathbf{r} =
\frac{N}{p} \frac{\hat{k}_i}{\hat{k}}.
While we know conceptually that each compute node will under normal
circumstances send two messages, many of these messages will overlap. Rather
than trying to determine the actual communication cost, we will instead attempt
to determine the maximum amount of data being communicated from one node to
another. At any given cycle, the number of fission sites that the :math:`j`-th
compute node will send or receive (:math:`\Lambda_j`) is
.. math::
:label: Lambda
\Lambda_j = \left | \sum_{i=1}^j M_i - \frac{jN}{p} \right |.
Noting that :math:`jN/p` is the expected value of the summation, we can write
the expected value of :math:`\Lambda_j` as the mean absolute deviation of the
summation:
.. math::
:label: mean-dev-lambda
E \left [ \Lambda_j \right ] = E \left [ \left | \sum_{i=1}^j M_i -
\frac{jN}{p} \right | \right ] = \text{MD} \left [ \sum_{i=1}^j M_i \right ]
where :math:`\text{MD}` indicates the mean absolute deviation of a random
variable. The mean absolute deviation is an alternative measure of variability.
In order to ascertain any information about the mean deviation of :math:`M_i`,
we need to know the nature of its distribution. Thus far, we have said nothing
of the distributions of the random variables in question. The total number of
fission sites resulting from the tracking of :math:`N` neutrons can be shown to
be normally distributed via the :ref:`central-limit-theorem` (provided that
:math:`N` is sufficiently large) since the fission sites resulting from each
neutron are "sampled" from independent, identically-distributed random
variables. Thus, :math:`\hat{k}` and :math:`\int \hat{S} (\mathbf{r}) \:
d\mathbf{r}` will be normally distributed as will the individual estimates of
these on each compute node.
Next, we need to know what the distribution of :math:`M_i` in equation
:eq:`sites-per-node` is or, equivalently, how :math:`\hat{k}_i / \hat{k}` is
distributed. The distribution of a ratio of random variables is not easy to
calculate analytically, and it is not guaranteed that the ratio distribution is
normal if the numerator and denominator are normally distributed. For example,
if :math:`X` is a standard normal distribution and :math:`Y` is also standard
normal distribution, then the ratio :math:`X/Y` has the standard `Cauchy
distribution`_. The reader should be reminded that the Cauchy distribution has
no defined mean or variance. That being said, Geary_ has shown that, for the
case of two normal distributions, if the denominator is unlikely to assume
values less than zero, then the ratio distribution is indeed approximately
normal. In our case, :math:`\hat{k}` absolutely cannot assume a value less than
zero, so we can be reasonably assured that the distribution of :math:`M_i` will
be normal.
For a normal distribution with mean :math:`\mu` and distribution function
:math:`f(x)`, it can be shown that
.. math::
:label: mean-dev-to-stdev
\int_{-\infty}^{\infty} f(x) \left | x - \mu \right | \: dx =
\sqrt{\frac{2}{\pi} \int_{-\infty}^{\infty} f(x) \left ( x - \mu \right )^2
\: dx}
and thus the mean absolute deviation is :math:`\sqrt{2/\pi}` times the standard
deviation. Therefore, to evaluate the mean absolute deviation of :math:`M_i`, we
need to first determine its variance. Substituting equation
:eq:`average-k-as-sum` into equation :eq:`sites-per-node`, we can rewrite
:math:`M_i` solely in terms of :math:`\hat{k}_1, \dots, \hat{k}_p`:
.. math::
:label: M-i
M_i = \frac{N \hat{k}_i}{\sum\limits_{j=1}^p \hat{k}_j}.
Since we know the variance of :math:`\hat{k}_i`, we can use the error
propagation law to determine the variance of :math:`M_i`:
.. math::
:label: M-variance
\text{Var} \left [ M_i \right ] = \sum_{j=1}^p \left ( \frac{\partial
M_i}{\partial \hat{k}_j} \right )^2 \text{Var} \left [ \hat{k}_j \right ] +
\sum\limits_{j \neq m} \sum\limits_{m=1}^p \left ( \frac{\partial
M_i}{\partial \hat{k}_j} \right ) \left ( \frac{\partial M_i}{\partial
\hat{k}_m} \right ) \text{Cov} \left [ \hat{k}_j, \hat{k}_m \right ]
where the partial derivatives are evaluated at :math:`\hat{k}_j = k`. Since
:math:`\hat{k}_j` and :math:`\hat{k}_m` are independent if :math:`j \neq m`,
their covariance is zero and thus the second term cancels out. Evaluating the
partial derivatives, we obtain
.. math::
:label: M-variance-2
\text{Var} \left [ M_i \right ] = \left ( \frac{N(p-1)}{kp^2} \right )^2
\frac{p\sigma^2}{N} + \sum_{j \neq i} \left ( \frac{-N}{kp^2} \right )^2
\frac{p\sigma^2}{N} = \frac{N(p-1)}{k^2p^2} \sigma^2.
Through a similar analysis, one can show that the variance of
:math:`\sum_{i=1}^j M_i` is
.. math::
:label: sum-M-variance
\text{Var} \left [ \sum_{i=1}^j M_i \right ] = \frac{Nj(p-j)}{k^2p^2}
\sigma^2
Thus, the expected amount of communication on node :math:`j`, i.e. the mean
absolute deviation of :math:`\sum_{i=1}^j M_i` is proportional to
.. math::
:label: communication-cost
E \left [ \Lambda_j \right ] = \sqrt{\frac{2Nj(p-j)\sigma^2}{\pi k^2p^2}}.
This formula has all the properties that one would expect based on intuition:
1. As the number of histories increases, the communication cost on each node
increases as well;
2. If :math:`p=1`, i.e. if the problem is run on only one compute node, the
variance will be zero. This reflects the fact that exactly :math:`N` sites
will be sampled if there is only one node.
3. For :math:`j=p`, the variance will be zero. Again, this says that when
you sum the number of sites from each node, you will get exactly :math:`N`
sites.
We can determine the node that has the highest communication cost by
differentiating equation :eq:`communication-cost` with respect to :math:`j`,
setting it equal to zero, and solving for :math:`j`. Doing so yields
:math:`j_{\text{max}} = p/2`. Interestingly, substituting :math:`j = p/2` in
equation :eq:`communication-cost` shows us that the maximum communication cost
is actually independent of the number of nodes:
.. math::
:label: maximum-communication
E \left [ \Lambda_{j_{\text{max}}} \right ] = \sqrt{ \frac{N\sigma^2}{2\pi
k^2}}.
.. only:: html
.. rubric:: References
.. [Troubetzkoy] E. Troubetzkoy, H. Steinberg, and M. Kalos, "Monte Carlo
Radiation Penetration Calculations on a Parallel Computer,"
*Trans. Am. Nucl. Soc.*, **17**, 260 (1973).
.. _first paper: https://doi.org/10.2307/2280232
.. _work of Forrest Brown: http://hdl.handle.net/2027.42/24996
.. _Brissenden and Garlick: https://doi.org/10.1016/0306-4549(86)90095-2
.. _MPICH2: http://www.mcs.anl.gov/mpi/mpich
.. _binomial tree: https://www.mcs.anl.gov/~thakur/papers/ijhpca-coll.pdf
.. _Geary: https://doi.org/10.2307/2342070
.. _Barnett: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.51.7772
.. _single-instruction multiple-data: https://en.wikipedia.org/wiki/SIMD
.. _vector computers: https://en.wikipedia.org/wiki/Vector_processor
.. _single program multiple data: https://en.wikipedia.org/wiki/SPMD
.. _message-passing interface: https://en.wikipedia.org/wiki/Message_Passing_Interface
.. _PVM: http://www.csm.ornl.gov/pvm/pvm_home.html
.. _MPI: http://www.mcs.anl.gov/research/projects/mpi/
.. _embarrassingly parallel: https://en.wikipedia.org/wiki/Embarrassingly_parallel
.. _sends: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Send.html
.. _broadcasts: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Bcast.html
.. _scatter: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Scatter.html
.. _allgather: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Allgather.html
.. _Cauchy distribution: https://en.wikipedia.org/wiki/Cauchy_distribution
.. _latency: https://en.wikipedia.org/wiki/Latency_(engineering)#Packet-switched_networks
.. _bandwidth: https://en.wikipedia.org/wiki/Bandwidth_(computing)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,72 @@
.. _methods_random_numbers:
========================
Random Number Generation
========================
In order to sample probability distributions, one must be able to produce random
numbers. The standard technique to do this is to generate numbers on the
interval :math:`[0,1)` from a deterministic sequence that has properties that
make it appear to be random, e.g. being uniformly distributed and not exhibiting
correlation between successive terms. Since the numbers produced this way are
not truly "random" in a strict sense, they are typically referred to as
pseudorandom numbers, and the techniques used to generate them are pseudorandom
number generators (PRNGs). Numbers sampled on the unit interval can then be
transformed for the purpose of sampling other continuous or discrete probability
distributions.
------------------------------
Linear Congruential Generators
------------------------------
There are a great number of algorithms for generating random numbers. One of the
simplest and commonly used algorithms is called a `linear congruential
generator`_. We start with a random number *seed* :math:`\xi_0` and a sequence
of random numbers can then be generated using the following recurrence relation:
.. math::
:label: lcg
\xi_{i+1} = g \xi_i + c \mod M
where :math:`g`, :math:`c`, and :math:`M` are constants. The choice of these
constants will have a profound effect on the quality and performance of the
generator, so they should not be chosen arbitrarily. As Donald Knuth stated in
his seminal work *The Art of Computer Programming*, "random numbers should not
be generated with a method chosen at random. Some theory should be used."
Typically, :math:`M` is chosen to be a power of two as this enables :math:`x
\mod M` to be performed using the bitwise AND operator with a bit mask. The
constants for the linear congruential generator used by default in OpenMC are
:math:`g = 2806196910506780709`, :math:`c = 1`, and :math:`M = 2^{63}` (see
`L'Ecuyer`_).
Skip-ahead Capability
---------------------
One of the important capabilities for a random number generator is to be able to
skip ahead in the sequence of random numbers. Without this capability, it would
be very difficult to maintain reproducibility in a parallel calculation. If we
want to skip ahead :math:`N` random numbers and :math:`N` is large, the cost of
sampling :math:`N` random numbers to get to that position may be prohibitively
expensive. Fortunately, algorithms have been developed that allow us to skip
ahead in :math:`O(\log_2 N)` operations instead of :math:`O(N)`. One algorithm
to do so is described in a paper by Brown_. This algorithm relies on the following
relationship:
.. math::
:label: lcg-skipahead
\xi_{i+k} = g^k \xi_i + c \frac{g^k - 1}{g - 1} \mod M
Note that equation :eq:`lcg-skipahead` has the same general form as equation :eq:`lcg`, so
the idea is to determine the new multiplicative and additive constants in
:math:`O(\log_2 N)` operations.
.. only:: html
.. rubric:: References
.. _L'Ecuyer: https://doi.org/10.1090/S0025-5718-99-00996-5
.. _Brown: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/anl-rn-arb-stride.pdf
.. _linear congruential generator: https://en.wikipedia.org/wiki/Linear_congruential_generator

View file

@ -0,0 +1,515 @@
.. _methods_tallies:
=======
Tallies
=======
Note that the methods discussed in this section are written specifically for
continuous-energy mode but equivalent apply to the multi-group mode if the
particle's energy is replaced with the particle's group
------------------
Filters and Scores
------------------
The tally capability in OpenMC takes a similar philosophy as that employed in
the MC21_ Monte Carlo code to give maximum flexibility in specifying tallies
while still maintaining scalability. Any tally in a Monte Carlo simulation can
be written in the following form:
.. math::
:label: tally-integral
X = \underbrace{\int d\mathbf{r} \int d\mathbf{\Omega} \int
dE}_{\text{filters}} \underbrace{f(\mathbf{r}, \mathbf{\Omega},
E)}_{\text{scores}} \psi (\mathbf{r}, \mathbf{\Omega}, E)
A user can specify one or more filters which identify which regions of phase
space should score to a given tally (the limits of integration as shown in
equation :eq:`tally-integral`) as well as the scoring function (:math:`f` in
equation :eq:`tally-integral`). For example, if the desired tally was the
:math:`(n,\gamma)` reaction rate in a fuel pin, the filter would specify the
cell which contains the fuel pin and the scoring function would be the radiative
capture macroscopic cross section. The following quantities can be scored in
OpenMC: flux, total reaction rate, scattering reaction rate, neutron production
from scattering, higher scattering moments, :math:`(n,xn)` reaction rates,
absorption reaction rate, fission reaction rate, neutron production rate from
fission, and surface currents. The following variables can be used as filters:
universe, material, cell, birth cell, surface, mesh, pre-collision energy,
post-collision energy, polar angle, azimuthal angle, and the cosine of the
change-in-angle due to a scattering event.
With filters for pre- and post-collision energy and scoring functions for
scattering and fission production, it is possible to use OpenMC to generate
cross sections with user-defined group structures. These multigroup cross
sections can subsequently be used in deterministic solvers such as coarse mesh
finite difference (CMFD) diffusion.
------------------------------
Using Maps for Filter-Matching
------------------------------
Some Monte Carlo codes suffer severe performance penalties when tallying a large
number of quantities. Care must be taken to ensure that a tally system scales
well with the total number of tally bins. In OpenMC, a mapping technique is used
that allows for a fast determination of what tally/bin combinations need to be
scored to a given particle's phase space coordinates. For each discrete filter
variable, a list is stored that contains the tally/bin combinations that could
be scored to for each value of the filter variable. If a particle is in cell
:math:`n`, the mapping would identify what tally/bin combinations specify cell
:math:`n` for the cell filter variable. In this manner, it is not necessary to
check the phase space variables against each tally. Note that this technique
only applies to discrete filter variables and cannot be applied to energy,
angle, or change-in-angle bins. For these filters, it is necessary to perform
a binary search on the specified energy grid.
-----------------------------------------
Volume-Integrated Flux and Reaction Rates
-----------------------------------------
One quantity we may wish to compute during the course of a Monte Carlo
simulation is the flux or a reaction rate integrated over a finite volume. The
volume may be a particular cell, a collection of cells, or the entire
geometry. There are various methods by which we can estimate reaction rates
Analog Estimator
----------------
The analog estimator is the simplest type of estimator for reaction rates. The
basic idea is that we simply count the number of actual reactions that take
place and use that as our estimate for the reaction rate. This can be written
mathematically as
.. math::
:label: analog-estimator
R_x = \frac{1}{W} \sum_{i \in A} w_i
where :math:`R_x` is the reaction rate for reaction :math:`x`, :math:`i` denotes
an index for each event, :math:`A` is the set of all events resulting in
reaction :math:`x`, and :math:`W` is the total starting weight of the particles,
and :math:`w_i` is the pre-collision weight of the particle as it enters event
:math:`i`. One should note that equation :eq:`analog-estimator` is
volume-integrated so if we want a volume-averaged quantity, we need to divided
by the volume of the region of integration. If survival biasing is employed, the
analog estimator cannot be used for any reactions with zero neutrons in the exit
channel.
Collision Estimator
-------------------
While the analog estimator is conceptually very simple and easy to implement, it
can suffer higher variance due to the fact low probability events will not occur
often enough to get good statistics if they are being tallied. Thus, it is
desirable to use a different estimator that allows us to score to the tally more
often. One such estimator is the collision estimator. Instead of tallying a
reaction only when it happens, the idea is to make a contribution to the tally
at every collision.
We can start by writing a formula for the collision estimate of the flux. Since
:math:`R = \Sigma_t \phi` where :math:`R` is the total reaction rate,
:math:`\Sigma_t` is the total macroscopic cross section, and :math:`\phi` is the
scalar flux, it stands to reason that we can estimate the flux by taking an
estimate of the total reaction rate and dividing it by the total macroscopic
cross section. This gives us the following formula:
.. math::
:label: collision-estimator-flux
\phi = \frac{1}{W} \sum_{i \in C} \frac{w_i}{\Sigma_t (E_i)}
where :math:`W` is again the total starting weight of the particles, :math:`C`
is the set of all events resulting in a collision with a nucleus, and
:math:`\Sigma_t (E)` is the total macroscopic cross section of the target
material at the incoming energy of the particle :math:`E_i`.
If we multiply both sides of equation :eq:`collision-estimator-flux` by the
macroscopic cross section for some reaction :math:`x`, then we get the collision
estimate for the reaction rate for that reaction:
.. math::
:label: collision-estimator
R_x = \frac{1}{W} \sum_{i \in C} \frac{w_i \Sigma_x (E_i)}{\Sigma_t (E_i)}
where :math:`\Sigma_x (E_i)` is the macroscopic cross section for reaction
:math:`x` at the incoming energy of the particle :math:`E_i`. In comparison to
equation :eq:`analog-estimator`, we see that the collision estimate will result
in a tally with a larger number of events that score to it with smaller
contributions (since we have multiplied it by :math:`\Sigma_x / \Sigma_t`).
Track-length Estimator
----------------------
One other method we can use to increase the number of events that scores to
tallies is to use an estimator the scores contributions to a tally at every
track for the particle rather than every collision. This is known as a
track-length estimator, sometimes also called a path-length estimator. We first
start with an expression for the volume integrated flux, which can be written as
.. math::
:label: flux-integrated
V \phi = \int d\mathbf{r} \int dE \int d\mathbf{\Omega} \int dt \,
\psi(\mathbf{r}, \mathbf{\hat{\Omega}}, E, t)
where :math:`V` is the volume, :math:`\psi` is the angular flux,
:math:`\mathbf{r}` is the position of the particle, :math:`\mathbf{\hat{\Omega}}`
is the direction of the particle, :math:`E` is the energy of the particle, and
:math:`t` is the time. By noting that :math:`\psi(\mathbf{r},
\mathbf{\hat{\Omega}}, E, t) = v n(\mathbf{r}, \mathbf{\hat{\Omega}}, E, t)`
where :math:`n` is the angular neutron density, we can rewrite equation
:eq:`flux-integrated` as
.. math::
:label: flux-integrated-2
V \phi = \int d\mathbf{r} \int dE \int dt v \int d\mathbf{\Omega} \, n(\mathbf{r},
\mathbf{\hat{\Omega}}, E, t)).
Using the relations :math:`N(\mathbf{r}, E, t) = \int d\mathbf{\Omega}
n(\mathbf{r}, \mathbf{\hat{\Omega}}, E, t)` and :math:`d\ell = v \, dt` where
:math:`d\ell` is the differential unit of track length, we then obtain
.. math::
:label: track-length-integral
V \phi = \int d\mathbf{r} \int dE \int d\ell N(\mathbf{r}, E, t).
Equation :eq:`track-length-integral` indicates that we can use the length of a
particle's trajectory as an estimate for the flux, i.e. the track-length
estimator of the flux would be
.. math::
:label: track-length-flux
\phi = \frac{1}{W} \sum_{i \in T} w_i \ell_i
where :math:`T` is the set of all the particle's trajectories within the desired
volume and :math:`\ell_i` is the length of the :math:`i`-th trajectory. In the
same vein as equation :eq:`collision-estimator`, the track-length estimate of a
reaction rate is found by multiplying equation :eq:`track-length-flux` by a
macroscopic reaction cross section:
.. math::
:label: track-length-estimator
R_x = \frac{1}{W} \sum_{i \in T} w_i \ell_i \Sigma_x (E_i).
One important fact to take into consideration is that the use of a track-length
estimator precludes us from using any filter that requires knowledge of the
particle's state following a collision because by definition, it will not have
had a collision at every event. Thus, for tallies with outgoing-energy filters
(which require the post-collision energy), scattering change-in-angle filters,
or for tallies of scattering moments (which require the scattering cosine of
the change-in-angle), we must use an analog estimator.
.. TODO: Add description of surface current tallies
----------
Statistics
----------
As was discussed briefly in :ref:`methods_introduction`, any given result from a
Monte Carlo calculation, colloquially known as a "tally", represents an estimate
of the mean of some `random variable`_ of interest. This random variable
typically corresponds to some physical quantity like a reaction rate, a net
current across some surface, or the neutron flux in a region. Given that all
tallies are produced by a `stochastic process`_, there is an associated
uncertainty with each value reported. It is important to understand how the
uncertainty is calculated and what it tells us about our results. To that end,
we will introduce a number of theorems and results from statistics that should
shed some light on the interpretation of uncertainties.
Law of Large Numbers
--------------------
The `law of large numbers`_ is an important statistical result that tells us
that the average value of the result a large number of repeated experiments
should be close to the `expected value`_. Let :math:`X_1, X_2, \dots, X_n` be an
infinite sequence of `independent, identically-distributed random variables`_
with expected values :math:`E(X_1) = E(X_2) = \mu`. One form of the law of large
numbers states that the sample mean :math:`\bar{X_n} = \frac{X_1 + \dots +
X_n}{n}` `converges in probability`_ to the true mean, i.e. for all
:math:`\epsilon > 0`
.. math::
\lim\limits_{n\rightarrow\infty} P \left ( \left | \bar{X}_n - \mu \right |
\ge \epsilon \right ) = 0.
.. _central-limit-theorem:
Central Limit Theorem
---------------------
The `central limit theorem`_ (CLT) is perhaps the most well-known and ubiquitous
statistical theorem that has far-reaching implications across many
disciplines. The CLT is similar to the law of large numbers in that it tells us
the limiting behavior of the sample mean. Whereas the law of large numbers tells
us only that the value of the sample mean will converge to the expected value of
the distribution, the CLT says that the distribution of the sample mean will
converge to a `normal distribution`_. As we defined before, let :math:`X_1, X_2,
\dots, X_n` be an infinite sequence of independent, identically-distributed
random variables with expected values :math:`E(X_i) = \mu` and variances
:math:`\text{Var} (X_i) = \sigma^2 < \infty`. Note that we don't require that
these random variables take on any particular distribution -- they can be
normal, log-normal, Weibull, etc. The central limit theorem states that as
:math:`n \rightarrow \infty`, the random variable :math:`\sqrt{n} (\bar{X}_n -
\mu)` `converges in distribution`_ to the standard normal distribution:
.. math::
:label: central-limit-theorem
\sqrt{n} \left ( \frac{1}{n} \sum_{i=1}^n X_i - \mu \right ) \xrightarrow{d}
\mathcal{N} (0, \sigma^2)
Estimating Statistics of a Random Variable
------------------------------------------
Mean
++++
Given independent samples drawn from a random variable, the sample mean is
simply an estimate of the average value of the random variable. In a Monte Carlo
simulation, the random variable represents physical quantities that we want
tallied. If :math:`X` is the random variable with :math:`N` observations
:math:`x_1, x_2, \dots, x_N`, then an unbiased estimator for the population mean
is the sample mean, defined as
.. math::
:label: sample-mean
\bar{x} = \frac{1}{N} \sum_{i=1}^N x_i.
Variance
++++++++
The variance of a population indicates how spread out different members of the
population are. For a Monte Carlo simulation, the variance of a tally is a
measure of how precisely we know the tally value, with a lower variance
indicating a higher precision. There are a few different estimators for the
population variance. One of these is the second central moment of the
distribution also known as the biased sample variance:
.. math::
:label: biased-variance
s_N^2 = \frac{1}{N} \sum_{i=1}^N \left ( x_i - \bar{x} \right )^2 = \left (
\frac{1}{N} \sum_{i=1}^N x_i^2 \right ) - \bar{x}^2.
This estimator is biased because its expected value is actually not equal to the
population variance:
.. math::
:label: biased-variance-expectation
E[s_N^2] = \frac{N - 1}{N} \sigma^2
where :math:`\sigma^2` is the actual population variance. As a result, this
estimator should not be used in practice. Instead, one can use `Bessel's
correction`_ to come up with an unbiased sample variance estimator:
.. math::
:label: unbiased-variance
s^2 = \frac{1}{N - 1} \sum_{i=1}^N \left ( x_i - \bar{x} \right )^2 =
\frac{1}{N - 1} \left ( \sum_{i=1}^N x_i^2 - N\bar{x}^2 \right ).
This is the estimator normally used to calculate sample variance. The final form
in equation :eq:`unbiased-variance` is especially suitable for computation since
we do not need to store the values at every realization of the random variable
as the simulation proceeds. Instead, we can simply keep a running sum and sum of
squares of the values at each realization of the random variable and use that to
calculate the variance.
Variance of the Mean
++++++++++++++++++++
The previous sections discussed how to estimate the mean and variance of a
random variable using statistics on a finite sample. However, we are generally
not interested in the *variance of the random variable* itself; we are more
interested in the *variance of the estimated mean*. The sample mean is the
result of our simulation, and the variance of the sample mean will tell us how
confident we should be in our answers.
Fortunately, it is quite easy to estimate the variance of the mean if we are
able to estimate the variance of the random variable. We start with the
observation that if we have a series of uncorrelated random variables, we can
write the variance of their sum as the sum of their variances:
.. math::
:label: bienayme-formula
\text{Var} \left ( \sum_{i=1}^N X_i \right ) = \sum_{i=1}^N \text{Var} \left
( X_i \right )
This result is known as the Bienaymé formula. We can use this result to
determine a formula for the variance of the sample mean. Assuming that the
realizations of our random variable are again identical,
independently-distributed samples, then we have that
.. math::
:label: sample-variance-mean
\text{Var} \left ( \bar{X} \right ) = \text{Var} \left ( \frac{1}{N}
\sum_{i=1}^N X_i \right ) = \frac{1}{N^2} \sum_{i=1}^N \text{Var} \left (
X_i \right ) = \frac{1}{N^2} \left ( N\sigma^2 \right ) =
\frac{\sigma^2}{N}.
We can combine this result with equation :eq:`unbiased-variance` to come up with
an unbiased estimator for the variance of the sample mean:
.. math::
:label: sample-variance-mean-formula
s_{\bar{X}}^2 = \frac{1}{N - 1} \left ( \frac{1}{N} \sum_{i=1}^N x_i^2 -
\bar{x}^2 \right ).
At this point, an important distinction should be made between the estimator for
the variance of the population and the estimator for the variance of the
mean. As the number of realizations increases, the estimated variance of the
population based on equation :eq:`unbiased-variance` will tend to the true
population variance. On the other hand, the estimated variance of the mean will
tend to zero as the number of realizations increases. A practical interpretation
of this is that the longer you run a simulation, the better you know your
results. Therefore, by running a simulation long enough, it is possible to
reduce the stochastic uncertainty to arbitrarily low levels.
Confidence Intervals
++++++++++++++++++++
While the sample variance and standard deviation gives us some idea about the
variability of the estimate of the mean of whatever quantities we've tallied, it
does not help us interpret how confidence we should be in the results. To
quantity the reliability of our estimates, we can use `confidence intervals`_
based on the calculated sample variance.
A :math:`1-\alpha` confidence interval for a population parameter is defined as
such: if we repeat the same experiment many times and calculate the confidence
interval for each experiment, then :math:`1 - \alpha` percent of the calculated
intervals would encompass the true population parameter. Let :math:`x_1, x_2,
\dots, x_N` be samples from a set of independent, identically-distributed random
variables each with population mean :math:`\mu` and variance
:math:`\sigma^2`. The t-statistic is defined as
.. math::
:label: t-statistic
t = \frac{\bar{x} - \mu}{s/\sqrt{N}}
where :math:`\bar{x}` is the sample mean from equation :eq:`sample-mean` and
:math:`s` is the standard deviation based on equation
:eq:`unbiased-variance`. If the random variables :math:`X_i` are
normally-distributed, then the t-statistic has a `Student's t-distribution`_
with :math:`N-1` degrees of freedom. This implies that
.. math::
:label: t-probability
Pr \left ( -t_{1 - \alpha/2, N - 1} \le \frac{\bar{x} - \mu}{s/\sqrt{N}} \le
t_{1 - \alpha/2, N - 1} \right ) = 1 - \alpha
where :math:`t_{1-\alpha/2, N-1}` is the :math:`1 - \alpha/2` percentile of a
t-distribution with :math:`N-1` degrees of freedom. Thus, the :math:`1 - \alpha`
two sided confidence interval for the sample mean is
.. math::
:label: two-sided-ci
\bar{x} \pm t_{1 - \alpha/2, N-1} \frac{s}{\sqrt{N}}.
One should be cautioned that equation :eq:`two-sided-ci` only applies if the
*underlying random variables* are normally-distributed. In general, this may not
be true for a tally random variable --- the central limit theorem guarantees
only that the sample mean is normally distributed, not the underlying random
variable. If batching is used, then the underlying random variable, which would
then be the averages from each batch, will be normally distributed as long as
the conditions of the central limit theorem are met.
Let us now outline the method used to calculate the percentile of the Student's
t-distribution. For one or two degrees of freedom, the percentile can be written
analytically. For one degree of freedom, the t-distribution becomes a standard
`Cauchy distribution`_ whose cumulative distribution function is
.. math::
:label: cauchy-cdf
c(x) = \frac{1}{\pi} \arctan x + \frac{1}{2}.
Thus, inverting the cumulative distribution function, we find the :math:`x`
percentile of the standard Cauchy distribution to be
.. math::
:label: percentile-1
t_{x,1} = \tan \left ( \pi \left ( x - \frac{1}{2} \right ) \right ).
For two degrees of freedom, the cumulative distribution function is the
second-degree polynomial
.. math::
:label: t-2-polynomial
c(x) = \frac{1}{2} + \frac{x}{2\sqrt{x^2 + 2}}
Solving for :math:`x`, we find the :math:`x` percentile to be
.. math::
:label: percentile-2
t_{x,2} = \frac{2\sqrt{2} (x - 1/2)}{\sqrt{1 - 4 (x - 1/2)^2}}
For degrees of freedom greater than two, it is not possible to obtain an
analytical formula for the inverse of the cumulative distribution function. We
must resort to either numerically solving for the inverse or to an
approximation. Approximations for percentiles of the t-distribution have been
found with high levels of accuracy. OpenMC uses the `following approximation`_:
.. math::
:label: percentile-n
t_{x,n} = \sqrt{\frac{n}{n-2}} \left ( z_x + \frac{1}{4} \frac{z_x^3 -
3z_x}{n-2} + \frac{1}{96} \frac{5z_x^5 - 56z_x^3 + 75z_x}{(n-2)^2} +
\frac{1}{384} \frac{3z_x^7 - 81z_x^5 + 417z_x^3 - 315z_x}{(n-2)^3} \right )
where :math:`z_x` is the :math:`x` percentile of the standard normal
distribution. In order to determine an arbitrary percentile of the standard
normal distribution, we use an `unpublished rational approximation`_. After
using the rational approximation, one iteration of Newton's method is applied to
improve the estimate of the percentile.
.. only:: html
.. rubric:: References
.. _following approximation: https://doi.org/10.1080/03610918708812641
.. _Bessel's correction: https://en.wikipedia.org/wiki/Bessel's_correction
.. _random variable: https://en.wikipedia.org/wiki/Random_variable
.. _stochastic process: https://en.wikipedia.org/wiki/Stochastic_process
.. _independent, identically-distributed random variables: https://en.wikipedia.org/wiki/Independent_and_identically_distributed_random_variables
.. _law of large numbers: https://en.wikipedia.org/wiki/Law_of_large_numbers
.. _expected value: https://en.wikipedia.org/wiki/Expected_value
.. _converges in probability: https://en.wikipedia.org/wiki/Convergence_of_random_variables#Convergence_in_probability
.. _normal distribution: https://en.wikipedia.org/wiki/Normal_distribution
.. _converges in distribution: https://en.wikipedia.org/wiki/Convergence_of_random_variables#Convergence_in_distribution
.. _confidence intervals: https://en.wikipedia.org/wiki/Confidence_interval
.. _Student's t-distribution: https://en.wikipedia.org/wiki/Student%27s_t-distribution
.. _Cauchy distribution: https://en.wikipedia.org/wiki/Cauchy_distribution
.. _unpublished rational approximation: https://web.archive.org/web/20150926021742/http://home.online.no/~pjacklam/notes/invnorm/
.. _MC21: http://www.osti.gov/bridge/servlets/purl/903083-HT5p1o/903083.pdf