file.sample flow-past: air at 25 mm/s past the upright L-bracket in a box of
2.5 mm lattice cells (Re 83), inlet, outlet, a no-slip floor and slip walls;
the fluid step reports the force on each named face. Its test holds the
drag coefficient on the upright's frontal area between 1 and 3.5 (2.3) and
the lattice's mass change under 1 %. No picture: the viewport does not draw
flow results yet (M12).
Part of #164 (its streamlines wait for a flow display in the viewport)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
file.sample rubber-seal: a gasket ring of hybrid hex8 elements in four
mapped quarter blocks, squeezed 2.5 mm between plates bonded to it
(nonlinear static, Mooney-Rivlin rubber). Its test frees the plates and
matches the closed form for incompressible uniaxial compression within 3 %,
and bounds the bonded ring's extra stiffness. The plates are prescribed
displacements until implicit contact (#161) can press it into a groove.
Closes#163
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
file.sample drop-test: an L-bracket standing on its foot (a SolidCraft script
in sample.rs) swept into hex8, falling 2 m onto a rigid floor tilted 15 deg;
explicit dynamics for 1 ms with gravity and plasticity. Its test checks the
mass against rho V, the impact speed sqrt(2 g h) and kinetic energy, the
energy balance and the momentum the floor takes. docs/examples.md starts
with it, and its picture is docs/media/examples/drop-test.png.
Closes#162
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
A sample may carry a SolidCraft script (the corpus recipe form) whose design
opens before its commands run, and its script sees the design's bodies.
fieldcraft-engine now builds fieldcraft-geometry with its scripts feature.
The start screen scrolls its sample list, and a test keeps the file.sample
form's choices in step with the samples.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
General contact, on a set's faces or every exterior face of the solids, is mortar contact
with one face list as both sides: each face against every other one that faces it, shares no
node with its element and is not reached through more than half an element, paired both
ways. Tested by a nearly incompressible Mooney-Rivlin C-ring (hybrid hex8) closing on itself
(no penetration beyond the mortar's weak enforcement, mirror symmetry to 1e-6) and, heavy, a
rubber bellows whose folds press on each other.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
Each secondary node follows the main surface under it as the weighted average
u_A = sum_J (M_AJ / sum_K M_AK) u_J with M_AJ = int psi_A N_J over the reference overlaps and
psi the secondary faces' dual shape functions (Wohlmuth 2000), so the secondary mortar
matrix is diagonal; the rows join the couplings' constraint elimination in every implicit
analysis. Uncovered secondary nodes stay free with a warning; heat transfer and explicit
dynamics refuse ties by name. Tested by the tie patch test on non-matching hex8/tet4 meshes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
solve.nonlinear counts the slipping nodes, solve.probe gives a secondary node's shear traction
and slip, and the results display draws contact_shear and contact_slip, contact_status coding
stick and slip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
A return map at the closed secondary nodes on the mortar slip in its rate form (w_A = B_A dx,
B_A = [int Phi_A N_i P, -int Phi_A N_j P]): stick within mu p_A, slip back to the cone, static
coefficient for a node that stuck in the last increment and kinetic for one that slipped; the
friction does the virtual work t . B_A dx, so a slipping interface carries mu N exactly. The
tangent is symmetrised (stick and slip stiffness kept, the coupling to the pressure and the
change of B left out; the residual is exact). Tractions and slip states are committed with the
converged increment. After a chattering cutback, nodes open by less than 1e-3 of their
element's thickness keep their penalty in the tangent for the rest of the step. Results carry
the shear traction, the slip and stick/slip status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
solve.nonlinear summarises each contact (normal force, largest pressure, closed nodes, deepest
penetration, augmentations), solve.probe gives a secondary node's pressure, gap and status per
contact, and the results display draws contact_pressure, contact_gap and contact_status. The
contact commands, the catalogue and the model docs say what nonlinear static steps do with a
pair.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
Frictionless contact pairs enforced by an augmented Lagrangian: nodal pressures from the
mortar weighted gaps, Uzawa updates after each converged increment until the gaps are within
1e-5 of the element thickness, and the consistent tangent (outer products of the weighted
gap gradients and the pressure-weighted Hessians from the automatic differentiation of the
mortar integrals). Finite sliding searches and integrates on the current configuration every
evaluation; without nonlinear geometry the pairs and their mortar integrals are fixed in the
reference configuration (small sliding, linear and exact). The tangent's pattern takes the
face pairs' blocks; the step cuts back when the active set chatters. Initial overclosure is
offset or refused; general contact, 2D models and arc length are refused by name; the linear
analyses refuse contact by name instead of the mesh.
With contact the convergence force scale comes from the converged states, the loads and the
current state. The loads-and-reactions balance check takes the nodal reactions into its scale
(no spurious warning for a part squeezed between held faces).
Tests: the contact patch test on non-matching hex8/tet4 meshes (exact with small sliding,
to 1e-4 with finite sliding), Hertz cylinder on a flat (peak pressure within 3%, half-width
bracketed) with quadratic Newton convergence, overclosure, thread-count determinism and the
refusals; Hertz sphere on a flat (heavy).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
The overlap of a secondary face with a main face (projection onto the secondary plane,
Sutherland-Hodgman clipping of their triangles, Radon's seven-point rule on the overlap's
fan) and the inverse linear and bilinear maps of both faces, written over a Real scalar so
that f64, Dual (gradient) and Hyper (gradient and packed Hessian, 24 variables) give the
values and the exact linearisation of the segmentation, the normal and the projection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
An explicit step may ask for mass scaling to a target time increment (on
a set, or the whole model): elements whose stable increment is below it
get the density that reaches it, with their lumped masses, frequency
bound and hourglass scales; adiabatic heating keeps the physical
density. The run reports the elements scaled, the added mass in all and
per section with its fraction, a warning, and the added mass's kinetic
energy in the energy account. Verified on a uniform bar scaled to twice
its increment (added mass 3×, increment doubled, a quarter of the
kinetic energy left to the physical mass). ROADMAP and parity note the
hyperelastic materials and mass scaling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
Neo-Hookean, Mooney–Rivlin and Yeoh materials in explicit dynamics, in
total form from the deformation gradient (objective by construction):
the one-point hex8 takes its mean F over the undeformed element and the
stress τ/J; tet4 takes the isochoric stress of its own F with the
average nodal pressure of the decoupled volumetric energy (Bonet–Burton
at finite strain). Each element's frequency bound follows the tangent's
largest normal stiffness. Small-displacement steps and thermal strain of
hyperelastic elements are refused by name.
Verified by uniaxial strain to λ = 1.5 of all three laws on hex8 and
tet4 against the material point (to rounding), and the oracle case
explicit-neo-hookean-implicit: a quasi-static explicit pull against the
implicit nonlinear solver (lateral contraction to 0.09%, stress to 0.03%).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
NonlinearStatic.arc_length {constraint, max_load_factor, displacement_limit}: Riks–Crisfield
arc-length increments (cylindrical or spherical arc, tangent predictor signed by the angle
criterion, the constraint's quadratic root chosen by the angle with the last iterate), arcs
grown, shrunk and cut back as increments are. The converged state's tangent is factored for the
next predictor (LDLᵀ when indefinite); its negative eigenvalues are recorded per increment and
changes in their count are reported as limit or bifurcation points. Ends at the maximum load
factor, a displacement limit or the increment cap. Abaqus *STATIC, RIKS; LS-DYNA refuses it by
name; forms gain the arc-length group. A shallow arch of hex20 snaps through on the closed-form
path (1.9 %), both limit points found, inertia 0 → 1 → 0 between them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
Fixes the build of develop: #171 crossed with the typed command forms, so BUCKLE lacked its
form. SOLVE_BUCKLE follows SOLVE_FREQUENCY; the step form gains the buckle variant and the
nonlinear step's imperfection group.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
NonlinearStatic.imperfection {step, mode, scale}: the buckling step's mode, scaled to a largest
component of `scale`, is added to the node coordinates before the step is solved, with a warning
naming it; Abaqus and LS-DYNA decks refuse it by name. A clamped–free column with its first mode
as imperfection under half its buckling load amplifies it by 1/(1 − α) within 2.8 %.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
Procedure::Buckle {modes, shift}: the step's loads as a linear preload, K_σ from its stresses,
and (K + λ K_σ) φ = 0 by block Lanczos in the K inner product: K⁻¹(−K_σ) on Cholesky without a
shift, the buckling transformation (K + σ K_σ)⁻¹ K on LDLᵀ with one, whose inertia counts the
load factors below σ (Grimes–Lewis–Simon 1994). Lanczos gains an operand and an eigenvalue map
(defaults keep frequency steps bit for bit) and reports converged negative eigenvalues. Results
are frequency results with a `buckling` part; modes display with their load factor.
solve.buckle (in the Static drop-down); Abaqus *BUCKLE, Nastran SOL 105 and LS-DYNA refuse it by
name. Euler columns of hex20 within 0.18 % of Engesser's load.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
K_σ = ∫ (∇N_a · σ ∇N_b) I dV for a stress state at the stiffness points (solids, plane and
axisymmetric elements with the hoop term), for linear buckling. Checked: uᵀK_σu = σ V for a
uniform stress on a distorted hex20 and a plane strain quad8, rigid translations in its null space.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CommandSpec::listing gives id, label, the parameter doc and the schema; MCP
list_commands, the control channel's engine.commands and the new
fieldcraft-cli commands --json use it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
The context panel's form shows typed fields instead of the JSON text: numbers
with the model's units, drop-downs of choices and of the model's sets,
materials, steps and bodies, checkboxes, vectors, groups and one-of variants
(a load's type switches its fields), lists of rows. Defaults are filled in,
required fields are marked, a value the schema finds wrong is noted under its
field, and OK waits while a field holds text that is not a value. The JSON
text stays as the Advanced view. fields.rs holds the generic widgets (row,
number, drop-down, text), adapted from SolidCraft's dialog rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
cmd/forms.rs describes each command's parameters: model definitions (sets,
materials with elasticity, plasticity, hyperelasticity, damage, thermal and fluid
options, sections, reference points, couplings, masses, springs, boundary
conditions, loads, fluid domains, steps including nonlinear and explicit),
meshers, solvers, deck export, files and results. Tests check that every command
has a form and that a form's defaults are accepted or refused only for the
required fields left empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
Field, Kind, Dim and Collection describe a command's JSON parameters: numbers with
unit dimensions, choices, vectors, references to model items, groups, one-of
variants and rows. prefill, switch_variant and problems serve forms; json_schema
gives the same as JSON Schema. No command is described yet (form: None).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Segment-to-segment penalty contact between element faces: each secondary
face and each main face facing it are projected onto the secondary face's
plane, split into triangles, clipped (Sutherland–Hodgman) and integrated
with a three-point rule per sub-triangle (Puso–Laursen segmentation, the
penalty at the integration points as Fischer–Wriggers), so a uniform
pressure gives the consistent nodal forces on both meshes and the contact
patch test passes on non-matching meshes. Every contact runs both ways at
half the penalty: pairs with each surface as the secondary, general and
self contact face against face. A bucket grid of face boxes every
increment, faces integrated in parallel and their forces added in face
order: the same numbers on any thread count.
The penalty per area ε = s γ min(m/A)/Δt² comes from the lumped masses,
each node's directional area and the stable increment, bounded by
Gershgorin's row sums so contact never shortens the increment. Coulomb
friction with exponential decay by a regularised return map at the
secondary nodes; rigid walls take Coulomb impulses. Contact work and
friction dissipation join the energy balance; each contact reports its
peak force, first and last contact, deepest penetration, friction and the
nodes adjusted at the start (initial penetrations adjusted strain-free or
refused with the node, face and depth); a cap on contact faces.
Verified (crates/explicit/tests/contact.rs, tests/oracle/explicit-*):
colliding bars (2L/c +1.2%, ρcAv₀/2, KE kept to 0.005%, contact energy
back to 8e-5 of it), blocks sliding to a stop on a rigid wall and on a
plate under constant and decaying friction (0.005–0.2%), the patch test
(σ_zz uniform to 0.06%), two spheres colliding against Hertz (peak +2.3%,
duration −0.4%; the oracle case waits for a graded sphere mesher), a free
body untouched by general contact, determinism across threads. Oracle
measures gain `over` (ratios). Existing fixtures do not move.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
Contact definitions on face sets: a pair (each surface's nodes against the
other's faces), general contact on a surface (self contact) or on every
exterior face of the solids; Coulomb friction with static and kinetic
coefficients and an exponential decay, a penalty scale and the choice to
adjust or refuse initially penetrating nodes, validated with caps. Rigid
walls of explicit steps gain friction. The check wants solid faces on
contact surfaces; sets used by a contact cannot be deleted.
fem::contact: the closest point of a triangle (Ericson) or a bilinear
quadrilateral (projected Newton) to a point, and a bucket grid of face
boxes for the global search, deterministic and bounded.
Commands interaction.contact.pair, .general, .edit, .delete and .find
(the touching parts of the mesh, with face sets and pairs made of them);
contacts in the model browser. Abaqus writes *SURFACE INTERACTION with
*FRICTION (exponential decay), *CONTACT PAIR and general *CONTACT; LS-DYNA
*CONTACT_AUTOMATIC_SURFACE_TO_SURFACE and _SINGLE_SURFACE with FS, FD, DC;
Nastran refuses contact by name. The implicit solver refuses contact by
name (M7); the explicit solver refuses it until the next commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
A tangent that is not positive definite is factored by LDLᵀ (Cholesky first, one symbolic
factorization) instead of cutting the increment back; each iteration records the tangent's
negative eigenvalues and the summary warns when a converged state keeps some. A model that can
move freely is still an error at its first tangent, located by the undeformed Cholesky factor;
a singular tangent later is a cutback. Nearly incompressible hybrid hex8, hex20 and tet10 at
ν = 0.4999 under prescribed displacement now converge to the closed form.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A supernodal LDLᵀ on the Cholesky factor's symbolic factorization (ordering, supernodes, fill,
cap and parallel subtree tasks shared through a Symbolic and a generic driver; the Cholesky
numerics are unchanged bit for bit). Pivots by the bounded Bunch–Kaufman (rook) search of
Ashcraft, Grimes & Lewis among each supernode's columns (Schenk & Gärtner's supernode
Bunch–Kaufman), 1 × 1 and 2 × 2, right-looking panels whose trailing update reuses the tiled
Cholesky kernels with L D formed on the fly. Reports the inertia; a column without a pivot above
round-off is a located Singular error; NaN is refused. Symmetric::new picks Cholesky, LDLᵀ, or
Cholesky with an LDLᵀ fallback; solve_refined adds iterative refinement. 1.1–1.2× the Cholesky
time on a 140 000-equation hex8 pattern.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
The mark is now a hex-meshed cube in isometric view whose faces carry a
banded colour fringe (blue to red, hottest at the front corner), with three
tapered streamlines wrapping around it. The small variant keeps the fringe
cube and one bolder streamline. Icon Composer gets four groups (floor, flow
behind, cube with mesh, flow in front), its maximum. The title-bar mark is
redrawn as the fringe cube.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The browser build has no threads, so a background job (opening a file,
meshing from a form, solving) ran inline in the click and the page froze
before it said what it was doing. Now the work waits until the window
has drawn the job's progress, then runs on the window thread in the
next frame. Native jobs still start on a worker thread at once.
Part of #119
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
file.open, geometry.open, geometry.import.mesh and reopening a model's
geometry read through the io crate's file edge instead of SolidCraft's
vfs, so in the browser they see the files the page holds (the same
store saves write to). Natively it is the same capped regular-file read.
fieldcraft-geometry drops its path-based readers (read_file,
open_design_file, read_3mf_file): it works on bytes, and reading files
is the io crate's job.
Part of #119
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
The browser has no file system a page can call synchronously. On wasm32,
fieldcraft_io::fs now reads and writes files kept in memory (memory.rs,
after SolidCraft's vfs): the web host puts picked and dropped files in
and takes out what commands wrote. The recovery folder works over the
same calls (list, remove, is_file), with in-memory entry locks and a
host-provided clock on wasm, so autosave and File > Recover work in the
browser as on the desktop. Native behaviour is unchanged.
Part of #119
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
Quality from the ribbon printed its whole JSON report into the status
bar, cut off after the first metric's histogram, so a user saw braces
and no answer. The report now carries a `summary` line ("30676 elements,
all valid; scaled Jacobian min 0.305, mean 0.963", with the invalid and
undetermined counts when there are any), and the status bar shows a
reply's summary instead of its JSON whenever it has one. The CLI and MCP
get the same line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
OK on a mesher's form ran the command on the window thread: octree hex
on the bolted assembly froze the window for over a minute, with no
progress and no way to stop it. The tet and hex meshers' forms now run
their command as a background job on a copy of the session, as opening
a file and solving do: the window keeps drawing with the job's name and
Cancel, the form closes when the mesh is in and shows the error when it
is not. ui.dialog {run} stays synchronous for programmatic callers.
The new test walks a first-time user's path: Open picks a SolidCraft
design, Tet Mesh opens its form, OK with the defaults meshes the part,
and a mapped hex mesh of a holed plate says why it does not fit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
The Open dialog is a native picker the control channel cannot click, so
nothing could walk a person's open path end to end: ui.start file.open
opens the command's JSON form instead. ui.drop {paths} hands the paths
to the next frame with the window system's drops, which open them
through the same route a picked file takes (open_path: by type, in the
background, the unsaved-changes prompt, the recent list). At most 16
paths of at most 4096 bytes each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
The Tet Mesh form opened with {} and failed with "give either body (with
tolerance) or surface", so a first-time user could not mesh a part
without knowing its body name and a chordal tolerance. The tet mesher
now takes its bodies as the hex meshers do: every body of the open
design by default, one named body, or an imported surface, with the
tessellation tolerance defaulting to 1e-3 of each body's diagonal. With
several bodies an error names the body it is about. The reply keeps its
totals (kind, counts, first labels, volumes) and lists each body's
counts, size and quality report under `bodies`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
The bracket's tet mesh is not bit-identical across platforms (#115): its fillet peak is 213.31 MPa
on macOS and 212.42 on Linux CI, which turned the oracle job red after #110. Those two quantities
are no longer frozen; the page names them mesh-dependent instead of printing one platform's value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
The end-to-end demo met a raw-CDT sliver that the validity gate accepted (exactly positive) and
the solver refused. Now:
- One validity criterion, `fieldcraft_model::admissible_jacobian` (normalized Jacobian above
1e-12), used by the implicit solver (fem `positive_inverse`), the explicit tet4/hex8
kinematics and the mesh gate, which proves it on the whole element (tet4: the solvers' own
Jacobian; curved kinds: a Bernstein floor), so whatever passes the gate passes the solvers.
- CDT rounding repaired by flips on the rounded mesh (Diazzi et al. 2023 §5.1) instead of the
±2-ulp nudge.
- `fieldcraft-mesh::tet`: a mutable tetrahedral complex whose every change is a checked
replacement (positive and admissible tetrahedra, faces matched with opposite orientation) with
an exact undo log; edge removal (Klincsek), multi-face removal, Delaunay insertion with
boundary faces, edge collapse; a curvature- and thickness-graded size field; constrained
Delaunay refinement with encroachment splits; ODT smoothing; the Klingner–Shewchuk schedule;
boundary vertices moved onto the exact B-rep surfaces at the end; tet10 with mid-edge nodes on
the geometry.
- `mesh.tet.generate` gains size, order, quality, quality_target, curvature_angle, grading,
max_passes, max_points and reports the CDT's and the mesh's quality; `solve.probe` takes a set
and reports its largest von Mises stress.
- Oracle cases filleted-bracket-fillet-stress(-coarse): the fillet's peak von Mises stress on
tet10 meshes of 2k and 13.5k elements, 213.3 MPa (a four-level study stays within 1.8%); the
CDT cases keep the raw tetrahedralization.
- Corpus quality columns: all 20 valid parts mesh and pass the gate.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
ui.select now also drives the viewport's selection: the filter, box
mode, items by name and label as model.set.create takes them, a pick at
a window point or the visible items in a box, changed by a mode
(replace, add, remove, toggle). ui.selection reads it back with its set
members and the hover; ui.dialog takes use_selection (the form's Target
row). Selection stays in the window: no engine command. Documented in
docs/control-protocol.md, with a test that picks the cantilever's free
end by a simulated click and a box, makes sets of it and resolves them,
and hostile-input tests (bad points and boxes, labels, huge lists).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
A click in the viewport picks what is under the pointer through the
selection filter (the selection bar, top centre): geometry faces, edges,
vertices and bodies, or mesh nodes, element faces and elements. Shift
adds, Cmd/Ctrl adds or removes, a click on nothing clears, Escape
clears; with Box on, a left drag selects the visible nodes, element
faces or elements inside the box. What a click would pick is
highlighted under the pointer with its name beside it.
The selection is kept in the model's persistent terms: geometry by name
(GeometryRef, named through the mesh's geometry tags or the drawn
bodies' face names), so it survives a remesh, and the mesh by label
(labels the model loses drop out). It reads as a set's members.
Picking runs on the CPU against the triangles both renderers draw. The
selection and the hover are drawn as an overlay in the theme's new
pick_selected and pick_hover colours (light and dark), outlined; the GPU
draws it as a second small layer, so a hover change uploads only that.
Command forms that apply to part of the model (sets, boundary
conditions, loads, sections, couplings) get a Target row whose Use
selection fills the members, or makes a set of the selection and names
it. A right click offers Create set from selection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
The pick module tests rays against triangles with the watertight
ray-triangle intersection of Woop, Benthin and Wald (JCGT 2013): a ray
through an edge or vertex two triangles share hits one of them, the
nearest hit wins and ties go to the lower index, so picks are
deterministic. A Projector holds the camera's projection for
screen-space picks (Camera::to_screen and the CPU renderer use it too),
and a DepthMap of the drawn faces, rasterised by the renderer's own
triangle routine, tells which points are in front (box selection).
Display triangles now carry their element and face number (numbered as
ElementKind::faces numbers them; seen_face gives a shell's bottom side
from behind), line elements their element, and geometry surface
triangles their face tag, so a hit names what a set refers to.
DisplayMesh::pick (results probing) uses the new test.
render_layers draws a second scene over the first: its faces over the
scene faces they repeat (a small depth allowance), then its lines and
points. The viewport draws selection and hover highlights with it
without rebuilding the model's scene.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
The corpus runs mesh.hex.octree's mesher on every part at 1/25 of its diagonal (the size of
the structured hex column) and reports hexahedra, the smallest and mean scaled Jacobian, the
mesh volume against SolidCraft's and the time; a part it cannot mesh gets the reason. The
column caps the elements at 500 000 so a part whose grid explodes is refused in seconds.
Loaded parts carry their bounding-box diagonal for the stages. docs/meshing/corpus.md
regenerated: 15 of 21 parts mesh, including filleted-bracket, rounded-box, sphere-on-plate
and the analytic sphere.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
Replaces the mesh with the octree all-hex mesh of a body, an imported surface or every body
of the open design, and tags boundary element faces with their B-rep faces so sets by
geometry resolve. Size, tolerance, refinement depth, element cap, attempts and optimiser
sweeps are validated and capped; the element cap defaults to the session limit
(`Limits::max_hex_elements`), which the hostile-params test keeps small. Body input and face
tagging are shared with mesh.hex.generate. The catalogue's Automatic Hex entry is ready.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
`fieldcraft_mesh::octree::octree_hex_mesh` meshes any closed part with hex8 only:
- an adaptive 3-refinement grid (27-tree) with strong balance and closure, filled with
conforming node-marking transition templates (Schneiders; checked over all 48 symmetries);
- a core of grid cells kept clear of the boundary (the HybridOctree buffer rule), with
non-manifold cleanup and a genus check against the part;
- a feature graph from the B-rep (corners, curves, patches) and a partition-first map of the
core's boundary onto it, then two boundary layers: a padding layer and a pillow per patch,
so every face boundary and sharp edge is captured by element edges;
- a Garanzha-type untangling and fitting energy (shape term |J|^3/chi, a volume term and a
soft fitting term, deterministic across platforms), a compass search on the worst
elements and a certified repair; failures refine the grid near them and rebuild, within
bounded attempts and growth.
The output passes the certified Jacobian check of every element and a gate that the
boundary encloses the elements' volume; boundary quads carry their B-rep faces. Quality is
reported with the same scaled Jacobian as mesh.quality. Literature notes for each step are in
docs/meshing/literature.md (B1.6-B1.10).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
Temperatures now strain the material in nonlinear steps instead of being
refused. At finite strain the deformation gradient splits multiplicatively,
F = F_m F_theta with F_theta = diag(1 + alpha_i dT) at each point: the material
sees F_m and the loops run on the thermally expanded volume J_theta dV0 (mixed
elements project det F_m), so the tangent stays consistent. At small strain the
split is additive, and elements on their linear stiffness take the equivalent
nodal forces as a dead load. The step's temperatures are its temperature loads,
or the steady conduction of its temperature conditions and heat loads (one-way
coupling, as the linear coupled step); both at once are refused. They ramp with
the load factor from each material's reference temperature. Johnson-Cook
plasticity softens at the point's temperature. Verified: free expansion and a
fully constrained neo-Hookean block at 20 % thermal stretch, full and hybrid
(exact); a bar held at its ends heated past yield (exact); the linear limit and
the conducted temperatures against the linear solvers; tangents by central
differences with a temperature field.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
Two more large-strain options on the isotropic constants, decoupled like
neo-Hookean with the volume kappa/2 (J - 1)^2: Mooney-Rivlin with C10 = mu/2 - C01
(0 <= C01 <= mu/2) and Yeoh with C10 = mu/2 and the higher terms C20, C30, so the
small-strain material is the elastic one everywhere. MaterialModel::Polynomial
gives the isochoric stress and tangent from Holzapfel's fictitious quantities
(eqs. 6.168, 6.193), consistent by central differences, equal to neo-Hookean
with C10 alone; hybrid elements take it. Abaqus writes *HYPERELASTIC,
MOONEY-RIVLIN and YEOH; LS-DYNA keeps refusing hyperelastic materials in
nonlinear steps by name. Verified in uniaxial and equibiaxial tension and pure
shear against the compressible closed form (exact).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm