Skip to content

Repair self-intersecting STL surfaces before TetGen meshing - #103

Closed
zasexton wants to merge 13 commits into
SimVascular:mainfrom
zasexton:issue-102
Closed

Repair self-intersecting STL surfaces before TetGen meshing#103
zasexton wants to merge 13 commits into
SimVascular:mainfrom
zasexton:issue-102

Conversation

@zasexton

@zasexton zasexton commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • diagnose opaque TetGen failures in an isolated verbose pass and retain a bounded, structured attempt report
  • recover geometry rejections with component-preserving MeshFix before a validated PyACVD fallback
  • install the exact selected recovery surface into Domain boundary and sampling state while preserving original_boundary
  • present concise GUI status/warnings with expandable, path-sanitized technical details
  • add generated-surface, worker-isolation, Domain-state, GUI-feedback, cross-platform, and opt-in attachment regressions

Fixes #102

Root cause and recovery policy

The issue attachment is topologically closed and manifold, but those properties do not rule out geometric self-intersections. A diagnostic TetGen pass reports 124 segment-facet intersections and one facet-facet intersection. The previous quiet worker surfaced only Unknown exception, and its PyACVD retry produced a non-manifold surface with 16 open edges before failing again.

The new order is:

  1. Try the unchanged input with the caller's real meshing options.
  2. On a geometry rejection only, run MeshFix with component joining and component removal disabled.
  3. Validate triangle topology, finite geometry, connected-component count, bounds, and symmetric displacement before retrying TetGen.
  4. Use PyACVD only as the final optional fallback, validate its output, and repair it before TetGen if necessary.

Native TetGen calls remain in child processes with temporary working directories and concurrently drained stdout/stderr. Infrastructure and programming failures do not start geometry recovery.

Exact attachment measurements

The source file checksum is ec3d4e23757659604c939e7d2f418587bfedc2a067479e4964f0ab40ee637275.

Measurement Input MeshFix-selected surface
Points 75,695 75,608
Triangles 151,386 151,212
Connected components 1 1
Manifold / open edges yes / 0 yes / 0
Surface area 1769.152501 1768.768293
Enclosed volume 1772.872378 1773.139641
  • maximum bounds delta: 0
  • relative area delta: 0.0217%
  • relative volume delta: 0.0151%
  • symmetric surface distance: 0.072304, or 0.1659% of the source diagonal (below the default 1% bound)
  • low-level output: 121,807 nodes and 551,035 linear tetrahedra

Timing from the exact-file low-level benchmark:

  • original attempt plus diagnostic pass: 10.29 s
  • MeshFix preparation and validation: approximately 5.76 s, derived from the total less the two TetGen attempt durations
  • successful repaired-surface TetGen attempt: 23.13 s
  • total recovery: 39.17 s
  • previous PyACVD path: 69.52 s and still failed; the successful path is about 43.7% faster

The full create/solve/build attachment regression at resolution 25 passed in 10:34 of test time (10:37 wall clock), with 4,787,744 KiB peak RSS and no swap.

Verification

  • focused recovery/diagnostic suite: 109 passed
  • full local suite: 311 passed, 1 skipped
  • basic smoke test: Domain, real folded-surface recovery, MMG, tree, forest, and GUI stages passed
  • supported CI matrix: 15/15 jobs passed on Linux, macOS, and Windows with Python 3.9-3.13
  • exact opt-in attachment regression: passed at build resolution 25, including positive finite volume, mesh indices/trees, normalized probabilities, boundary sampling, source immutability, and selected-boundary alignment
  • dependency check: no broken requirements
  • Python 3.9 syntax, HTML structure, whitespace, authorship, privacy, and repository-content audits: passed
  • independent code review of the final delta: no remaining actionable issues

The attachment and local planning file are intentionally not committed or downloaded by normal CI. The pull-request workflow exercises the focused tests and smoke test across the supported operating-system/Python matrix.

Compatibility and controls

The lower-level tetrahedralize() function keeps its historical (grid, nodes, elements) return by default; return_result=True opts into the selected surface and structured report. Domain continues to enforce order=1 and nobisect=True, rejects raw switch strings that could bypass that contract, and accepts the documented recovery controls. Recovery remains optional through repair_on_failure and remesh_on_failure, with a configurable repair_max_distance_ratio that defaults to 1%.

@zasexton zasexton closed this Sep 1, 2026
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.

TetGen fails to tetrahedralize a closed, watertight STL in the GUI

1 participant