|
17 | 17 | # julia --project=./examples examples/scopf.jl --case case9 --mode twostage --form rect |
18 | 18 | # julia --project=./examples examples/scopf.jl --gpu --inertia free |
19 | 19 | # |
20 | | -# GPU two-stage (Schur) converges to the true optimum on case9 with this config — it is a |
21 | | -# per-run roulette (~60%), so retry; see SCOPF-GPU-TWOSTAGE-FINDINGS.md for why: |
22 | | -# julia --project=./examples examples/scopf.jl --case case9 --mode twostage --gpu \ |
23 | | -# --inertia based --tol 5e-3 --richardson 50 --retry 6 |
| 20 | +# GPU two-stage (Schur) converges reliably to the true optimum on case9 at the default |
| 21 | +# tol=1e-4 (deterministic Schur assembly; see SCOPF-GPU-TWOSTAGE-FINDINGS.md): |
| 22 | +# julia --project=./examples examples/scopf.jl --case case9 --mode twostage --gpu --inertia based |
24 | 23 |
|
25 | 24 | using ExaModelsPower |
26 | 25 | using ExaModels: solution |
@@ -178,19 +177,10 @@ elseif mode == :twostage |
178 | 177 | lin = backend === nothing ? MadNLP.MumpsSolver : MadNLPGPU.CUDSSSolver |
179 | 178 | kkt_opts = schur_kkt_options(info, backend, cudss_ir) |
180 | 179 |
|
181 | | - # On GPU the Schur solve can converge to a suboptimal local point, so use the reliable |
182 | | - # :single path as a reference objective; the retry then accepts only the true optimum. |
183 | | - refobj = nothing |
184 | | - if backend !== nothing |
185 | | - mref, vref, _ = scopf_model(case, contingencies; form = form, backend = backend) |
186 | | - rref = madnlp(mref; tol = 1.0e-4, print_level = MadNLP.ERROR, |
187 | | - kkt_system = MadNLP.SparseCondensedKKTSystem, linear_solver = MadNLPGPU.CUDSSSolver, |
188 | | - inertia_correction_method = inertia, max_iter = max_iter) |
189 | | - rref.status == MadNLP.SOLVE_SUCCEEDED && (refobj = rref.objective) |
190 | | - @info "single-path reference objective (retry target)" refobj |
191 | | - end |
192 | | - |
193 | | - result = solve_with_retry(; ref = refobj) do |
| 180 | + # With the deterministic GPU Schur assembly the two-stage solve converges reliably at |
| 181 | + # tol=1e-4 (matching CPU); --retry defaults to 1 (a single solve) and is kept only as a |
| 182 | + # safety net. |
| 183 | + result = solve_with_retry() do |
194 | 184 | madnlp(model; |
195 | 185 | callback = MadNLP.SparseCallback, |
196 | 186 | kkt_system = MadNLP.SchurComplementKKTSystem, |
@@ -222,20 +212,16 @@ elseif mode == :compare |
222 | 212 | # Sparse first-stage Schur complement solver: MUMPS on CPU, cuDSS on GPU. The |
223 | 213 | # per-scenario sparse blocks use cuDSS/MUMPS internally. |
224 | 214 | lin = backend === nothing ? MadNLP.MumpsSolver : MadNLPGPU.CUDSSSolver |
225 | | - # Retry the (GPU-flaky) two-stage solve against the :single objective so the comparison |
226 | | - # is over the true optimum, not a suboptimal local point. |
227 | | - r2 = solve_with_retry(; ref = r1.objective) do |
228 | | - madnlp(m2; |
229 | | - callback = MadNLP.SparseCallback, |
230 | | - kkt_system = MadNLP.SchurComplementKKTSystem, |
231 | | - linear_solver = lin, |
232 | | - kkt_options = schur_kkt_options(info, backend, cudss_ir), |
233 | | - inertia_correction_method = inertia, |
234 | | - max_iter = max_iter, |
235 | | - tol = tol, print_level = MadNLP.ERROR, |
236 | | - rich_kw()..., |
237 | | - ) |
238 | | - end |
| 215 | + r2 = madnlp(m2; |
| 216 | + callback = MadNLP.SparseCallback, |
| 217 | + kkt_system = MadNLP.SchurComplementKKTSystem, |
| 218 | + linear_solver = lin, |
| 219 | + kkt_options = schur_kkt_options(info, backend, cudss_ir), |
| 220 | + inertia_correction_method = inertia, |
| 221 | + max_iter = max_iter, |
| 222 | + tol = tol, print_level = MadNLP.ERROR, |
| 223 | + rich_kw()..., |
| 224 | + ) |
239 | 225 | pg0 = Array(solution(r2, v2.pg0)) |
240 | 226 |
|
241 | 227 | obj_gap = abs(r1.objective - r2.objective) |
|
0 commit comments