Tallies and Post-processing#
Tallies specify which transport quantities MC/DC records and how those
quantities are divided into bins. This page extends the model from
First MC/DC Simulation; add the examples below before
simulation.run(). The code
snippets assume NumPy has been imported as np.
Each tally combines:
one or more scores, such as flux, collision rate, or current;
optional spatial, angular, energy, time, surface, or cell filters; and
a name used to identify the tally in the output file.
Only tallies passed to simulation.set_tallies(...) are scored.
Mesh Tallies#
The introductory example scores flux over a structured z mesh:
mesh = mcdc.MeshStructured(z=np.linspace(0.0, 6.0, 61))
flux_tally = mcdc.Tally(
name="slab_flux",
mesh=mesh,
scores=["flux"],
)
simulation.set_tallies([flux_tally])
The 61 grid points define 60 spatial bins. Track-length scores include
"flux", "density", "collision", "capture", and
"fission".
Angular Filters#
Add polar-cosine boundaries to retain angular information:
angular_flux_tally = mcdc.Tally(
name="angular_flux",
mesh=mesh,
mu=np.linspace(-1.0, 1.0, 33),
scores=["flux"],
)
simulation.set_tallies([angular_flux_tally])
The resulting tally contains 32 polar-angle bins in each spatial bin. A
reference direction can be supplied with polar_reference; its default is
the positive z direction.
Surface-crossing Tallies#
A surface filter scores net current across a particular surface:
interface_current = mcdc.Tally(
name="interface_current",
surface=interface,
scores=["current-net"],
)
The current sign follows the orientation of the surface normal. For the
PlaneZ at the material interface, crossings toward increasing z contribute
positively and crossings toward decreasing z contribute negatively.
A cell filter can score current across every boundary of a cell:
shield_cell_current = mcdc.Tally(
name="shield_cell_current",
cell=shield_cell,
scores=["current-net", "current-in", "current-out"],
)
"current-in" and "current-out" are positive partial currents;
"current-net" retains the crossing sign.
Combining cell and surface filters restricts the tally to one surface while using the cell to classify incoming and outgoing crossings:
shield_interface_current = mcdc.Tally(
name="shield_interface_current",
surface=interface,
cell=shield_cell,
scores=["current-net", "current-in", "current-out"],
)
Attach every requested tally in one call:
simulation.set_tallies(
[
flux_tally,
interface_current,
shield_cell_current,
shield_interface_current,
]
)
Statistical Uncertainty#
MC/DC estimates tally uncertainty from statistically independent batches:
simulation.settings.N_particle = 1_000
simulation.settings.N_batch = 10
N_particle is the number of histories per batch. Increasing
N_particle reduces the noise within each batch, while N_batch controls
how many independent batch results contribute to the reported standard
deviation. At least two batches are required for a nonzero estimate.
Reading Tally Output#
Named tallies are stored under tallies/<name> in the output HDF5 file.
The filter grids and score results are stored below that group:
tallies/
slab_flux/
grid/
z
flux/
mean
sdev
Load and normalize the mesh flux with h5py:
import h5py
with h5py.File("slab_shielding.h5", "r") as output:
tally = output["tallies/slab_flux"]
z = tally["grid/z"][:]
flux = tally["flux/mean"][:]
flux_sdev = tally["flux/sdev"][:]
dz = z[1:] - z[:-1]
flux /= dz
flux_sdev /= dz
The score arrays contain values integrated over their bins. Divide by the applicable spatial, angular, energy, or time widths when a differential result is required.
Reducing an Angular Tally#
For the angular tally above, sum the angle-bin contributions to recover scalar flux. Weighting by the polar-cosine midpoint gives a midpoint approximation of the z-directed current:
with h5py.File("slab_shielding.h5", "r") as output:
tally = output["tallies/angular_flux"]
z = tally["grid/z"][:]
mu = tally["grid/mu"][:]
angular_flux = tally["flux/mean"][:]
angular_flux_sdev = tally["flux/sdev"][:]
dz = z[1:] - z[:-1]
dmu = mu[1:] - mu[:-1]
mu_mid = 0.5 * (mu[:-1] + mu[1:])
scalar_flux = np.sum(angular_flux, axis=0) / dz
scalar_flux_sdev = np.linalg.norm(angular_flux_sdev, axis=0) / dz
current = np.sum(
angular_flux * mu_mid[:, np.newaxis],
axis=0,
) / dz
The exact array-axis order follows the active filters and is recorded by the corresponding grids in the tally group. Inspect the output shapes before performing reductions.
Verification#
Before drawing conclusions from a tally:
Confirm that its filters cover the intended phase-space region.
Check that the result changes consistently when the mesh is refined.
Increase the particle population and verify that uncertainty decreases.
Use current tallies to check particle balance where appropriate.
Compare against an analytic or benchmark solution when one is available.
The mcdc.Tally API reference documents all supported scores and filter
combinations.