forked from 1b-agent/fieldcraft-upstream
Tet mesh quality: one validity criterion, Delaunay refinement, ODT, Klingner–Shewchuk, tet10
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
This commit is contained in:
1 parent
fed27d816d
commit
9469ac0853
53 files changed
+5880
-278
No files matched your search
+33
-11
@@ -15,6 +15,8 @@
|
||||
pub mod autosave;
|
||||
pub mod catalogue;
|
||||
pub mod cmd;
|
||||
|
||||
pub use cmd::analytic_surfaces;
|
||||
pub mod history;
|
||||
pub mod results;
|
||||
pub mod sample;
|
||||
@@ -365,7 +367,8 @@ mod tests {
|
||||
json!({"format": "nastran", "path": "/no/such/dir/part.bdf", "steps": vec!["x".repeat(1000); 2000]}),
|
||||
json!({"format": "nastran", "path": "/no/such/dir/part.bdf", "steps": {}}),
|
||||
json!({"path": 7}),
|
||||
json!({"body": "Body1", "tolerance": 0.05}),
|
||||
// (`max_points` and `max_passes` keep mesh.tet.generate's quality stage small.)
|
||||
json!({"body": "Body1", "tolerance": 0.05, "max_points": 3000, "max_passes": 1}),
|
||||
json!({"body": null, "tolerance": "fine"}),
|
||||
json!({"elements": [u32::MAX, -1, 1.5]}),
|
||||
json!({"elements": {}, "bins": -5}),
|
||||
@@ -385,7 +388,7 @@ mod tests {
|
||||
json!({"polygon": [[0, 0], [1, 0], [0, 1]], "holes": [[[0.1, 0.1], [0.2, 0.1], [0.1, 0.2]], [[0.1, 0.1], [0.2, 0.1], [0.1, 0.2]]], "size": 0.2}),
|
||||
json!({"polygon": [["a", 0], [1, null], {}], "size": "big", "kind": 7}),
|
||||
json!({"polygon": [[0, 0], [1, 0], [0, 1]], "size": 1e300, "kind": "tri6", "max_elements": 1e300}),
|
||||
json!({"body": "Body1", "face": "", "tolerance": 0.05, "size": 1}),
|
||||
json!({"body": "Body1", "face": "", "tolerance": 0.05, "size": 1, "max_points": 3000, "max_passes": 1}),
|
||||
// Structured hex meshing.
|
||||
json!({"size": -1, "order": 9, "method": "x"}),
|
||||
json!({"size": 1e-300}),
|
||||
@@ -515,15 +518,34 @@ mod tests {
|
||||
json!({"body": "Body1", "tolerance": 1e-300}),
|
||||
json!({"body": "Body1", "tolerance": 1e300}),
|
||||
json!({"body": "Body2", "tolerance": 0.1}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "surface": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "weld": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "weld": 1e-3}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "weld": f64::MIN_POSITIVE}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "weld": 0, "weld_distance": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "weld_distance": 1e300}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "weld_distance": 0.05}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "feature_angle": 1e-300}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "feature_angle": null}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1, "surface": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1, "weld": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1, "weld": 1e-3}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1, "weld": f64::MIN_POSITIVE}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1, "weld": 0, "weld_distance": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1, "weld_distance": 1e300}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1, "weld_distance": 0.05}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1, "feature_angle": 1e-300}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1, "feature_angle": null}),
|
||||
// The quality stage's own parameters, hostile, with small caps so this stays fast.
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": -1}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 1e300}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 1e-300, "max_points": 2000, "max_tets": 5000}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": "fine"}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "order": "cubic"}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "order": "quadratic", "quality": false}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "quality": "yes"}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "quality_target": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "quality_target": 1e300}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "curvature_angle": -1}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "curvature_angle": 1e-300, "max_points": 2000}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "grading": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "grading": f64::MAX}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_passes": 1e9}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_points": 0}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 20, "max_points": 30, "max_passes": 1}),
|
||||
json!({"body": "Body1", "tolerance": 0.5, "size": 0.5, "max_tets": 3000, "max_passes": 1}),
|
||||
json!({"body": "Body1", "size": 1e-300}),
|
||||
json!({"body": "Body1", "size": 1e300, "order": 2}),
|
||||
json!({"body": "Body1", "size": 4, "method": "sweep", "order": 2}),
|
||||
|
||||
Reference in new issue
Block a user