1+ using ExaModels, KernelAbstractions
2+ using Downloads, Test, SparseArrays
3+ using NLPModels, NLPModelsIpopt
4+ using PowerModels; PowerModels. silence ()
5+
6+ using CoolPDLP, QuadraticModels
7+
8+ const TMPDIR = tempdir ()
9+
10+ function get_power_case (filename)
11+ if ! isfile (filename)
12+ ff = joinpath (TMPDIR, filename)
13+ if ! isfile (ff)
14+ @info " Downloading $filename "
15+ Downloads. download (
16+ " https://raw.githubusercontent.com/power-grid-lib/pglib-opf/dc6be4b2f85ca0e776952ec22cbd4c22396ea5a3/$filename " ,
17+ joinpath (TMPDIR, filename),
18+ )
19+ return joinpath (TMPDIR, filename)
20+ else
21+ return ff
22+ end
23+ else
24+ return filename
25+ end
26+ end
27+
28+
29+ function get_power_data_ref (filename)
30+ case = get_power_case (filename)
31+ data = PowerModels. parse_file (case)
32+ PowerModels. standardize_cost_terms! (data, order = 2 )
33+ PowerModels. calc_thermal_limits! (data)
34+ return PowerModels. build_ref (data)[:it ][:pm ][:nw ][0 ]
35+ end
36+
37+ convert_data (data:: N , backend) where {names,N<: NamedTuple{names} } =
38+ NamedTuple {names} (ExaModels. convert_array (d, backend) for d in data)
39+ parse_ac_power_data (filename, backend) =
40+ convert_data (parse_ac_power_data (filename), backend)
41+
42+
43+ function parse_ac_power_data (filename)
44+ ref = get_power_data_ref (filename)
45+
46+ arcdict = Dict (a => k for (k, a) in enumerate (ref[:arcs ]))
47+ busdict = Dict (k => i for (i, (k, v)) in enumerate (ref[:bus ]))
48+ gendict = Dict (k => i for (i, (k, v)) in enumerate (ref[:gen ]))
49+ branchdict = Dict (k => i for (i, (k, v)) in enumerate (ref[:branch ]))
50+
51+ return (
52+ bus = [
53+ begin
54+ bus_loads = [ref[:load ][l] for l in ref[:bus_loads ][k]]
55+ bus_shunts = [ref[:shunt ][s] for s in ref[:bus_shunts ][k]]
56+ pd = sum (load[" pd" ] for load in bus_loads; init = 0.0 )
57+ gs = sum (shunt[" gs" ] for shunt in bus_shunts; init = 0.0 )
58+ qd = sum (load[" qd" ] for load in bus_loads; init = 0.0 )
59+ bs = sum (shunt[" bs" ] for shunt in bus_shunts; init = 0.0 )
60+ (i = busdict[k], pd = pd, gs = gs, qd = qd, bs = bs)
61+ end for (k, v) in ref[:bus ]
62+ ],
63+ gen = [
64+ (
65+ i = gendict[k],
66+ cost1 = v[" cost" ][1 ],
67+ cost2 = v[" cost" ][2 ],
68+ cost3 = v[" cost" ][3 ],
69+ bus = busdict[v[" gen_bus" ]],
70+ ) for (k, v) in ref[:gen ]
71+ ],
72+ arc = [
73+ (i = k, rate_a = ref[:branch ][l][" rate_a" ], bus = busdict[i]) for
74+ (k, (l, i, j)) in enumerate (ref[:arcs ])
75+ ],
76+ branch = [
77+ begin
78+ f_idx = arcdict[i, branch[" f_bus" ], branch[" t_bus" ]]
79+ t_idx = arcdict[i, branch[" t_bus" ], branch[" f_bus" ]]
80+ g, b = PowerModels. calc_branch_y (branch)
81+ tr, ti = PowerModels. calc_branch_t (branch)
82+ ttm = tr^ 2 + ti^ 2
83+ g_fr = branch[" g_fr" ]
84+ b_fr = branch[" b_fr" ]
85+ g_to = branch[" g_to" ]
86+ b_to = branch[" b_to" ]
87+ c1 = (- g * tr - b * ti) / ttm
88+ c2 = (- b * tr + g * ti) / ttm
89+ c3 = (- g * tr + b * ti) / ttm
90+ c4 = (- b * tr - g * ti) / ttm
91+ c5 = (g + g_fr) / ttm
92+ c6 = (b + b_fr) / ttm
93+ c7 = (g + g_to)
94+ c8 = (b + b_to)
95+ (
96+ i = branchdict[i],
97+ j = 1 ,
98+ f_idx = f_idx,
99+ t_idx = t_idx,
100+ f_bus = busdict[branch[" f_bus" ]],
101+ t_bus = busdict[branch[" t_bus" ]],
102+ c1 = c1,
103+ c2 = c2,
104+ c3 = c3,
105+ c4 = c4,
106+ c5 = c5,
107+ c6 = c6,
108+ c7 = c7,
109+ c8 = c8,
110+ rate_a_sq = branch[" rate_a" ]^ 2 ,
111+ )
112+ end for (i, branch) in ref[:branch ]
113+ ],
114+ ref_buses = [busdict[i] for (i, k) in ref[:ref_buses ]],
115+ vmax = [v[" vmax" ] for (k, v) in ref[:bus ]],
116+ vmin = [v[" vmin" ] for (k, v) in ref[:bus ]],
117+ pmax = [v[" pmax" ] for (k, v) in ref[:gen ]],
118+ pmin = [v[" pmin" ] for (k, v) in ref[:gen ]],
119+ qmax = [v[" qmax" ] for (k, v) in ref[:gen ]],
120+ qmin = [v[" qmin" ] for (k, v) in ref[:gen ]],
121+ rate_a = [ref[:branch ][l][" rate_a" ] for (k, (l, i, j)) in enumerate (ref[:arcs ])],
122+ angmax = [b[" angmax" ] for (i, b) in ref[:branch ]],
123+ angmin = [b[" angmin" ] for (i, b) in ref[:branch ]],
124+ )
125+ end
126+
127+ function exa_ac_power_model (backend, data)
128+
129+ w = ExaModels. ExaCore (backend = backend)
130+
131+ va = ExaModels. variable (w, length (data. bus);)
132+
133+ vm = ExaModels. variable (
134+ w,
135+ length (data. bus);
136+ start = fill! (similar (data. bus, Float64), 1.0 ),
137+ lvar = data. vmin,
138+ uvar = data. vmax,
139+ )
140+ pg = ExaModels. variable (w, length (data. gen); lvar = data. pmin, uvar = data. pmax)
141+
142+ qg = ExaModels. variable (w, length (data. gen); lvar = data. qmin, uvar = data. qmax)
143+
144+ p = ExaModels. variable (w, length (data. arc); lvar = - data. rate_a, uvar = data. rate_a)
145+
146+ q = ExaModels. variable (w, length (data. arc); lvar = - data. rate_a, uvar = data. rate_a)
147+
148+ o = ExaModels. objective (
149+ w,
150+ g. cost1 * pg[g. i]^ 2 + g. cost2 * pg[g. i] + g. cost3 for g in data. gen
151+ )
152+
153+ c1 = ExaModels. constraint (w, va[i] for i in data. ref_buses)
154+
155+ c2 = ExaModels. constraint (
156+ w,
157+ p[b. f_idx] - b. c5 * vm[b. f_bus]^ 2 -
158+ b. c3 * (vm[b. f_bus] * vm[b. t_bus] * cos (va[b. f_bus] - va[b. t_bus])) -
159+ b. c4 * (vm[b. f_bus] * vm[b. t_bus] * sin (va[b. f_bus] - va[b. t_bus])) for
160+ b in data. branch
161+ )
162+
163+ c3 = ExaModels. constraint (
164+ w,
165+ q[b. f_idx] +
166+ b. c6 * vm[b. f_bus]^ 2 +
167+ b. c4 * (vm[b. f_bus] * vm[b. t_bus] * cos (va[b. f_bus] - va[b. t_bus])) -
168+ b. c3 * (vm[b. f_bus] * vm[b. t_bus] * sin (va[b. f_bus] - va[b. t_bus])) for
169+ b in data. branch
170+ )
171+
172+ c4 = ExaModels. constraint (
173+ w,
174+ p[b. t_idx] - b. c7 * vm[b. t_bus]^ 2 -
175+ b. c1 * (vm[b. t_bus] * vm[b. f_bus] * cos (va[b. t_bus] - va[b. f_bus])) -
176+ b. c2 * (vm[b. t_bus] * vm[b. f_bus] * sin (va[b. t_bus] - va[b. f_bus])) for
177+ b in data. branch
178+ )
179+
180+ c5 = ExaModels. constraint (
181+ w,
182+ q[b. t_idx] +
183+ b. c8 * vm[b. t_bus]^ 2 +
184+ b. c2 * (vm[b. t_bus] * vm[b. f_bus] * cos (va[b. t_bus] - va[b. f_bus])) -
185+ b. c1 * (vm[b. t_bus] * vm[b. f_bus] * sin (va[b. t_bus] - va[b. f_bus])) for
186+ b in data. branch
187+ )
188+
189+ c6 = ExaModels. constraint (
190+ w,
191+ va[b. f_bus] - va[b. t_bus] for b in data. branch;
192+ lcon = data. angmin,
193+ ucon = data. angmax,
194+ )
195+ c7 = ExaModels. constraint (
196+ w,
197+ p[b. f_idx]^ 2 + q[b. f_idx]^ 2 - b. rate_a_sq for b in data. branch;
198+ lcon = fill! (similar (data. branch, Float64, length (data. branch)), - Inf ),
199+ )
200+ c8 = ExaModels. constraint (
201+ w,
202+ p[b. t_idx]^ 2 + q[b. t_idx]^ 2 - b. rate_a_sq for b in data. branch;
203+ lcon = fill! (similar (data. branch, Float64, length (data. branch)), - Inf ),
204+ )
205+
206+ c9 = ExaModels. constraint (w, b. pd + b. gs * vm[b. i]^ 2 for b in data. bus)
207+
208+ c10 = ExaModels. constraint (w, b. qd - b. bs * vm[b. i]^ 2 for b in data. bus)
209+
210+ c11 = ExaModels. constraint! (w, c9, a. bus => p[a. i] for a in data. arc)
211+ c12 = ExaModels. constraint! (w, c10, a. bus => q[a. i] for a in data. arc)
212+
213+ c13 = ExaModels. constraint! (w, c9, g. bus => - pg[g. i] for g in data. gen)
214+ c14 = ExaModels. constraint! (w, c10, g. bus => - qg[g. i] for g in data. gen)
215+
216+ return ExaModels. ExaModel (w; prod = true ),
217+ (va, vm, pg, qg, p, q),
218+ (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10)
219+
220+ end
221+
222+ @testset " Taylor of ACOPF via QuadraticModels.jl " begin
223+ backend = CPU ()
224+ data = parse_ac_power_data (" pglib_opf_case14_ieee.m" , backend)
225+ examodel = first (exa_ac_power_model (backend, data))
226+ res = ipopt (examodel, print_level = 0 )
227+ milp = CoolPDLP. MILP (QuadraticModel (examodel, res. solution), ignore_islp = true )
228+ sol, cres = CoolPDLP. solve (milp, PDLP (
229+ Float32, Int32, SparseMatrixCSC; backend,
230+ termination_reltol = 1.0f-4 , time_limit = 10.0 ,
231+ ))
232+ @test cres. termination_status == CoolPDLP. OPTIMAL
233+ end
0 commit comments