4.2.3. An Infinite-Medium Eigenvalue

This tutorial verifies a one-group \(k\)-eigenvalue calculation against an analytic infinite-medium result and checks its normalized particle balance.

4.2.3.1. Define an exactly critical material

The one-group material has \(\Sigma_t=\Sigma_a=1\), \(\Sigma_f=0.5\), and \(\nu=2\). With reflecting boundaries there is no net leakage, so

\[k_\infty=\frac{\nu\Sigma_f}{\Sigma_a}=1.\]

The analytic value is formed directly from the cross-section properties exposed by MultiGroupXS.

[ ]:
from mpi4py import MPI
from pyopensn.aquad import GLProductQuadrature1DSlab
from pyopensn.context import Finalize
from pyopensn.mesh import OrthogonalMeshGenerator
from pyopensn.solver import DiscreteOrdinatesProblem, PowerIterationKEigenSolver
from pyopensn.xs import MultiGroupXS

rank = MPI.COMM_WORLD.rank
nodes = [i / 10.0 for i in range(11)]
mesh = OrthogonalMeshGenerator(node_sets=[nodes]).Execute()
mesh.SetUniformBlockID(0)

xs = MultiGroupXS()
xs.LoadFromOpenSn("simple_fissile_1g.xs")
analytic_keff = float(xs.nu_sigma_f[0] / xs.sigma_a[0])
quadrature = GLProductQuadrature1DSlab(n_polar=8, scattering_order=0)
problem = DiscreteOrdinatesProblem(
    mesh=mesh,
    num_groups=1,
    groupsets=[{"groups_from_to": (0, 0), "angular_quadrature": quadrature}],
    xs_map=[{"block_ids": [0], "xs": xs}],
    boundary_conditions=[
        {"name": "zmin", "type": "reflecting"},
        {"name": "zmax", "type": "reflecting"},
    ],
    options={
        "use_precursors": False,
        "verbose_inner_iterations": False,
        "verbose_outer_iterations": False,
    },
)

4.2.3.2. Verify the eigenvalue and balance

For a \(k\)-eigenvalue solve, ComputeBalanceTable divides fission production by the converged eigenvalue before forming the balance. The separate fission-rate and fission-production methods also make the material’s average neutron yield visible through their ratio.

[ ]:
solver = PowerIterationKEigenSolver(
    problem=problem, k_tol=1.0e-12, compute_balance=True
)
solver.Initialize()
solver.Execute()

keff = float(solver.GetEigenvalue())
balance = solver.ComputeBalanceTable()
balance_residual = abs(float(balance["balance"]))
fission_rate = float(problem.ComputeFissionRate("new"))
fission_production = float(problem.ComputeFissionProduction("new"))
average_nu = fission_production / fission_rate

if rank == 0:
    print(f"Analytic infinite-medium eigenvalue={analytic_keff:.8e}")
    print(f"Computed infinite-medium eigenvalue={keff:.8e}")
    print(f"Eigenvalue balance residual={balance_residual:.8e}")
    print(f"Fission production-to-rate ratio={average_nu:.8e}")
assert abs(keff - analytic_keff) < 1.0e-10
assert balance_residual < 1.0e-10
assert abs(average_nu - 2.0) < 1.0e-12

if "opensn_console" not in globals():
    from IPython import get_ipython
    if get_ipython() is not None:
        Finalize()
        MPI.Finalize()