You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Investigate adding angle-informed weight windows (FW/CADIS-Ω) to OpenMC's random-ray FW-CADIS pipeline for fusion fixed-source VR (deep penetration + streaming through ports/ducts/gaps), and do it as a staged, gated plan that measures benefit before building anything large. The conclusion of the design analysis below is: extend random ray — do not build a from-scratch Denovo-like Sₙ solver — and prove the benefit over a converged ℓ=0 FW-CADIS map before writing any angular-moment machinery.
Related: #106 (RR FW-CADIS distance auto-tune) and the convert_to_multigroup convergence auto-tune. Tier 0 of this plan is that work.
Background: the angular-importance gap
CADIS/FW-CADIS build weight windows (WW) from the scalar adjoint flux φ†. In streaming geometry the importance is strongly direction-dependent (a neutron headed into a duct mouth is worth orders of magnitude more than one headed at the wall beside it), and the scalar adjoint averages that away.
FW/CADIS-Ω (Munk & Slaybaugh, ANS M&C 2017, arXiv:1612.00793) replaces φ† with a forward-weighted adjoint scalar flux
and feeds it into the same FW-CADIS WW formula. The output is still ordinary space/energy WW (angle-informed, not angle-dependent), so the WW file and the CE transport step are unchanged. By spherical-harmonic orthogonality the numerator is a contraction of the angular flux moments of the forward and adjoint solves; the dominant correction is the ℓ=1 (current) term J·J†, which is large and positive where the forward and adjoint currents align along a streaming channel.
Key finding: today this is a no-op in OpenMC
OpenMC's random-ray solver is strictly P0 / isotropic-scatter and stores only the scalar (ℓ=0) flux:
scatter source is a pure G×G sigma_s × scalar-flux contraction, no Legendre index — src/random_ray/flat_source_domain.cpp:121-139
anisotropic MGXS is hard-rejected — src/random_ray/random_ray_simulation.cpp:79 (fatal_error("Anisotropic MGXS detected..."))
the only stored "moments" are spatial linear-source gradients (MomentArray = Position), not angular — include/openmc/random_ray/moment_matrix.h:10-14
With only ℓ=0 available, φ†_Ω collapses exactly to φ† → CADIS-Ω is bit-identical to FW-CADIS. The missing input is angular flux moments (ℓ≥1) for both the forward and adjoint solves.
Decision: extend random ray, not build Sₙ
A 4-lens design review (performance / engineering-cost / physics-accuracy / OpenMC-strategy) was unanimous for extending RR. Rationale:
Ray effects cut for RR. Sₙ ray effects are worst in optically-thin ducts/voids/streaming with localized sources — the exact fusion regime — and the standard mitigations (first-collision source, quadrature rotation, finer order) are problem-specific knobs. RR/MOC samples direction continuously and is ray-effect-free by construction. (The CADIS-Ω paper itself flagged ray effects on its labyrinth case.)
Low-knob. Sₙ irreducibly adds quadrature order/type, spatial differencing, DSA/upscatter acceleration, negative-flux fixup, and a CAD→Cartesian voxel mesh. RR adds ~one internal knob (moment order, default P1) and reuses the existing CSG/DAGMC geometry and CE→MGXS pipeline.
Engineering / strategy. The FW-CADIS pipeline already exists and is tested on RR (forward+adjoint solves, set_fw_adjoint_sources at flat_source_domain.cpp:1234, WW export, 26 random_ray_* regression dirs incl. weightwindows_fw_cadis*); CADIS-Ω reuses it unchanged. A Denovo-like Sₙ is person-decades of zero-overlap code that doubles OpenMC's transport surface area and breaks the self-contained-MC ethos RR was built to honor.
Sₙ would only win if angular fidelity (high moments) dominated over ray-effect-freedom and the importance solve became the memory/wall-clock wall and the low-knob constraint were relaxed — a conjunction that does not hold here.
Important caveats / risks
RR moment noise is not benign. φ†_Ω's numerator is a product of two noisy estimators, and RR has documented structured biases (large overestimates, negative fluxes) in exactly the low-flux void/duct regions of interest (docs/source/methods/random_ray.rst:533-561). RR's real edge over Sₙ is ray-effect-freedom + geometry/pipeline reuse — not that its error averages away. Needs denominator flooring, moment smoothing, and an auto-fallback to ℓ=0.
Self-consistency. A physically consistent ℓ=1 current moment requires anisotropic scatter in the solve (lift the fatal_error, generate anisotropic MGXS — which grows the MGXS burden adding trigger to mgxs convert openmc-dev/openmc#3925/auto-tune is shrinking, and adds per-region angular source state). Prior art exists: anisotropic scattering (and linear sources) in the random ray method (Neame & Cosgrove, NSE 2024) — but validated on a thick reactor benchmark (C5G7), not sparse-hit fusion streaming.
No MPI domain decomposition in RR (random_ray_simulation.cpp:269, rank-0 only). Adding forward+adjoint × P1/P3 moments multiplies the single-node footprint and could hit the memory ceiling on the first real fusion CAD model. This is the one factor that would flip the answer toward Sₙ at extreme scale — gate on a memory/runtime check.
WW are applied angle-blind (space/energy bins only). CADIS-Ω is a scalar reweighting of the same map: in a straight duct it largely rescales importance ℓ=0 already places along the channel; the genuine redistribution is at bends/corners/junctions. So the benefit over a converged ℓ=0 map may be modest, and the published ~28× (Denovo vs coarse CADIS, one labyrinth) does not transfer.
Proposed staged plan
Tier 0 — ship auto-tuned ℓ=0 FW-CADIS (this largely exists).
Close #106 (runtime miss-rate-driven distance_inactive/distance_active) and fold in the convert_to_multigroup convergence auto-tune. This is today's genuine lowest-knob / highest-performance baseline — and possibly the whole answer.
GO / NO-GO gate (before any moment plumbing).
On a representative fusion single-bend duct problem, measure the FOM delta of an angle-informed importance map over the converged Tier-0 map. If the delta is small → stop; Tier 0 is the workflow.
Tier 1 — cheap post-hoc P1 current tally (only if Tier 0 underperforms).
Accumulate a per-region/per-group current moment J = ∫ ψ Ω dΩ on the existing P0 solve (accept P0-transport / P1-diagnostic inconsistency), for forward and adjoint, and form φ†_Ω from the ℓ=0 + ℓ=1 (J·J†) terms. Avoids lifting the fatal_error and avoids anisotropic MGXS. Reuses the existing directional-accumulation + locking + reduction pattern already proven for the linear-source spatial moments (src/random_ray/random_ray.cpp ~446-461, ~583-639). Substitute φ†_Ω at the single WW scoring point flat_source_domain.cpp:640-641 (scoped to the FW-CADIS flux path — that line also feeds total/fission scores). Add an auto-fallback to ℓ=0 when the J·J† moment is too noisy.
Tier 2 — full self-consistent CADIS-Ω (only if Tier 1 insufficient and measured FOM gap justifies).
Lift random_ray_simulation.cpp:79, add anisotropic-scatter transport + anisotropic MGXS (fold into the auto-tune, not a user knob), leverage the NSE-2024 anisotropic-RR machinery, extend to P3.
Parallel watch-item. Add MPI domain decomposition (or a hierarchical/coarsened importance mesh) to RR if the importance solve becomes the bottleneck.
The φ†_Ω contraction (P1 form to implement)
Expanding to first order, ψ(Ω) ≈ (1/4π)(φ + 3 J·Ω) with scalar flux φ = ∫ψ dΩ and current J = ∫ψΩ dΩ, and using ∫Ω dΩ = 0 and ∫(J·Ω)(J†·Ω) dΩ = (4π/3) J·J†, the CADIS-Ω quantity reduces (up to an overall constant absorbed by the WW 1/(2·max) normalization) to:
First term = the ordinary scalar adjoint flux → recovers plain FW-CADIS exactly (the N=0 baseline / regression check).
Second term = the angular correction: forward current · adjoint current, normalized by forward scalar flux; large and positive where both currents align along a streaming channel.
Inputs per (region, group): φ_fwd, φ†_adj (both already computed) plus the NEW current vectors J_fwd, J†_adj.
Watch: φ_fwd → 0 in deep voids (needs a floor); J·J† can be negative when currents anti-align (clamp φ†_Ω ≥ the ℓ=0 term, or floor at 0).
Mirror the linear-source spatial-moment accumulation at random_ray.cpp:600-646 (MomentArray, locking, MPI reduce). Ray direction u() and angular_flux_[g] are already in scope.
No duct/streaming geometry exists in-tree. The RR FW-CADIS regression tests tests/regression_tests/weightwindows_fw_cadis{,_local,_mesh}/ all use a simple three-region void-layered cube (random_ray_three_region_cube() in openmc/examples.py ~:875), not a true duct. The gate needs a new single-bend duct benchmark — extend the random_ray_* model factories in openmc/examples.py:875+, modeled on the labyrinth / single-bend duct from the references.
The metric is built in:tally.figure_of_merit = 1/(rel_err² · simulation_time) (openmc/tallies.py:955-962), with tally.std_dev (:589-609) and tally.mean. Gate = FOM(angle-informed WW) vs FOM(converged ℓ=0 FW-CADIS WW) at the deepest tally, both consuming their WW in the same CE transport.
Documented FOM comparison: angle-informed vs converged ℓ=0 on that problem (GO/NO-GO)
If GO: Tier-1 P1 current tally with N=0 reproducing FW-CADIS bit-for-bit as a regression baseline, plus auto-fallback to ℓ=0
Noise/robustness check at the deepest tally site / duct exit
Open questions / decisions to make
Moment order: start at P1 (current only). Is P3 ever worth it, given RR is ray-effect-free?
Consistency (the Tier-1→Tier-2 gate): does the cheap post-hoc P1 tally on a P0 solve yield a usable φ†_Ω, or is self-consistent anisotropic transport required? (Empirical.)
Denominator flooring: what floor for φ_fwd → 0 in deep voids? (reuse the ZERO_FLUX_CUTOFF pattern, or a relative floor.)
Negative correction: clamp φ†_Ω ≥ the ℓ=0 term, or floor at 0, when J·J† < 0?
Moment denoising: is coarse-mesh averaging of the currents needed, and at what length scale?
Auto-fallback trigger: what rel-err threshold on the J·J† moment falls back to ℓ=0 FW-CADIS?
Memory: does fwd+adj × (scalar + 3-vector current) per region·group fit a single node (no MPI DD) on a real fusion CAD model?
Side discussions & dead ends (for the record)
Approaches considered and rejected:
Build an in-tree Denovo-like Sₙ solver — rejected (see Decision): ray effects are worst in the fusion ducts/voids we target, it explodes user-facing knobs, it's person-decades of zero-overlap code, and it breaks the self-contained-MC ethos RR exists to honor.
Couple to an external Sₙ code (Denovo/ADVANTG) — rejected: reintroduces exactly the external deterministic dependency RR was built to eliminate; Denovo is SCALE-gated and drives MCNP, not OpenMC.
"Just swap φ†_Ω in" as a quick patch — dead end: there are no ℓ>0 angular moments in RR to swap, and the single WW scoring line (flat_source_domain.cpp:640-641) also feeds total/fission scores, so a blind variable replacement would corrupt other RR tallies. The substitution must be scoped to the FW-CADIS flux path.
Useful side insights that shaped the plan:
Backend-invariance collapses the RR-vs-Sₙ question. CADIS-Ω emits ordinary space/energy WW, so the FOM-dominating CE transport step is identical regardless of backend. The decision reduces to "which backend gives the best importance map per unit of one-time preprocessing effort" — which is why solver raw speed matters far less than map quality in the ducts.
The angular correction is dominated by ℓ=1. By spherical-harmonic orthogonality ∫ ψ ψ† dΩ = Σ_ℓm φ_ℓm φ†_ℓm; in a streaming duct the dominant correction is the current term J·J† (both currents align with the duct axis). So low order (P1) likely captures most of the benefit — this is what makes the cheap Tier-1 path viable and means high-order Sₙ fidelity is not where the win is.
"Add P1 to RR" has two distinct meanings (often conflated): a cheap post-hoc current tally on the existing P0 solve (Tier 1, inconsistent but possibly sufficient) vs self-consistent anisotropic P1 transport (Tier 2, needs anisotropic MGXS + lifting the fatal_error). Keeping these separate is what keeps Tier 1 cheap.
Course-correction during analysis: RR moment noise was initially framed as "benign zero-mean that averages out" — that's wrong. RR has structured biases (huge overestimates, negative fluxes) in low-flux void/duct regions, so the correct justification for RR is ray-effect-freedom + pipeline reuse, and the plan must include moment smoothing + ℓ=0 auto-fallback.
WW are applied angle-blind, so CADIS-Ω only reshapes the scalar map — it rescales importance ℓ=0 already places in a straight duct and redistributes meaningfully only at bends/corners. Hence the benefit-vs-converged-ℓ=0 question, and the GO/NO-GO gate, rather than assuming the paper's ~28× (which was Denovo vs coarse CADIS on one labyrinth and does not transfer).
MGXS auto-tune — branch trigger-mgxs-generation (commit 9f1dea8f0): convert_to_multigroup(nparticles=None) converges via a rel-err trigger (knob-free redo of the abandoned PR adding trigger to mgxs convert openmc-dev/openmc#3925). Tier 2's anisotropic-MGXS requirement must fold into this, not become a new knob.
References
Munk & Slaybaugh, "FW/CADIS-Ω: An Angle-Informed Hybrid Method for Deep-Penetration Radiation Transport," ANS M&C 2017 — https://arxiv.org/pdf/1612.00793
Summary
Investigate adding angle-informed weight windows (FW/CADIS-Ω) to OpenMC's random-ray FW-CADIS pipeline for fusion fixed-source VR (deep penetration + streaming through ports/ducts/gaps), and do it as a staged, gated plan that measures benefit before building anything large. The conclusion of the design analysis below is: extend random ray — do not build a from-scratch Denovo-like Sₙ solver — and prove the benefit over a converged ℓ=0 FW-CADIS map before writing any angular-moment machinery.
Related: #106 (RR FW-CADIS distance auto-tune) and the
convert_to_multigroupconvergence auto-tune. Tier 0 of this plan is that work.Background: the angular-importance gap
CADIS/FW-CADISbuild weight windows (WW) from the scalar adjoint flux φ†. In streaming geometry the importance is strongly direction-dependent (a neutron headed into a duct mouth is worth orders of magnitude more than one headed at the wall beside it), and the scalar adjoint averages that away.FW/CADIS-Ω (Munk & Slaybaugh, ANS M&C 2017, arXiv:1612.00793) replaces φ† with a forward-weighted adjoint scalar flux
and feeds it into the same FW-CADIS WW formula. The output is still ordinary space/energy WW (angle-informed, not angle-dependent), so the WW file and the CE transport step are unchanged. By spherical-harmonic orthogonality the numerator is a contraction of the angular flux moments of the forward and adjoint solves; the dominant correction is the ℓ=1 (current) term J·J†, which is large and positive where the forward and adjoint currents align along a streaming channel.
Key finding: today this is a no-op in OpenMC
OpenMC's random-ray solver is strictly P0 / isotropic-scatter and stores only the scalar (ℓ=0) flux:
sigma_s× scalar-flux contraction, no Legendre index —src/random_ray/flat_source_domain.cpp:121-139src/random_ray/random_ray_simulation.cpp:79(fatal_error("Anisotropic MGXS detected..."))MomentArray = Position), not angular —include/openmc/random_ray/moment_matrix.h:10-14With only ℓ=0 available, φ†_Ω collapses exactly to φ† → CADIS-Ω is bit-identical to FW-CADIS. The missing input is angular flux moments (ℓ≥1) for both the forward and adjoint solves.
Decision: extend random ray, not build Sₙ
A 4-lens design review (performance / engineering-cost / physics-accuracy / OpenMC-strategy) was unanimous for extending RR. Rationale:
set_fw_adjoint_sourcesatflat_source_domain.cpp:1234, WW export, 26random_ray_*regression dirs incl.weightwindows_fw_cadis*); CADIS-Ω reuses it unchanged. A Denovo-like Sₙ is person-decades of zero-overlap code that doubles OpenMC's transport surface area and breaks the self-contained-MC ethos RR was built to honor.Sₙ would only win if angular fidelity (high moments) dominated over ray-effect-freedom and the importance solve became the memory/wall-clock wall and the low-knob constraint were relaxed — a conjunction that does not hold here.
Important caveats / risks
docs/source/methods/random_ray.rst:533-561). RR's real edge over Sₙ is ray-effect-freedom + geometry/pipeline reuse — not that its error averages away. Needs denominator flooring, moment smoothing, and an auto-fallback to ℓ=0.fatal_error, generate anisotropic MGXS — which grows the MGXS burden adding trigger to mgxs convert openmc-dev/openmc#3925/auto-tune is shrinking, and adds per-region angular source state). Prior art exists: anisotropic scattering (and linear sources) in the random ray method (Neame & Cosgrove, NSE 2024) — but validated on a thick reactor benchmark (C5G7), not sparse-hit fusion streaming.random_ray_simulation.cpp:269, rank-0 only). Adding forward+adjoint × P1/P3 moments multiplies the single-node footprint and could hit the memory ceiling on the first real fusion CAD model. This is the one factor that would flip the answer toward Sₙ at extreme scale — gate on a memory/runtime check.Proposed staged plan
Tier 0 — ship auto-tuned ℓ=0 FW-CADIS (this largely exists).
Close #106 (runtime miss-rate-driven
distance_inactive/distance_active) and fold in theconvert_to_multigroupconvergence auto-tune. This is today's genuine lowest-knob / highest-performance baseline — and possibly the whole answer.GO / NO-GO gate (before any moment plumbing).
On a representative fusion single-bend duct problem, measure the FOM delta of an angle-informed importance map over the converged Tier-0 map. If the delta is small → stop; Tier 0 is the workflow.
Tier 1 — cheap post-hoc P1 current tally (only if Tier 0 underperforms).
Accumulate a per-region/per-group current moment J = ∫ ψ Ω dΩ on the existing P0 solve (accept P0-transport / P1-diagnostic inconsistency), for forward and adjoint, and form φ†_Ω from the ℓ=0 + ℓ=1 (J·J†) terms. Avoids lifting the
fatal_errorand avoids anisotropic MGXS. Reuses the existing directional-accumulation + locking + reduction pattern already proven for the linear-source spatial moments (src/random_ray/random_ray.cpp~446-461, ~583-639). Substitute φ†_Ω at the single WW scoring pointflat_source_domain.cpp:640-641(scoped to the FW-CADIS flux path — that line also feeds total/fission scores). Add an auto-fallback to ℓ=0 when the J·J† moment is too noisy.Tier 2 — full self-consistent CADIS-Ω (only if Tier 1 insufficient and measured FOM gap justifies).
Lift
random_ray_simulation.cpp:79, add anisotropic-scatter transport + anisotropic MGXS (fold into the auto-tune, not a user knob), leverage the NSE-2024 anisotropic-RR machinery, extend to P3.Parallel watch-item. Add MPI domain decomposition (or a hierarchical/coarsened importance mesh) to RR if the importance solve becomes the bottleneck.
The φ†_Ω contraction (P1 form to implement)
Expanding to first order,
ψ(Ω) ≈ (1/4π)(φ + 3 J·Ω)with scalar fluxφ = ∫ψ dΩand currentJ = ∫ψΩ dΩ, and using∫Ω dΩ = 0and∫(J·Ω)(J†·Ω) dΩ = (4π/3) J·J†, the CADIS-Ω quantity reduces (up to an overall constant absorbed by the WW1/(2·max)normalization) to:φ_fwd,φ†_adj(both already computed) plus the NEW current vectorsJ_fwd,J†_adj.φ_fwd → 0in deep voids (needs a floor);J·J†can be negative when currents anti-align (clampφ†_Ω ≥the ℓ=0 term, or floor at 0).Σ_{ℓ=2,3} φ_ℓm φ†_ℓm; defer unless P1 proves insufficient.Implementer's map (code pointers, branch
making-wheel-3)src/random_ray/random_ray.cpp:440-462(flat-source scalar accum) + void/linear variantsrandom_ray.cpp:600-646(MomentArray, locking, MPI reduce). Ray directionu()andangular_flux_[g]are already in scope.include/openmc/random_ray/source_region.h:367-385(besidescalar_flux_*)flux_swap(src/random_ray/source_region.cpp:175-181)set_fw_adjoint_sources(flat_source_domain.cpp:1234-1273) snapshots the forward scalar flux.transpose_scattering_matrix(flat_source_domain.cpp:1395-1414)flat_source_domain.cpp:640-649(random_ray_tally,SCORE_FLUX)src/weight_windows.cpp:624-658= 1/φ†, normalized by1/(2·max).fw_cadis_omegaopenmc/weight_windows.py:614; C++src/weight_windows.cpp:790-814(branch after:798)fatal_erroratrandom_ray_simulation.cpp:79random_ray_simulation.cpp:268-271to_hdf5weight_windows.cpp:739-747; importsimulation.cpp:162GO/NO-GO benchmark & metric
tests/regression_tests/weightwindows_fw_cadis{,_local,_mesh}/all use a simple three-region void-layered cube (random_ray_three_region_cube()inopenmc/examples.py~:875), not a true duct. The gate needs a new single-bend duct benchmark — extend therandom_ray_*model factories inopenmc/examples.py:875+, modeled on the labyrinth / single-bend duct from the references.tally.figure_of_merit = 1/(rel_err² · simulation_time)(openmc/tallies.py:955-962), withtally.std_dev(:589-609) andtally.mean. Gate = FOM(angle-informed WW) vs FOM(converged ℓ=0 FW-CADIS WW) at the deepest tally, both consuming their WW in the same CE transport.Acceptance criteria
Open questions / decisions to make
φ_fwd → 0in deep voids? (reuse theZERO_FLUX_CUTOFFpattern, or a relative floor.)φ†_Ω ≥the ℓ=0 term, or floor at 0, whenJ·J† < 0?J·J†moment falls back to ℓ=0 FW-CADIS?Side discussions & dead ends (for the record)
Approaches considered and rejected:
flat_source_domain.cpp:640-641) also feeds total/fission scores, so a blind variable replacement would corrupt other RR tallies. The substitution must be scoped to the FW-CADIS flux path.Useful side insights that shaped the plan:
∫ ψ ψ† dΩ = Σ_ℓm φ_ℓm φ†_ℓm; in a streaming duct the dominant correction is the current term J·J† (both currents align with the duct axis). So low order (P1) likely captures most of the benefit — this is what makes the cheap Tier-1 path viable and means high-order Sₙ fidelity is not where the win is.fatal_error). Keeping these separate is what keeps Tier 1 cheap.Related in-flight work
trigger-mgxs-generation(commit9f1dea8f0):convert_to_multigroup(nparticles=None)converges via a rel-err trigger (knob-free redo of the abandoned PR adding trigger to mgxs convert openmc-dev/openmc#3925). Tier 2's anisotropic-MGXS requirement must fold into this, not become a new knob.References