Skip to content

Add full-DOF matrix-free action entry point#307

Merged
cmhamel merged 1 commit into
mainfrom
feature/matrix-free-action-full-dof
Jun 8, 2026
Merged

Add full-DOF matrix-free action entry point#307
cmhamel merged 1 commit into
mainfrom
feature/matrix-free-action-full-dof

Conversation

@lxmota

@lxmota lxmota commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds assemble_matrix_free_action_full!(asm, func, U_full, v_full, p), a sibling of the existing assemble_matrix_free_action! that accepts full-DOF U_full and v_full vectors instead of free-DOF Uu/Vu. Both vectors must have length length(p.field.data).

Why this primitive

The existing free-DOF entry point writes Vu into p.hvp_scratch_field's free slots only, leaving the BC slots implicitly zero. That's correct for Krylov-style J·v calls — v is a perturbation ranging over unknowns and the BC component genuinely is zero.

It is wrong for residual-style evaluations like c_M·M·dU in implicit Newmark with time-varying Dirichlet BCs: there, dU has a nonzero BC slice dU_BC = g(t_{n+1}) − U_pred_BC, and the silently-dropped M_{f,BC}·dU_BC cross-term biases the inertial residual at boundary-adjacent free rows. (The downstream Carina.jl integrator currently has this exact bug; the fix is a small Carina-side change once this primitive lands.)

The new entry point gives the caller a direct way to inject the BC contribution: copyto! into p.field.data and p.hvp_scratch_field.data bypasses _update_for_assembly!'s "scatter free into unknowns, write BC from bc_cache.vals" logic; the element kernel then sees v_el including the BC component and produces the cross-term naturally. This mirrors Norma.jl's full-DOF-state design without changing how Carina-style free-DOF integrators store U/V/A elsewhere.

Carefully restoring the contract

After the action loop, BC slots of p.hvp_scratch_field are zeroed out. Were this skipped, a subsequent free-DOF Krylov J·v call would silently read leftover v_BC values and return a contaminated action — turning what is a useful new entry point into a tripwire for anyone alternating the two paths.

Introduces assemble_matrix_free_action_full!(asm, func, U_full, v_full, p),
a sibling of the existing free-DOF assemble_matrix_free_action! that
accepts full-DOF U_full and v_full vectors instead of the free-DOF Uu
and Vu.  Both vectors must have length equal to p.field.data.

The existing free-DOF entry point writes Vu into p.hvp_scratch_field's
free slots only, leaving the BC slots implicitly zero.  This is the
right semantics for Krylov-style J·v calls, where v is a perturbation
ranging over unknowns and the BC component genuinely is zero.  It is
the wrong semantics for residual-style evaluations of c_M·M·dU in
implicit Newmark with time-varying Dirichlet BCs: there, dU has a
nonzero BC slice equal to g(t_{n+1}) − U_pred_BC, and the silently
dropped M_{f,BC}·dU_BC cross-term biases the inertial residual at
boundary-adjacent free rows.

The full-DOF entry point gives the caller a direct way to inject the
BC contribution: copyto! into p.field.data and p.hvp_scratch_field.data
bypasses _update_for_assembly!'s "scatter free into unknowns, write BC
from bc_cache.vals" logic; the element kernel then sees v_el including
the BC component and produces the cross-term naturally.  This mirrors
Norma.jl's full-DOF-state design without changing how Carina-style
free-DOF integrators store U/V/A elsewhere.

Carefully restores the contract for the free-DOF entry point: after
the action loop, BC slots of p.hvp_scratch_field are zeroed out.  Were
this skipped, a subsequent free-DOF Krylov J·v call would silently
read leftover v_BC values and return a contaminated action.

Test (TestAssemblers.jl) validates three invariants against a sibling
no-BC assembler that produces the unrestricted element-scatter K_full:
  (a) On free rows, the full-DOF action equals (K_full · v_full)[free]
      for v_full with nonzero BC slots (rtol=1e-6 accommodates sparse
      SpMV vs per-element scatter floating-point reordering).
  (b) Zeroing v_full's BC slots collapses the full-DOF entry point
      onto the free-DOF entry point bit-for-bit.
  (c) The two answers in (a) and (b) genuinely differ — i.e., the
      cross-term is actually carrying signal on this mesh, so the
      validation is non-trivial.

The condensed-assembler approach normally used elsewhere does NOT
work as a reference: FEC's sparsity-pattern construction
(SparsityPatterns.jl:200-249) drops entries whose row OR column is a
Dirichlet DOF, even in condensed mode.  Removing the BCs from the
assembler entirely is the only way to retain K_{f,BC}.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.77%. Comparing base (56270a8) to head (a594e3a).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #307      +/-   ##
==========================================
+ Coverage   65.59%   65.77%   +0.18%     
==========================================
  Files          56       56              
  Lines        4868     4894      +26     
==========================================
+ Hits         3193     3219      +26     
  Misses       1675     1675              

☔ 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.

@lxmota

lxmota commented Jun 7, 2026

Copy link
Copy Markdown
Contributor Author

The two failure modes here are pre-existing on main and not introduced by this PR.

1. "CI" matrix (GitHub-hosted runners, Julia 1.10/1.12 × macOS/Ubuntu/Windows) — the Julia test step itself passes on every runner (Testing FiniteElementContainers tests passed shows up in the log right before the post-test step). What kills the job is the subsequent codecov/codecov-action@v5 step, which fails its GPG signature check on the downloaded codecov binary:

gpg: Can't check signature: No public key
==> Could not verify signature. Please contact Codecov if problem continues
##[error]Process completed with exit code 1.

Same failure on main commit 56270a8 — see main CI run 27076979406. This is a Codecov-side regression hitting every PR; nothing in this PR touches the workflow or the upload step.

The self-hosted runners (which presumably take a different path that doesn't hit the broken codecov-action step) pass on both this PR and main. codecov/patch and codecov/project report pass, so coverage did make it through somehow.

2. build job (Documenter) — fails with :missing_docs. Cited symbols:

  • FiniteElementContainers.assemble_lumped_mass!
  • FiniteElementContainers.lumped_mass
  • FiniteElementContainers.InitialCondition

None of these are touched by this PR — assemble_lumped_mass! and lumped_mass came in with #306, InitialCondition came in with an earlier PR (#303 family). The Documenter workflow has been failing on main since at least commit fd5f098, see main Documentation run 27076979415.

Happy to send a separate small PR adding the missing docs/src/ entries for the three symbols if that's wanted — just say the word. Not bundling it here so this PR stays scoped to the matrix-free action change.

My new assemble_matrix_free_action_full! IS picked up cleanly by the existing Pages = [\"MatrixAction.jl\"] @autodocs block in docs/src/assemblers.md; it isn't on the missing-docs list.

@cmhamel cmhamel merged commit 4eb7192 into main Jun 8, 2026
4 of 19 checks passed
@cmhamel cmhamel deleted the feature/matrix-free-action-full-dof branch June 8, 2026 00:35
@cmhamel

cmhamel commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

The two failure modes here are pre-existing on main and not introduced by this PR.

1. "CI" matrix (GitHub-hosted runners, Julia 1.10/1.12 × macOS/Ubuntu/Windows) — the Julia test step itself passes on every runner (Testing FiniteElementContainers tests passed shows up in the log right before the post-test step). What kills the job is the subsequent codecov/codecov-action@v5 step, which fails its GPG signature check on the downloaded codecov binary:

gpg: Can't check signature: No public key
==> Could not verify signature. Please contact Codecov if problem continues
##[error]Process completed with exit code 1.

Same failure on main commit 56270a8 — see main CI run 27076979406. This is a Codecov-side regression hitting every PR; nothing in this PR touches the workflow or the upload step.

The self-hosted runners (which presumably take a different path that doesn't hit the broken codecov-action step) pass on both this PR and main. codecov/patch and codecov/project report pass, so coverage did make it through somehow.

2. build job (Documenter) — fails with :missing_docs. Cited symbols:

* `FiniteElementContainers.assemble_lumped_mass!`

* `FiniteElementContainers.lumped_mass`

* `FiniteElementContainers.InitialCondition`

None of these are touched by this PR — assemble_lumped_mass! and lumped_mass came in with #306, InitialCondition came in with an earlier PR (#303 family). The Documenter workflow has been failing on main since at least commit fd5f098, see main Documentation run 27076979415.

Happy to send a separate small PR adding the missing docs/src/ entries for the three symbols if that's wanted — just say the word. Not bundling it here so this PR stays scoped to the matrix-free action change.

My new assemble_matrix_free_action_full! IS picked up cleanly by the existing Pages = [\"MatrixAction.jl\"] @autodocs block in docs/src/assemblers.md; it isn't on the missing-docs list.

I'm fixing up the codecov in #308 and should get around to the docs in a subsequent PR. Thank for this PR @lxmota this is a subtlety I had not thought about carefully before.

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