Skip to content

Contain type instability in G-1 outage-driven typeof dispatches - #166

Merged
luke-kiernan merged 7 commits into
ac/g1-portfrom
ac/g1-port-typestability
Jul 7, 2026
Merged

Contain type instability in G-1 outage-driven typeof dispatches#166
luke-kiernan merged 7 commits into
ac/g1-portfrom
ac/g1-port-typestability

Conversation

@luke-kiernan

Copy link
Copy Markdown
Collaborator

Summary

Stacked on #148. Reviewing that PR surfaced type-instability from typeof(d)-driven variable/multiplier lookups scattered through the outage/generator code — PSY's get_component_supplemental_attribute_pairs/get_contributing_devices return heterogeneous, abstractly-typed collections, so these dispatches are unavoidable without a much larger refactor (discussed inline, not pursued here as low-leverage for the cost).

  • Wrapped each typeof(d)-driven lookup's downstream per-t loop in a function barrier, so the dynamic dispatch is isolated to once per device/outage instead of leaking into the inner loop (verified with @code_warntype: the loop body compiles fully specialized post-barrier).
  • Hoisted one lookup (_outaged_generator_terms) that was redundantly re-executed per (entries, t) pair despite not depending on either, down to once per outage.
  • Routed two constraint sites through IOM.add_range_bound_constraint!/add_range_equality_constraint! where they cleanly fit the existing (name, t) signature, matching established convention (storage_models.jl, thermal_generation.jl). Deliberately did not extend IOM's API (e.g. a Tuple-index overload) to force-fit the 3-dim outage containers into the same helpers — that would've bent a shared abstraction around a single caller for marginal benefit; those sites stay as direct JuMP.@constraint calls.

Test plan

  • julia --project=test test/runtests.jl test_static_injection_security_constrained_models — 1219/1219 pass
  • julia --project=test test/runtests.jl test_services_constructor test_device_hvdc test_device_branch_constructors — 162/162 pass
  • Formatter run (scripts/formatter/formatter_code.jl)

🤖 Generated with Claude Code

luke-kiernan and others added 3 commits July 1, 2026 08:26
PSY's heterogeneous outage/generator supplemental-attribute maps force
typeof(d)-based get_variable/get_variable_multiplier lookups in the
G-1 security-constrained reserve code. Wrap each lookup's downstream
per-timestep loop in a function barrier so the unstable dispatch is
isolated to once per device/outage instead of leaking into the inner
loop; hoist one redundant per-(entries,t) lookup to once per outage.

Also route two constraint sites through IOM.add_range_bound_constraint!/
add_range_equality_constraint! where they fit the existing 2-arg
(name, t) signature, matching established POM convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two add_constraints! methods placed contributing_devices before
service, reversed from the (service, contributing_devices) order
every other service constraint uses (reserves.jl and the other
outage add_constraints! methods). No dispatch impact — just matches
the established convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…atch

The system-wide, PTDF-nodal, and AreaBalance-area post-contingency
balance/deployment expressions differed only in which extra index
component sits between outage_id and t; the surrounding accumulation
logic was duplicated three times per (healthy-deployment,
outaged-generator) pair. Mirrors the _system_expression_type/
_balance_expression_targets pattern already used in add_to_expression.jl:
_post_contingency_target_axes/_post_contingency_target_index dispatch on
the expression type T (not on network_model's runtime type, since
PostContingencyActivePowerBalance is built unconditionally under every
network model) to resolve the axis/index, and one generic method per
pair replaces the six network-model-specific copies.

Also threads attribute_device_map through to the AreaBalance
pre-contingency call site instead of recomputing it via a second
PSY.get_component_supplemental_attribute_pairs call, which the unified
signature now requires consistently across all three network models.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reduces type-instability overhead in the G-1 security-constrained reserves / outage-driven code paths by introducing function barriers around typeof(device)-based lookups, hoisting a redundant per-(entries, t) generator-term lookup to once-per-outage, and aligning some constraint construction with existing IOM.add_range_*_constraint! helper conventions.

Changes:

  • Added function barriers around runtime-type (typeof(d)) variable/multiplier lookups so inner per-time-step loops can compile specialized.
  • Hoisted _outaged_generator_terms to be computed once per outage (instead of repeatedly inside nested loops) and reused in flow expression construction.
  • Replaced a few direct JuMP.@constraint assignments with IOM.add_range_bound_constraint! / IOM.add_range_equality_constraint! where the (name, t) shape matches existing helper patterns.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/services_models/static_injection_security_constrained_models.jl Introduces type aliases for resolved monitored arcs, adds multiple function barriers around dynamic-typed lookups, hoists per-outage generator term resolution, and routes one constraint site through an IOM range helper.
src/services_models/reserves.jl Routes several bounds constraints through IOM.add_range_bound_constraint! and adds a function barrier to isolate dynamic get_variable(..., typeof(d)) dispatch from the inner time-step loop.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Performance Results

Version Precompile Time
Main 3.120292201
This Branch 3.008624296
Version Build Time
Main-Build Time Precompile 87.973089299
Main-Build Time Postcompile 1.42635348
This Branch-Build Time Precompile 85.482709245
This Branch-Build Time Postcompile 1.396836768
Version Solve Time
Main-Solve Time Precompile 3375.424269664
Main-Solve Time Postcompile 3336.714472781
This Branch-Solve Time Precompile 2142.661427447
This Branch-Solve Time Postcompile 2102.147375476

@acostarelli acostarelli left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main takeaways:

  • Some redundancies in helpers and comments
  • If this type issue is everywhere in services, maybe we should think of a more robust solution

Comment thread src/services_models/reserves.jl Outdated
Comment thread src/services_models/reserves.jl Outdated
Comment thread src/services_models/reserves.jl Outdated
service_model::ServiceModel,
net_reduction_data::PNM.NetworkReductionData,
)
)::ResolvedMonitoredArcs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this here just for clarity, or does it help performance? I could be in favor of either reason, but if only the former, it does stick out to me as unusual.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for clarity and readability. No performance benefit.

sys::PSY.System,
::Type{T},
::Type{U},
attribute_device_map::Vector{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we also want to create an alias for this like you did for MonitoredArcs?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't but we could, I'm not against it

Comment thread src/services_models/static_injection_security_constrained_models.jl
Comment thread src/services_models/static_injection_security_constrained_models.jl Outdated
Comment thread src/services_models/static_injection_security_constrained_models.jl Outdated
Comment thread src/services_models/static_injection_security_constrained_models.jl Outdated
Comment thread src/services_models/static_injection_security_constrained_models.jl
@luke-kiernan

Copy link
Copy Markdown
Collaborator Author

If this type issue is everywhere in services, maybe we should think of a more robust solution

Yeah the more robust solution would be to group all of our iterables by generator type.

I just realized that if I push things here, I'll have to rebase the stacked PR...I guess I'll go address the comments on that one first

luke-kiernan and others added 4 commits July 1, 2026 14:56
@luke-kiernan
luke-kiernan force-pushed the ac/g1-port-typestability branch from d851e99 to 32cff2f Compare July 7, 2026 19:35
@luke-kiernan
luke-kiernan merged commit 8723ad0 into ac/g1-port Jul 7, 2026
2 of 6 checks passed
@luke-kiernan
luke-kiernan deleted the ac/g1-port-typestability branch July 7, 2026 19:37
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