Repository navigation
Races in the parallel passes, the not_a_disk map flake, and what preserve_topology keeps - #1158
Merged
Merged
Conversation
…vertex's own fan The link condition asks the order of the link vertices of a collapsing edge. With feature edges tracked, the order walked the vertex's neighbours u and read fan(u) through tuple_from_edge and eid(): two steps from the edge, outside the collapse's lock, where another collapse or its rollback rewrites it. ThreadSanitizer flagged the read on double_sphere at 8 threads; on macOS, where libc++'s move assignment empties the fan first, 6 runs in 25 threw "Tuple::eid ... empty tet vector", 0 in 25 after. The edge's slot is its lowest tet's tid * 6 + local index, and the fan is sorted ascending, so the first tet of the vertex's own fan holding u gives the same slot: outputs byte-identical to main at 1 thread (double_sphere held and protected, cube), ctest unchanged; the collapse pass 11% faster. Introduced with the feature-edge tags (728b493, #1049). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
split_after_vertex() stored the new vertex in one size_t of the mesh, which every thread of the parallel split pass wrote: a write-write race, reported by ThreadSanitizer in 6 of 6 dense simwild 2D runs at 8 threads. Its readers, AnnotationsTri.cpp and TightSealGeom.cpp, split serially and read their own split's vertex right after, so it is per thread now, as 3D's SplitTagCache::v_new: 0 reports in 3 runs, and the output byte-identical at 1 thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SurfaceMap::flatten tried to split the edge (prev, next) for the removed outline vertex before letting the survivor stand in its place. When the survivor was inside the patch, every edge the collapse makes holds the survivor, so an edge (prev, next) the patch has after was there before -- the third side of the face that survives a survivor of three faces -- and splitting it cut the outline: the edit was counted not_a_disk. That is python_tracking_correspondences' intermittent CI failure (1 in about 160 Linux runs; on the mac-mini 9 in 1400 tetwild runs of the open bunny, every hit this collapse). The survivor now takes the slot first; the split stays for a collapse along the outline. Test: the 4-vertex history (valence 3) and the valence-4 control, 0 warnings and a round trip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…insertion crosses itself The surface simplification before insertion keeps the connectivity only, and the envelope admits a fold of the surface through itself; the insertion then cuts the crossing triangles, which changes the topology. Not prevented: under preserve_topology one exact check of the simplified surface counts the crossing pairs and degenerate triangles, warns, and reports them, with the Euler characteristics right after the insertion. The octocat test checks by phase: always that the optimization keeps the inserted topology, and the input's only when nothing crosses. Octocat, 10 runs at 8 threads: 2 with 3 crossing pairs, Euler [2] -> [3] at the insertion; 8 with 0, all [2]. The check costs 3-32 ms. Specs and docs state the three phases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he insertion, as 3D does Without preserve_topology simwild simplifies before the insertion in both dimensions; with it, 3D simplifies the tet mesh after the insertion (SimWildMesh::simplify), where the exact inversion check and the substructure link condition keep the topology, and 2D did not simplify at all -- no reason was recorded; it was never written. SimWildMeshTri::simplify() is the 2D twin: the curve envelopes at eps_simplify, every edge collapsed with the quality check on, then the envelopes rebuilt around the result (envelope_reference "simplified") or restored around the input. 2D differs where its base does: no order-2 retagging (the curve ends and junctions are held by their anchors alone) and no quality switch. Default 2D remeshing now simplifies. Topology identical before and after on every 2D case and across eps_simplify 2e-5..1e-2 and both references; simwild suite 49/49. One case grows (curves_2d 2432 -> 3554 vertices): a triangle at a 1.5e-3 rad curve wedge sits at its energy floor, which stuck-refinement keeps splitting -- the same on main at 8 threads, not this change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ve-topology # Conflicts: # components/simwild/wmtk/components/simwild/SimWildMeshTri.cpp
…ope it hands on Since #1156 simwild hands on the envelope it held to, its edges labelled by their inputs (m_E_envelope_edge_sets), and the 3D simplification refreshes those labels when it rebuilds the envelope around the simplified surface (m_F_envelope_face_sets). The 2D twin rebuilt the envelope without them, so under envelope_reference "simplified" the envelope handed on carried no labels at all: a 64-edge straight interface simplified to 23 edges and came out unlabelled; now 23 edges, 23 label rows (tag_0). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
udaykusupati
force-pushed
the
uday/races-and-preserve-topology
branch
from
November 5, 2026 18:37
7a36cff to
a3e5f99
Compare
The hidden case tetwild-surface-folds-compare runs tetwild twice per model, with the fold check off and on. Since #1118 the input simplification uses the exact envelope by default, and on crown.obj (171k faces) that simplification alone took 34 min (2054 s, macOS arm64), so the case exceeded the hidden run's 5400 s limit. data2's tetwild_crown config already sets simplify_use_sample_envelope for this reason; this test builds its own settings and was not covered. folds_of_run takes the flag, and only the compare case sets it, so the CI case tetwild-surface-folds-none-made is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ve-topology # Conflicts: # components/simwild/wmtk/components/simwild/SimWildMeshTri.cpp # components/tetwild/wmtk/components/tetwild/tetwild.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two data races in the parallel passes, the intermittent
python_tracking_correspondencesCI failure, and what preserve_topology does and does not keep.Commits
Engine 3D: a vertex's tagged edges are counted from its own fan. The link condition asks the order of the link vertices of a collapsing edge; with feature edges tracked, the count read the fans of their neighbours, two steps from the edge and outside the collapse's lock. ThreadSanitizer flagged it on double_sphere at 8 threads. On macOS it threw "Tuple::eid ... empty tet vector": 6 runs in 25 before the fix, 0 in 25 after. It also accounts for about 1% of tetwild runs on the open bunny. Outputs are byte-identical to main at 1 thread. Introduced with the feature-edge tags (Preserve input feature edges and points #1049).
simwild 2D: the last split's vertex is per thread. A write-write race in the parallel split pass (ThreadSanitizer, 6 of 6 dense runs). Its readers are serial; the output is byte-identical at 1 thread.
tracking: a boundary vertex collapsed into one inside keeps the outline.
SurfaceMap::flattensplit an edge that was already there, and counted the edit asnot_a_disk. That ispython_tracking_correspondences' intermittent CI failure: about 1 in 160 Linux runs; on the mac-mini, 9 hits in 1400 tetwild runs, every one this collapse. Regression test: a 4-vertex history, with a 4-face control.tetwild: preserve_topology reports whether the surface handed to the insertion crosses itself. The surface simplification before insertion can fold the surface through itself, and the insertion then changes the topology. The hidden test
tetwild-surface-history-octocatfailed about 1 run in 3–5 for this reason. This is now warned about and reported, not prevented: crossing pairs, degenerate triangles, and the Euler characteristics right after insertion. The octocat test checks by phase. The check costs 3–32 ms. The specs state the three phases.simwild 2D: under preserve_topology, the remeshing simplifies after the insertion, as 3D does. Before, 2D did not simplify at all under preserve_topology. Topology is identical on every 2D case and across eps_simplify; the simwild suite is 49/49. One case gains vertices, through a separate, older weakness: stuck-refinement splits a triangle at its energy floor, and it does the same on main at 8 threads.
simwild 2D: the simplification relabels the envelope it hands on. Envelope IO: components write and read what they hold as tags #1156 labels the envelope simwild hands to the next component, and the 3D simplification refreshes those labels when it rebuilds the envelope. The 2D twin did not, so under
envelope_reference"simplified" the envelope came out unlabelled. Measured: a 64-edge straight interface simplified to 23 edges with no labels; now 23 edges with 23 label rows.Tests
ctest 234/234 on the tree merged with main, the new map test, the 4-vertex history through Python (0 warnings; main gives
not_a_disk1), and octocat 3/3 (macOS arm64, Release). ThreadSanitizer: 2D runs of triwild, simwild 2D and the 2D offset show no other race in the collapse path, with a 3D positive control.🤖 Generated with Claude Code