Skip to content

QF Solver 0.2.5a0 performance plan

Purpose

Characterize nonlinear cost and memory without turning 0.2.5 into an HPC rewrite. Optimization begins only after a profile identifies a stable hotspot.

Instrumentation

Measure per increment and total:

  • constitutive update time and call count;
  • quadrature and B/kinematic operator time;
  • material, geometric and contact tangent assembly;
  • contact search/projection and active-set changes;
  • sparse assembly conversion, nnz and allocation count;
  • linear solve/factorization and iterative diagnostics;
  • state begin-trial, copy, commit and rollback;
  • continuation constraint work;
  • peak RSS where available and deterministic storage estimates.

The first reproducible implementation is scripts/benchmark_nonlinear_025.py. It records raw samples for the regular two-cell shared J2 TET4/TET10/HEX8/HEX20 benchmark, including DOF count, Newton iterations, wall time, Python peak allocations, optional RSS and mean phase timings. Repeated runs also retain median, minimum, maximum and sample standard deviation for wall time, together with median phase timings where available; these statistics describe repeatability and do not define release thresholds. The experimental finite-kinematic branch can be selected explicitly; it is not a release qualification claim. For example:

python scripts/benchmark_nonlinear_025.py --families TET4 HEX8 --repeats 3 --output results/benchmark_0_2_5/nonlinear.json
python scripts/benchmark_nonlinear_025.py --families TET4 --kinematics total_lagrangian_j2 --repeats 1 --output results/benchmark_0_2_5/tl_j2.json
python scripts/benchmark_nonlinear_025.py --families TET4 --path arc_length --repeats 1 --output results/benchmark_0_2_5/arc_length.json
python scripts/benchmark_nonlinear_025.py --families TET4 TET10 HEX8 HEX20 --path arc_length_finite_kinematic --repeats 1 --output results/benchmark_0_2_5/arc_length_finite_kinematic.json
python scripts/benchmark_nonlinear_025.py --families TET4 --path coupled --repeats 1 --output results/benchmark_0_2_5/coupled.json

The benchmark now exposes explicit load_control, arc_length, arc_length_finite_kinematic, contact and coupled paths. The load-control path exposes non-invasive per-step timers for assembly_seconds, linear_solve_seconds and line_search_seconds, plus component timings for element setup, element kernel, local scatter, sparse conversion and contact assembly, plus nonlinear element-plan and Total-Lagrangian reference-geometry cache hit/miss counts. These are observational phase timings, not claims of a scaling law or a release performance target.

Finite-kinematic arc-length replay (one repeat, working tree)

The latest explicit replay is archived at results/benchmark_0_2_5/arc_length_finite_kinematic_latest.json. All four families reached the signed target factor 0.5 with the common adaptive-radius driver:

Family DOF Newton Wall s Assembly s Sparse solve s Max relative residual
TET4 24 76 2.943 2.137 0.297 1.65e-08
TET10 78 670 83.906 70.081 2.797 9.58e-08
HEX8 24 120 6.546 4.961 0.478 6.57e-08
HEX20 60 1479 235.151 197.523 6.060 9.90e-08

The benchmark uses max_arc_steps=512 because the HEX20 path needs a longer continuation budget; its successful run used 319 accepted steps. These are single-run, dirty-worktree observations. They identify the current HEX20 element-kernel cost but do not establish scaling, a speedup, or close 025-G08.

Bounded path smoke (working tree)

One TET4 repeat was executed for each new path on the current working tree. These values are characterization only, not frozen release thresholds:

Path Kinematics DOF Newton Wall s Assembly s Linear solve s Max relative residual
load_control total_lagrangian_j2 36 23 1.335 1.092 0.064 9.88e-09
arc_length small_strain 12 14 0.177 0.027 0.057 8.73e-11
contact small_strain 24 21 0.388 0.219 0.056 4.91e-09
coupled total_lagrangian_j2 24 24 0.811 0.612 0.068 2.93e-08

The values are one local repeat and are not a release threshold or a scaling claim. RSS and Python allocation counters are retained in the raw JSON output.

Two-repeat all-family load-control characterization

The same regular two-cell J2 path was repeated twice for each supported solid family on the current Windows worktree. The latest raw report is results/benchmark_0_2_5/nonlinear_load_control_all_families_repeats2_latest.json. All eight runs converged with the same Newton iteration count per family. The coefficient of variation (CV) is descriptive only:

Family DOF Newton Mean wall s CV Mean assembly s Mean sparse solve s Max Python peak bytes Max RSS bytes
TET4 36 22 0.607 2.46% 0.392 0.0614 1,343,608 112,435,200
TET10 129 21 2.318 0.19% 1.648 0.0646 1,426,913 114,286,592
HEX8 36 17 1.028 2.31% 0.747 0.0473 582,095 114,790,400
HEX20 96 22 16.453 0.16% 11.160 0.0654 1,112,348 114,573,312

This is a reproducibility and hotspot characterization, not a scaling or release-acceptance result. The report records provenance as commit e368c0ce00874c16ff1e8fa9158ea0a8cd2dd745 with a dirty worktree; it must be replayed on a clean candidate SHA before G08 can close.

Component-level replay (one repeat, working tree)

The latest raw report is results/benchmark_0_2_5/nonlinear_load_control_component_profile_latest.json. All four runs converged. The decomposition is:

Family DOF Wall s Assembly s Element kernel s Scatter s Sparse conversion s Sparse solve s Tangent nnz
TET4 36 0.619 0.404 0.205 0.120 0.017 0.058 684
TET10 129 2.390 1.719 0.918 0.565 0.060 0.066 5,967
HEX8 36 1.029 0.744 0.626 0.062 0.014 0.045 1,008
HEX20 96 16.622 11.336 10.677 0.438 0.050 0.065 6,624

The result confirms that the current engineering-medium HEX20 cost is dominated by the element kernel, not the sparse linear solve. It is still a dirty-worktree, single-repeat characterization; no optimization or scaling claim is inferred and G08 remains open until clean-SHA repeatability and numerical before/after evidence exist.

Exploratory result: reusable nonlinear assembly plan

The nonlinear driver now prepares a per-solve assembly plan containing the material objects, element kernels, local coordinates and global DDL maps. It does not retain integration-point history: trial and committed material states remain supplied by the Newton transaction on every assembly. This removes the repeated construction of those objects from the Newton loop while preserving the existing sparse COO-to-CSR assembly path.

The one-repeat replay is stored in results/benchmark_0_2_5/nonlinear_load_control_cache_latest.json on the same dirty working tree. All four families converged and reported zero cache misses during assembly. The observed setup-time comparison against the prior component profile was:

Family Previous setup s Cached setup s Cache hits Cache misses Newton Final displacement norm
TET4 0.00543 0.00040 260 0 22 0.1728238257
TET10 0.00960 0.00059 250 0 21 0.5240095385
HEX8 0.00106 0.00009 42 0 17 0.0382647740
HEX20 0.00185 0.00014 52 0 22 0.4306610359

The assembly and total wall times remain within the variability of a single local run, while the numerical outputs and iteration counts match the prior profile. This is a bounded implementation result, not a release speedup or scaling claim. A clean-SHA replay, repeated samples and the relevant before/after numerical comparison remain required before G08 can close.

Exploratory result: reference-geometry reuse

Total-Lagrangian J2 kernels now cache the reference quadrature measures and gradients for the immutable reference coordinates of one solve. The cache key contains the coordinate shape, dtype and bytes; a changed reference mesh invalidates it. Hit/miss counters are exported in each nonlinear step and in the benchmark samples, making the optimization auditable. This removes repeated reference Jacobian/inverse work from Newton iterations without changing the material state or tangent formulas. A before/after speedup is not claimed until the same clean-SHA multi-repeat benchmark is available.

The first post-change observation is archived at results/benchmark_0_2_5/nonlinear_load_control_reference_cache_latest.json. The four finite-kinematic family runs converged and reported reference-cache misses only on their first element evaluation: TET4 260/10, TET10 270/10, HEX8 40/2 and HEX20 52/2 hits/misses. This confirms reuse across Newton iterations; it is a dirty-worktree single-repeat observation and does not close 025-G08.

Exploratory result: finite-kinematic TET4 assembly

After vectorizing the finite-kinematic TET4 tangent contraction, the same dirty working-tree run (10 elements, 36 DOF, one repeat, Python 3.13.1 on Windows) changed as follows:

Metric Before After Interpretation
Total elapsed time 43.207 s 1.341 s exploratory characterization
Assembly time 41.445 s 1.096 s measured hotspot reduction
Linear solve time 0.0663 s 0.0654 s unchanged within this sample
Newton iterations 23 23 same convergence regime
Maximum relative residual 4.26e-9 9.88e-9 same configured criterion
Final displacement norm 0.1453241371 0.1453241370 numerical agreement in this sample
Final PEEQ 0.1018066229 0.1018066229 numerical agreement in this sample

Raw files are local exploratory artifacts under tmp/ and are not release evidence. The comparison is a single small case on a dirty worktree; it does not close 025-G08, prove scaling, or justify a general performance claim.

The corresponding one-repeat HEX8 run completed in 1.552 s, with 1.271 s in assembly, 17 Newton iterations and a final relative residual of 5.85e-10. The earlier 27.77 s value was an intermediate vectorization step and is retained only in local raw artifacts; it is not the final after measurement.

Exploratory result: HEX20 validation reuse

The first all-family profile exposed a redundant full geometry validation inside each HEX20 Gauss-point B-matrix build. The public b_matrix() contract still validates geometry for direct callers, while the element integration loop now validates once and reuses the validated local kernel. On the same two-cell, 96-DOF, one-repeat small-strain J2 case, the dirty working-tree comparison was:

Metric Before After Interpretation
Total elapsed time 114.288 s 17.321 s 84.8% lower in this sample
Assembly time 96.639 s 11.573 s 88.0% lower in this sample
Linear solve time 0.0656 s 0.0669 s unchanged at this scale
Newton iterations 22 22 same convergence regime
Maximum relative residual not retained in baseline summary 5.49e-08 configured criterion remained satisfied
Final displacement norm not retained in baseline summary 0.4306610359 recorded for replay
Final PEEQ not retained in baseline summary 0.1716514432 recorded for replay

The before value is the all-family baseline captured before the local kernel change; the after value is stored in results/benchmark_0_2_5/nonlinear_load_control_hex20_optimized.json. This is an optimization characterization only: it uses one local Windows repeat, a dirty worktree and an engineering-medium mesh. It does not close 025-G08, establish a scaling law, or qualify HEX20 for large models. A clean SHA replay, repeated measurements and numerical comparison against the frozen baseline remain required.

Targeted studies

Study Variants Question
Element family TET4/TET10/HEX8/HEX20 cost per Gauss point, element and converged DOF
State storage element/Gauss count and state width deep-copy memory/time scaling
Newton iterations and tangent reuse where total solve time is spent
Geometry material vs geometric tangent incremental cost of large deformation
Contact candidates/active pairs/sliding distance search vs projection vs tangent cost
Arc-length load control vs continuation augmented solve and retry overhead
HEX20 integration/constitutive/assembly/copies explain and measure the known high cost before/after the local optimization

Benchmark hygiene

  • pin process/thread counts where supported;
  • record warm-up and at least enough repetitions to estimate variability;
  • separate CPU time, wall time and external solver time;
  • use identical mesh/history/options for before/after;
  • archive raw samples, not only averages;
  • do not assert tight wall-time limits on shared CI runners.

Optimization acceptance

An optimization is accepted only when the target metric improves outside the measured noise band, all relevant numerical outputs remain within frozen tolerances, focused and full regression pass, and readability/maintenance cost is documented. Otherwise retain the profile as characterization evidence.

Gate 025-G08 closure

G08 can close without a speedup when all mandatory paths have a reproducible cost model, hotspots and remaining limits. It cannot close with unmeasured claims such as “faster”, “scalable” or “memory efficient”.

Exploratory result: bounded nonlinear tangent assembly

The common nonlinear assembly no longer retains all global tangent triplets in unbounded Python lists. Local tangent contributions are converted to CSR chunks and merged by SparseCsrAccumulator; nonlinear_assembly_chunk_size controls the peak chunk (default 256 elements). Each nonlinear step now records sparse_chunk_count, sparse_peak_chunk_entries, sparse_peak_chunk_bytes_estimate and sparse_accumulator_levels alongside the existing timing counters. The byte value is a conservative estimate of the temporary row/column/value staging buffers; it is not a process RSS measurement and excludes the final CSR accumulator. This keeps the default sparse path and the numerical formulas unchanged while making the temporary assembly footprint observable and bounded by the configured chunk.

The same bounded CSR accumulation is now used by the high-order Total-Lagrangian geometric assembly (TET10/HEX20) used by the geometric and buckling paths. Its latest assembly metrics are exposed through assembly_diagnostics() and included in geometric-static solver diagnostics; the finite-kinematic kernels and their constitutive response are unchanged.

The change is an implementation/performance observation only. It does not claim a speedup or close 025-G08 until a clean-SHA repeated benchmark compares the old and new paths with numerical non-regression evidence.

The first post-change run is archived at results/benchmark_0_2_5/nonlinear_load_control_sparse_chunks_latest.json. It converged on all four finite-kinematic families. The maximum observed chunk counts were TET4 5, TET10 10, HEX8 2 and HEX20 2; the corresponding peak local-entry counts were 288, 900, 576 and 3600; the corresponding staging-buffer estimates were 13,824, 43,200, 27,648 and 172,800 bytes. These counters make the bounded temporary assembly policy auditable, while the dirty one-repeat run remains only exploratory evidence.

The bounded geometric path replay is archived at results/benchmark_0_2_5/geometric_static_all_families_latest.json. TET4, TET10, HEX8 and HEX20 all converged on the recorded regular meshes with 18 Full-Newton iterations; the maximum relative residuals were respectively 4.86e-12, 2.81e-11, 1.31e-12 and 2.37e-11. The run records elapsed time and RSS samples, plus shared-driver assembly and linear-solve timers. The high-order rows also expose the CSR staging estimates. This is bounded characterization only and does not close 025-G08 or qualify the high-order geometric path.

Targeted contact and coupled-path replay

The same benchmark harness was also replayed once for the existing TET4 contact and coupled paths. The raw reports are results/benchmark_0_2_5/contact_tet4_latest.json and results/benchmark_0_2_5/coupled_tet4_latest.json. Both runs are PASS on the current dirty worktree:

Path Kinematics DOF Newton Wall s Assembly s Linear solve s Contact assembly s Max relative residual
contact small_strain 24 18 0.374 0.195 0.053 0.028 1.64e-08
coupled total_lagrangian_j2 24 24 0.870 0.639 0.078 0.037 2.93e-08

The contact run records PEEQ=0.1205, plastic dissipation 0.0748 and contact_tangent_nnz=388; the coupled run records PEEQ=0.1045, plastic dissipation 0.0564 and contact_tangent_nnz=396. These measurements are diagnostic evidence for the common driver only. They do not establish general surface contact, external coupled correlation, scaling, or close 025-G05, 025-G06 or 025-G08. Both reports carry the provenance e368c0ce00874c16ff1e8fa9158ea0a8cd2dd745 with worktree_dirty=true.

Controlled G08 closure - final candidate

The preceding exploratory sections are retained as historical records. They must not be read as the current G08 evidence. The authoritative controlled replay is results/vnv_0_2_5/g08_latest/, whose manifest records the exact source SHA, dirty=false, commands, environment, tool versions and artifact digests.

The final load-control campaign covers TET4, TET10, HEX8 and HEX20 with three repetitions per family (12/12 samples PASS). It records total wall time, element setup/kernel/scatter, assembly, sparse conversion, sparse solve, line-search time, Python allocation peak, RSS observations, DOF, nonzeros, Newton iterations and residual/output fields. The same exact-SHA output path contains one-repeat path characterization for geometric-static, arc-length, contact and coupled runs. These path smokes characterize costs only and do not close G02, G04, G05 or G06.

The aggregate report computes mean, minimum, maximum, standard deviation and coefficient of variation for each family. It also quantifies the HEX20 element-kernel share of assembly and total time, which is the dominant measured hotspot in this bounded case. Repeated displacement, PEEQ, residual, plastic-dissipation and Newton-iteration fields are checked for numerical consistency; all controlled samples converge.

This closes 025-G08 as a characterization gate only. It does not claim a performance improvement, a scaling law, multi-million-DOF support, or a general memory-efficiency guarantee. No new optimization, tolerance or functional claim is introduced by this closure.