64 Commits
Author SHA1 Message Date
40f98f0af9 Implicit solver: fem kernels (tet4/10, hex8, hex20), sparse Cholesky, block Lanczos, solve.* commands (M4) (#23)
* Renumber the layer table for solver crates: fem at L1, solver at L2, fsi at L3

L0 model -> L1 geometry, mesh, fem, decks, render -> L2 solver (explicit, lbm later)
-> L3 fsi (later) -> L4 doc -> L5 io -> L6 engine -> L7 ui-egui, mcp -> apps.
UI_MIN_LAYER moves to 7, the testkit sits above it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* modelcraft-fem: element kernels for tet4, tet10, hex8 (full, incompatible modes), hex20 (full, reduced)

Shape functions, Gauss and conical-product quadrature, stiffness, consistent and HRZ-lumped
mass, body forces, consistent pressure loads on faces, integration-point stresses and their
least-squares extrapolation to nodes, isotropic and orthotropic elasticity. The element table is
one row per kind, so wedges, shells and beams are added locally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* modelcraft-solver: linear static and frequency analysis with an analytic verification suite

Assembly from the resolved model on the node graph, boundary conditions by elimination,
consistent pressure, gravity and body-force loads, nested-dissection ordering, sparse up-looking
Cholesky with a factor-size cap, reactions, integration-point and nodal-averaged stresses, von
Mises; shift-invert block Lanczos with full reorthogonalization for natural modes, participation
and effective mass. ADR 0001 records the own-linear-algebra decision (determinism, wasm).

Verified against closed forms: displacement and traction patch tests for every element, cantilever
tip deflection (Timoshenko) with convergence rates, Lamé cylinder, Kirsch hole (Kt -> 3),
cantilever and free-free beam frequencies with six rigid-body modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* Engine: solve.static, solve.frequency and solve.probe; results kept in the session

Results are stored per step, restored on a failed command and cleared when the model changes.
Caps (degrees of freedom, factor entries, Lanczos vectors) may only be lowered; hostile parameters
are tested against a solvable session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* docs: implicit solver architecture, roadmap M4, parity and references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* Simplify the solver: shared face integration, block assembly, cached extrapolation

- model: one face integrator (nodal::face_normal_weights) for tractions and pressures
- fem: mid-side tables from ElementKind::edges, orthotropic via inverse3, symmetric
  accumulation without a symmetrize pass, incompatible-mode recovery without K_uu, model's
  coordinate check, dead table rows removed
- solver: one pattern search per node pair (block, scalar and diagonal adds), extrapolation built
  once per element type, reactions from the prescribed rows only, matrices dropped after
  restriction, the free-body shift keyed on the factorization error, step selection in the solver,
  Error::Element naming the element
- engine: the solver owns the default step and the shift check; the model comparison runs once

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* docs: results are stamped with the session revision

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:44:21 -04:00
a71f85eb58 Abaqus input writer and deck.write (M3) (#22)
* decks: Abaqus input writer with golden decks, read-back, Jacobian and unsupported tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* engine: deck.write command (format dispatch, atomic file write), docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* decks: format table, one resolve per write, direct buffer writes; engine: shared path parameter

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:27:44 -04:00
2d99a8571c The .modelcraft file, undo/redo on the journal, atomic saves and autosave recovery (M3) (#21)
* modelcraft-io: the .modelcraft file (modelcraft/1), atomic writes, autosave storage

A stored ZIP of a manifest (format tag, geometry sources with SHA-256,
part sizes and hashes), the model definition as stable pretty JSON, the
mesh and geometry tags in a compact binary part, and the journal one
command per line. Reading caps every size, checks every checksum and
replays the model through its checked methods. Model::without_mesh and
empty mesh fields left out of the model's JSON; geometry::read_file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* engine: undo/redo on the journal, file.open/save/save_as, autosave and recovery

CommandSpec gains a kind: Model commands are journalled and become undo
steps; Session commands (file.*, edit.*) manage the session. Undo history
is bounded by steps and estimated memory. Geometry sources are recorded
with content hashes and reopened on file.open (missing or changed files
are warnings). Desktop app autosaves every 30 s and reports recoverable
models; Ctrl/Cmd+Z and Shift+Z/Y undo and redo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* docs: the .modelcraft format, undo history, roadmap and parity status

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* simplify: shared path parameter, one load path for open/recover, history accounting in History

Session commands skip the per-command model copy; saved revision is an
Option; journal written from borrowed entries and read without an
intermediate tree; backups copied by the file system; recovery lock and
error helpers; geometry file wrappers use read_file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:22:01 -04:00
9fb00317ad Add the modelcraft-decks crate (empty: error type only) (#19)
So the Abaqus, Nastran and LS-DYNA writer tasks add their modules in parallel.


Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:54:25 -04:00
02cc9dab57 Boundary surface preparation (weld, manifold/orientation, exact self-intersection, feature edges) and mesh.surface.check (#15)
* mesh: boundary surface preparation (weld, manifold and orientation checks, exact self-intersection, feature edges)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* engine: mesh.surface.check on an open design's body or an imported surface

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* engine: struct-init the test session

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* surface: weld at an absolute distance too; the engine welds CAD tessellations at twice SolidCraft's linear tolerance

Faces of a SolidCraft tessellation carry their own copies of shared boundary points, which agree
only to within the kernel's tolerance (up to ~2e-6 mm on the test corpus).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* docs: surface preparation in the literature survey, architecture and roadmap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* surface: simplify after review

- The weld distance travels with the mesh (SurfaceMesh::weld_distance, set by Shape::tessellate),
  so every consumer welds a tessellation correctly without opting in.
- Exact tests are infallible on predicates::orient2d_projected (shared with generic_orient2d), and
  compute each vertex's plane side once per pair with an early reject.
- One Box3 for the weld, the BVH and shells; a Half struct for edges; the weld's two lookups share
  one loop, with chained grid cells; cached normals for feature edges; shared parameter helpers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* corpus: boundary surface preparation as a corpus stage

20 of 21 parts are valid boundaries with their expected genus; cross-cylinders' tessellation has
cracks and flipped triangles where its two intersection curves meet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:01:42 -04:00
3c1485d83a Desktop app: egui/wgpu viewport, model tree, commands and control channel (#9)
* render: display data from the FE model, orbit camera and CPU rasterizer

modelcraft-render (L1, no GPU): DisplayMesh (the unshared faces of solid
elements, shells, element edges, line elements, nodes), the orbit camera
and a CPU rasterizer with PNG output for headless snapshots, adapted from
SolidCraft's render crate. Workspace: egui/eframe/egui-wgpu 0.36 and
SolidCraft's patched egui-wgpu from the pinned rev.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* engine: sample model script (a hex8 block and a tet4 cube)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* Desktop app: egui/wgpu viewport, model tree, commands, status bar, control channel

modelcraft-ui-egui (L5): ModelApp over the engine Session; GPU viewport
(lit faces, element edges, nodes) with a CPU fallback; orbit/pan/zoom,
standard views and fit; model tree; commands panel generated from the
registry with a JSON parameter form; status bar; control request
handling (engine.*, ui.*, ui.screenshot, ui.render). apps/modelcraft:
eframe window, --sample, --control PORT loopback JSON-lines server.
Adapted from SolidCraft's ui-egui and app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* THIRD-PARTY-LICENSES: the desktop app's dependencies

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* docs: control protocol, desktop app in architecture, roadmap and attribution

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* render: simplify the display mesh (sorted face keys over maps, no unused fields)

Interior faces are found by sorting fixed-size face keys instead of a BTreeMap of Vecs, edges
are deduplicated by sort, and each element's corners are looked up once. Drops the unused
`element` fields on DisplayTri/DisplayLine, the unused Mat4::IDENTITY and corner_count, and
hoists the line colour out of the raster loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* viewport: split GPU geometry into batches within the buffer limit; refuse oversized CPU renders

A mesh with a few million boundary triangles made one vertex buffer larger than the device's
max_buffer_size (256 MiB under the limits eframe requests), and wgpu's default error handler
panicked. Geometry is now uploaded in batches of whole primitives, each at most 128 MiB and the
device limit, and all are drawn. A device error handler logs errors and shows them in the status
bar instead of panicking.

The CPU renderer refuses sizes over 8192 per side or 16 MP, and scenes over 20 million
primitives, with an error, and allocates its buffers fallibly. ui.render reports a size out of
range instead of clamping it; the CPU viewport fits a large window into the limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:29:49 -04:00
a817a62440 Meshing test corpus built with SolidCraft; assembly placement in open_design (#11)
* geometry: build designs from SolidCraft command scripts (feature scripts)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* geometry: place component bodies through their occurrences when opening a design

An assembly's instances were missing and moved components sat at their own origin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* Meshing test corpus: 16 SolidCraft recipes, analytic shapes, loader and runner

modelcraft-testkit holds the corpus (tests/corpus/<name>/recipe.json run through SolidCraft's
engine, then opened as a design) and the runner binary behind cargo xtask corpus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* docs: corpus results, test corpus architecture and the scripts feature; licences

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* corpus: measure each body once, check stage widths, simpler loader and runner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:19:06 -04:00
d8b3bb51e0 Element quality metrics and the certified validity gate (mesh.quality) (#12)
* mesh: element quality metrics and a certified Jacobian validity check

Bernstein-form Jacobian determinants for tet4/tet10/wedge6/hex8/hex20, certified
by bounds and subdivision (Johnen-Remacle-Geuzaine 2013); Shewchuk 2002 tet
metrics, Knupp 2001 hex/wedge/quad metrics; quality report and the validity
gate meshers run on their output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* engine: mesh.quality command; model documents its node order

mesh.quality reports certified validity, volume and per-metric statistics with
fixed-range histograms. The model's ElementKind doc states the Abaqus/VTK node
order and that deck code reorders formats like Nastran CHEXA20.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* quality: simplify after review; exact orient3d for tet4; gate on raw mesher arrays

- tet4 validity from the exact orient3d of the input coordinates
- validity_gate_elements takes (label, kind, coordinates) straight from a mesher
- one Jacobian per solid; certify caches piece bounds; product weights precomputed
- model::check_coordinates shared by set_node and quality; ElementKind is Ord
- engine: shared labels() parameter parser

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:14:30 -04:00
4667655e08 mesh: exact predicates (Shewchuk adaptive, symbolic perturbation, indirect LPI/TPI) (#8)
* mesh: exact predicates (Shewchuk adaptive orient/incircle/insphere), symbolic perturbation, indirect LPI/TPI predicates

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* mesh: predicate benchmark (filter hit rate, CAD-degenerate inputs); record the predicate decision

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* mesh: simplify predicates (destructuring over fallbacks, shared-minor exact insphere, cheaper expansion product)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:04:56 -04:00
2c1821d0ad geometry: read tagged meshes through the VFS, refuse singular transforms, share the model's coordinate limit (#6)
read_3mf_file reads through solidcraft_io::vfs like open_design_file (regular files only,
browser files too), so the engine drops its own capped reader and EngineError::File.
A build item whose transform flattens its object is an error, and coordinates use
modelcraft_model::MAX_COORD (1e9) instead of a looser limit of their own.


Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:52:53 -04:00
jyvikoandClaude Opus 5.5 f181c98b75 Face-tagged 3MF exchange: format spec, reader, geometry.import.mesh
docs/formats/tagged-mesh.md defines the exchange: 3MF Core with an optional
ft:face attribute per triangle (and ft:faces per object) in a ModelCraft
namespace. modelcraft_geometry::tagged_mesh::read_3mf reads it from bytes
(wasm), capping every size and count and range-checking every index and
coordinate. geometry.import.mesh adds the surfaces to the session, which keeps
them beside the open design and restores them when a command fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
2026-10-13 14:48:22 -04:00
f36b1d9a69 Open .solidcraft designs: rebuild, persistent face names, geometry commands (#4)
* geometry: open .solidcraft designs, rebuild them and keep persistent face names

open_design(bytes) reads a design (container or JSON) with solidcraft-io, rebuilds its
timeline with solidcraft-doc and returns its bodies; each Shape carries SolidCraft's
persistent face names by B-rep face index, so a face keeps its name through a parameter
edit. sample::plate_design builds a parametric test plate in code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* engine: geometry.open, geometry.bodies and geometry.tessellate

The session holds the open design (shared, restored on a failed command, journalled when
it changes). Hostile-params coverage includes non-files, missing files, oversized paths and
extreme tolerances against an open design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* docs: opening SolidCraft designs in the architecture and roadmap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* geometry: open_design_file reads through SolidCraft's bounded file access; simplify

The engine's hand-rolled capped read is replaced by open_design_file, which uses
solidcraft_io::vfs::read (refuses non-regular files before opening, works on the browser's
in-memory files). Drops the duplicate size check, simplifies the sample sketch and the
design-change test in Session::execute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:41:05 -04:00
620ef6bc64 Add the modelcraft-mesh crate (empty: error type only) (#3)
So the predicates and quality tasks can add modules in parallel without both creating the crate.


Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:31:15 -04:00
jyvikoandClaude Opus 5.5 b64ec3f698 ModelCraft foundation: workspace, quality gates, FE model, command engine, geometry boundary
A pure-Rust CAE modeling workspace following the SolidCraft conventions:
- modelcraft-model: nodes and elements by label with hostile-input validation
- modelcraft-geometry: SolidCraft's kernel (pinned git dependency) behind our own API;
  tessellation into a face-tagged boundary surface for volume meshing
- modelcraft-engine: CommandSpec registry, Session::execute with rollback and panic guard,
  scripts and a journal
- modelcraft-cli: list commands, run scripts
- xtask: layers (only geometry may use solidcraft-*), assets, licences, tests, wasm, ci
- AGENTS.md, architecture, ADR 0001 (libraries and licences), roadmap, CI

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
2026-10-13 13:56:42 -04:00