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:
- Both endpoints anchored. Interpolate element sizes between the two known sizes.
- One endpoint anchored. Initial element size =
anchor × (1 + SegmentStart%), then grow each subsequent element by× (1 + SegmentEnd%)toward the loose end (geometric progression). - 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
CrackElementsdiffer.
Refinement is a property of the rule set, not of any single edge.