Differentiable path
The pure functional analysis path: ModelState, extract_state, solve, and compliance.
Asap.ModelState — Type
ModelState{T}The differentiable state of a model: everything automatic differentiation is allowed to vary, extracted as plain data. Element kernels are pure functions, so evaluating the pipeline on a ModelState involves no mutation anywhere — Zygote (and other AD engines) differentiate it directly, with only two custom rules (sparse construction and the linear solve) supplied by the ChainRules extension.
Fields
X::Matrix{T}: node positions as a3 × n_nodesmatrix (columniis nodei) — plain arrays differentiate cleanly through every AD engine; static vectors remain internal to the kernelssections::Vector{AbstractSection{T}}: per-ELEMENT sections, indexed likemodel.elements(elements sharing a section object simply repeat it)
Construct the reference state with extract_state, then produce perturbed states in your objective (e.g. rebuild positions from a design vector, or sections from area variables) — see the gradient tests for patterns.
Loads are treated as constant data in the pure path for now (P and Pf are read from the cache); differentiating through load values/FEFs is a Phase 5a extension if needed.
Asap.assemble_K — Method
assemble_K(cache, state) -> SparseMatrixCSCPure (non-mutating) assembly of the free×free stiffness matrix from a differentiable ModelState. Identical numerics to assemble_K! — the two paths share every element kernel and the frozen sparsity pattern — verified by parity tests.
Asap.compliance — Method
compliance(model, state; solver = nothing) -> TExternal work Fᵀu_free of the pure solve — the canonical smooth stiffness objective, differentiable end-to-end.
Asap.extract_state — Method
extract_state(model) -> ModelStateThe model's current geometry and sections as a differentiable state.
CommonSolve.solve — Method
solve(model, state; solver = nothing) -> VectorPure linear solve on a processed model: assemble from the differentiable state, solve, and return the FULL-space displacement vector (inactive and fixed slots zero). Requires process! (or a prior solve!) to have built the cache; load vectors are taken from the cache as constants — call assemble_loads! first if loads changed.
solver selects the linear-solver backend for the reduced solve (see solve_free); nothing is the built-in CHOLMOD path.
This is the AD entry point: gradients of any scalar of the returned vector with respect to node positions and section properties flow through Zygote with no further ceremony.
Internals
The linear-solve seam of the differentiable path (the function AD rules attach to):
Asap.solve_free — Function
solve_free(K, F) -> u_free
solve_free(solver, K, F) -> u_freeSolve the reduced linear system. The AD extension supplies the adjoint (one extra back-substitution on the cached factorization — the classical adjoint method).
The 3-arg form goes through the solver seam (_factorize(solver, K)): solver may be nothing (built-in CHOLMOD), any LinearSolve algorithm (with AsapLinearSolveExt), or a CachedSolver wrapper that reuses one factorization across value/adjoint/tangent passes at the same design. The 2-arg form is the default-solver fast path — kept as its own method (not a forwarder) because Enzyme's imported rules match this exact signature.