Deleting a cap tetrahedron (two boundary faces of one B-rep face) gives each of its
two other neighbours a boundary face; a neighbour that had three was left with every
face on the boundary, joined to the mesh by its vertices alone. On Linux the dome on
a plate (cmd::plan::decompose::tests::a_dome_on_a_plate...) got one and the static
solve found the stiffness singular. The kernel's tessellation and sphere parameters
differ in their last bits between macOS and Linux (SolidCraft, outside this repo);
given the same input the tet mesher gives the same mesh on both (checked by replaying
Linux's surface and spheres on macOS, which reproduced the floating tetrahedron).
The deletion now refuses when a neighbour would float, and the linear validity gate
fails a mesh with a tetrahedron that shares no face with the rest, saying so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
SolidCraft lists a solid's faces in a different order from run to run
(the rounded box, for one), which reordered the triangle soup and the
face tags with every coordinate the same; welding keeps the first copy
it meets and every later stage visits ids in order, so the tet mesh
changed between runs. CanonicalSoup sorts the triangles by their
coordinates, numbers vertices by first use and face tags by first
appearance, and maps the tags back; mesh.tet.generate and the corpus
use it.
Tests: the washer's tet10 mesh has a fixed fingerprint (identical on
arm64 and x86-64 builds); a reordered, renumbered input gives the same
mesh; heavy: rounded-box and filleted-bracket mesh bit for bit the same
on repeated runs and with 1 and 8 threads.
The bracket oracle cases stay unfrozen: SolidCraft's tessellation
itself differs in the last bits between arm64 and x86-64. The coarse
case moves to 203.6 MPa with the canonical order; its band is 5%.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
transition::pyramid_surface puts a pyramid on every hex interface quad and
builds the tet region's surface closed by the pyramid sides, re-stitching the
neighbouring faces to the hex rim nodes. QualityOptions::frozen_faces keeps
those sides exactly (TetComplex refuses changes that remove or add a frozen
face; their vertices are corners), and apex_points checks the result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
fieldcraft_model::progress: a host installs a Progress on the worker thread
and reads its snapshot from another; the engine names the step being solved,
the nonlinear solver its load factor, the explicit solver its time, the
fluid solver its steps, transient heat its increments and the tet mesher its
stage and pass. Without one installed every report is a no-op (#322, #357).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
The seed's spacing becomes size-field samples the field grades from, and
after coarsening its nodes are put on the feature curve and pinned, so the
edge has exactly the seeded divisions at the seeded spacing. The quality
report gives, per seed, the boundary edges along it.
Part of #230.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
The end-to-end demo met a raw-CDT sliver that the validity gate accepted (exactly positive) and
the solver refused. Now:
- One validity criterion, `fieldcraft_model::admissible_jacobian` (normalized Jacobian above
1e-12), used by the implicit solver (fem `positive_inverse`), the explicit tet4/hex8
kinematics and the mesh gate, which proves it on the whole element (tet4: the solvers' own
Jacobian; curved kinds: a Bernstein floor), so whatever passes the gate passes the solvers.
- CDT rounding repaired by flips on the rounded mesh (Diazzi et al. 2023 §5.1) instead of the
±2-ulp nudge.
- `fieldcraft-mesh::tet`: a mutable tetrahedral complex whose every change is a checked
replacement (positive and admissible tetrahedra, faces matched with opposite orientation) with
an exact undo log; edge removal (Klincsek), multi-face removal, Delaunay insertion with
boundary faces, edge collapse; a curvature- and thickness-graded size field; constrained
Delaunay refinement with encroachment splits; ODT smoothing; the Klingner–Shewchuk schedule;
boundary vertices moved onto the exact B-rep surfaces at the end; tet10 with mid-edge nodes on
the geometry.
- `mesh.tet.generate` gains size, order, quality, quality_target, curvature_angle, grading,
max_passes, max_points and reports the CDT's and the mesh's quality; `solve.probe` takes a set
and reports its largest von Mises stress.
- Oracle cases filleted-bracket-fillet-stress(-coarse): the fillet's peak von Mises stress on
tet10 meshes of 2k and 13.5k elements, 213.3 MPa (a four-level study stays within 1.8%); the
CDT cases keep the raw tetrahedralization.
- Corpus quality columns: all 20 valid parts mesh and pass the gate.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm