Skip to content

Include nodal normal derivatives in mortar contact Jacobians - #33390

Open
Andreas-Lepak wants to merge 9 commits into
idaholab:nextfrom
Andreas-Lepak:PR_nodal_normal_derivatives
Open

Include nodal normal derivatives in mortar contact Jacobians#33390
Andreas-Lepak wants to merge 9 commits into
idaholab:nextfrom
Andreas-Lepak:PR_nodal_normal_derivatives

Conversation

@Andreas-Lepak

@Andreas-Lepak Andreas-Lepak commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

closes #33378

Reason

Mechanical mortar contact updates its averaged secondary nodal normals as the displaced geometry changes, but their coordinate variation is currently omitted from the Jacobian. This leaves out normal-direction and tangent-basis contributions on curved interfaces.

Design

Include derivatives of the normalized, nodal-JxW-weighted secondary normals and their Householder
tangent basis by default for quasistatic frictionless and Coulomb contact using the mortar and
non-augmented mortar_penalty formulations. Residual-only evaluations retain the stored geometry
values.

The implementation shares the oriented face-area construction used by Assembly::computeFaceMap().
It forms the JxW-weighted nodal area-vector sum directly from nodal coordinates,

$$\mathbf a_A=\sum_{e\in(A)}\mathbf n_{eA}J_{eA}, \qquad \mathbf n_A=\frac{\mathbf a_A}{|\mathbf a_A|},$$

and lets AD differentiate the edge rotation or face cross product, averaging, normalization, and
Householder tangent construction. The stored residual normals and their Jacobian derivatives use
this same construction.

The nodal-normal derivatives span the secondary face one-ring, so the existing mortar relationship
manager path adds the corresponding ghosting and sparsity couplings. Because different contact
residual rows can then have different sparse AD supports, Assembly::cacheJacobian() forms their
union column layout before applying constraints. Its common-support path remains unchanged.

The mortar segment topology, projection coordinates, integration measures, moving overlap boundaries, and active-set changes remain frozen. Dynamic mortar, augmented-Lagrangian penalty contact, Cartesian-LM, cohesive-zone, and nonmortar formulations retain their existing behavior.

Impact

Contact values and residual equations are unchanged, but supported quasistatic mortar inputs gain additional Jacobian entries and one-ring sparsity couplings by default. This can change nonlinear convergence and adds setup, storage, and assembly cost. The required AD capacity depends on the valence of the secondary node star; high-order or highly connected interfaces may require configuring MOOSE with a larger derivative size.

Direct dynamic-contact inputs now use dedicated dynamic weighted-gap and weighted-velocity objects
so their frozen-normal behavior remains explicit. ContactAction selects these objects
automatically.

@moosebuild

moosebuild commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Job Documentation, step Docs: sync website on 250e9c7 wanted to post the following:

View the site here

This comment will be updated on new commits.

@Andreas-Lepak
Andreas-Lepak force-pushed the PR_nodal_normal_derivatives branch 3 times, most recently from 99aaad9 to 8d19377 Compare July 23, 2026 15:48

@lindsayad lindsayad left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that you're able to make tests pass by simply changing the comparison tolerance which is good and makes sense. If we'd needed a bunch of regolds (which would be an indication of something problematic) I would've suggested also doing #33422 at this time ... but let's not

exodiff = 'frictional-nodal-min-normal-lm-mortar-pdass-tangential-lm-mortar-disp_out.e'
detail = 'using verbose input file'
cli_args = "Postprocessors/active=''"
# The nodal-normal Jacobian changes the PJFNK path while preserving the converged solution.

@lindsayad lindsayad Jul 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to modify AGENTS.md or something so that agents stop adding comments based in historical context. I don't think this comment nor other comments like it will be helpful outside of this pull request and may even add confusion to future readers of the test spec

@Andreas-Lepak

Copy link
Copy Markdown
Collaborator Author

Distributed LM contact can still cause local-local PETSc reallocations under strict preallocation. Fixing this properly requires a new mortar-specific extra-sparsity registration mechanism; the relationship manager cannot return locally owned elements. I left that out because it is not causing these CIVET failures and the attempted shortcuts either violated libMesh ownership rules or substantially over-densified the matrix. I do not recommend restoring structural-zero insertion.

(generated by GPT-5 via Codex)

Andreas Henrik Frederiksen and others added 9 commits July 31, 2026 11:05
refs idaholab#33378

Co-authored-by: Andreas Henrik Frederiksen <andreas.frederiksen@inl.gov>

Co-authored-by: OpenAI Codex <codex@openai.com>
refs idaholab#33378

Co-authored-by: Andreas Henrik Frederiksen <andreas.frederiksen@inl.gov>

Co-authored-by: OpenAI Codex <codex@openai.com>
refs idaholab#33378

Co-authored-by: Andreas Henrik Frederiksen <andreas.frederiksen@inl.gov>

Co-authored-by: OpenAI Codex <codex@openai.com>
refs idaholab#33378

Co-authored-by: Andreas Henrik Frederiksen <andreas.frederiksen@inl.gov>

Co-authored-by: OpenAI Codex <codex@openai.com>
@Andreas-Lepak
Andreas-Lepak force-pushed the PR_nodal_normal_derivatives branch from a9eba82 to 250e9c7 Compare July 31, 2026 22:12
@lindsayad

Copy link
Copy Markdown
Member

You can use hash table matrix assembly to avoid relying on preallocated sparsity patterns

Comment on lines +3140 to +3143
// MetaPhysicL stores sparse derivative indices in sorted order, so equal index arrays identify
// rows with the same derivative support regardless of insertion order.
if (current_dofs.size() != first_dofs.size() ||
!std::equal(first_dofs.begin(), first_dofs.end(), current_dofs.begin()))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite a disruptive change. We have never failed this assertion before, sometimes with dof set sizes of several hundred. This has now introduced an O(several hundred) comparison for those dense sparsity cases roughly per element in all compilation methods. I think we should try very hard not to do this

@moosebuild

Copy link
Copy Markdown
Contributor

Job Test, step Results summary on 250e9c7 wanted to post the following:

Framework test summary

Compared against 846a5c4 in job civet.inl.gov/job/4025777.

Added tests

Test Time (s) Memory (MB)
kernels/diffusion_with_hanging_node.test_ad_row_dependent_jacobian 0.60 58.79
kernels/diffusion_with_hanging_node.test_ad_reordered_row_support 0.54 90.05

Modules test summary

Compared against 846a5c4 in job civet.inl.gov/job/4025777.

Added tests

Test Time (s) Memory (MB)
contact/test:nodal_normal_derivatives.mortar_penalty_friction_jacobian 1.75 156.85
contact/test:nodal_normal_derivatives.mortar_contact_pjfnk 0.82 123.35
contact/test:nodal_normal_derivatives.interpolated_normals_rejected 0.59 67.85
contact/test:nodal_normal_derivatives.tri6_normal_derivatives SKIP 0.00
contact/test:nodal_normal_derivatives.curved_3d_parallel SKIP 0.00
contact/test:nodal_normal_derivatives.hex27_quad9 SKIP 0.00

@lindsayad lindsayad left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have left line-level comments on the remaining correctness and SQA issues.

Design note: I do not think static preallocation can be made reliable for moving-mesh contact. The contact sparsity pattern may change at the first nonlinear iteration, and coupling functors and matrix preallocation will not be rerun at every nonlinear iteration. If the goal is to avoid new nonzero allocations, hash-table matrix assembly is the appropriate mechanism.

Suggested: add a July 2026 newsletter entry because this changes default Jacobian behavior and the assembly implications are important for users.

Review generated by GPT-5 via Codex, at the request of @lindsayad.


/// Enable derivatives during construction of a supported quasistatic contact constraint.
/// Dynamic contact uses the same user object classes and intentionally leaves this disabled.
void includeNodalNormalDerivatives() const;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required: includeNodalNormalDerivatives() changes observable behavior by setting _use_nodal_normal_derivatives, so this is configuration state rather than a logical cache. Please avoid configuring it through a const method and mutable flag. Make this construction-time state, or use an explicit non-const configuration path.

(generated by GPT-5 via Codex)

"Face nodal geometry vectors and quadrature weights must have the same size.");

// Use the parent-side map rather than a standalone lower-dimensional basis. This preserves
// enriched mappings such as the TET14-to-TRI7 face.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required: Add a TET14/TRI7 contact regression that exercises this enriched parent-side mapping. The new tests cover TET10/TRI6 and HEX27/QUAD9, but not the exceptional mapping singled out here; without that test, this branch’s stated design rationale is not covered by an SQA requirement.

(generated by GPT-5 via Codex)

return;

if (!_allow_nodal_normal_derivatives)
mooseError("Nodal-normal derivatives are not supported by user object '", name(), "'.");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required: Add RunException coverage for the new validation paths: an unsupported weighted-gap user object, a non-nodal displacement variable, and a displacement variable in a different nonlinear system. The current interpolated_normals_rejected test exercises only the interpolation check.

(generated by GPT-5 via Codex)


auto nodal_normal = weighted_area_vector.unit();
const auto & stored_normal = libmesh_map_find(_secondary_node_to_nodal_normal, secondary_node);
mooseAssert((MetaPhysicL::raw_value(nodal_normal) - stored_normal).norm() < 100 * TOLERANCE,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required: Add nearby rationale for why 100 * TOLERANCE is the appropriate equivalence threshold here. AGENTS.md requires the source or numerical rationale for non-obvious tolerances so future changes can distinguish numerical intent from an arbitrary allowance.

(generated by GPT-5 via Codex)

TEST(MortarNodalGeometryTest, weightedNormalAndHouseholderTangents)
{
constexpr dof_id_type derivative_index = 0;
constexpr Real epsilon = 1e-7;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required: Add a nearby comment explaining the finite-difference step and how it supports the 1e-8 derivative and 1e-14 orthogonality tolerances below, including the relevant truncation or roundoff rationale.

(generated by GPT-5 via Codex)

@moosebuild

Copy link
Copy Markdown
Contributor

Job Coverage, step Generate coverage on 250e9c7 wanted to post the following:

Framework coverage

846a5c #33390 250e9c
Total Total +/- New
Rate 86.00% 85.96% -0.04% 100.00%
Hits 138820 138852 +32 168
Misses 22598 22675 +77 0

Diff coverage report

Full coverage report

Modules coverage

Contact

846a5c #33390 250e9c
Total Total +/- New
Rate 91.29% 91.35% +0.06% 100.00%
Hits 5021 5109 +88 117
Misses 479 484 +5 0

Diff coverage report

Full coverage report

Full coverage reports

Reports

This comment will be updated on new commits.

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.

Include nodal-normal derivatives in mortar contact Jacobians

3 participants