Source code for orpheus.derivations.homogeneous

"""SymPy derivations for infinite homogeneous medium eigenvalues.

Derives analytical k_inf for 1-group, 2-group, and 4-group systems
using symbolic matrix algebra, then evaluates numerically.
"""

from __future__ import annotations

import numpy as np
import sympy as sp
from scipy.sparse import csr_matrix

from orpheus.data.macro_xs.mixture import Mixture

from ._eigenvalue import kinf_homogeneous
from ._types import VerificationCase


def _make_mixture(
    sig_t: np.ndarray,
    sig_c: np.ndarray,
    sig_f: np.ndarray,
    nu: np.ndarray,
    chi: np.ndarray,
    sig_s: np.ndarray,
) -> Mixture:
    """Build a Mixture from N-group arrays."""
    ng = len(sig_t)
    eg = np.logspace(7, -3, ng + 1)
    return Mixture(
        SigC=sig_c.copy(), SigL=np.zeros(ng),
        SigF=sig_f.copy(), SigP=(nu * sig_f).copy(),
        SigT=sig_t.copy(), SigS=[csr_matrix(sig_s)],
        Sig2=csr_matrix((ng, ng)), chi=chi.copy(), eg=eg.copy(),
    )


# ═══════════════════════════════════════════════════════════════════════
# 1-group: k = nu * Sigma_f / Sigma_a  (fully symbolic)
# ═══════════════════════════════════════════════════════════════════════

[docs] def derive_1g() -> VerificationCase: r"""1-group infinite medium eigenvalue. .. math:: k_\infty = \frac{\nu \Sigma_f}{\Sigma_a} """ nu_s, Sig_f, Sig_a = sp.symbols(r'\nu \Sigma_f \Sigma_a', positive=True) k_expr = nu_s * Sig_f / Sig_a # Numeric XS xs = dict(sig_t=1.0, sig_c=0.2, sig_f=0.3, nu=2.5, sig_s_diag=0.5) sig_a_val = xs["sig_c"] + xs["sig_f"] k_val = kinf_homogeneous( sig_t=np.array([xs["sig_t"]]), sig_s=np.array([[xs["sig_s_diag"]]]), nu_sig_f=np.array([xs["nu"] * xs["sig_f"]]), chi=np.array([1.0]), ) latex = ( r"For a 1-group infinite homogeneous medium, the neutron balance is:" "\n\n" r".. math::" "\n" r" \Sigma_a \phi = \frac{1}{k} \nu \Sigma_f \phi" "\n\n" r"which gives the analytical eigenvalue:" "\n\n" r".. math::" "\n" rf" k_\infty = {sp.latex(k_expr)} = " rf"\frac{{{xs['nu']} \times {xs['sig_f']}}}" rf"{{{sig_a_val}}} = {k_val:.6f}" ) mix = _make_mixture( sig_t=np.array([xs["sig_t"]]), sig_c=np.array([xs["sig_c"]]), sig_f=np.array([xs["sig_f"]]), nu=np.array([xs["nu"]]), chi=np.array([1.0]), sig_s=np.array([[xs["sig_s_diag"]]]), ) return VerificationCase( name="homo_1eg", k_inf=k_val, method="homo", geometry="--", n_groups=1, n_regions=1, materials={0: mix}, geom_params={}, latex=latex, description="1-group infinite medium: k = nu*Sig_f / Sig_a = 1.5", tolerance="< 1e-12", )
# ═══════════════════════════════════════════════════════════════════════ # 2-group: solve characteristic polynomial of 2x2 inv(A)*F # ═══════════════════════════════════════════════════════════════════════
[docs] def derive_2g() -> VerificationCase: r"""2-group infinite medium eigenvalue via characteristic polynomial. The eigenvalue problem is :math:`\mathbf{A}\phi = \frac{1}{k}\mathbf{F}\phi` where :math:`\mathbf{A} = \text{diag}(\Sigma_t) - \Sigma_s^T` and :math:`\mathbf{F} = \chi \otimes (\nu\Sigma_f)`. """ # Numeric XS (same as benchmarks.py) sig_t = np.array([0.50, 1.00]) sig_c = np.array([0.01, 0.02]) sig_f = np.array([0.01, 0.08]) nu = np.array([2.50, 2.50]) chi = np.array([1.00, 0.00]) sig_s = np.array([ [0.38, 0.10], [0.00, 0.90], ]) # Symbolic derivation A_sym = sp.Matrix([ [sig_t[0] - sig_s[0, 0], -sig_s[1, 0]], [-sig_s[0, 1], sig_t[1] - sig_s[1, 1]], ]) F_sym = sp.Matrix([ [chi[0] * nu[0] * sig_f[0], chi[0] * nu[1] * sig_f[1]], [chi[1] * nu[0] * sig_f[0], chi[1] * nu[1] * sig_f[1]], ]) k_val = kinf_homogeneous( sig_t=sig_t, sig_s=sig_s, nu_sig_f=nu * sig_f, chi=chi, ) latex = ( r"For 2-group infinite medium with downscatter only:" "\n\n" r".. math::" "\n" rf" \mathbf{{A}} = {sp.latex(A_sym)}, \quad " rf"\mathbf{{F}} = {sp.latex(F_sym)}" "\n\n" r"The eigenvalue is the dominant root of " r":math:`\det(\mathbf{A}^{-1}\mathbf{F} - \lambda \mathbf{I}) = 0`:" "\n\n" r".. math::" "\n" rf" k_\infty = {k_val:.10f}" ) mix = _make_mixture(sig_t, sig_c, sig_f, nu, chi, sig_s) return VerificationCase( name="homo_2eg", k_inf=k_val, method="homo", geometry="--", n_groups=2, n_regions=1, materials={0: mix}, geom_params={}, latex=latex, description="2-group infinite medium (fast + thermal, downscatter only)", tolerance="< 1e-12", )
# ═══════════════════════════════════════════════════════════════════════ # 4-group: symbolic matrix structure, numeric eigenvalue # ═══════════════════════════════════════════════════════════════════════
[docs] def derive_4g() -> VerificationCase: r"""4-group infinite medium eigenvalue. Symbolic 4x4 matrix with numeric XS substituted before solving. """ sig_c = np.array([0.01, 0.02, 0.03, 0.05]) sig_f = np.array([0.005, 0.01, 0.05, 0.10]) nu = np.array([2.80, 2.60, 2.50, 2.45]) chi = np.array([0.60, 0.35, 0.05, 0.00]) sig_s = np.array([ [0.28, 0.08, 0.02, 0.005], [0.00, 0.40, 0.12, 0.06], [0.00, 0.00, 0.55, 0.22], [0.00, 0.00, 0.00, 0.90], ]) sig_t = sig_c + sig_f + sig_s.sum(axis=1) k_val = kinf_homogeneous( sig_t=sig_t, sig_s=sig_s, nu_sig_f=nu * sig_f, chi=chi, ) latex = ( r"For 4-group infinite medium (fast :math:`\to` epithermal " r":math:`\to` thermal1 :math:`\to` thermal2), " r"with downscatter cascade and fission in all groups:" "\n\n" r".. math::" "\n" rf" \mathbf{{A}} = \text{{diag}}(\Sigma_t) - \Sigma_s^T \in " rf"\mathbb{{R}}^{{4 \times 4}}" "\n\n" r".. math::" "\n" rf" \mathbf{{F}} = \chi \otimes (\nu\Sigma_f) \in " rf"\mathbb{{R}}^{{4 \times 4}}" "\n\n" r"The dominant eigenvalue of :math:`\mathbf{A}^{-1}\mathbf{F}`:" "\n\n" r".. math::" "\n" rf" k_\infty = {k_val:.10f}" ) mix = _make_mixture(sig_t, sig_c, sig_f, nu, chi, sig_s) return VerificationCase( name="homo_4eg", k_inf=k_val, method="homo", geometry="--", n_groups=4, n_regions=1, materials={0: mix}, geom_params={}, latex=latex, description="4-group infinite medium (downscatter cascade)", tolerance="< 1e-12", )
[docs] def all_cases() -> list[VerificationCase]: """Return all homogeneous verification cases.""" return [derive_1g(), derive_2g(), derive_4g()]