diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8e10a93 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,294 @@ +# AGENTS.md: Quick guide for AI coding agents working on JuliaFEM.jl + +This file is the entry point for any AI agent (Cursor, Copilot, Codex, …) +that opens this repository. Read it first; then jump to the linked, +authoritative documents for details. + +The package version is **0.x** (see `Project.toml`); the repository is in the +middle of a deliberate architectural reset toward a **stable 1.0** (monorepo, +type-stable, zero-allocation, GPU-ready). Many older files, README.md sections, +and design notes still describe the *previous* API. When in doubt, trust the +code and this file over older READMEs. + +--- + +## 1. What this project is + +JuliaFEM.jl is an open-source finite element framework written in Julia. +It is being rebuilt from first principles around four ideas: + +1. Ciarlet's triple as a type. `Element{K, P, S, N}` encodes the + reference domain `K`, polynomial space `P`, DOF specification `S` + and total DOF count `N` purely at the type level. The element + instance carries only `id` and `dof_indices::NTuple{N, UInt64}`. +2. Element as template, not bag of fields. Heavy structural + information (which local DOF is which field/entity/component) is + produced by `@generated` functions over the element type, so the + compiler can fold it into constants. The canonical example is + `local_dof_layout(::Type{Element{K,P,S,N}})` returning an + `NTuple{N, DOFLayoutEntry}`. +3. Microkernels. Assembly is built from small `evaluate`-style + functions that compute a single scalar (or block) and are + dispatched at compile time. No `Dict`, no `Any`, no boxing. +4. Zero-allocation hot paths. Pre-allocated caches plus + trait-based material dispatch let the inner loops run with zero GC + allocation sites in the optimized LLVM IR. + +The long-term vision (parallelism, GPU, Newton–Krylov, multiphysics, +billions of DOF) is documented in [`llm/vision/vision_2.0.md`](llm/vision/vision_2.0.md) +(the filename is historical; it is not a shipped “JuliaFEM 2.0” product line). + +--- + +## 2. Where things live + +The authoritative file-organization guide is +[`docs/src/repository_layout.md`](docs/src/repository_layout.md). Highlights: + +| Folder | What goes there | +|---|---| +| `src/topology/` | Reference shapes (`Triangle`, `Tetrahedron`, `Hexahedron`, …). | +| `src/basis/` | Shape functions (`Lagrange`, `Serendipity`). | +| `src/quadrature/` | Integration rules. | +| `src/mesh/` | `Mesh{N,Topo}`, structured/unstructured meshes. | +| `src/dofs/` | `DOF{Q,E}`, `@DOFSet`, `DOFHandler` (type-stable), DOF connectivity. | +| `src/elements/` | `Element{K,P,S,N}` + `local_dof_layout`. | +| `src/materials/` | `LinearElastic`, `PerfectPlasticity`, …; trait-based dispatch. | +| `src/assemblers/` | `ElementBasedAssembler`, `DOFBasedCOOAssembler`, caches, scatter routines. | +| `src/domains/` | Physics kernels per discipline (`continuum/`, `beams/`, `plates/`, …). | +| `src/solvers/` | Linear/nonlinear solvers (still minimal). | +| `src/physics/` | High-level user API (BCs, problems). | +| `src/io/` | Mesh readers (Gmsh by default; Abaqus + Aster live in `Legacy`). | +| `src/legacy/` | Older pre-reset API (`Problem`/`Assembly`/`Solver`/`Analysis`, Dict-based fields, Abaqus reader, …) wrapped in `module Legacy`; loaded only when `JULIAFEM_ENABLE_LEGACY=1`. | +| `test//` | Mirrors `src//`. | +| `benchmarks/regression/`, `benchmarks/analysis/` | Timestamped reports. | +| `docs/src/` | Documenter-built `index.md` and `api.md`. | +| `docs/NEWS.md` | Short 0.x changelog (not the Quarto site). | +| `docs/tutorials/` | Historical Jupyter notebooks (2015–2016 API; reference only). | +| `prototypes/`, `.trash/`, `llm/` | Gitignored: experiments, throwaway, AI session logs. | + +The per-module READMEs under `src//` and the user-facing pages +under `docs/src/` were rewritten on 2026-05-08 to match the current +type-stable API. If you find a document that still references `DOFManager`, +`register_fields!`, `count_field_dofs`, `@NamedTuple{u::Tuple{...}}`, +the legacy `Physics`/`DirichletBC`/`add_dirichlet!` API or the +nonexistent `docs/contributor/` tree, treat it as stale and update or +delete it in the same change. + +--- + +## 3. Current architecture (0.x) + +### 3.1 DOF specification + +```julia +# Single field +S = @DOFSet{u::DOF{Displacement{3}, Vertex}} + +# Multi-field (e.g. thermo-mechanical) +S = @DOFSet{T::DOF{Temperature, Vertex}, + u::DOF{Displacement{3}, Vertex}} + +# `S` is a NamedTuple type whose values are `DOF{Quantity, Entity}`. +``` + +`Quantity` is anything with a defined `dof_size` (`Float64 → 1`, `Vec{3} → 3`, +`Tensor{2,3} → 9`, …). `Entity` is one of `Vertex`, `Edge`, `Face`, `Cell`. + +### 3.2 DOF handler + +`DOFHandler{M, S, NF}` (in `src/dofs/dof_handler.jl`) is the type-stable +replacement for the legacy `Dict`-based `DOFManager`: + +- One flat `Vector{Int}` per field stores the starting DOF for each + entity ID. +- `total_dofs` is computed once at element creation. +- A `@generated _make_element_dofs(...)` unrolls the + field/entity/component loop at compile time, so building + `element.dof_indices` is allocation-free. +- `DOFManager` is kept as a backward-compat alias. + +```julia +elements, handler = create_elements!(mesh, Element{Hex8, Lagrange{1}, S}) +# handler isa DOFHandler{Mesh{...}, S, NF} +# elements::Vector{Element{Hex8, Lagrange{1}, S, 24}} +# handler.dof_connectivity::DOFConnectivity already built +``` + +### 3.3 Element template (compile-time DOF layout) + +`local_dof_layout(::Type{Element{K,P,S,N}})` is a `@generated` function +returning `NTuple{N, DOFLayoutEntry}` where each entry describes +`(field_idx, entity_local, component)` for one local DOF. The compiler +folds this into `Core.Const(...)` at the call site, so DOF decoding +becomes a tuple lookup with no runtime arithmetic. + +Use `field_idx`, `entity_local`, `component` accessors (exported). + +### 3.4 Assemblers + +Two production assemblers, both type-stable and zero-allocation after +warmup: + +- `ElementBasedAssembler` (`src/assemblers/element_based/element_based_coo.jl`): + classical element-by-element assembly, scatter into COO triplets. + Gold standard for correctness. +- `DOFBasedCOOAssembler` (`src/assemblers/dof_based/dof_based_coo.jl`): + walks DOF rows, dispatches into per-element scratch using + `local_dof_layout(E)`. Stepping stone toward a GPU/matrix-free + pipeline. Currently single-kernel, `ContinuumKernel`-only. + +### 3.5 Materials and caches + +- `AbstractMaterial` → behavior trait (`StatelessConstantTangent`, + `StatelessStrainDependent`, `StatefulStrainDependent`). +- `compute_stress(material, ε, state, t) → (σ, 𝔻, new_state)`. +- Caches: + - `GeometryCache`: coords, `∇N`, `detJ·w`. + - `ElementCache`: element-level scratch (DOF mapping, IPs). + - `AssemblyMaterialWorkspace{FieldType, StateType}`: per-IP + NamedTuples of fields and states (type-stable, zero-allocation). + - `GlobalMaterialCache`: state across timesteps. + +### 3.6 Distributed matrix-free matvec (MPI) + +`MPI` is a weak dependency; `using MPI` after `JuliaFEM` loads `JuliaFEMMPIExt`. +Partition metadata lives in `src/assemblers/partitioning.jl`, `halo_exchange.jl`, +and `packed_layout.jl` (`build_partition_packed_layout_for_matvec`, +`build_matvec_halo_exchanges`, `RankHaloExchange`, `PartitionPackedLayout`). + +For Krylov solves without a full-length replicated workspace, use +`mpi_partitioned_operator_matvec_owned!` with `partitioned_mpi_owned_matvec_workspace` +and pass a persistent `mpi_requests` buffer from `allocate_exchange_matvec_halo_mpi_requests` +so each matvec does not allocate a fresh `Vector{MPI.Request}`. + +Reference drivers: `test/mpi/partitioned_matvec_smoke.jl`, +`test/mpi/partitioned_matvec_cg.jl`. CI runs both under mpiexec (see +`.github/workflows/CI.yml`, job `mpi-partitioned-matvec-smoke`). + +--- + +## 4. Invariants the agent must preserve + +These are non-negotiable. Tests and code analysis enforce them. + +1. Zero allocations in hot paths. + `test/assemblers/test_dof_based_zero_alloc.jl` asserts + `@allocated assemble!(...) == 0` and `0` GC allocation sites in the + optimized LLVM IR. Don't introduce `Dict`, `Vector{Any}`, untyped + closures, or `Vector` literals inside loops. +2. Type stability everywhere on the hot path. + `Base.promote_op(assemble!, ...) === Nothing` and the inferred + types must be concrete. Use `NamedTuple` (typed), `NTuple`, and + compile-time helpers, not `Dict`. +3. Element template == single source of truth. + Anything you'd be tempted to compute as `div`/`mod` over local DOF + indices probably belongs in a `@generated` function on + `Element{K,P,S,N}` and queried via accessors like + `local_dof_layout`. +4. Mirror src/ in test/. Tests for `src/foo/bar.jl` go in + `test/foo/test_bar.jl`. A topic-level test file should be wired + into `test/runtests.jl`. +5. Zero stdlib drift. New Julia stdlib deps (`InteractiveUtils`, + `Profile`, …) must be added to `[extras]` and the relevant + `[targets]` in `Project.toml`. + +--- + +## 5. Critical workflow rules + +Contributor-facing workflow text lives in `.github/prompts/commit.prompt.md` +and `.github/copilot-instructions.md`. Optional `.githooks/pre-commit` +(enabling `core.hooksPath`) caps staged paths at two per commit. +Editor-local Cursor rules under `.cursor/` are not part of the git tree. + +- Commits: prefer **small** steps (default one file per commit). Combine + multiple files only when they share one logical story (exception—justify + why). Never `git add .` or `git add -A` unless the user asks; stage paths + deliberately. Read the **full** staged diff (no `head`/`tail`). Message + format: Conventional Commits **subject**, blank line, **1–3 sentence** + summary, then optional **bullet** details. **Propose** paths + full message + and wait for **explicit approval** before each `git commit`. See + `.github/prompts/commit.prompt.md` for the full protocol. +- Never commit without an explicit user command to start committing. Do not + propose or initiate commits unprompted. +- Never create files in the root except for `README*` files. + Session logs go to `llm/sessions/YYYY-MM-DD-topic.md`. Throwaway + scripts go to `.trash/`. Experimental code goes to `prototypes/`. +- `prototypes/`, `.trash/`, `llm/` are gitignored. Use freely; do + not try to commit them. +- Run the full test suite before declaring done. +`julia --project=. -e 'using Pkg; Pkg.test()'`. All bundled topic tests must pass +with no errors (the exact test count grows with the tree under `test/`). + +--- + +## 6. Documentation style + +When writing documentation, session logs, READMEs, guides, comments, or +design notes: + +- Avoid emoji. +- Avoid markdown bold for emphasis. Do not write `bold` prose unless + preserving an existing quoted source that already uses it. +- Prefer plain technical writing with clear headings, short paragraphs, + and precise code references. +- Do not over-emphasize ordinary terms. If every second word needs + emphasis, the sentence should be rewritten instead. + +The goal is professional engineering documentation, not marketing copy. + +--- + +## 7. Common entry points + +- Build a model and assemble: + ```julia + mesh = create_structured_box_mesh(...) + S = @DOFSet{u::DOF{Displacement{3}, Vertex}} + ET = Element{Hex8, Lagrange{1}, S} + elements, handler = create_elements!(mesh, ET) + + material = LinearElastic(E=210e9, ν=0.3) + kernel = ContinuumKernel(ContinuumFormulation{FullThreeD}(), + material, Displacement{3}()) + + asm = DOFBasedCOOAssembler() # or COOAssembler() + cache = create_cache(asm, elements, handler, mesh, kernel) + assemble!(cache, asm, kernel, mesh) + K, f = extract_system(cache) + ``` +- Inspect the compile-time DOF layout: + ```julia + using JuliaFEM + S = @DOFSet{u::DOF{Displacement{3}, Vertex}} + ET = Element{Hex8, Lagrange{1}, S, 24} + local_dof_layout(ET) # NTuple{24, DOFLayoutEntry} + ``` +- Regression + code analysis: + `test/assemblers/test_dof_based_zero_alloc.jl` +- Top-level architecture overview: `src/README.md`. Per-module + details: the `README.md` files inside `src//`. +- Vision and roadmap: `llm/vision/vision_2.0.md`. + +### Documentation doors (which prose to open first) + +| Goal | Start here | +|------|------------| +| Shortest runnable assembly in the package docs | `docs/src/index.md` and `docs/src/snippets/minimal_elasticity_quickstart.jl` | +| Ordered reading on the Quarto site | `juliafem.github.io/learning_path.md` | +| Authoritative versus archival site material | `juliafem.github.io/docs/documentation-map.md` | +| New domain kernel or assembler hook | `juliafem.github.io/docs/developer-guide/kernel_extension_contract.md` | +| Multi-field thermo-mechanical narrative | `docs/src/thermo_elastic_walkthrough.md` | +| Quarto site colors / logo / naming | `juliafem.github.io/docs/contributor-guide/design_system.md` | +| Where new files go in the repo | `docs/src/repository_layout.md` (pointer: `docs/repository_layout.md`) | + +--- + +## 8. When you are unsure + +1. Search the code first (`Grep` / `SemanticSearch`). +2. Check the relevant `test//` for examples. +3. If there's a recent `llm/sessions/YYYY-MM-DD-*.md` log on the topic, + read it for context. +4. Ask the user. Do not guess silently in a high-impact module. diff --git a/docs/src/repository_layout.md b/docs/src/repository_layout.md new file mode 100644 index 0000000..fa2f60b --- /dev/null +++ b/docs/src/repository_layout.md @@ -0,0 +1,273 @@ +# Repository layout + +Guide for contributors (and editor tooling) on where files belong in the JuliaFEM.jl tree. Read this together with [`AGENTS.md`](https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/AGENTS.md) (architecture and invariants) and [`src/README.md`](https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/src/README.md) (per-module overview). + +Last updated: 2026-05-09. + +--- + +## Organisational philosophy + +Prefer subdirectories over flat structures. + +When several files deal with the same topic, group them into a topic subdirectory: + +- The subdirectory has its own `README.md` describing purpose and status. +- Related files stay together. +- An optional `Project.toml` keeps extra dependencies local instead of growing the main package manifest. + +Examples: + +- `src/materials/` for material models. +- `test/materials/` for matching tests. +- `benchmarks/regression/` for CI regression benchmarks and reports. +- `benchmarks/analysis/` for exploratory or architecture benchmarks. + +Avoid flat directories with dozens of unrelated files. + +--- + +## Source code + +```text +src/ +├── topology/ # Reference shapes (Triangle, Tetrahedron, Hexahedron, ...) +├── basis/ # Lagrange / Serendipity / DKT shape functions +├── quadrature/ # Integration rules +├── geometry/ # Jacobians, physical derivatives, strain helpers +├── dofs/ # DOF{Quantity, Entity}, @DOFSet, DOFHandler, connectivity +├── elements/ # Element{K, P, S, N} template + extraction / interpolation +├── materials/ # LinearElastic, NeoHookean, PerfectPlasticity, HeatConductivity, ... +├── assemblers/ # caches/, element_based/, dof_based/, matrix_free/, kernel_interface +├── domains/ # Per-physics kernels: continuum/, heat/, thermo_elastic/, plates/, ... +├── fields/ # AbstractField, Displacement, Temperature, LocalField +├── physics/ # Type tags + microkernel hooks (Elasticity, Thermal, ...) +├── mesh/ # Mesh{N, Topo}, structured / unstructured / refine / ordering +├── io/ # Gmsh-oriented I/O (legacy mesh readers live under legacy/) +├── sparse/ # SparseMatrixCOO / SparseVectorCOO scratch helpers +├── legacy/ # Pre-2.0 API in `module Legacy`; load with JULIAFEM_ENABLE_LEGACY=1 +├── exports.jl # Grouped public API exports +└── JuliaFEM.jl # Module entry point +``` + +Rules: + +- One concept per directory. +- File names: `lowercase_with_underscores.jl`. +- Aim for roughly 500 lines per file as a soft limit; split when a file grows far beyond that. +- No extra standalone `.md` files inside `src//` beyond that topic's `README.md`. Per-symbol documentation lives in docstrings. + +--- + +## Tests + +```text +test/ +├── runtests.jl # Topic list and runner +├── README.md +├── testdata/ # Small meshes and fixtures +├── topology/, basis/, mesh/, elements/, fields/, materials/, ... +├── domains/ # continuum/, heat/, darcy/, thermo_elastic/, ... +├── assemblers/, dofs/, physics/, sparse/, io/, quadrature/, geometry/, ... +├── interface/, docs/, mpi/, backend/metal/ +├── validation/, verification/, reference/ +└── solvers/ # Reserved; see README there if present +``` + +Rules: + +- Mirror `src/` where practical: tests for `src/foo/bar.jl` belong under `test/foo/`. +- Topic folders may include a `README.md` for scope and conventions. +- Test files: prefer `test_.jl`. +- Long-form comparison or verification methodology can live in topic `README.md` files; timestamp machine-generated reports as `reports/YYYY-MM-DD_HHMMSS_name.txt` under the relevant folder when you add them. +- Avoid dropping loose session write-ups under `test/`; use a gitignored notes area or a dated report path as above. + +--- + +## Examples, demos, benchmarks + +Some clones use optional top-level folders: + +```text +examples/ # Full programs for users (often one subdirectory per example) +demos/ # Short API demonstrations for developers +benchmarks/ +├── regression/ # CI-oriented performance checks +├── analysis/ # Exploratory benchmarks +└── reports/ # Timestamped outputs (YYYY-MM-DD_HHMMSS_*.txt) +``` + +These directories are not guaranteed to exist in every checkout; add them when you introduce new material. + +Distinction: + +- `examples/`: runnable end-to-end scripts, usually with local `README.md`. +- `demos/`: small focused scripts. +- `benchmarks/`: timing and allocation measurements; regression versus analysis split by purpose. + +Rules: + +- Timestamp benchmark output files under a `reports/` subdirectory. +- Group related benchmarks in the same subtree. + +--- + +## Documentation + +```text +docs/ +├── CONTRIBUTING.md +├── README.md +├── repository_layout.md # Short pointer to docs/src/repository_layout.md +├── make.jl +├── Project.toml +├── logo/ +├── tutorials/ # Historical notebooks (older API; reference only) +└── src/ + ├── index.md + ├── api.md + ├── assembler_choice.md + ├── elements_multiphysics_teaser.md + ├── thermo_elastic_walkthrough.md + ├── legacy.md + ├── repository_layout.md # This guide (built into the manual) + ├── developer/ + │ └── architecture_layers.md + └── snippets/ # Executable snippets for Documenter pages +``` + +The high-level architecture narrative lives in the repository root [`AGENTS.md`](https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/AGENTS.md). `docs/src/api.md` is generated from docstrings and exports. + +What belongs in `docs/`: + +- Documenter sources for the published manual (pages listed in `docs/make.jl`). +- Logo assets and docs-specific `Project.toml`. +- Contributor-facing notes such as `CONTRIBUTING.md`. +- This layout guide (`docs/src/repository_layout.md`) plus the short pointer `docs/repository_layout.md`. + +What does not: + +- Per-module developer notes that belong next to code: use `src//README.md` instead. +- Large generated logs or scratch files: keep them out of `docs/src/` or timestamp under a dedicated reports location. + +--- + +## Experimental and disposable paths + +```text +prototypes/ # Gitignored in normal setups: experiments by topic +.trash/ # Gitignored: pending deletion or scratch outputs +``` + +Rules: + +- Use `prototypes/` for spike code you might promote into `src/` or delete. +- Use `.trash/` for short-lived files; delete once obsolete. + +--- + +## Gitignored local notes (`llm/`) + +Many developer setups keep a **local-only**, gitignored `llm/` tree for dated session logs and drafts. Nothing under `llm/` should be committed. If your checkout does not use it, you can ignore this section. + +Naming convention when present: `llm/sessions/YYYY-MM-DD-topic.md` (lowercase, datestamped). + +--- + +## Scripts + +```text +scripts/ +├── README.md +└── *.jl / *.sh # Dev tooling, CI helpers, mesh utilities (not package code) +``` + +Rules: + +- Scripts are not loaded by `using JuliaFEM`; they are run explicitly. + +--- + +## Configuration + +```text +.github/workflows/ # CI +.github/prompts/ # Maintainer prompts (e.g. commit message conventions) +Project.toml / Manifest.toml +``` + +Optional on a developer machine (gitignored): `.cursor/` for editor-specific agent rules—not committed. + +--- + +## Anti-patterns + +Avoid: + +1. Session logs or narrative scratch files mixed into `test/` without a clear reports convention. +2. Backup suffixes (`.old`, `.bak`) committed next to active sources. +3. Log or coverage clutter in the repository root (prefer deletion or `.trash/`). +4. Hidden scratch files (`.cleanup_*`, `.temp_*`) under `src/` or `test/`. +5. Long-form design documents inside `src/` (keep module READMEs short; larger write-ups belong in `docs/` or local notes). +6. Untimestamped benchmark dumps at the top level of `benchmarks/` (use `reports/` with a timestamp prefix). + +--- + +## Decision tree + +Executable Julia code? + +- Package implementation: `src//.jl`. +- Test: `test//test_.jl`. +- Example program: `examples//` when that tree exists. +- Demo script: `demos/` when that tree exists. +- Benchmark driver: `benchmarks//`. +- One-off maintainer script: `scripts/`. +- Spike: `prototypes//` or `.trash/` if disposable. + +Documentation? + +- Manual page: add `docs/src/...` and wire it in `docs/make.jl`. +- API reference: docstrings and exports (see `docs/src/api.md`). +- Layering rules: `docs/src/developer/architecture_layers.md`. +- Module orientation: `src//README.md`. + +Data? + +- Small fixtures: `test/testdata/`. +- Large meshes: keep outside the repo or under a clearly marked local path. +- Benchmark output: `benchmarks//reports/YYYY-MM-DD_HHMMSS_*.txt`. + +Temporary? + +- Spike code: `prototypes//`. +- Pending cleanup: `.trash/`. + +--- + +## Pre-commit checklist + +1. No stray `.old`, `.bak`, `.tmp` in tracked trees. +2. Tests mirror `src/` layout where applicable. +3. Examples and benchmarks include README or timestamped reports as appropriate. +4. Module-facing docs updated in `src//README.md` when public behaviour changes. +5. `.gitignore` continues to exclude `.trash/`, `prototypes/`, `.cursor/`, and local-only trees such as `llm/` when used. + +--- + +## Golden rules + +1. Prefer mirroring `test/` after `src/` for unit tests. +2. Keep generated and experimental trees gitignored unless deliberately checked in. +3. Treat [`AGENTS.md`](https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/AGENTS.md) as the architecture source of truth. +4. Prefer short per-module READMEs in `src/` over growing orphan markdown in random folders. + +--- + +When in doubt: + +1. File placement: re-read this page. +2. Architecture: read [`AGENTS.md`](https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/AGENTS.md). +3. Module conventions: read `src//README.md`. +4. Maintainer automation: see `.github/copilot-instructions.md` (and local `.cursor/` if you use Cursor; it is gitignored). +5. Ask before introducing a new top-level directory.