Files
jyvikoandClaude Opus 5.5 a5c71bc5b0 skills: meshing skill fixes from its corpus run (#482)
Plan before geometry.check (its size), mesh.plan.apply's own symmetry and the plan's saved
name, the fallback order, touching bodies, tet aspect ratio, undoing a relaxation that leaves
its region worse, and merging the cells of a split part.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
2026-10-16 14:57:51 -04:00

9.8 KiB

name, description
name description
fieldcraft-meshing Agentic hex meshing (AHM) in FieldCraft — look at the part, use its symmetry, plan the blocks, mesh, mark and fix the bad elements, and show sections an analyst would accept. Use before any mesh.* command on a part that matters, and whenever a mesh comes back with fans, slivers or a low scaled Jacobian.

Agentic hex meshing (AHM)

Mesh like an analyst: look first, plan the blocks, then call a mesher. A mesh is judged by its worst elements, not its count. A fast mesh with fans, slivers or collapsed corners is a failed mesh, even when every element is valid.

The loop: look → symmetry → plan → inspect the plan → apply → quality + mark → relax → section pictures → report. Never skip a step because the last mesh looked fine.

1. Look before you mesh

  1. inspect_model / geometry.bodies: bodies, volumes, bounding boxes, faces, holes, fillets. For a .solidcraft design also geometry.features {"body"}: how each body was built (profiles, extrude directions, holes, shells, fillets, patterns, mirrors).
  2. geometry.check {"body", "size"} at the size you intend (run mesh.plan first and take its size): short edges, small and sliver faces, near faces (gaps, overlaps, walls thinner than the gap). Each finding names its faces and a point at, and is marked in pictures. Defeature or resize before meshing; never mesh over a finding you have not explained.
  3. Take pictures. Headless: fieldcraft-cli snapshot script.json --out a.png with --view iso|front|top|… or --camera '{"yaw":-45,"pitch":30,"target":[x,y,z],"fit":true}', and --section x|y|z[=VALUE]. Over MCP: screenshot {view | camera: {yaw, pitch, target, distance | fit}, section}. Aim target at the feature you are judging (a boss, a corner), not the model's centre. Read every PNG. Name, in words, what the part is.
  4. Primary vs secondary features. Primary (walls, bosses, the load path, thin directions, the sweep direction) set the block topology. Secondary (small fillets, chamfers, logos, small holes far from the region of interest) fit into it or are defeatured; they never drive it.

2. Symmetry first

Test symmetry geometrically (mirror the bounding box and the key faces). mesh.plan.apply does it itself for a projection plan (symmetry, default true): it meshes one cell of a body mirror symmetric across planes through its middle and mirrors it, and lists the planes it used in its reply's symmetry. Read that list. An empty one on a part you know is symmetric (the crank arm comes back without its y = 0 plane) means the plan meshed it whole: check that the halves came out alike, or partition and mirror by hand. If the part and its loads are symmetric, mesh one half or quarter and mirror the mesh: mesh.mirror {"plane": <datum plane> | {point, normal}}. Nodes on the plane are merged, so the result is conforming. Never mesh a symmetric part whole. If the halves come out unequal (one corner good, its mirror image bad), keep the good half and mirror it.

mesh.plan refuses a partitioned body and its cells today (#473): for a half, mesh the cell with mesh.cells or mesh.hex.project, then mirror.

3. Plan, then inspect the plan

  1. mesh.plan {"body", "size"?, "layers"?}: the block plan from the features. Nothing is meshed.
  2. Read the plan before applying it:
    • mesher: project (one block topology over the body: O-grid rings round circles, blend layers along fillets, chamfers and shell walls; for a section at an angle to the axes, such as a crank arm, an annulus round each boss's hole and a carved grid with a pillow layer to the outline) is the target; cells/sweep is the fallback.
    • rings: centre, radius, frame half-side. Frames should be near square with even margins.
    • cuts: each with its feature and reason.
    • notes: why the body did not fit the projection layout.
    • pending (rings left boxed) and unmapped (features with no template: revolves, sweeps, lofts).
    • size and layers against the thinnest wall (§5). Fix what is wrong by editing the plan object and applying the edit.
  3. mesh.plan.apply {"plan": <the plan reply's name> | edited object}. The saved name is not always Plan-<body>: names with spaces or punctuation are made safe (Plan-Clamp_body). Read skipped_cuts, all_hex, conforming, min_scaled_jacobian, layout, blend_layers. A skipped cut is a defect to explain.
  4. When the plan does not fit, mesh regions by hand and keep the plan's cuts that work: geometry.partition + mesh.cells; mesh.hex.generate / mesh.hex.sweep / mesh.hex.mapped; mesh.hex.extrude for a section carried to a curved or tilted end (source, to_surface, sides); mesh.hex.project for a block topology projected on the part (template sphere or butterfly, ops o_grid, split, extrude, connect; preview: true lists block indices). Tet (tet10 for stress) only for cells not worth blocking, far from the region of interest. In that order: mesh.hex.generate (it picks extrude, sweep, revolve or mapped), then mesh.hex.project with the template the shape asks for (sphere for a ball), then tet10. Name each failure in the report.
  5. Mesh touching bodies one at a time and read the error: mesh.plan.apply on a body that touches one already meshed must meet that mesh face to face, and fails ("must meet the mesh face to face") when the divisions differ. Plan both with the same size, or mesh the second with mesh.hex.generate and tie them (§4).

Shapes without a timeline (STEP, 3MF) skip mesh.plan and go to step 4.

4. Block rules

  • No sharp corners. Every element corner near 90°. Where a block corner is far from 90°, cut differently or add an O-grid.
  • No skewed, tapered or long thin cells. Check every cell a cut makes; a wedge, sliver or strip means the cut is wrong.
  • O-grid round every hole, boss, cylinder and fillet: a square round the curve and a ring between, element lines radial, square corners at 45° on the curve. Never fan into a hole or pinch at a tangency.
  • Never cut on an existing face. A coincident cut leaves slivers, and one the kernel can only make by nudging off the plane is refused (geometry.partition). Offset the cut one element out of the feature. Never cut through the axis of a small hole.
  • Parity at corners. Division counts must close round every block loop. Where a corner comes out bad and its mirror image is good, change the count by one on the edges leading into it (mesh.seed.edge) first.
  • Pattern a good corner. When identical features (four corner bosses) mesh unequally, the good one is the template: mirror it onto the others (mesh.mirror) instead of accepting the worst.
  • Accept a wedge column where the layout forces one node to absorb a corner: one column of wedge6 is better than a column of skewed hex. State it in the report.
  • Keep cuts consistent across touching bodies so the meshes match, or tie them (interaction.tie).

5. Size and walls

  • Through a thin wall: at least 2 elements for bending (3 for stress in the wall), 1 only for pure conduction. Set layers in mesh.plan, or mesh.seed.edge across the wall.
  • Matching counts on shared edges: mesh.seed.edge.
  • Size from the physics: finer at stress gradients, fillets and contact; in explicit, from the wave length.

6. Quality, mark, relax

  1. mesh.quality: all valid; volume equal to the geometry's (a mismatch means missing or orphan cells, or elements too coarse for the curvature: a 1 mm tube wall swept at the default size loses 17 %). A tet mesh has no edge_ratio: judge it by aspect_ratio.
  2. mesh.quality.mark {"limits": {"scaled_jacobian": 0.3, "edge_ratio": 10}}: failing elements become the set quality_marked, drawn red in pictures, each with label, at, failed. Acceptance limits:
    • scaled Jacobian ≥ 0.3 everywhere, ≥ 0.5 in the region of interest;
    • edge ratio and aspect ratio ≤ 10;
    • face angles 15°–165°, dihedrals 10°–165° (the defaults).
  3. Picture the marked elements (camera target at their at, a section through them). Clustered marks mean the blocks are wrong: back to §3/§4. Isolated spikes only: mesh.node.relax {"around_element": <label>} for each marked element (by CAD face once #471 lands; sets are for the analysis steps, not for relaxing). Surface nodes slide on their faces; nothing inverts. Add "reapply": true so it follows a remesh. Read the reply's before and after: relaxing guards the scaled Jacobian, not the edge ratio, and on the crank arm's bore corner it raised the region's edge ratio from 10.5 to over 2000. When after is worse on either, edit.undo it. Relaxing is the final touch, never a substitute for a better plan.
  4. Mark again until nothing is marked, or every remaining mark has a stated reason.
  5. mesh.check.duplicates: no cracks or free edges inside a body. Cells of one part meshed one by one (split bodies) join with mesh.node.merge; separate parts that touch are tied, never merged.
  6. Section pictures through every primary feature: fans, pinches, wedges, slivers or zigzag lines → back to §3.
  7. After solving, refine once and compare the quantity of interest (convergence).

7. Report

State: symmetry used, primary features, the plan's mesher, every cut and skipped cut with its reason, method per region, element counts, min scaled Jacobian, max edge ratio, marked count, what was relaxed, any wedge column, anything defeatured, and the section pictures. If the plan failed, name the rule that failed and the next thing you will try. Never silently fall back to a worse mesh.

What this procedure gives today on every corpus part and sample, and why each failure fails: docs/meshing/skill-results.md (cargo xtask skill-results).