forked from 1b-agent/fieldcraft-upstream
mesh: strong relaxation morphs round the bad elements (harmonic interpolation within a reach)
The owner's direction: bad nodes move to better places, the nodes within a reach follow by the graph Laplacian's harmonic extension, edges held, nothing outside the reach moves. mesh.node.relax strong mode takes below and reach and reports each round. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
6f94346253
commit
4908f5679c
8 files changed
+430
-253
No files matched your search
+6
-5
@@ -210,11 +210,12 @@ When `mesh.quality` shows a handful of spikes in an otherwise good mesh:
|
||||
vertex nodes stay; nodes on a partition cut face move freely (both cells share them). The
|
||||
reply gives the nodes moved, the largest displacement, and the region's smallest scaled
|
||||
Jacobian, largest edge ratio and worst elements before and after. One undo step.
|
||||
For a whole region at once, `mesh.node.relax {"mode": "strong", "bodies": ["Base"]}` (or
|
||||
`"below": 0.7, "layers": 2` for the elements below 0.7 and two rings round them) optimises
|
||||
every node together toward scaled Jacobian 1, edge and vertex nodes held, single elements
|
||||
allowed to get worse on the way; it keeps the best state, never one with a lower smallest
|
||||
scaled Jacobian, and replies the objective, min and mean per sweep (`history`).
|
||||
For a whole region at once, `mesh.node.relax {"mode": "strong", "bodies": ["Base"], "below": 0.7}`
|
||||
morphs the mesh round every element below 0.7: their nodes move to better places and the
|
||||
nodes within `reach` (default three element lengths) follow by harmonic interpolation; edge
|
||||
and vertex nodes are held, face nodes stay on their faces, nothing outside the reach moves,
|
||||
and the smallest scaled Jacobian never drops. The reply lists each round's bad elements,
|
||||
clusters and region size (`rounds`). It runs as a job (`job.status`, `job.cancel`).
|
||||
2. The relaxation is kept as a field of the model (`field`, default `Relax1`, …; saved with it).
|
||||
After a remesh, `mesh.node.relax.apply {"field": "Relax1"}` puts the relaxed shape on the new
|
||||
mesh (each node takes the displacement interpolated in the old cell it lies in, on its face,
|
||||
|
||||
+11
-10
@@ -970,16 +970,17 @@ input points), Steiner points kept half the local spacing from the boundary.
|
||||
mesh takes the displacement at each node's parametric coordinates in the old cell holding it (barycentric or
|
||||
trilinear shape functions), slides it onto its face or edge, undoes any move that would invert an element, and
|
||||
smooths once (`mesh.node.relax.apply`, or `reapply` after every remesh).
|
||||
- **Strong mode** (`"mode": "strong"`, `fieldcraft-mesh::relax::optimize`). An optimisation of every corner node
|
||||
of the region at once: interior nodes in space, face nodes in their face's parameters, edge and vertex nodes held.
|
||||
It minimises Σ φ(SJ) over the elements by nonlinear Gauss–Seidel (per node: the neighbours' centroid, then a
|
||||
central-difference gradient step with backtracking, each kept when it lowers the node's part of the sum), first
|
||||
with φ = (1/SJ − 1)², then with φ = SJ⁻¹⁶ (a smooth stand-in for the smallest SJ); an invalid element costs
|
||||
far more than any valid one (a barrier, and an untangling objective while elements are inverted; P. Knupp,
|
||||
*Hexahedral and tetrahedral mesh untangling*, Eng. Comput. 17, 2001; L. Freitag, P. Plassmann, IJNME 49, 2000).
|
||||
A term ½ ln²(V/V₀) keeps element sizes, and no element may exceed the region's largest edge ratio before: the
|
||||
scaled Jacobian alone lets elements flatten against the boundary. The best state passed through is kept, and
|
||||
only if it is valid and its smallest SJ is not below the start.
|
||||
- **Strong mode** (`"mode": "strong"`, `fieldcraft-mesh::relax::morph`). A mesh morph round the bad elements
|
||||
(scaled Jacobian below `below`): their movable corner nodes are grouped into clusters (nodes within the reach of
|
||||
each other, from a fixed-radius grid query); per cluster, the bad nodes first move to better places by a few
|
||||
sweeps of their own optimisation with the rest held (a central-difference gradient step with backtracking on
|
||||
Σ SJ⁻¹⁶ + ½ ln²(V/V₀) over their elements, an invalid element costing far more than any valid one), then that
|
||||
displacement is carried to every node within the reach by harmonic interpolation: the graph Laplacian of the
|
||||
element edges solved (Gauss–Seidel) with the bad nodes' displacements and zero at held nodes and beyond the reach.
|
||||
Face nodes go back on their face (UV), edge and vertex nodes are held, nodes outside the reach do not move. A
|
||||
cluster's morph is kept, at full or a halved scale, only when no more elements are invalid, the worst element it
|
||||
touches is no worse, the smooth objective Σ (1/SJ − 1)² there drops, and no element gets longer than the longest
|
||||
was; rounds repeat while any cluster is kept. The result never has a lower smallest SJ than the start.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user