Differentiable path

The pure functional analysis path: ModelState, extract_state, solve, and compliance.

Asap.ModelStateType
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 a 3 × n_nodes matrix (column i is node i) — plain arrays differentiate cleanly through every AD engine; static vectors remain internal to the kernels
  • sections::Vector{AbstractSection{T}}: per-ELEMENT sections, indexed like model.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.

source
Asap.assemble_KMethod
assemble_K(cache, state) -> SparseMatrixCSC

Pure (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.

source
Asap.complianceMethod
compliance(model, state; solver = nothing) -> T

External work Fᵀu_free of the pure solve — the canonical smooth stiffness objective, differentiable end-to-end.

source
Asap.extract_stateMethod
extract_state(model) -> ModelState

The model's current geometry and sections as a differentiable state.

source
CommonSolve.solveMethod
solve(model, state; solver = nothing) -> Vector

Pure 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.

source

Internals

The linear-solve seam of the differentiable path (the function AD rules attach to):

Asap.solve_freeFunction
solve_free(K, F) -> u_free
solve_free(solver, K, F) -> u_free

Solve 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.

source