Skip to content

Add selectable 3D mortar triangulation options#32820

Open
Andreas-Lepak wants to merge 9 commits intoidaholab:nextfrom
Andreas-Lepak:mortar-triangulation-pr
Open

Add selectable 3D mortar triangulation options#32820
Andreas-Lepak wants to merge 9 commits intoidaholab:nextfrom
Andreas-Lepak:mortar-triangulation-pr

Conversation

@Andreas-Lepak
Copy link
Copy Markdown
Collaborator

@Andreas-Lepak Andreas-Lepak commented Apr 23, 2026

refs #32816

Reason

The 3D mortar path needs explicit user control over how clipped overlap polygons are triangulated,
and it also needs a way to control whether polygons that are already triangles should be split any
further.

Design

This change adds a triangulation parameter to 3D mortar contact and threads it through the
direct mortar-object path and the Contact/mortar action path. The supported user-facing values
are:

  • vertex
  • centroid
  • ear_clipping
  • delaunay

This change also adds a triangulate_triangles boolean parameter, defaulting to false.

The implementation updates the mortar triangulation helper so that:

  • vertex uses the vertex-fan path,
  • centroid uses the centroid split path,
  • ear_clipping uses the ear-clipping path,
  • delaunay uses the constrained Delaunay path when available,
  • already-triangular polygons remain intact when triangulate_triangles=false,
  • already-triangular polygons are centroid-subdivided when triangulate_triangles=true.

Regression coverage was added in the 3D mortar contact tests for both the action path and the
direct mortar-object path:

  • centroid triangulation with triangle subdivision enabled,
  • centroid triangulation with triangle subdivision disabled,
  • ear-clipping selection,
  • delaunay selection.

In addition to the focused MOOSE regressions, the four triangulation modes were exercised on 3D
HEX8, HEX27, and TET4 contact configurations during local verification.

Impact

This change adds new optional 3D mortar input parameters:

  • triangulation
  • triangulate_triangles

Existing inputs do not need to change. The default triangulate_triangles=false preserves the default behavior of leaving already-triangular polygons untouched.

- add triangulation selection for 3D mortar contact
- add triangulate_triangles control for triangular polygons
- add regression coverage for centroid, ear_clipping, and delaunay paths

refs idaholab#32816
@Andreas-Lepak Andreas-Lepak force-pushed the mortar-triangulation-pr branch from 377c4cf to eb665c3 Compare April 23, 2026 19:04
@Andreas-Lepak
Copy link
Copy Markdown
Collaborator Author

triangulation_methods_comparison

@Andreas-Lepak Andreas-Lepak force-pushed the mortar-triangulation-pr branch from 0d2a7bf to 950e2d4 Compare April 24, 2026 00:13
@moosebuild
Copy link
Copy Markdown
Contributor

moosebuild commented Apr 24, 2026

Job Documentation, step Docs: sync website on 60b8714 wanted to post the following:

View the site here

This comment will be updated on new commits.

@Andreas-Lepak Andreas-Lepak force-pushed the mortar-triangulation-pr branch from 950e2d4 to a8bb78d Compare April 24, 2026 14:32
Andreas Henrik Frederiksen and others added 2 commits April 24, 2026 14:19
- 3D mortar action/debug tests: RunApp → CheckFiles
  (check_files is only valid on the CheckFiles tester; RunApp rejected
  it, causing a fatal parser error and a non-zero exit that failed every
  test-running CI job)
- continuity-3d-non-conforming: restore Outputs/file_base cli_args on
  the hex8 and penalty-hex8 tests so the emitted Exodus output matches
  the existing gold (continuity_{penalty_,}sphere_hex8_out.e)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- MortarSegmentHelper::triangulatePoly: for the default centroid mode
  (without triangulate_triangles) short-circuit to the pre-PR algorithm
  before the new canonicalization/area-filtering path. The new path
  used an area-weighted polygon centroid and skipped degenerate
  triangles, which shifted integration weights for existing 3D mortar
  baselines (e.g. test/tests/mortar/3d-periodic.3d_periodic_mortar and
  modules/contact/test/tests/3d-mortar-contact.frictionless-mortar-3d).
  Reverting the default to the arithmetic-mean centroid fan preserves
  those baselines while keeping the opt-in modes (vertex, ear_clipping,
  delaunay, centroid+triangulate_triangles) on the new path.
- 3d-mortar-contact tests: change Executioner/end_time=0 to
  end_time=0.5 dtmin=0.5 for the action-ear-clipping-debug-mesh and
  action-delaunay-debug-mesh cases. With end_time=0 the simulation
  never takes a step, which trips restep testing ("Time 0 was never
  retried..."). The centroid variants already use end_time=0.5; this
  makes ear_clipping and delaunay consistent and exercises a real
  solve through each triangulation backend.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@moosebuild
Copy link
Copy Markdown
Contributor

Job Precheck, step Clang format on 772a270 wanted to post the following:

Your code requires style changes.

A patch was auto generated and copied here
You can directly apply the patch by running, in the top level of your repository:

curl -s https://mooseframework.inl.gov/docs/PRs/32820/clang_format/style.patch | git apply -v

Alternatively, with your repository up to date and in the top level of your repository:

git clang-format 162e78c71709df5be85663d216c89383cf162192

The previous commit's centroid-mode short-circuit had a 2-line "if"
condition that fits on a single line; clang-format flagged it in
Precheck. Collapsing it unblocks the rest of the CI matrix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@moosebuild
Copy link
Copy Markdown
Contributor

moosebuild commented Apr 27, 2026

Job Coverage, step Generate coverage on 60b8714 wanted to post the following:

Framework coverage

4e53b3 #32820 60b871
Total Total +/- New
Rate 85.87% 85.86% -0.02% 100.00%
Hits 132516 132818 +302 426
Misses 21798 21881 +83 0

Diff coverage report

Full coverage report

Modules coverage

Contact

4e53b3 #32820 60b871
Total Total +/- New
Rate 91.19% 91.13% -0.06% 100.00%
Hits 4957 4960 +3 11
Misses 479 483 +4 0

Diff coverage report

Full coverage report

Full coverage reports

Reports

This comment will be updated on new commits.

@Andreas-Lepak Andreas-Lepak marked this pull request as ready for review April 27, 2026 16:15
Copy link
Copy Markdown
Member

@lindsayad lindsayad left a comment

Choose a reason for hiding this comment

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

The majority of the code added is in the framework. We should add framework level testing (in test) for that code. And something a little more stringent than CheckFiles would be good. If all of a sudden our triangulation changed dramatically, it would be good to know. At the same time, especially when relying on upstream libraries, we don't want to be overly stringent ... e.g. if the Delaunay triangulation changes slightly due to an algorithmic tweak change in triangle or poly2tri then we don't want to fail tests. So I don't know the exact answer for the right test choice

Comment thread framework/include/constraints/MortarInterfaceWarehouse.h Outdated
Comment thread framework/src/constraints/MortarInterfaceWarehouse.C Outdated
Comment thread framework/src/constraints/MortarInterfaceWarehouse.C Outdated
Comment thread framework/src/constraints/MortarSegmentHelper.C
Comment thread framework/src/constraints/MortarSegmentHelper.C
Comment thread framework/src/constraints/MortarSegmentHelper.C
Comment thread framework/src/constraints/MortarSegmentHelper.C Outdated
Comment thread modules/contact/src/actions/ContactAction.C Outdated
requirement = 'The system shall allow centroid-based 3D mortar triangulation through the '
'contact action and explicitly subdivide already-triangular clipped polygons '
'when triangulate_triangles is enabled.'
max_parallel = 1
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.

why do we need max_parellel?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This mirrors the pre-existing frictionless-mortar-3d-debug-mesh test. I believe it is to avoid issues from multiple ranks writing to the same mortar_segment_mesh.e debug mesh file.

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've opened #32885 which addresses this. This is fine for now since that's not merged

Comment thread modules/contact/test/tests/3d-mortar-contact/tests
- Capitalize MortarSegmentTriangulationMode enumerators
  (Delaunay/Centroid/EarClipping/Vertex) per MOOSE convention.
- Collapse the 10 per-interface maps in MortarInterfaceWarehouse
  into a MortarInterfaceConfig struct keyed by two unordered_maps
  (undisplaced / displaced). The canonical_name string duplicate
  is gone; the enum is the single source of truth.
- Restore in-function comments in MortarSegmentHelper.C, add
  comments to orient2dHelper and canonicalEdgeHelper, replace
  "pre-PR" wording with "legacy".
- Drop the offset parameter from triangulatePoly; it now works in
  local indices and getMortarSegments applies the offset once when
  shifting tri_map into the global node numbering.
- DRY the triangulation parameter definitions via a static
  MortarConsumerInterface::triangulationParams() factory consumed
  by both MortarConsumerInterface::validParams() and
  ContactAction::validParams().
- Group new 3D mortar contact triangulation tests under shared
  requirement blocks using the detail field; add a comment
  explaining why max_parallel = 1 is needed for tests that exercise
  the mortar debug-mesh exodus output.
- Add framework-level coverage of the new triangulation modes in
  test/tests/mortar/continuity-3d-non-conforming with per-mode
  gold files. Tolerances are tight enough to catch genuine
  algorithmic regressions while tolerating numerical drift in
  upstream libmesh PSLG triangulation backends.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Andreas Henrik Frederiksen and others added 2 commits April 29, 2026 14:08
Picks up the inline comments that were missed in the previous
review-response commit:

- Add a code comment to makeCCWTriangleHelper explaining what
  CCW means and why the triangulation paths assume CCW input.
- Drop the redundant std::sort calls on the node and element
  ranges produced by libMesh's serial ReplicatedMesh; they
  already iterate in id order.
- Use the libMesh Node directly instead of constructing a fresh
  Point with explicit (x, y, 0) coordinates - Node IS-a Point
  and the triangulator operates on a 2D mesh, so z = 0 already.
- Switch the non-TRI3 element check from mooseError to
  mooseAssert, since this is an internal invariant of the
  delaunay backend rather than a user-facing error.
- Use index_range(local_triangle) instead of make_range(3u) so
  the loop range follows the array size.
- Use libmesh_map_find instead of std::unordered_map::at when
  looking up node ids, matching the rest of the MOOSE codebase.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MortarSegmentHelper.C uses libmesh_map_find but never included
libmesh/utility.h directly. The unity build pulls it in transitively
through neighboring TUs in framework/src/constraints/, but the
non-unity build does not, breaking compilation of this TU.
@moosebuild
Copy link
Copy Markdown
Contributor

Job Test, step Results summary on 60b8714 wanted to post the following:

Framework test summary

Compared against 4e53b3b in job civet.inl.gov/job/3786076.

Added tests

Test Time (s) Memory (MB)
mortar/continuity-3d-non-conforming.triangulation-modes/centroid-with-retessellation 0.89 137.53
mortar/continuity-3d-non-conforming.triangulation-modes/vertex 0.88 123.72
mortar/continuity-3d-non-conforming.triangulation-modes/ear_clipping 0.80 123.41
mortar/continuity-3d-non-conforming.triangulation-modes/delaunay 0.78 139.51

Modules test summary

Compared against 4e53b3b in job civet.inl.gov/job/3786076.

Added tests

Test Time (s) Memory (MB)
contact/test:3d-mortar-contact.frictionless-mortar-3d-action-triangulation/delaunay SKIP 0.00
contact/test:3d-mortar-contact.frictionless-mortar-3d-debug-mesh-triangulation/centroid-no-retessellation SKIP 0.00
contact/test:3d-mortar-contact.frictionless-mortar-3d-debug-mesh-triangulation/delaunay SKIP 0.00
contact/test:3d-mortar-contact.frictionless-mortar-3d-action-triangulation/centroid-with-retessellation SKIP 0.00
contact/test:3d-mortar-contact.frictionless-mortar-3d-debug-mesh-triangulation/centroid-with-retessellation SKIP 0.00
contact/test:3d-mortar-contact.frictionless-mortar-3d-action-triangulation/ear_clipping SKIP 0.00
contact/test:3d-mortar-contact.frictionless-mortar-3d-debug-mesh-triangulation/ear_clipping SKIP 0.00
contact/test:3d-mortar-contact.frictionless-mortar-3d-action-triangulation/centroid-no-retessellation SKIP 0.00

@GiudGiud GiudGiud self-assigned this Apr 30, 2026
@moosebuild
Copy link
Copy Markdown
Contributor

Job HPC on 60b8714 : invalidated by @Andreas-Lepak

Copy link
Copy Markdown
Member

@lindsayad lindsayad left a comment

Choose a reason for hiding this comment

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

Looks like we're still using CheckFiles. #32885 makes mortar segment mesh output be uniquely prefixed by the input file name plus a mortar interface name that includes the paired secondary and primary IDs and whether the mortar mesh is displaced or not. Would that change make it so you can avoid using CheckFiles here which just checks that the file exists and not that it compares favorably against some gold?


#include "MooseTypes.h"
#include "MooseHashing.h"
#include "MortarSegmentInfo.h"
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 don't think this include should be necessary

Comment on lines +199 to +200
// Node inherits from Point and the triangulator operates on a 2D mesh, so
// the libMesh node already lives at z = 0 and we can use it directly.
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 would say 2D plane because 2D mesh could mean a 2D manifold that doesn't live entirely within a plane

return;

// Transform clipped poly back to (linearized) 3d and append to list
const auto offset = static_cast<unsigned int>(nodes.size());
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 generally use cast_int for these kinds of conversions but as we likely don't need a 64 bit int to represent the number of mortar segment nodes generated for a single primary element, this is fine

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants