forked from 1b-agent/fieldcraft-upstream
Holes by diameter, fillets by radius, chamfers by distance and bosses by height below thresholds, or under a geometry.check finding; the reply panel switches each feature off and on (geometry.defeature) and defeatures the whole proposal. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
158 lines
10 KiB
Markdown
158 lines
10 KiB
Markdown
---
|
|
name: fieldcraft-meshing
|
|
description: 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. `geometry.defeature.propose {"body", "below": {"hole_diameter",
|
|
"fillet_radius", "chamfer", "boss_height"}, "from_check": true}` lists the holes, fillets,
|
|
chamfers and bosses below a size or under a finding; `geometry.defeature {"name", "features"}`
|
|
takes them out (the design rebuilt without them), `geometry.defeature.delete` puts them back.
|
|
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. Refine locally with `mesh.size.set` (faces, bodies, a box or a sphere) rather
|
|
than making the whole part fine; `mesh.size.growth` sets how fast it coarsens away.
|
|
|
|
## 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`).
|