Skip to content

Constrained spline fit, exact discrete adjoint and QoI estimates for unstructured nearby problems - #2

Open
bwhewe-13 wants to merge 5 commits into
masterfrom
nearby-spline-fit
Open

bwhewe-13 wants to merge 5 commits into
masterfrom
nearby-spline-fit

Conversation

@bwhewe-13

@bwhewe-13 bwhewe-13 commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

The per-cell polynomial fit on unstructured meshes fits each side of a material interface independently, and the two sides disagree by about the size of the scheme error. That is enough to throw off the estimate near interfaces (1.55-2.07x the true error on the triangle MMS test), and on multi-material cores the k estimate can come out with the wrong sign.

_spline_fit.py fits one quintic tensor B-spline per connected material region, all regions and groups in one sparse least-squares solve, with weighted rows for flux and current continuity on interface faces and the Robin condition on boundary faces. Two configs:

  • smooth: also the diffusion equation (and its gradient) at the face points. Good when interfaces run straight to the boundary.
  • corner: used when interfaces meet at a corner (>30 deg) or enclose a small region (<8 knot spans across, e.g. a rod). The face PDE rows make the fit ill-posed there, so the PDE is applied softly at the cell centres instead, and r^alpha Phi(theta) corner functions are added, cut off radially before the shortest interface out of the corner ends.

Knots are 2.5 shortest diffusion lengths apart, clipped to 2-3 cells.

API: nearby_fixed_source / nearby_k_eigenvalue take bc= and use the spline fit when it is given (fit="local" keeps the old one, and nothing changes for calls without bc). nearby_k_eigenvalue(adjoint_solver=...) adds k_error_adjoint and the per-cell k_error_cells; nearby=False skips the nearby power iteration. Fixed-source fits use the corner config since only its interior rows can carry the source. NearbyKResult gets its new fields at the end with None defaults.

Second part: fv_operator.unstructured_operator builds the unstructured solver's operator as a sparse matrix, including the deferred non-orthogonal correction, so a converged solve satisfies it to the solver tolerance. Two things use it:

  • nearby_k_eigenvalue(method="direct") factorizes the operator once for the nearby power iteration and gets the adjoint for the k estimate from a bordered system instead of a second k solve. No fixed-source or adjoint solver needed. On 7k-15k cell meshes the whole estimate now takes about half a base solve (it was 1-2 base solves with SOR).
  • nearby_qoi estimates the error in ratios of reaction-rate integrals (region powers, absorber rates) from a generalized adjoint, several at once off one factorization, and returns the per-cell contributions.

The transpose has to be of the actual discrete operator. Running a solver on make_adjoint_materials transposes scatter and fission but not the least-squares gradient correction, which isn't symmetric, so on triangles and hexes adjoint-weighted estimates are off by 1-2% at every mesh size. On orthogonal quads the correction is zero and both agree.

Also fixes _xs_arrays reading a one-group nusigf as a fission matrix, which broke one-group nearby_k_eigenvalue.

Checks: the fit is identical (to the bit) to the research version on IAEA, a curved rodded core, a slab and a disk. MMS: quads 1.000, triangles 1.04-1.07. The assembled operator matches the research version exactly and its assembly-power estimates agree to 1e-12% on IAEA triangles. test_fv_operator.py checks the matrix against fixed-source and k solves on regular and jittered meshes, that the one-shot QoI estimate equals a central difference of the perturbed problem, and that it tracks the nearby estimate. Full suite passes, docs build with -W.

master is merged in, so this includes the round-off sweep stop from #1.

…al region and add a one-shot adjoint k estimate.
@codecov-commenter

codecov-commenter commented Oct 15, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 90.78014% with 78 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.49%. Comparing base (af2ed02) to head (302771d).

Files with missing lines Patch % Lines
src/ndiffusion/_spline_fit.py 89.87% 57 Missing ⚠️
src/ndiffusion/nearby.py 90.15% 13 Missing ⚠️
src/ndiffusion/fv_operator.py 95.33% 7 Missing ⚠️
src/ndiffusion/__init__.py 0.00% 1 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##           master       #2      +/-   ##
==========================================
+ Coverage   74.87%   81.49%   +6.62%     
==========================================
  Files          10       12       +2     
  Lines        1190     2027     +837     
==========================================
+ Hits          891     1652     +761     
- Misses        299      375      +76     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@bwhewe-13 bwhewe-13 changed the title Constrained spline fit and adjoint k estimate for unstructured nearby problems Constrained spline fit, exact discrete adjoint and QoI estimates for unstructured nearby problems Oct 17, 2026
@bwhewe-13
bwhewe-13 marked this pull request as ready for review October 17, 2026 18:20

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants