From d3204f2ff805a4b00e02be56da4aa3773cb935a7 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 8 May 2026 14:35:19 +1200 Subject: [PATCH 1/8] Fix a scoping issue in _get_irreducible_buses_due_to_dlrs --- src/network_models/instantiate_network_model.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network_models/instantiate_network_model.jl b/src/network_models/instantiate_network_model.jl index ca0342e..4a3b784 100644 --- a/src/network_models/instantiate_network_model.jl +++ b/src/network_models/instantiate_network_model.jl @@ -75,7 +75,7 @@ function _get_irreducible_buses_due_to_dlrs( irreducible_buses = Set{Int64}() for branch_type in network_model.modeled_branch_types branch_type <: PSY.ACTransmission || continue - device_model = branch_models[Symbol(branch_type)] + device_model = branch_models[nameof(branch_type)] if !haskey( get_time_series_names(device_model), DynamicBranchRatingTimeSeriesParameter, From b786b4bbe2adcecc95cd91501021c1131f78b54a Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 8 May 2026 17:18:53 +1200 Subject: [PATCH 2/8] [Do not merge] a script to benchmark impact of lazy PTDF --- src/ac_transmission_models/AC_branches.jl | 29 +++++----- test/performance/simple_cats.jl | 70 +++++++++++++++++++++++ 2 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 test/performance/simple_cats.jl diff --git a/src/ac_transmission_models/AC_branches.jl b/src/ac_transmission_models/AC_branches.jl index ea24559..c76c988 100644 --- a/src/ac_transmission_models/AC_branches.jl +++ b/src/ac_transmission_models/AC_branches.jl @@ -373,24 +373,21 @@ function _add_flow_rate_constraint!( branch_maps_by_type::Dict, name::String, ) where {T <: PSY.ACTransmission} - reduction_entry = branch_maps_by_type[arc] time_steps = get_time_steps(container) + limits = get_min_max_limits(branch_maps_by_type[arc], FlowRateConstraint, StaticBranch) + model = get_jump_model(container) if use_slacks - slack_ub = get_variable(container, FlowActivePowerSlackUpperBound, T)[name, :] - slack_lb = get_variable(container, FlowActivePowerSlackLowerBound, T)[name, :] - end - limits = get_min_max_limits(reduction_entry, FlowRateConstraint, StaticBranch) - for t in time_steps - con_ub[name, t] = - JuMP.@constraint( - get_jump_model(container), - var[name, t] - (use_slacks ? slack_ub[t] : 0.0) <= limits.max - ) - con_lb[name, t] = - JuMP.@constraint( - get_jump_model(container), - var[name, t] + (use_slacks ? slack_lb[t] : 0.0) >= limits.min - ) + slack_ub = get_variable(container, FlowActivePowerSlackUpperBound, T) + slack_lb = get_variable(container, FlowActivePowerSlackLowerBound, T) + for t in time_steps + con_ub[name, t] = JuMP.@constraint(model, var[name, t] - slack_ub[name, t] <= limits.max) + con_lb[name, t] = JuMP.@constraint(model, var[name, t] + slack_lb[name, t] >= limits.min) + end + else + for t in time_steps + con_ub[name, t] = JuMP.@constraint(model, var[name, t] <= limits.max) + con_lb[name, t] = JuMP.@constraint(model, var[name, t] >= limits.min) + end end return end diff --git a/test/performance/simple_cats.jl b/test/performance/simple_cats.jl new file mode 100644 index 0000000..ab026ec --- /dev/null +++ b/test/performance/simple_cats.jl @@ -0,0 +1,70 @@ +using Revise +import Dates +import Gurobi +import PowerNetworkMatrices as PNM +import PowerOperationsModels as POM +import PowerSystems as PSY +sys = PSY.System(joinpath(@__DIR__, "CATS_Sienna.json")) +PSY.transform_single_time_series!(sys, Dates.Hour(24), Dates.Hour(24)) +ptdf = POM.VirtualPTDF( + sys; + tol = 0.01, + network_reductions = [PNM.RadialReduction(), PNM.DegreeTwoReduction()], +) +network_model = POM.NetworkModel( + POM.PTDFPowerModel; + # use_slacks = true, + # duals = [POM.CopperPlateBalanceConstraint], + PTDF_matrix = ptdf, + reduce_radial_branches = true, + reduce_degree_two_branches = true, + # power_flow_evaluation = POM.DCPPowerModel(), # ??? +) +template = POM.OperationsProblemTemplate(network_model); +POM.set_device_model!(template, PSY.ThermalStandard, POM.ThermalBasicUnitCommitment) +POM.set_device_model!(template, PSY.RenewableDispatch, POM.RenewableFullDispatch) +POM.set_device_model!(template, PSY.HydroDispatch, POM.HydroDispatchRunOfRiver) +POM.set_device_model!(template, PSY.PowerLoad, POM.StaticPowerLoad) +# POM.set_device_model!(template, PSY.Line, POM.StaticBranch) +POM.set_device_model!( + template, + POM.DeviceModel( + PSY.Line, + POM.StaticBranch; + use_slacks = true, + # Options are: 66, 115, 230 + attributes = Dict( + "filter_function" => + x -> (x |> PSY.get_arc |> PSY.get_from |> PSY.get_base_voltage) >= 115.0, + ) + ) +) +POM.set_device_model!(template, PSY.Transformer2W, POM.StaticBranch) +model = POM.DecisionModel( + template, + sys; + name = "CATS_UC2", + optimizer = Gurobi.Optimizer, + direct_mode_optimizer = true, + optimizer_solve_log_print = true, +); +@time POM.build!(model; output_dir = mktempdir(; cleanup = true)) +import JuMP +# With: 330 seconds. +# Without: 776.688322 seconds +# for (k, v) in model.internal.container.constraints +# if k isa POM.ConstraintKey{POM.FlowRateConstraint,PSY.Line} +# JuMP.set_attribute.(v, Gurobi.ConstraintAttribute("Lazy"), 1) +# end +# end +@time solve = POM.solve!(model) + +# import PProf +# POM.build!(model; output_dir = mktempdir(; cleanup = true)) +# PProf.@pprof POM.build!(model; output_dir = mktempdir(; cleanup = true)) + + +# using SnoopCompileCore +# invs = @snoop_invalidations using PowerSystems, PowerOperationsModels +# using SnoopCompile, AbstractTrees +# trees = invalidation_trees(invs) From 5797b60525a3df28b6c774a964a3f71f16083f8a Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Thu, 14 May 2026 15:08:22 +1200 Subject: [PATCH 3/8] Update --- Project.toml | 14 ++-- src/PowerOperationsModels.jl | 2 + src/ac_transmission_models/AC_branches.jl | 20 ++++-- test/Project.toml | 11 +-- test/performance/simple_cats.jl | 81 ++++++++++++++++++++--- 5 files changed, 107 insertions(+), 21 deletions(-) diff --git a/Project.toml b/Project.toml index e749a78..920d69c 100644 --- a/Project.toml +++ b/Project.toml @@ -13,8 +13,10 @@ JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" +MathOptLazy = "5d5fe9b5-b0a4-4485-81f6-7b1b939155e1" PowerNetworkMatrices = "bed98974-b02a-5e2f-9fe0-a103f5c450dd" PowerSystems = "bcd98974-b02a-5e2f-9ee0-a103f5c450dd" +PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -23,14 +25,15 @@ TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" [weakdeps] PowerFlows = "94fada2c-0ca5-4b90-a1fb-4bc5b59ccfc7" +[sources] +InfrastructureOptimizationModels = {rev = "lk/pom-test-fixes", url = "https://github.com/NREL-Sienna/InfrastructureOptimizationModels.jl"} +InfrastructureSystems = {rev = "IS4", url = "https://github.com/NREL-Sienna/InfrastructureSystems.jl"} +MathOptLazy = {rev = "main", url = "https://github.com/jump-dev/MathOptLazy.jl"} +PowerSystems = {rev = "psy6", url = "https://github.com/NREL-Sienna/PowerSystems.jl"} + [extensions] PowerFlowsExt = "PowerFlows" -[sources] -InfrastructureSystems = {url = "https://github.com/NREL-Sienna/InfrastructureSystems.jl", rev = "IS4"} -PowerSystems = {url = "https://github.com/NREL-Sienna/PowerSystems.jl", rev = "psy6"} -InfrastructureOptimizationModels = {url = "https://github.com/NREL-Sienna/InfrastructureOptimizationModels.jl", rev = "lk/pom-test-fixes"} - [compat] Dates = "1" DocStringExtensions = "~0.8, ~0.9" @@ -40,6 +43,7 @@ InteractiveUtils = "1.11.0" JuMP = "^1.28" PowerNetworkMatrices = "^0.19" PowerSystems = "5.3" +PrettyTables = "3" ProgressMeter = "1.11.0" TimerOutputs = "~0.5" julia = "^1.11" diff --git a/src/PowerOperationsModels.jl b/src/PowerOperationsModels.jl index 27411c1..3abcc51 100644 --- a/src/PowerOperationsModels.jl +++ b/src/PowerOperationsModels.jl @@ -9,8 +9,10 @@ import InfrastructureSystems: @assert_op, TableFormat import JuMP import JuMP.Containers: DenseAxisArray, SparseAxisArray import Logging +import MathOptLazy import PowerNetworkMatrices import ProgressMeter +import PrettyTables import PowerSystems import PowerSystems: get_component import Serialization diff --git a/src/ac_transmission_models/AC_branches.jl b/src/ac_transmission_models/AC_branches.jl index c76c988..b50cd07 100644 --- a/src/ac_transmission_models/AC_branches.jl +++ b/src/ac_transmission_models/AC_branches.jl @@ -362,6 +362,17 @@ function get_min_max_limits( return (min = -π / 2, max = π / 2) end +function _get_tag(model::JuMP.GenericModel) + if JuMP.MOI.supports_constraint( + JuMP.unsafe_backend(model), + JuMP.MOI.ScalarAffineFunction{Float64}, + MathOptLazy.LazyScalarSet{JuMP.MOI.GreaterThan{Float64}}, + ) + return (MathOptLazy.Lazy(),) + end + return () +end + function _add_flow_rate_constraint!( container::OptimizationContainer, ::Type{T}, @@ -376,17 +387,18 @@ function _add_flow_rate_constraint!( time_steps = get_time_steps(container) limits = get_min_max_limits(branch_maps_by_type[arc], FlowRateConstraint, StaticBranch) model = get_jump_model(container) + tag = _get_tag(model) if use_slacks slack_ub = get_variable(container, FlowActivePowerSlackUpperBound, T) slack_lb = get_variable(container, FlowActivePowerSlackLowerBound, T) for t in time_steps - con_ub[name, t] = JuMP.@constraint(model, var[name, t] - slack_ub[name, t] <= limits.max) - con_lb[name, t] = JuMP.@constraint(model, var[name, t] + slack_lb[name, t] >= limits.min) + con_ub[name, t] = JuMP.@constraint(model, var[name, t] - slack_ub[name, t] <= limits.max, tag...) + con_lb[name, t] = JuMP.@constraint(model, var[name, t] + slack_lb[name, t] >= limits.min, tag...) end else for t in time_steps - con_ub[name, t] = JuMP.@constraint(model, var[name, t] <= limits.max) - con_lb[name, t] = JuMP.@constraint(model, var[name, t] >= limits.min) + con_ub[name, t] = JuMP.@constraint(model, var[name, t] <= limits.max, tag...) + con_lb[name, t] = JuMP.@constraint(model, var[name, t] >= limits.min, tag...) end end return diff --git a/test/Project.toml b/test/Project.toml index 9445668..465c27f 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -10,11 +10,13 @@ InfrastructureOptimizationModels = "bed98974-b02a-5e2f-9ee0-a103f5c45069" InfrastructureSystems = "2cd47ed4-ca9b-11e9-27f2-ab636a7671f1" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" +JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +MathOptLazy = "5d5fe9b5-b0a4-4485-81f6-7b1b939155e1" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" PowerFlows = "94fada2c-fd9a-4e89-8d82-81405f5cb4f6" PowerNetworkMatrices = "bed98974-b02a-5e2f-9fe0-a103f5c450dd" @@ -32,10 +34,11 @@ TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [sources] -InfrastructureSystems = {url = "https://github.com/NREL-Sienna/InfrastructureSystems.jl", rev = "IS4"} -PowerSystems = {url = "https://github.com/NREL-Sienna/PowerSystems.jl", rev = "psy6"} -InfrastructureOptimizationModels = {url = "https://github.com/NREL-Sienna/InfrastructureOptimizationModels.jl", rev = "lk/pom-test-fixes"} -PowerSystemCaseBuilder = {url = "https://github.com/NREL-Sienna/PowerSystemCaseBuilder.jl", rev = "psy6"} +InfrastructureOptimizationModels = {rev = "lk/pom-test-fixes", url = "https://github.com/NREL-Sienna/InfrastructureOptimizationModels.jl"} +InfrastructureSystems = {rev = "IS4", url = "https://github.com/NREL-Sienna/InfrastructureSystems.jl"} +MathOptLazy = {rev = "main", url = "https://github.com/jump-dev/MathOptLazy.jl"} +PowerSystemCaseBuilder = {rev = "psy6", url = "https://github.com/NREL-Sienna/PowerSystemCaseBuilder.jl"} +PowerSystems = {rev = "psy6", url = "https://github.com/NREL-Sienna/PowerSystems.jl"} [compat] HiGHS = "1" diff --git a/test/performance/simple_cats.jl b/test/performance/simple_cats.jl index ab026ec..6ec15ab 100644 --- a/test/performance/simple_cats.jl +++ b/test/performance/simple_cats.jl @@ -1,9 +1,12 @@ using Revise import Dates import Gurobi +import MathOptLazy import PowerNetworkMatrices as PNM import PowerOperationsModels as POM import PowerSystems as PSY +using PowerSystems + sys = PSY.System(joinpath(@__DIR__, "CATS_Sienna.json")) PSY.transform_single_time_series!(sys, Dates.Hour(24), Dates.Hour(24)) ptdf = POM.VirtualPTDF( @@ -32,24 +35,39 @@ POM.set_device_model!( PSY.Line, POM.StaticBranch; use_slacks = true, - # Options are: 66, 115, 230 - attributes = Dict( - "filter_function" => - x -> (x |> PSY.get_arc |> PSY.get_from |> PSY.get_base_voltage) >= 115.0, - ) ) ) POM.set_device_model!(template, PSY.Transformer2W, POM.StaticBranch) +import JuMP +import HiGHS model = POM.DecisionModel( template, sys; name = "CATS_UC2", - optimizer = Gurobi.Optimizer, + # optimizer = Gurobi.MOI.OptimizerWithAttributes(() -> MathOptLazy.Optimizer(Gurobi.Optimizer)), + # optimizer = Gurobi.Optimizer, + optimizer = JuMP.MOI.OptimizerWithAttributes( + # () -> MathOptLazy.Optimizer(HiGHS.Optimizer), + HiGHS.Optimizer, + # "mip_rel_gap" => 1e-3, + ), direct_mode_optimizer = true, optimizer_solve_log_print = true, ); @time POM.build!(model; output_dir = mktempdir(; cleanup = true)) -import JuMP + +jmp = POM.IOM.get_optimization_container(model).JuMPmodel +@time JuMP.optimize!(jmp) + +# Gurobi +# Default: 540 seconds +# Gurobi.ConstraintAttribute("Lazy")=1: 80 seconds +# MathOptLazy: 120 seconds +# HiGHS +# mip_rel_gap = 1e-3 +# Default: 192 seconds +# MathOptLazy: 160 seconds + # With: 330 seconds. # Without: 776.688322 seconds # for (k, v) in model.internal.container.constraints @@ -57,7 +75,54 @@ import JuMP # JuMP.set_attribute.(v, Gurobi.ConstraintAttribute("Lazy"), 1) # end # end -@time solve = POM.solve!(model) +# @time solve = POM.solve!(model) + +# function solve_with_loop(model) +# jmp = POM.IOM.get_optimization_container(model).JuMPmodel +# constraints_lb, constraints_ub = Dict{Any,Any}(), Dict{Any,Any}() +# for (k, v) in model.internal.container.constraints +# if k == POM.ConstraintKey{POM.FlowRateConstraint,PSY.Line}("lb") +# for vi in v +# constraints_lb[vi] = JuMP.constraint_object(vi) +# end +# elseif k == POM.ConstraintKey{POM.FlowRateConstraint,PSY.Line}("ub") +# for vi in v +# constraints_ub[vi] = JuMP.constraint_object(vi) +# end +# end +# end +# JuMP.delete(jmp, [k for k in keys(constraints_lb)]) +# JuMP.delete(jmp, [k for k in keys(constraints_ub)]) +# JuMP.set_silent(jmp) +# total_solve_time = 0.0 +# while true +# start_time = time() +# JuMP.optimize!(jmp) +# total_solve_time += time() - start_time +# n_constraints_added = 0 +# for (k, c) in constraints_lb +# if JuMP.value(c.func) < c.set.lower +# JuMP.@constraint(jmp, c.func in c.set) +# n_constraints_added += 1 +# delete!(constraints_lb, k) +# end +# end +# for (k, c) in constraints_ub +# if JuMP.value(c.func) > c.set.upper +# JuMP.@constraint(jmp, c.func in c.set) +# n_constraints_added += 1 +# delete!(constraints_ub, k) +# end +# end +# if n_constraints_added == 0 +# break +# else +# @show n_constraints_added +# end +# end +# @show total_solve_time +# return jmp +# end # import PProf # POM.build!(model; output_dir = mktempdir(; cleanup = true)) From 862f50469b6d5b2d2919ffad4b201a838f9e5d5b Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Thu, 14 May 2026 16:41:10 +1200 Subject: [PATCH 4/8] Update --- test/performance/simple_cats.jl | 129 +++++++++++++++++--------------- 1 file changed, 70 insertions(+), 59 deletions(-) diff --git a/test/performance/simple_cats.jl b/test/performance/simple_cats.jl index 6ec15ab..064f70b 100644 --- a/test/performance/simple_cats.jl +++ b/test/performance/simple_cats.jl @@ -5,71 +5,82 @@ import MathOptLazy import PowerNetworkMatrices as PNM import PowerOperationsModels as POM import PowerSystems as PSY +import HiGHS +using JuMP using PowerSystems -sys = PSY.System(joinpath(@__DIR__, "CATS_Sienna.json")) -PSY.transform_single_time_series!(sys, Dates.Hour(24), Dates.Hour(24)) -ptdf = POM.VirtualPTDF( - sys; - tol = 0.01, - network_reductions = [PNM.RadialReduction(), PNM.DegreeTwoReduction()], -) -network_model = POM.NetworkModel( - POM.PTDFPowerModel; - # use_slacks = true, - # duals = [POM.CopperPlateBalanceConstraint], - PTDF_matrix = ptdf, - reduce_radial_branches = true, - reduce_degree_two_branches = true, - # power_flow_evaluation = POM.DCPPowerModel(), # ??? -) -template = POM.OperationsProblemTemplate(network_model); -POM.set_device_model!(template, PSY.ThermalStandard, POM.ThermalBasicUnitCommitment) -POM.set_device_model!(template, PSY.RenewableDispatch, POM.RenewableFullDispatch) -POM.set_device_model!(template, PSY.HydroDispatch, POM.HydroDispatchRunOfRiver) -POM.set_device_model!(template, PSY.PowerLoad, POM.StaticPowerLoad) -# POM.set_device_model!(template, PSY.Line, POM.StaticBranch) -POM.set_device_model!( - template, - POM.DeviceModel( - PSY.Line, - POM.StaticBranch; - use_slacks = true, +function run_problem(optimizer) + sys = PSY.System(joinpath(@__DIR__, "CATS_Sienna.json")) + PSY.transform_single_time_series!(sys, Dates.Hour(24), Dates.Hour(24)) + ptdf = POM.VirtualPTDF( + sys; + tol = 0.01, + network_reductions = [PNM.RadialReduction(), PNM.DegreeTwoReduction()], ) -) -POM.set_device_model!(template, PSY.Transformer2W, POM.StaticBranch) -import JuMP -import HiGHS -model = POM.DecisionModel( - template, - sys; - name = "CATS_UC2", - # optimizer = Gurobi.MOI.OptimizerWithAttributes(() -> MathOptLazy.Optimizer(Gurobi.Optimizer)), - # optimizer = Gurobi.Optimizer, - optimizer = JuMP.MOI.OptimizerWithAttributes( - # () -> MathOptLazy.Optimizer(HiGHS.Optimizer), - HiGHS.Optimizer, - # "mip_rel_gap" => 1e-3, - ), - direct_mode_optimizer = true, - optimizer_solve_log_print = true, -); -@time POM.build!(model; output_dir = mktempdir(; cleanup = true)) + network_model = POM.NetworkModel( + POM.PTDFPowerModel; + PTDF_matrix = ptdf, + reduce_radial_branches = true, + reduce_degree_two_branches = true, + ) + template = POM.OperationsProblemTemplate(network_model); + POM.set_device_model!(template, PSY.ThermalStandard, POM.ThermalBasicUnitCommitment) + POM.set_device_model!(template, PSY.RenewableDispatch, POM.RenewableFullDispatch) + POM.set_device_model!(template, PSY.HydroDispatch, POM.HydroDispatchRunOfRiver) + POM.set_device_model!(template, PSY.PowerLoad, POM.StaticPowerLoad) + POM.set_device_model!( + template, + POM.DeviceModel(PSY.Line, POM.StaticBranch; use_slacks = true) + ) + POM.set_device_model!(template, PSY.Transformer2W, POM.StaticBranch) + model = POM.DecisionModel( + template, + sys; + name = "CATS_UC2", + optimizer, + direct_mode_optimizer = true, + optimizer_solve_log_print = true, + ); + POM.build!(model; output_dir = mktempdir(; cleanup = true)) + jmp = POM.IOM.get_optimization_container(model).JuMPmodel + @time JuMP.optimize!(jmp) + return +end + +# | Solver | Lazy? | tol=1e-2 | tol=1e-3 | +# | :----- | :---- | -------: | -------: | +# | Gurobi | false | 55 | 57 | +# | Gurobi | true | 12 | 17 | +# | HiGHS | false | 175 | 201 | +# | HiGHS | true | 173 | 670 | +# | HiGHS | true* | 94 | 589 | -jmp = POM.IOM.get_optimization_container(model).JuMPmodel -@time JuMP.optimize!(jmp) +# run_problem( +# optimizer_with_attributes( +# Gurobi.Optimizer, +# MOI.RelativeGapTolerance() => 1e-3, +# ), +# ) +# run_problem( +# optimizer_with_attributes( +# () -> MathOptLazy.Optimizer(Gurobi.Optimizer), +# MOI.RelativeGapTolerance() => 1e-3, +# ), +# ) +# run_problem( +# optimizer_with_attributes( +# HiGHS.Optimizer, +# MOI.RelativeGapTolerance() => 1e-3, +# ), +# ) +# run_problem( +# optimizer_with_attributes( +# () -> MathOptLazy.Optimizer(HiGHS.Optimizer), +# MOI.RelativeGapTolerance() => 1e-3, +# ), +# ) -# Gurobi -# Default: 540 seconds -# Gurobi.ConstraintAttribute("Lazy")=1: 80 seconds -# MathOptLazy: 120 seconds -# HiGHS -# mip_rel_gap = 1e-3 -# Default: 192 seconds -# MathOptLazy: 160 seconds -# With: 330 seconds. -# Without: 776.688322 seconds # for (k, v) in model.internal.container.constraints # if k isa POM.ConstraintKey{POM.FlowRateConstraint,PSY.Line} # JuMP.set_attribute.(v, Gurobi.ConstraintAttribute("Lazy"), 1) From 43a81b9f8db67b0cb90ac9a87d1cdcf3ec300761 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 15 May 2026 14:52:23 +1200 Subject: [PATCH 5/8] Update --- test/Project.toml | 1 + test/performance/simple_cats.jl | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/test/Project.toml b/test/Project.toml index 465c27f..cff9cfe 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -5,6 +5,7 @@ DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataFramesMeta = "1313f7d8-7da2-5740-9ea0-a2ca25f37964" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b" HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" InfrastructureOptimizationModels = "bed98974-b02a-5e2f-9ee0-a103f5c45069" InfrastructureSystems = "2cd47ed4-ca9b-11e9-27f2-ab636a7671f1" diff --git a/test/performance/simple_cats.jl b/test/performance/simple_cats.jl index 064f70b..4347931 100644 --- a/test/performance/simple_cats.jl +++ b/test/performance/simple_cats.jl @@ -42,8 +42,7 @@ function run_problem(optimizer) optimizer_solve_log_print = true, ); POM.build!(model; output_dir = mktempdir(; cleanup = true)) - jmp = POM.IOM.get_optimization_container(model).JuMPmodel - @time JuMP.optimize!(jmp) + @time POM.solve!(model) return end @@ -53,7 +52,7 @@ end # | Gurobi | true | 12 | 17 | # | HiGHS | false | 175 | 201 | # | HiGHS | true | 173 | 670 | -# | HiGHS | true* | 94 | 589 | +# | HiGHS | true* | 94 | 589 182, 201, 650 | # run_problem( # optimizer_with_attributes( @@ -61,12 +60,12 @@ end # MOI.RelativeGapTolerance() => 1e-3, # ), # ) -# run_problem( -# optimizer_with_attributes( -# () -> MathOptLazy.Optimizer(Gurobi.Optimizer), -# MOI.RelativeGapTolerance() => 1e-3, -# ), -# ) +run_problem( + optimizer_with_attributes( + () -> MathOptLazy.Optimizer(Gurobi.Optimizer), + MOI.RelativeGapTolerance() => 1e-2, + ), +) # run_problem( # optimizer_with_attributes( # HiGHS.Optimizer, @@ -77,6 +76,7 @@ end # optimizer_with_attributes( # () -> MathOptLazy.Optimizer(HiGHS.Optimizer), # MOI.RelativeGapTolerance() => 1e-3, +# "random_seed" => 123, # ), # ) From 2bb2a4965e058c99b1dd7dbfa3e549e0d9d5edf3 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 15 May 2026 14:48:25 +1200 Subject: [PATCH 6/8] Remove model from being interpolated into warning strings --- src/PowerModels/core/objective.jl | 2 +- src/operation/decision_model.jl | 4 ++-- src/operation/emulation_model.jl | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PowerModels/core/objective.jl b/src/PowerModels/core/objective.jl index 3a09b20..6304f68 100644 --- a/src/PowerModels/core/objective.jl +++ b/src/PowerModels/core/objective.jl @@ -157,7 +157,7 @@ function expression_pg_cost(pm::AbstractPowerModel; report::Bool = true) ) else error( - "Only cost models of types 1 and 2 are supported at this time, given cost model type of $(model) on generator $(i)", + "Only cost models of types 1 and 2 are supported at this time, given cost model type of $(gen["model"]) on generator $(i)", ) end end diff --git a/src/operation/decision_model.jl b/src/operation/decision_model.jl index b6ce1b9..5dce302 100644 --- a/src/operation/decision_model.jl +++ b/src/operation/decision_model.jl @@ -67,7 +67,7 @@ function build!( IOM.register_recorders!(model, file_mode) logger = IS.configure_logging(get_internal(model), IOM.PROBLEM_LOG_FILENAME, file_mode) if store_system_in_results - @warn "store_system_in_results for $(model) is set to true. This will do nothing unless a Simulation is being built." + @warn "store_system_in_results is set to true. This will do nothing unless a Simulation is being built." end try Logging.with_logger(logger) do @@ -151,7 +151,7 @@ function solve!( kwargs..., ) if store_system_in_results - @warn "store_system_in_results for $(model) is set to true. This will do nothing unless a Simulation is being built." + @warn "store_system_in_results is set to true. This will do nothing unless a Simulation is being built." end build_if_not_already_built!( model; diff --git a/src/operation/emulation_model.jl b/src/operation/emulation_model.jl index 9241729..6cf711d 100644 --- a/src/operation/emulation_model.jl +++ b/src/operation/emulation_model.jl @@ -69,7 +69,7 @@ function build!( file_mode, ) if store_system_in_results - @warn "store_system_in_results for $(model) is set to true. This will do nothing unless a Simulation is being built." + @warn "store_system_in_results is set to true. This will do nothing unless a Simulation is being built." end try Logging.with_logger(logger) do @@ -197,7 +197,7 @@ function run!( kwargs..., ) if store_system_in_results - @warn "store_system_in_results for $(model) is set to true. This will do nothing unless a Simulation is being built." + @warn "store_system_in_results is set to true. This will do nothing unless a Simulation is being built." end build_if_not_already_built!( model; From a1a462a5784d4844a42f880179d38003b4d09c59 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 15 May 2026 14:57:39 +1200 Subject: [PATCH 7/8] Fix format --- src/ac_transmission_models/AC_branches.jl | 6 ++++-- test/performance/simple_cats.jl | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ac_transmission_models/AC_branches.jl b/src/ac_transmission_models/AC_branches.jl index b50cd07..d6afcf8 100644 --- a/src/ac_transmission_models/AC_branches.jl +++ b/src/ac_transmission_models/AC_branches.jl @@ -392,8 +392,10 @@ function _add_flow_rate_constraint!( slack_ub = get_variable(container, FlowActivePowerSlackUpperBound, T) slack_lb = get_variable(container, FlowActivePowerSlackLowerBound, T) for t in time_steps - con_ub[name, t] = JuMP.@constraint(model, var[name, t] - slack_ub[name, t] <= limits.max, tag...) - con_lb[name, t] = JuMP.@constraint(model, var[name, t] + slack_lb[name, t] >= limits.min, tag...) + con_ub[name, t] = + JuMP.@constraint(model, var[name, t] - slack_ub[name, t] <= limits.max, tag...) + con_lb[name, t] = + JuMP.@constraint(model, var[name, t] + slack_lb[name, t] >= limits.min, tag...) end else for t in time_steps diff --git a/test/performance/simple_cats.jl b/test/performance/simple_cats.jl index 4347931..e766639 100644 --- a/test/performance/simple_cats.jl +++ b/test/performance/simple_cats.jl @@ -23,14 +23,14 @@ function run_problem(optimizer) reduce_radial_branches = true, reduce_degree_two_branches = true, ) - template = POM.OperationsProblemTemplate(network_model); + template = POM.OperationsProblemTemplate(network_model) POM.set_device_model!(template, PSY.ThermalStandard, POM.ThermalBasicUnitCommitment) POM.set_device_model!(template, PSY.RenewableDispatch, POM.RenewableFullDispatch) POM.set_device_model!(template, PSY.HydroDispatch, POM.HydroDispatchRunOfRiver) POM.set_device_model!(template, PSY.PowerLoad, POM.StaticPowerLoad) POM.set_device_model!( template, - POM.DeviceModel(PSY.Line, POM.StaticBranch; use_slacks = true) + POM.DeviceModel(PSY.Line, POM.StaticBranch; use_slacks = true), ) POM.set_device_model!(template, PSY.Transformer2W, POM.StaticBranch) model = POM.DecisionModel( @@ -40,7 +40,7 @@ function run_problem(optimizer) optimizer, direct_mode_optimizer = true, optimizer_solve_log_print = true, - ); + ) POM.build!(model; output_dir = mktempdir(; cleanup = true)) @time POM.solve!(model) return @@ -60,18 +60,21 @@ end # MOI.RelativeGapTolerance() => 1e-3, # ), # ) + run_problem( optimizer_with_attributes( () -> MathOptLazy.Optimizer(Gurobi.Optimizer), MOI.RelativeGapTolerance() => 1e-2, ), ) + # run_problem( # optimizer_with_attributes( # HiGHS.Optimizer, # MOI.RelativeGapTolerance() => 1e-3, # ), # ) + # run_problem( # optimizer_with_attributes( # () -> MathOptLazy.Optimizer(HiGHS.Optimizer), @@ -80,7 +83,6 @@ run_problem( # ), # ) - # for (k, v) in model.internal.container.constraints # if k isa POM.ConstraintKey{POM.FlowRateConstraint,PSY.Line} # JuMP.set_attribute.(v, Gurobi.ConstraintAttribute("Lazy"), 1) @@ -139,7 +141,6 @@ run_problem( # POM.build!(model; output_dir = mktempdir(; cleanup = true)) # PProf.@pprof POM.build!(model; output_dir = mktempdir(; cleanup = true)) - # using SnoopCompileCore # invs = @snoop_invalidations using PowerSystems, PowerOperationsModels # using SnoopCompile, AbstractTrees From 8947af71583b7923cc5b47640352bcb25b9b12dc Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 15 May 2026 15:22:33 +1200 Subject: [PATCH 8/8] Update --- src/ac_transmission_models/AC_branches.jl | 32 ++++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/ac_transmission_models/AC_branches.jl b/src/ac_transmission_models/AC_branches.jl index d6afcf8..938ad6c 100644 --- a/src/ac_transmission_models/AC_branches.jl +++ b/src/ac_transmission_models/AC_branches.jl @@ -362,15 +362,19 @@ function get_min_max_limits( return (min = -π / 2, max = π / 2) end -function _get_tag(model::JuMP.GenericModel) - if JuMP.MOI.supports_constraint( - JuMP.unsafe_backend(model), - JuMP.MOI.ScalarAffineFunction{Float64}, - MathOptLazy.LazyScalarSet{JuMP.MOI.GreaterThan{Float64}}, - ) - return (MathOptLazy.Lazy(),) +_get_tag(model::JuMP.GenericModel) = _get_tag(JuMP.backend(model)) + +_get_tag(::JuMP.MOI.ModelLike) = () + +_get_tag(::MathOptLazy.Optimizer) = (MathOptLazy.Lazy(),) + +_get_tag(model::JuMP.MOI.Bridges.LazyBridgeOptimizer) = _get_tag(model.model) + +function _get_tag(model::JuMP.MOI.Utilities.CachingOptimizer) + if JuMP.MOI.Utilities.state(model) == JuMP.MOI.Utilities.NO_OPTIMIZER + return () end - return () + return _get_tag(model.optimizer) end function _add_flow_rate_constraint!( @@ -393,9 +397,17 @@ function _add_flow_rate_constraint!( slack_lb = get_variable(container, FlowActivePowerSlackLowerBound, T) for t in time_steps con_ub[name, t] = - JuMP.@constraint(model, var[name, t] - slack_ub[name, t] <= limits.max, tag...) + JuMP.@constraint( + model, + var[name, t] - slack_ub[name, t] <= limits.max, + tag... + ) con_lb[name, t] = - JuMP.@constraint(model, var[name, t] + slack_lb[name, t] >= limits.min, tag...) + JuMP.@constraint( + model, + var[name, t] + slack_lb[name, t] >= limits.min, + tag... + ) end else for t in time_steps