Skip to content

Mesh Generation Rules

XdTd's BEM mesher does not ask you to enter element counts. It applies a fixed set of rules to the geometry at hand, anchored by physical features (fastener pitches, crack tips, user-forced overrides), and lets element counts emerge. The same geometry meshed at a different refinement level produces a denser or coarser mesh — but the rules that produced it are the same.

The rules below describe the engine's behavior at a high level. They mirror the actual order of operations in DtdDiscretizationEngine.run().


Rule 1 — The refinement slider sets rules, not counts

The mesh-refinement slider (0–4) maps to a table of divisors and factors:

Parameter Role
LooseSegmentDivisor loose_size = perimeter / divisor
PatchBoundaryFactor Multiplier applied to loose size on patch boundaries
StiffenerBoundaryFactor Multiplier applied to loose size on stiffener boundaries
CrackElements Number of elements per crack edge
SegmentStartMultiplier Initial-element-size kick when growing from an anchor
SegmentEndMultiplier Geometric growth ratio along a one-anchored segment

Element counts downstream are derived by applying those rules to local geometry. The user never enters element counts directly.

Rule 2 — Forced overrides win, and are applied before the engine starts

Any edge with forced_discretization > 0 is pre-meshed at its endpoints with size length / forced. The engine treats those endpoints as anchors and never overwrites them. Everything else flows around the override.

Rule 3 — Fastened stiffeners are discretized first, anchored at their pitch

Each fastened stiffener is collapsed into a single full-length combined edge (not two half-length segments) so only the total number of elements has to be even. Element size = fastener pitch; both endpoints come out of step 1 already meshed at 2 × pitch. Fastened-stiffener pitches are the very first anchors in the mesh — everything else propagates around them.

Rule 4 — Cracks are discretized second, in CrackElements segments per edge

If cracks exist, discretize_crack_tip_segments runs next. Each crack edge gets exactly CrackElements elements (a small integer set by the slider). Crack-tip endpoints become the next layer of anchors.

Rule 5 — The mesh propagates from anchored points outward, never from empty space

After the seed anchors (fastened-stiffener pitches + crack tips + forced overrides), the engine walks outward in a fixed priority:

  1. Both endpoints anchored. Interpolate element sizes between the two known sizes.
  2. One endpoint anchored. Initial element size = anchor × (1 + SegmentStart%), then grow each subsequent element by × (1 + SegmentEnd%) toward the loose end (geometric progression).
  3. Re-check (1) after every pass. Newly-meshed endpoints may have promoted other segments to the both-anchored category.

Rule 6 — Loose segments (anchors at neither end) are discretized last

Default element size = perimeter / LooseSegmentDivisor. Then:

  • Patch boundaries: size × PatchBoundaryFactor
  • Stiffener boundaries (bonded, no crack): size × StiffenerBoundaryFactor
  • Clamped to [perimeter / (divisor × 7), perimeter / (divisor / 3)] — the absolute min/max guardrails.
  • Virtual edges (DRM / body-force regions) are processed before other loose segments at the unmodified loose size.

Rule 7 — Bonded stiffeners stay loose unless a crack reaches them

Bonded stiffener endpoints are not pre-anchored. If a crack intersects, the crack-tip anchor pulls the bonded stiffener mesh toward crack-element size on that side, and Rule 5 grows the rest outward. Otherwise they fall through to Rule 6 and get a uniform mesh at the loose size × stiffener factor.

Rule 8 — The engine loops until no progress is made, then exits cleanly

Steps in Rules 4–6 repeat. If a pass produces no new discretized segments, a warning is logged and the engine exits — there is no infinite loop. Any leftover undiscretized segments are reported as a warning rather than silently dropped.

Rule 9 — Internal points are computed off the finished boundary mesh

Internal points are not part of the discretization engine. They are generated afterwards from a boundary spacing field:

  • Each boundary node's local size is the average of its two adjacent edge lengths.
  • Hole-boundary sizes (cracks, internal boundaries) are capped at the median external-boundary size so coarse holes still attract a reasonable interior-point density.
  • Interior target spacing grows linearly with distance from the nearest boundary: local_size + 0.2 × distance.

Rule 10 — Counts emerge; they are not chosen

Two consequences worth keeping in mind:

  • Two identical geometries with different crack positions mesh differently, because the anchor topology differs.
  • Two identical configurations at different refinement levels mesh differently, because the divisors and CrackElements differ.

Refinement is a property of the rule set, not of any single edge.