* ui.capture: the whole window drawn offscreen and rasterised on the CPU ui.screenshot falls back to it when no frame is presented (hidden window, locked screen), so agents can look at panels and the toolbar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Draw the open design and imported surfaces in the viewport DisplayMesh keeps node labels on every element primitive (for results), gains add_surface (geometry faces and their boundaries, coincident points welded) and displaced (elements moved by nodal offsets). Bodies are tessellated at a thousandth of their size; meshed models show the geometry as boundaries only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * results.* commands: deformed shape, contours, mode shapes, animation, reaction sums The results display is session state (ResultsView), set by commands that are not edits. Nodal fields (displacement, von Mises, principal and component stresses; principal stresses in closed form after Smith 1961) and reaction force and moment sums per set. Results kept from an earlier revision stay viewable, marked stale; solve.probe says when a result is stale. The Results tab's display and reaction entries are ready. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Results display: deformed shape, contours with legend, mode shapes and animation, probe, reaction sums The viewport draws the session's results view: elements moved by their nodes' displacements or a mode shape (automatic or given scale), faces split exactly along contour band boundaries into flat-coloured pieces (same on GPU and CPU), a legend with levels and extremes, a results panel whose controls run the results.* commands, a click probing the nearest node (solve.probe), and stale results marked. Solving from the form shows the result on the Results tab. sample::cantilever is a solvable model for tests and demos. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Simplify the results display (review) One Contour in the plot; the results scene reuses scene::build; a solve shows its result through the engine (ResultsView::for_result) and the UI raises the Results tab when a view appears; the plot's scale and contour values are kept across animation frames; design bodies are tessellated once per design; the first content is framed where the display is rebuilt; field_values in one match; probe reuses results::displacements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Results display for explicit steps: field frames, energy histories The results view works on the engine's step results (static, frequency, explicit): ResultsView.frame and results.frame {frame | time} pick an explicit field frame; the deformed shape (true scale) and contours (element stresses averaged to the nodes, velocity) follow it; animation runs through the frames. The Results panel has a frame slider and plots the kinetic, internal, hourglass and external energies with their values at the frame. sample::impact is a steel bar flying onto a rigid wall. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * docs/features.md after the rebase 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>
9.0 KiB
ModelCraft — instructions for agents
ModelCraft is a clean-room, open-source, pure-Rust CAE modeling application: it turns geometry into analysis-ready finite element models (volume meshes, materials, sections, boundary conditions, loads and steps), writes them as Abaqus input, Nastran bulk data and LS-DYNA keyword decks, and solves linear static and modal problems with its own solver. It targets the pre-processing done today in Abaqus/CAE, Nastran pre-processors and LS-PrePost, and aims to beat them on meshing robustness, speed, openness and agent control.
Siblings with the same conventions: ../solidcraft (parametric CAD, Fusion-class), and the
storytold Craft apps (storytold/cadcraft, vectorcraft, photocraft, …). Code adapted from
them (MIT OR Apache-2.0) is noted in ATTRIBUTION.md.
Product name
ModelCraft everywhere: crates modelcraft-*, binaries modelcraft and modelcraft-cli,
.modelcraft models, MODELCRAFT_* environment variables, docs, CI and packaging.
Start every session here
- Read
ROADMAP.md(milestones, what is next) and your task. docs/architecture.md: crates, layers, the meshing pipeline.docs/adr/: library decisions (what we may depend on, what is rejected for licence reasons).- Meshing work:
docs/meshing/literature.md(the papers each algorithm comes from).
Never crash
People trust ModelCraft with their models; a crash loses their work. This outranks feature work.
- No panics in non-test code: no
unwrap(),expect(),panic!,unreachable!,todo!,unimplemented!; nounsafe(unsafe_code = "forbid"). - Errors are
Result<T, E>through the crate's error type and?. An unfinished feature returns an error ("not supported yet: …"); it never panics. - Input-derived numbers are hostile (command parameters, model files, solver decks,
control-channel input):
get()not[i], finite checks, caps on sizes and loop counts. - Bound every iterative algorithm. Mesh refinement, smoothing, untangling and solver iterations have explicit iteration and element-count caps and report when they stop early.
- SolidCraft calls are guarded: everything that calls
solidcraft-kernelgoes throughsolidcraft_kernel::guardinsidemodelcraft-geometry, which turns a panic into an error. - Last-resort guard:
Session::executeruns every command undercatch_unwind; an error or an escaped panic restores the model and reportsEngineError. - Prove it: every crash fix lands with a regression test (see
hostile_params_never_panic). - Every production crate root carries
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)].
Non-negotiables
- Independent. Behaviour is designed from public documentation, published papers, general CAE conventions and user feedback. Never copy another product's code, icons, artwork, help text, element or material libraries, templates or examples.
- No commercial CAE products. Agents must not drive, script, automate or extract data from commercial CAE or meshing products (Abaqus, MSC/NX/Simcenter Nastran, LS-DYNA, LS-PrePost, HyperMesh, ANSA, Femap, Patran, ANSYS, …): no test data, measurements, decks, results or ids produced with them. Deck formats come from the vendors' publicly available reference manuals; we write our own descriptions and our own examples, never copy manual text or example decks.
- Verification and validation (docs/verification.md). Every solver capability is checked
three ways, each case in
tests/oracle/tagged with its source: theory (closed-form solutions, patch tests, mesh invariants), experiment (measurements published in papers and reports) and published benchmarks (reference results published by benchmark bodies and by solver vendors' benchmark and verification manuals, e.g. NAFEMS, Abaqus). Owner decision 2026-10-13: published vendor results may be used as reference values, cited per case. We never run a commercial product, never copy its input files, text or figures: each case is our own model built from the published description, and only the published numbers are used. Once our result matches its reference within the stated tolerance, it is frozen as a fixture (fixture.json), so any later change that moves it fails CI (fidelity never drifts silently). - Algorithms come from papers. Meshing and solver methods are implemented from published
papers and theses, cited in the module docs and in
docs/meshing/literature.md. - Never read, copy or link GPL/LGPL/AGPL code: TetGen, CGAL, Gmsh, Netgen, CalculiX, Code_Aster, OpenRadioss, Elmer, deal.II, FreeFEM, Salome, OpenFOAM — don't even read their sources. Permissively licensed code (MIT, BSD, Apache-2.0, MPL-2.0) may be read for reference; anything adapted is noted in ATTRIBUTION.md with its licence.
- Assets — absolutely essential. ModelCraft contains no iconography, images, fonts or
artwork from commercial CAE products — ever. Icons are original artwork made with VectorCraft:
commit the editable
.vectorcraftsource beside the exported SVG/PNG. Any file asset must be original or openly licensed and must have a row in ATTRIBUTION.md (cargo xtask assetsenforces it). Screenshots of commercial software are never committed. - Test geometry is our own. Meshing and solver test parts are built with SolidCraft and committed as recipes (scripts or designs) that regenerate them; analytic shapes are generated in code. No public CAD datasets (ABC, MAMBO, Thingi10K, GrabCAD) and nothing made in a commercial CAD or CAE product.
- Everything is a command. User-visible behaviour = a command in
crates/engine/src/cmd/*(CommandSpec: our own dotted, lower-case id<area>.<thing>[.<variant>]such asmesh.node.addormesh.tet.generate, never another product's keyword; label; params doc; JSONrun) + tests. The UI, scripts, CLI and control channel all reach the same commands. - Programmatic calls never open dialogs.
- Layering is enforced by
cargo xtask layers(docs/architecture.md): L0model→ L1geometry,mesh,fem,decks,render→ L2 solvers (solver;explicit,lbmlater) → L3fsi(later) → L4doc→ L5io→ L6engine→ L7ui-egui,mcp→ apps. Nothing below L7 depends on egui/eframe/winit/wgpu/rfd. Onlycrates/geometrymay usesolidcraft-*. - Determinism. The same input gives the same mesh, deck and CPU solver result, bit for bit, on every platform: no hash-order iteration into output, no thread-count-dependent results. GPU solver backends (wgpu, f32) are the one exception: deterministic per device, and checked against the CPU reference within a stated tolerance.
- Robust geometry. Meshing decisions (orientation, in-sphere, intersection) use exact or adaptive-precision predicates, never a bare floating-point sign.
- Units. The model is unit-consistent and never converts (the user picks e.g. mm, t, s, N, MPa); geometry from SolidCraft is in millimetres. Z is up.
- Rust only. Never break wasm: everything below the apps builds for
wasm32-unknown-unknown(cargo xtask wasm, part ofcargo xtask ci). - Quality gates: GitHub CI runs
cargo xtask ci(fmt, assets, licences, layers, clippy -D warnings, tests, wasm) on every PR; don't run the full gate locally. Before pushing:cargo fmt --all,cargo checkthe touched crates, and run the tests you added or changed. Commit after each working step (a layer that compiles with its tests passing), not one big commit per PR. - Publishing:
originis github.com/jyviko/modelcraft. Changes land through pull requests: branch, rebase onto the latestorigin/mainright before opening the PR and before every push that updates it (never mergemaininto a branch), push, open a PR, let CI run. A PR merges (gh pr merge --squash) only after review and only when its green CI ran on top of currentmain(mergeStateStatusCLEAN; rebase again ifmainmoved); after a merge,main's own CI must pass. Never force-pushmain. Never commitplan/,.agent-files/,worktrees/ortarget*/; checkgit ls-filesbefore pushing.
Running
cargo run -p modelcraft-cli -- commandslists every command with its parameters.cargo run -p modelcraft-cli -- run script.jsonruns a script (a JSON array of{"command", "params"}) and prints the model summary.cargo run --release -p modelcraft -- --sample --control PORTopens the desktop app with the sample model and a control channel on127.0.0.1:PORT(docs/control-protocol.md). For UI work, look at the result:ui.capture {path}(the whole window drawn offscreen; works with the screen locked),ui.screenshot {path}(a presented frame) orui.render {path}(the model only), then read the PNG.- Build with
CARGO_TARGET_DIR=<repo>/target. Parallel agents: separate target dirs, one worktree each.
Roadmap
ROADMAP.md (committed) tracks milestones. Update it whenever a milestone task lands.