Skip to content

Add precompilation workload to reduce first-call latency - #417

Draft
jd-lara wants to merge 3 commits into
psy6from
claude/powerflows-precompilation-workload-be5f5n
Draft

Add precompilation workload to reduce first-call latency#417
jd-lara wants to merge 3 commits into
psy6from
claude/powerflows-precompilation-workload-be5f5n

Conversation

@jd-lara

@jd-lara jd-lara commented Jul 24, 2026

Copy link
Copy Markdown
Member

Summary

Adds a precompilation workload that exercises the core AC and DC power-flow solve paths at package build time, moving first-call latency from user sessions into the cached package image.

Key Changes

  • New src/precompile.jl: Defines a minimal in-memory test system (4 buses, mixed component types covering REF/PV/PQ partitions) and a PrecompileTools.@compile_workload block that exercises:

    • AC polar power flow via explicit PowerFlowData construction + in-place solve (twice, to compile cache refresh/reuse paths)
    • AC polar power flow via one-shot public API with DataFrame results
    • DC power flow (ABA direct solve)
    • PTDF-based DC power flow
    • All solver logging is silenced to keep precompile output clean
  • Exposed _precompilation_workload_system(): Plain function so the test suite can exercise the exact workload inputs and validate precompilation coverage

  • Updated src/PowerFlows.jl: Added import PrecompileTools and included precompile.jl last (after all solve paths are defined)

  • Updated Project.toml: Added PrecompileTools dependency (compat 1.2)

  • Updated docs/src/reference/api/internal_solvers.md: Added documentation section for the precompilation workload

Implementation Details

The workload is designed to:

  • Avoid I/O and parsing costs by hand-building the system from PSY constructors (same pattern as test utilities)
  • Prevent KLU factorization handles from leaking into the package image by keeping all objects local to the workload block
  • Be skippable during development via PrecompileTools preference for fast rebuild iterations
  • Cover the PSI-stable surface (explicit PowerFlowData + in-place solve, plus one-shot API)

https://claude.ai/code/session_01EXkkoqjT6CejCV2AbnhTiP

Run the core solve paths at precompile time on a tiny hand-built 4-bus
system (REF/PV/PQ/PQ; Source, ThermalStandard, PowerLoads, ZIP
StandardLoad, meshed Lines) so first-call latency moves into the cached
pkgimage build. The fixture is constructed purely from PSY constructors
in memory: no PowerSystemCaseBuilder, no file parsing, and no
deserialization cost at precompile time.

Workload coverage (Tier 1): PowerFlowData construction + in-place polar
NR solve (twice, to compile the PolarNRCache reuse path), the one-shot
DataFrame results API, DCPowerFlow, and PTDFDCPowerFlow.

Measured on Julia 1.12.6 (Linux x86_64): first AC solve 13.1 s -> 0.07 s,
first DC solve 3.4 s -> 0.013 s, first PTDF solve 1.0 s -> 0.001 s, load
time 3.3 s -> 3.9 s; one-time Pkg.precompile cost 11.6 s -> 135 s and
pkgimage 3.9 MB -> 58.7 MB. Developers can opt out per PrecompileTools
convention via the "precompile_workload" preference.

The new internal docstring is registered in the internals autodocs page
to keep the strict docs build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXkkoqjT6CejCV2AbnhTiP
@jd-lara
jd-lara marked this pull request as draft July 24, 2026 13:00
claude and others added 2 commits July 24, 2026 13:11
Cover the most-used solver set in the precompilation workload: polar NR
(already present), TrustRegionACPowerFlow (new; compiles the dogleg/TR
driver and the TR-typed PowerFlowData chain), and the DC family (ABA and
PTDF, already present).

Per review direction, precompilation does not need converged power
flows: the iterative workload solves now run with maxIterations = 2,
which exercises the same iteration machinery (step, refinement, Jacobian
refresh, and the non-convergence writeback) without paying for
convergence. Two carve-outs: the one-shot NR call stays uncapped because
write_results only executes on a converged solve, and capped runs end
NaN-overwritten (OVERWRITE_NON_CONVERGED), so clear_injection_data!
resets state before the cache-reuse solves to keep NaNs out of the KLU
factorizations at precompile time.

Measured (Julia 1.12.6, Linux x86_64): TR first solve 3.9 s -> 0.06 s;
NR/DC/PTDF first solves unchanged (0.07 s / 0.015 s / 0.001 s); load
time unchanged (~4 s); one-time Pkg.precompile 135 s -> 165 s; pkgimage
58.7 -> 61.1 MB. The Q-limit retry path needs no workload entry - it is
already inferred transitively (first solve with
check_reactive_power_limits = true measures 0.001 s).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXkkoqjT6CejCV2AbnhTiP
Precompile the common power-flow methods so first-call latency moves into
the cached pkgimage:
- Polar NR and Trust Region: one-shot solve plus in-place solve_power_flow!
  (2 capped iters, reset and resolve) to warm the cache-reuse hot path.
- FastDecoupled variants: FDDecoupled/XB default and FDFixedJacobian.
- Rectangular current-injection NR (2 capped iters).
- DC ABA direct solve: one-shot plus in-place build-and-reuse.

Exclude PTDF: uncommon, and its factorization adds build cost without
lowering first-call latency. Drop the internal-fixture autodocs entry.

Measured first-call: TR 2.13s to 0.017s, FD 2.12s to 0.064s, FD-fixed
5.25s to 0.017s, rectangular 3.47s to 0.072s; about +3s precompile.
@luke-kiernan luke-kiernan self-assigned this Jul 27, 2026
@jd-lara
jd-lara requested a review from luke-kiernan August 10, 2026 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants