New 327-line mock demonstration (prototype before real hardware version):
MockCUDA module (lines 20-64):
- Mock CuArray type wrapping CPU arrays
- Mock cu() transfer (simulates CPU→GPU)
- Mock @cuda macro (simulates kernel launch)
- Mock thread/block indexing functions
- Demonstrates API without requiring CUDA.jl dependency
GPU kernel example (lines 66-180):
- Type-stable element assembly kernel
- Shows concrete types required (Float64, Matrix{Float64})
- Demonstrates zero-allocation pattern
- Mock execution showing what real CUDA would do
MPI communication examples (lines 182-280):
- Mock MPI module with Send/Recv
- Type-stable data transfer patterns
- Demonstrates fast vs slow paths
Summary (lines 282-327):
- Why type stability matters for GPU/MPI
- GPU: type-unstable code FAILS to compile
- MPI: typed arrays 100× faster than serialization
- Zero allocations required in GPU kernels
- Pattern: typed structures → pre-allocated buffers → type-stable code
- Critical insight: type stability is REQUIREMENT not optimization
Purpose: Educational prototype demonstrating concepts before real hardware.
Superseded by: gpu_mpi_demo.jl (real CUDA and MPI)
New 400-line distributed FEM solver demonstration with 6 parts:
Part 1: Generate test problem (lines 61-100)
- 10×10 SPD system, condition number ~3.45
- Distributed nodal assembly: each rank owns nodes
- Exact solution x=[1,2,...,10], RHS b=A*x
Part 2: Nodal assembly pattern (lines 101-140)
- get_row(i) and get_rhs(i) abstractions
- Row-by-row matrix construction
- Each rank assembles its local rows
Part 3: GPU transfer (lines 141-167)
- Transfer local data to GPU if CUDA available
- Falls back to CPU arrays if no GPU
- Reports bytes transferred per rank
Part 4: Distributed matrix-vector product (lines 168-203)
- matvec_distributed! function
- Each rank computes y_local = A_local * x_global
- GPU acceleration if available, CPU fallback
Part 5: Conjugate Gradient solver (lines 204-318)
- cg_distributed() with MPI collectives
- Allreduce for global dot products
- Allgatherv for vector assembly
- Reports convergence progress per iteration
Part 6: Verification (lines 319-345)
- Compare computed vs exact solution
- Report relative error
- Pass/fail verification (threshold 1e-6)
Summary (lines 346-400):
- Reports what was demonstrated on real hardware
- Nodal assembly, distributed computing, multi-GPU, Krylov CG
- Key insight: type-stable + nodal → scalable
- Relevance to JuliaFEM contact mechanics
Results: Converges in 9 iterations, 7.73×10⁻¹⁴ relative error
Hardware: 2 MPI ranks, NVIDIA RTX A2000 12GB per rank
Run: mpiexec -np 2 julia --project=. demos/krylov_mpi_gpu_demo.jl
New 322-line demonstration script proving type-stable data flows to GPU and MPI:
Part 1: Type-stable data structures (lines 54-77)
- Creates nodes, connectivity, displacement as typed arrays
- Material properties E, ν as Float64
- All structures explicitly typed (Matrix{Float64}, not Dict)
Part 2: MPI communication (lines 79-123)
- Rank 0 sends 24KB displacement data to rank 1
- Transfers material properties
- Validates data integrity with checksum
- Uses MPI.Send/Recv with typed buffers
Part 3: GPU kernel execution (lines 125-192)
- Defines assemble_element_kernel! for CUDA
- Type-stable kernel: Float64, Int32, no allocations
- Transfers data to GPU (CuArray)
- Launches kernel with thread blocks
- Validates results against expected values
Part 4: Combined GPU+MPI workflow (lines 194-271)
- Rank 0 computes on GPU
- Transfers results via MPI to rank 1
- End-to-end validation
Summary section (lines 273-322):
- Reports hardware used (GPU model, MPI ranks)
- Key insights: type stability required for GPU, enables fast MPI
- Conclusion: type-stable fields are foundation for modern FEM
Requirements: MPI (required), CUDA (optional, detects and uses if available)
Run: mpiexec -np 2 julia --project=. demos/gpu_mpi_demo.jl
New 77-line guide documenting:
- Prerequisites (MPI and CUDA globally installed)
- Running commands for MPI communication test
- Running commands for combined GPU+MPI test
- Single-process GPU test instructions
- What gets demonstrated (type stability requirement, MPI fast transfer, real hardware)
- Success indicators and result interpretation
- Key insight: same patterns enable CPU speedup, GPU execution, and MPI efficiency
New 125-line README documenting:
- Two main demonstrations (GPU+MPI and Krylov solver)
- Requirements (Julia 1.9+, MPI, optional CUDA)
- Key insights: type stability required for GPU/MPI/Krylov
- Nodal assembly pattern explanation
- Architecture validation (v0.5.1 vs v1.0 comparison)
- References to benchmarks and design docs
- Contributing guidelines for new demos
Major documentation update (380 additions, 167 deletions):
- Explained topology is pure geometry (NO hardcoded node counts)
- Clarified basis determines BOTH polynomial degree AND node count
- Distinguished node count (connectivity) vs DOF count (unknowns)
- Added examples: Nedelec (edge DOFs), Raviart-Thomas (face DOFs)
- Documented Lagrange{Topology, P} parametric architecture
- Showed why Tri3/Quad4/Tet10 names are anti-pattern
- Updated all code examples to use new architecture
- Explained Serendipity vs full Lagrange tensor products
- Added performance implications and trade-offs
- Showed how one Triangle topology works for P1/P2/P3/Nedelec/etc
- Commented out assemble_mass_matrix! specialization for Element{Tet10}
- Tet10 is now a topology type, not a basis type (name conflict)
- Needs refactoring to use Tet10Basis or new parametric architecture
- Fixed code formatting (spacing around operators, indentation)
- Added TODO comment explaining the issue
- Removed nnodes from topology exports (now in basis module)
- Added exports for new topology names (Triangle, Quadrilateral, etc.)
- Kept old names as exports (they're aliases for backwards compatibility)
- Added explanatory comments about topology vs basis separation
- Added examples showing how node count comes from basis now
- Organized exports by dimension (0D/1D/2D/3D)
- Commented out topology_to_basis() helper and old Element constructors
- These mapped old names (Tri3→Tri3Basis) which no longer exist
- Need to update for new Lagrange{Topology, P} parametric architecture
- Added TODO comment explaining migration needed
- Temporary measure until new Element constructors are implemented
- Changed struct name from Wedge6 to Wedge
- Removed nnodes() method (node count now determined by basis)
- Updated all function signatures to use Wedge
- Added Wedge6 as backwards compatibility alias
- Added note explaining basis determines node count (P1=6, P2=15 nodes)
- Changed struct name from Pyr5 to Pyramid
- Removed nnodes() method (node count now determined by basis)
- Updated all function signatures to use Pyramid
- Added Pyr5 as backwards compatibility alias
- Added note explaining basis determines node count (P1=5, P2=13, P3=29)
- Changed struct name from Hex8 to Hexahedron
- Removed nnodes() method (node count now determined by basis)
- Updated all function signatures to use Hexahedron
- Added Hex8 as backwards compatibility alias
- Added note explaining basis determines node count (Q1=8, Q2=27 nodes)
- Changed struct name from Tet4 to Tetrahedron
- Removed nnodes() method (node count now determined by basis)
- Updated all function signatures to use Tetrahedron
- Added Tet4 as backwards compatibility alias
- Added note explaining basis determines node count (P1=4, P2=10 nodes)
- Changed struct name from Seg2 to Segment
- Removed nnodes() method (node count now determined by basis)
- Updated all function signatures to use Segment instead of Seg2
- Added Seg2 as backwards compatibility alias
- Added note explaining basis determines node count
- Changed struct name from Quad4 to Quadrilateral
- Removed nnodes() method (node count now determined by basis)
- Updated documentation to explain topology vs basis separation
- Added examples showing Lagrange and Serendipity differences
- Added Quad4 as deprecated alias for backwards compatibility
- Clarified that topology defines geometry only, basis determines nodes
- Changed struct name from Tri3 to Triangle
- Removed nnodes() method (node count now determined by basis)
- Updated documentation to explain topology vs basis separation
- Added examples showing Lagrange{Triangle, P} for different degrees
- Added Tri3 as deprecated alias for backwards compatibility
- Clarified that topology defines geometry only, basis determines nodes
Deleted: scripts/generate_lagrange_basis.jl (720 lines)
Reason: Functionality merged into src/basis/lagrange_generator.jl
The generator is now both a library (for inclusion) and a script (for execution).
Run as: julia --project=. src/basis/lagrange_generator.jl
Consolidates scripts/generate_lagrange_basis.jl into src/basis/lagrange_generator.jl
Changes:
- Added Vecish type alias handling for standalone/included execution
- Added vandermonde_matrix() function (~40 lines) for polynomial basis construction
- Added ElementDescription struct with keyword constructor for readability
- Added 15 element definitions with reference coordinates and polynomial ansatz:
* 1D: Seg2, Seg3
* 2D triangles: Tri3, Tri6
* 2D quads: Quad4, Quad8, Quad9
* 3D tets: Tet4, Tet10
* 3D hexes: Hex8, Hex20, Hex27
* 3D pyramid: Pyr5
* 3D wedges: Wedge6, Wedge15
- Added generation script block (~550 lines) that runs when file executed directly
- Generator now appends "Basis" suffix to all types (Tri3Basis, Quad4Basis, etc.)
- Outputs to src/basis/lagrange_generated.jl with clean formatting
- Includes progress reporting and next steps guidance
Total: 254 → 813 lines (+559 lines)
Run as: julia --project=. src/basis/lagrange_generator.jl
- Removed redundant H1 heading (already in YAML frontmatter)
- Moved "Coding Standards" above "Architecture" in What's Here section
- Updated "Before Contributing" list to prioritize standards (now item 2)
- Marked coding standards as REQUIRED for all contributions
- Changed reference from "Code Style" to "Coding Standards" (file renamed)
- Added blank line after "We assume you:" for better formatting
New 500-line standards document covering:
- Core principles (readability, type stability, zero allocations, explicit code)
- Variable naming: NO Greek letters in code (critical rule - use u,v,w not ξ,η,ζ)
- Type naming: PascalCase for types, snake_case for functions, Basis suffix pattern
- Performance guidelines: type stability, zero allocations, tuple returns
- Documentation style: docstrings with examples, theory, performance notes
- Testing standards: test organization, floating point comparisons
- Anti-patterns: Dict without types, abstract types in structs, globals, type piracy
- Git commit style: Conventional Commits format with examples
- Editor configuration: .editorconfig and JuliaFormatter.toml settings
- Summary checklist for pre-submission verification
Rationale for no Greek letters: keyboard accessibility, editor compatibility,
copy-paste issues, search/replace problems, terminal rendering, git diffs,
internationalization, and accessibility concerns.
- Expanded contributing text with clearer call to action
- Added links to CONTRIBUTING.md, coding standards, and contributor manual
- Added key requirements list (type stability, tests, Greek letter rule, clean commits)
- Fixed code block syntax highlighting (markdown → text) for Zen and citation
- Improved formatting with proper newlines before code blocks
- Added friendly questions/help invitation
**Added Gauss quadrature mappings for all 17 topology types**
Extended src/integration/gauss.jl to support all element types from 1D to 3D,
both linear and quadratic variants.
## Integration Rule Mappings
### 1D Segments (Seg2, Seg3)
- Tensor product rules: GLSEG1, GLSEG2, GLSEG3, GLSEG4, GLSEG5
- Support for Gauss{1} through Gauss{5}
### 2D Triangles (Tri3, Tri6, Tri7)
- Dedicated triangular rules: GLTRI1, GLTRI3, GLTRI4, GLTRI6, GLTRI7, GLTRI12
- Support for Gauss{1}, Gauss{3}, Gauss{4}, Gauss{6}, Gauss{7}, Gauss{12}
- Same rules used for linear (Tri3) and quadratic (Tri6, Tri7) topologies
### 2D Quadrilaterals (Quad4, Quad8, Quad9)
- Tensor product rules: GLQUAD1, GLQUAD4, GLQUAD9, GLQUAD16, GLQUAD25
- Support for Gauss{1} through Gauss{5}
- Same rules for linear (Quad4) and quadratic (Quad8, Quad9) variants
### 3D Tetrahedra (Tet4, Tet10)
- Dedicated tetrahedral rules: GLTET1, GLTET4, GLTET5, GLTET15
- Support for Gauss{1}, Gauss{4}, Gauss{5}, Gauss{15}
### 3D Hexahedra (Hex8, Hex20, Hex27)
- Tensor product rules: GLHEX1, GLHEX8, GLHEX27, GLHEX64, GLHEX125
- Support for Gauss{1} through Gauss{5}
- Same rules for linear (Hex8) and quadratic (Hex20, Hex27) variants
### 3D Wedges/Prisms (Wedge6, Wedge15)
- Dedicated wedge rules: GLWED6, GLWED21
- Support for Gauss{6}, Gauss{21}
### 3D Pyramids (Pyr5)
- Dedicated pyramid rules: GLPYR5
- Support for Gauss{5}
## Design Notes
**Quadrature rules from src/quadrature/**
All actual integration point data comes from src/quadrature/*.jl files
(consolidated from FEMQuad.jl). This file just maps high-level scheme + topology
to the appropriate low-level rule name.
**Tensor product elements:**
Segments, quads, and hexes use tensor product quadrature generated programmatically
in glquad.jl. Number follows pattern: N_points = N_per_dim^dimension
- GLSEG3 = 3 points in 1D
- GLQUAD9 = 3² = 9 points in 2D
- GLHEX27 = 3³ = 27 points in 3D
**Simplex elements:**
Triangles, tetrahedra use specialized rules (not tensor products) with optimized
point locations. Number roughly indicates integration order capability.
**Quadratic elements use same rules:**
Quadratic variants (Tri6, Quad8, Hex20, etc.) use same quadrature rules as
linear counterparts. User selects integration order via Gauss{N} parameter,
not topology type. Higher order topologies typically need higher N for exact
integration.
**Zero-allocation maintained:**
All functions return tuples, no heap allocation in integration point queries.
## Usage Examples
```julia
# Linear triangle with 1-point rule
ips = integration_points(Gauss{1}(), Tri3())
# Quadratic triangle with 6-point rule (more accurate)
ips = integration_points(Gauss{6}(), Tri6())
# Linear hex with 8-point rule (2³)
ips = integration_points(Gauss{2}(), Hex8())
# Quadratic hex with 27-point rule (3³)
ips = integration_points(Gauss{3}(), Hex27())
```
## Completeness
✅ All 17 topology types now supported
✅ Linear and quadratic variants covered
✅ 1D, 2D, and 3D elements complete
✅ Zero-allocation design maintained
## References
- src/quadrature/glquad.jl (tensor product generation)
- src/quadrature/gltri.jl (triangle rules)
- src/quadrature/gltet.jl (tetrahedron rules)
- src/quadrature/glwed.jl (wedge rules)
- src/quadrature/glpyr.jl (pyramid rules)
- Dunavant, "High degree efficient symmetrical Gaussian quadrature rules for the triangle"
- Abramowitz & Stegun, "Handbook of Mathematical Functions"
**Problem:** CI documentation build failing with:
UndefVarError: `create_basis_and_eval` not defined
**Root Cause:**
- Commented out include("basis/lagrange_generator.jl")
- But existing lagrange_*.jl files still use create_basis_and_eval()
- Generator must be loaded at runtime (for now)
**Solution:**
- Re-enable include("basis/lagrange_generator.jl")
- Add TODO comment explaining this is temporary
- Once we generate lagrange_generated.jl, we can:
1. Remove old lagrange_*.jl includes
2. Include lagrange_generated.jl instead
3. Remove lagrange_generator.jl from runtime
**Status:**
- ✓ Package loads successfully
- ✓ Should fix CI documentation build
**Next Steps:**
1. Run scripts/generate_lagrange_basis.jl
2. Create src/basis/lagrange_generated.jl
3. Replace dynamic includes with static generated file
4. Remove __precompile__(false) completely
**Purpose:** Comprehensive justification for all technical decisions prioritizing
performance over convenience.
**Key Principles:**
- Efficiency > Educativeness (when forced to choose)
- Type stability over everything (100× performance difference)
- No free lunch - Julia doesn't make miracles
- HPC requires discipline and trade-offs
**Core Decisions Justified:**
1. **No Dynamic Field System**
- field["foo"] = x is 100× slower (Dict{String,Any})
- Type-stable structs only
- Sacrifice: Runtime flexibility
- Gain: Performance
2. **Immutable Data Structures**
- struct over mutable struct
- Sacrifice: Convenient mutation
- Gain: 2-10× speedup, thread-safety, stack allocation
3. **NTuple Over Vector**
- Compile-time size → SIMD optimization
- Sacrifice: Dynamic sizing
- Gain: Zero allocations, type stability
4. **Monolithic Over Multi-Package**
- Learned from 2015-2019 mistake
- Sacrifice: Small dependencies
- Gain: It actually works
5. **Manual Derivatives (hot paths)**
- 30× faster than AD for Tet10
- Sacrifice: More code
- Gain: Assembly loops stay fast
6. **Matrix-Free Methods**
- Design for 1M+ DOF from day 1
- Cannot retrofit later
7. **Explicit Over Implicit**
- No magic, show the steps
- Debuggable and teachable
**Hierarchy of Values:**
1. Correctness
2. Performance
3. Maintainability
4. Educativeness
5. Convenience
**What We're Giving Up:**
- Runtime flexibility (no element["custom_field"])
- Dynamic problem definition (no runtime topology changes)
- Duck typing convenience
- Small dependencies
- Beginner-friendly magic
**What We're Getting:**
- 10× single-thread speedup target
- 1M DOF contact problems
- Thread/GPU/distributed scalability
- Real HPC capability
**The Hard Truth:**
From Issue #266: "Do like Python, be slow like Python. Know what you do
before compiling, and be fast like C. There's no free lunch."
**Success Metrics:**
- ✅ Zero allocations in assembly
- ✅ Type-stable hot paths
- 🎯 10× faster than v0.5.1
- 🎯 1M DOF in < 1 hour
- 🎯 100+ thread scaling
**Use Cases:**
- "Why can't I use Dict?" → Point here
- "Why immutable?" → Point here
- "Why manual derivatives?" → Point here
- Any "why not convenience?" → Point here
**Status:** Living document, updated as we learn
See: Issue #266, TECHNICAL_VISION.md, benchmark results
**Three Manuals for Three Audiences:**
1. **User Manual** (docs/user/) - "Just Get It Done"
- For end users, engineers, students
- Simple, practical, step-by-step
- Quick start, tutorials, examples, troubleshooting
- Philosophy: Show me how to solve my problem
2. **Contributor Manual** (docs/contributor/) - "Show Me the Code"
- For developers, contributors, advanced users
- Technical, detailed, design rationale
- Testing, architecture, performance, CI/CD
- Philosophy: Explain HOW and WHY
3. **The JuliaFEM Book** (docs/book/) - "Let Me Show You How I Think"
- For researchers, theory nerds, and Jukka
- Comprehensive, educational, opinionated, personal
- Math foundations, design philosophy, history, research
- Philosophy: Mix theory, code, and personal experience
**Reorganization:**
- Moved: TESTING_PHILOSOPHY.md → contributor/testing_philosophy.md
- Moved: STATUS.md → contributor/status.md
- Moved: TEST_FIXES_NEEDED.md → contributor/test_fixes_needed.md
- Moved: lagrange_basis_functions.md → book/lagrange_basis_functions.md
- Moved: benchmarks/ → book/benchmarks/
- Created: docs/README.md (main index explaining structure)
- Created: README.md in each section explaining audience and contents
- Updated: All references in scripts and source files
**Naming:** All docs now lowercase (testing_philosophy not TESTING_PHILOSOPHY)
**Benefits:**
- Clear separation of concerns
- Users don't get overwhelmed with implementation details
- Contributors get technical depth
- Book preserves deep theory and personal insights
- Each manual optimized for its audience
**Next:** Populate each section with appropriate content
- Moved docs/theory/lagrange_basis_functions.md → src/lagrange_basis_functions.md
- Updated all references in scripts and source files
- Using lowercase for consistency (no uppercase in filenames)
- Documentation now under src/ for automated doc generation
Rationale: Documentation should be close to implementation and follow
consistent naming conventions (lowercase).
No functional changes - only whitespace and formatting adjustments:
- Removed spaces around = in named tuple syntax (name = → name=)
- Adjusted spacing in array literals
- Standardized spacing around operators
RESEARCH QUESTION: Should JuliaFEM use hand-calculated derivatives or AD?
Created comprehensive benchmark comparing:
- Manual: Hand-calculated derivatives (traditional FEM)
- AD: Tensors.jl gradient() (automatic differentiation)
RESULTS (AMD Ryzen 9, Julia 1.12.1):
- Manual: 8.7 ns, 0 allocations
- AD: 268.1 ns, 0 allocations
- AD is 30× SLOWER than manual
KEY FINDINGS:
✅ Both achieve zero allocations (Tensors.jl is well-optimized)
❌ AD has 30× compute overhead from dual number arithmetic
⚠️ In assembly loops: millions of calls = 10+ seconds extra per solve
RECOMMENDATION:
- Keep manual derivatives for common elements (Tet10, Hex8, Quad4, etc.)
- Use AD for prototyping and rare elements
- Unit test manual vs AD to catch errors
- Future: Generate derivatives symbolically (Symbolics.jl)
WHY NOT AD EVERYWHERE?
Assembly is hottest path in FEM. 30× overhead = unacceptable for
production code. Users will notice the performance difference.
WHY NOT ABANDON AD?
- Excellent for prototyping
- Required for exotic bases (NURBS)
- Perfect for unit testing manual derivatives
- Zero allocations impressive
Files:
- benchmarks/tet10_derivatives_benchmark.jl (runnable benchmark)
- docs/benchmarks/shape_function_derivatives_ad_vs_manual.md (analysis)
Dependencies added: BenchmarkTools
This answers the research question definitively with data.
Changed Point from 'mutable struct' to 'struct'.
The Dict for fields remains a reference type, so field updates via setindex!
and update! still work correctly. This change improves type stability and
enables better compiler optimizations.
Benefits:
- Better compiler optimizations (immutable types)
- Type stability improvements
- Stack allocation when possible
- No breaking changes (Dict fields still mutable)
Tests: All 157 tests passing
MAJOR PERFORMANCE REFACTORING:
1. Shape functions return tuples instead of allocating vectors:
- eval_basis!(): Returns NTuple{N,T} directly (zero allocations)
- eval_dbasis!(): Returns NTuple{N,Vec{D}} directly (zero allocations)
- API boundary (get_basis/get_dbasis) still returns vectors for compat
2. Element is now immutable with compile-time known structure:
- connectivity: Vector{UInt} → NTuple{N,UInt}
- integration_points: Vector{IP} → NTuple{NIP,IP}
- Element{N,NIP,M,B} parametrized by connectivity/IP count
- Changed from 'mutable struct' to 'struct'
3. Helper function for immutability:
- with_integration_points(element, ips) returns new element
- get_integration_points() returns tuple directly
Benefits:
- Zero allocations in hot paths (basis evaluation)
- Compile-time sizes enable better optimization
- Type stability improvements
- Stack allocation instead of heap
Breaking changes:
- Element.connectivity is now tuple (use collect() for vector)
- Element is immutable (use with_integration_points for updates)
Tests: All 157 tests passing
Gmsh returns node and element IDs as UInt64, so we should use unsigned
integers consistently throughout JuliaFEM to avoid unnecessary conversions.
Changes:
- Point.id: Int → UInt
- Element.id: Int → UInt
- Element.connectivity: Vector{Int} → Vector{UInt}
- Element constructors: Accept Integer (converts to UInt internally)
- Default element_id: -1 → 0 (UInt has no negative values)
Benefits:
- Direct compatibility with Gmsh.jl (no Int/UInt conversions)
- Semantically correct (node/element IDs are never negative)
- Slightly more efficient (no sign checks)
Tests: All 156 tests passing
Closes#267
Major architectural decision: Use Tensors.jl consistently everywhere
for geometric vectors, integration points, and coordinates.
Changes to src/elements/elements.jl:
- get_basis(): Convert ip to Vec, use Vector (not Matrix) for eval_basis!
- get_dbasis(): Convert ip to Vec
- jacobian evaluation: Convert geometry and ip.coords to Vec properly
- Handle both raw coordinates (Tuple) and IP struct transparently
New Tutorial 3: Numerical Integration and Jacobian (49 tests)
- Integration point structure and weights
- Jacobian determinant and matrix evaluation
- Numerical integration (constant, linear, quadratic functions)
- Multiple element types (Quad4, Seg2, Tri3)
Tests: 107 → 156 passing (49 new)
Runtime: ~7 seconds
Closes architectural standardization on Tensors.jl.
Related to Issue #250 (merge conflict resolution).
Why Tensors.jl:
- Type stability (100× performance vs Dict-based)
- Material science compatibility (stress tensors)
- Zero-cost abstractions
- Consistent API across all geometric calculations
Current state discovery:
- Element basis function evaluation broken (eval_basis! signature mismatch)
- Field interpolation at integration points broken (same root cause)
- Jacobian evaluation at integration points broken
- These are fundamental API issues affecting multiple test paths
Impact:
- Tutorial 3 (basis functions) deferred until API fixed
- Affects any code trying to evaluate fields at integration points
- Related to Quad4 assembly issues discovered in Tutorial 4
Working tutorials (107/107 tests passing):
- Tutorial 1: Element creation (5 tests)
- Tutorial 2: Gmsh mesh reading (72 tests)
- Tutorial 4: 1-element validation (35 tests)
Next: Focus on tutorials using working APIs only
Educational validation test for Issue #265 use case (JuliaFEM as reference).
Covers:
- Element creation and connectivity
- Field assignment (geometry, material properties)
- Field retrieval with function call syntax
- Hand-calculated constitutive matrix for plane stress
- Geometry validation (dimensions, center, area)
- Material property validation (physical ranges)
Note: Defers stiffness matrix assembly to future work due to current
Quad4 assembly issues. Focus is on element setup validation that
other FEM developers can use as reference.
Tutorial series now: 107/107 tests passing
- Tutorial 1: Creating elements (5 tests)
- Tutorial 2: Gmsh mesh reading (72 tests)
- Tutorial 4: 1-element validation (35 tests - done before Tutorial 3)