Skip to content

Driver refactor in HyperFEM (tracking, companion of #21) #23

Closed

Description

@jmartfrut

Summary

Tracking issue for the driver refactor in HyperFEM (src/ComputationalModels/Drivers.jl, src/Solvers/, ext/HyperFEMKernelsExt.jl). It is the driver-side companion of the scalable linear-solver block (#21) and appears in the master plan (#22).

Goal: drivers with concrete types and no allocations per load step or Newton iteration, one incremental loop shared by all models, and a backend contract that HyperFEMKernels and the solver block plug into, without changing the user interface (constructors, solve!, PostProcessor, Adv_Mater scripts).

Stages are named R0–R5 so they do not clash with the stages (S0–S5) and decisions (D1–D5) of #21.

Relation with the solver block (#21)

  • R0 is measured inside S0: one baseline for drivers and solvers.
  • S0′ also takes the allocation fixes of the efficiency review: line searches without x + α*dx (trial vector in the cache), x .+= α .* dx, IterativeSolver without eval(Meta.parse(...)) per solve!, views in Injectivity_Preserving_LS.
  • R4 (single Newton) goes after S1 and before S3. It is designed with what S1 (PETSc Krylov + AMG, lagged AMG) and S3 (matrix-free Jacobian inside Newton) need, and it is the evolution of Newton_RaphsonSolver after S0′, so S0′ is not lost. It follows whatever Scalable linear solvers: plan, decisions and traceability #21 decides in D5 (curvature criterion).
S0 (incl. R0) ─► S0′ ─┬─► R1 ─► R2 ─► R3
                      └─► S1 ─────────────► R4a ─► R4 ─► S3 ─► S4 ─► S5
                                  (S2 after S1, independent of R*)
R5 MPI: postponed

Stages

  • R0 — Driver baseline (inside S0, benchmark/drivers/ in HyperFEMKernels)
    • Time and bytes per Newton iteration (residual, Jacobian, factorization, rest), per load step and per solve!; driver allocations outside assembly and the linear solve.
    • StaticMechanicalDirichlet (with ProjectDirichlet), EM plate P1 and Q2 box, hMRE, with :gridap and :kernel.
  • R1 — Typed caches, backend as a type (HyperFEM, drivers-typed-caches, from kernel-backend)
    • AlgebraicBackend / GridapBackend / KernelBackend (the latter in the extension); backend = :gridap | :kernel still accepted; the Val hooks stay one cycle as a bridge.
    • Typed NonlinearStepCache; caches as a NamedTuple (so post.comp_model.caches[3] keeps working).
    • Remove dead code (post_solve!, broken get_measure / get_assemblers of StaticLinearModel).
    • StaggeredModel returns all(flags); kargsolve accepts NamedTuples.
    • No Vector{Float64} in signatures; no GC.gc() in the dynamic driver.
    • Clear errors for custom/distributed assemblers with :kernel (open item of [Kernel backend]: HyperFEMKernels integration — phases, status and path to main HyperFEM.jl#258).
    • Bit-identical.
  • R2 — Single incremental loop (HyperFEM, drivers-single-loop)
    • LoadStepping, TimeStepping, step!(m, Λ, ∆Λ; load) with today's order of operations, used by the static, dynamic (Midpoint) and staggered models.
    • DynamicNonlinearModel gets backend (open item of HyperFEM.jl#258); optional hooks on_bisection, presolver; post.state.
    • Bit-identical on all simulation tests with both backends.
  • R3 — Dirichlet projection and StaticLinearModel (HyperFEM, drivers-dirichlet-linear)
    • dirichlet_projection! assembles into the Newton cache instead of a new AffineFEOperator per step.
    • StaticLinearModel: numerical_setup! only if K was reassembled; one inner constructor; shared _solve_linear!.
    • Bit-identical, or rounding accepted and documented.
  • R4a — Newton ↔ linear-solver interface (docs/design/newton-linear-solver.md, after S1)
    • Cache that accepts a matrix-free Jacobian (needed by S3); preconditioner refresh with x (numerical_setup!(ns, A, x), lagged AMG from S1); optional inexact tolerance (Eisenstat–Walker), off by default; one-pass residual_and_jacobian! with fallback.
    • Checkpoint (Jesús).
  • R4 — Single Newton (HyperFEM, drivers-newton)
    • HyperFEM.NewtonSolver as the evolution of Newton_RaphsonSolver (line search, ctol, linear-solver convergence check from S0′); Newton_RaphsonSolver deprecated; GridapSolvers' NewtonSolver still accepted.
    • Convergence through converged(nls, cache) instead of reading nls.log.residuals; Injectivity_Preserving_LS over blocks with a global reduction of α.
    • Bit-identical with LU; zero-allocation test per iteration (skipped with --check-bounds=yes).
  • R5 — MPI: postponed. Meanwhile R1–R4 keep the serial code MPI-ready: no Vector{Float64}, no scalar indexing, global reductions for control-flow decisions, output from the main rank only.

Decisions (@jmartfrut, 2026-10-24)

  • Single Newton HyperFEM.NewtonSolver; Newton_RaphsonSolver deprecated.
  • Dirichlet-projection operator stored untyped in the cache; ProjectDirichlet stays a solve! kwarg.
  • StaggeredModel returns all(flags).
  • If the projection on the cache is not bit-identical, rounding is accepted and documented.
  • MPI postponed.
  • Backend as a type: AlgebraicBackend / GridapBackend / KernelBackend.

User-visible changes

Only two: the convergence flag of StaggeredModel (today it is wrong) and a depwarn for Newton_RaphsonSolver. Everything else is optional and additive.

Rules

  • One branch per stage, from kernel-backend, merged back into it; stacked if the previous PR is under review.
  • Validation: HyperFEM Pkg.test() with both backends; bit-identical on the simulation tests and the Adv_Mater scripts (dev/run_sims.jl, compare_sims.jl, advmater_compare.jl); Julia 1.10 only for the :gridap path (HyperFEMKernels and GridapPETSc require 1.12).
  • A HyperFEM stage that needs something new from HyperFEMKernels is merged after the corresponding HyperFEMKernels PR (HyperFEM CI clones HyperFEMKernels main).
  • Each stage: dated entry in ESTADO.md, PR text in docs/PR_<branch>.md, checkbox ticked here with a short comment. PRs reference this issue and the stage (R1, …).

Activity

  1. changed the title [-]Plan: driver refactor (HyperFEM) + iterative solver block (tracking)[/-] [+]Driver refactor in HyperFEM (tracking, companion of #21)[/+] on Oct 24, 2026
  2. jmartfrut commented on Oct 29, 2026

    @jmartfrut
    MemberAuthor

    Update after unifying this plan with the solver block (#21), 2026-10-29. Full detail in the project document design/solvers.md, section "Unified stage structure".

    • R4 (single Newton) is absorbed by S0′ and done now, before S1. Name: NewtonRaphson (not HyperFEM.NewtonSolver); Newton_RaphsonSolver deprecated for one release. GridapSolvers is dropped from HyperFEM (own tolerances and convergence log); its NewtonSolver keeps working for one release through a weak-dependency extension with a deprecation warning. The Newton takes two strategies: linesearch (FullStepLS, BacktrackingLS, InjectivityLS) and tangent (ConsistentTangent, HybridTangent); the operator exposes optional energy, max_step and jacobian!(…, :projected) hooks implemented by the drivers.
    • R4a is reduced to the post-S1 checkpoint: preconditioner refresh with x, optional Eisenstat–Walker, one-pass residual_and_jacobian!. The matrix-free-ready cache and the linear_converged hook are already in S0′.
    • R0 runs inside S0 (HyperFEMKernels benchmark/drivers/), as planned.
    • R2 gains the load-stepping changes of the third-medium note (2026-10-25, §6): ΔΛ growth ×1.5 after ≤ 10 iterations, maxbisec as consecutive bisections. The Dirichlet reset after a bisection (§6, point 3) is a bug fix and goes in S0′ with its own test. R2's acceptance test: the full piston (15d) against Horák's curve, local only (HYPERFEM_TEST_LONG=1).
    • R1: backend names are now :lazy / :compiled (HyperFEM #263); the types become LazyBackend / CompiledBackend, and they absorb S0′'s operator hooks without changing their signature.
    • Execution order: S0 → S0′ → R1 → R2 → S1 → R3 → S2 → R4a → S3 → S4 → S5.
  3. jmartfrut commented on Nov 6, 2026

    @jmartfrut
    MemberAuthor

    Closing this tracking issue: the driver refactor is complete.

    What landed

    • Drivers now go through a backend abstraction (backend = :gridap | :kernel), so the assembly engine can be swapped without touching the drivers' public API. The default stays :gridap.
    • The compiled backend plugs in through the HyperFEMKernelsExt extension (weak dependency). HyperFEM does not depend on HyperFEMKernels at load time.
    • Newton–Raphson works with the new linear solvers through explicit hooks (convergence, iteration count, tolerance, preconditioner builds), including the forcing-term control.
    • Matrix-free Jacobian inside Newton is available via CompiledBackend(; jacobian = :matrixfree), and energy / injectivity-step evaluation is dispatched on the backend.

    Validation

    • The refactor of the existing drivers was checked against the previous behaviour: bit-for-bit identical results in the test simulations and in the Adv_Mater scripts.
    • The matrix-free path follows the same Newton trajectory as the direct solvers (LU/MUMPS).
    • New functionality is covered by tests (test/SolversTests/).

    PRs

    • Driver refactor: #__
    • Solver stages and matrix-free Newton: #__ (HyperFEM), HyperFEMKernels#__

    Notes

    • Requires HyperFEMKernels 0.2 for the compiled backend ([compat] bumped in #__).
    • Follow-ups are tracked separately and are not blockers for this issue: AMG-type preconditioning for the matrix-free path, and performance on simplex meshes.

    Companion of #21.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions