Skip to content

Commit 5988c81

Browse files
committed
fix: pyomo needs one fewer finite element than # tsteps
1 parent 33ad60a commit 5988c81

File tree

2 files changed

+35
-22
lines changed

2 files changed

+35
-22
lines changed

ext/MTKPyomoDynamicOptExt.jl

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,21 @@ end
7676

7777
function MTK.generate_state_variable!(m::ConcreteModel, u0, ns, ts)
7878
m.u_idxs = pyomo.RangeSet(1, ns)
79-
m.U = pyomo.Var(m.u_idxs, m.t, initialize = 0)
79+
init_f = Pyomo.pyfunc((m, i, t) -> (u0[Pyomo.pyconvert(Int, i)]))
80+
m.U = pyomo.Var(m.u_idxs, m.t, initialize = init_f)
8081
PyomoVar(m.U)
8182
end
8283

8384
function MTK.generate_input_variable!(m::ConcreteModel, c0, nc, ts)
8485
m.v_idxs = pyomo.RangeSet(1, nc)
85-
m.V = pyomo.Var(m.v_idxs, m.t, initialize = 0)
86+
init_f = Pyomo.pyfunc((m, i, t) -> (c0[Pyomo.pyconvert(Int, i)]))
87+
m.V = pyomo.Var(m.v_idxs, m.t, initialize = init_f)
8688
PyomoVar(m.V)
8789
end
8890

8991
function MTK.generate_timescale!(m::ConcreteModel, guess, is_free_t)
90-
m.tₛ = is_free_t ? PyomoVar(pyomo.Var(initialize = guess, bounds = (0, Inf))) : PyomoVar(Pyomo.Py(1))
92+
m.tₛ = is_free_t ? pyomo.Var(initialize = guess, bounds = (0, Inf)) : Pyomo.Py(1)
93+
PyomoVar(m.tₛ)
9194
end
9295

9396
function MTK.add_constraint!(pmodel::PyomoDynamicOptModel, cons; n_idxs = 1)
@@ -100,7 +103,6 @@ function MTK.add_constraint!(pmodel::PyomoDynamicOptModel, cons; n_idxs = 1)
100103
cons.lhs - cons.rhs 0
101104
end
102105
expr = Symbolics.substitute(expr, SPECIAL_FUNCTIONS_DICT)
103-
@show expr
104106

105107
cons_sym = Symbol("cons", hash(cons))
106108
if occursin(Symbolics.unwrap(t_sym), expr)
@@ -133,12 +135,8 @@ end
133135

134136
function MTK.lowered_integral(m::PyomoDynamicOptModel, arg, lo, hi)
135137
@unpack model, model_sym, t_sym = m
136-
@show arg
137-
@show Symbolics.build_function(arg, model_sym, t_sym)
138138
arg_f = eval(Symbolics.build_function(arg, model_sym, t_sym))
139-
@show arg_f
140139
int_sym = Symbol(:int, hash(arg))
141-
@show int_sym
142140
setproperty!(model, int_sym, dae.Integral(model.t, wrt = model.t, rule=Pyomo.pyfunc(arg_f)))
143141
PyomoVar(model.tₛ * model.int_sym)
144142
end
@@ -168,21 +166,36 @@ function MTK.prepare_and_optimize!(prob::PyomoDynamicOptProblem, collocation; ve
168166
dm = collocation.derivative_method
169167
discretizer = TransformationFactory(dm)
170168
ncp = Pyomo.is_finite_difference(dm) ? 1 : dm.np
171-
discretizer.apply_to(solver_m, wrt = solver_m.t, nfe = solver_m.steps, scheme = Pyomo.scheme_string(dm))
169+
discretizer.apply_to(solver_m, wrt = solver_m.t, nfe = solver_m.steps - 1, scheme = Pyomo.scheme_string(dm))
172170
solver = SolverFactory(string(collocation.solver))
173171
results = solver.solve(solver_m, tee = true)
174-
ConcreteModel(solver_m)
172+
PyomoOutput(results, solver_m)
173+
end
174+
175+
struct PyomoOutput
176+
result::Pyomo.Py
177+
model::Pyomo.Py
175178
end
176179

177-
MTK.get_U_values(m::ConcreteModel) = [[Pyomo.pyconvert(Float64, pyomo.value(m.U[i, t])) for i in m.u_idxs] for t in m.t]
178-
MTK.get_V_values(m::ConcreteModel) = [[Pyomo.pyconvert(Float64, pyomo.value(m.V[i, t])) for i in m.v_idxs] for t in m.t]
179-
MTK.get_t_values(m::ConcreteModel) = [Pyomo.pyconvert(Float64, t) for t in m.t]
180+
function MTK.get_U_values(output::PyomoOutput)
181+
m = output.model
182+
[[Pyomo.pyconvert(Float64, pyomo.value(m.U[i, t])) for i in m.u_idxs] for t in m.t]
183+
end
184+
function MTK.get_V_values(output::PyomoOutput)
185+
m = output.model
186+
[[Pyomo.pyconvert(Float64, pyomo.value(m.V[i, t])) for i in m.v_idxs] for t in m.t]
187+
end
188+
function MTK.get_t_values(output::PyomoOutput)
189+
m = output.model
190+
Pyomo.pyconvert(Float64, pyomo.value(m.tₛ)) * [Pyomo.pyconvert(Float64, t) for t in m.t]
191+
end
180192

181-
function MTK.successful_solve(m::ConcreteModel)
182-
return true
183-
ss = m.solver.status
184-
tc = m.solver.termination_condition
185-
if ss == opt.SolverStatus.ok && (tc == opt.TerminationStatus.optimal || tc == opt.TerminationStatus.locallyOptimal)
193+
function MTK.successful_solve(output::PyomoOutput)
194+
r = output.result
195+
ss = r.solver.status
196+
Main.xx[] = ss
197+
tc = r.solver.termination_condition
198+
if Bool(ss == opt.SolverStatus.ok) && (Bool(tc == opt.TerminationCondition.optimal) || Bool(tc == opt.TerminationCondition.locallyOptimal))
186199
return true
187200
else
188201
return false

test/extensions/dynamic_optimization.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ end
250250
@test isol.sol[h(t)][end] > 1.012
251251

252252
pprob = PyomoDynamicOptProblem(rocket, u0map, (ts, te), pmap; dt = 0.001, cse = false)
253-
psol = solve(pprob, PyomoCollocation("ipopt"))
253+
psol = solve(pprob, PyomoCollocation("ipopt", MidpointEuler()))
254254
@test psol.sol.u[end][1] > 1.012
255255

256256
# Test solution
@@ -319,7 +319,7 @@ end
319319

320320
u0map = [x(t) => 1.0, v(t) => 0.0]
321321
tspan = (0.0, tf)
322-
parammap = [u(t) => 0.0, tf => 1.0]
322+
parammap = [u(t) => 1.0, tf => 1.0]
323323
jprob = JuMPDynamicOptProblem(block, u0map, tspan, parammap; steps = 51)
324324
jsol = solve(jprob, JuMPCollocation(Ipopt.Optimizer, constructVerner8()))
325325
@test isapprox(jsol.sol.t[end], 2.0, atol = 1e-5)
@@ -329,11 +329,11 @@ end
329329
@test isapprox(csol.sol.t[end], 2.0, atol = 1e-5)
330330

331331
iprob = InfiniteOptDynamicOptProblem(block, u0map, tspan, parammap; steps = 51)
332-
isol = solve(iprob, InfiniteOptCollocation(Ipopt.Optimizer))
332+
isol = solve(iprob, InfiniteOptCollocation(Ipopt.Optimizer), verbose = true)
333333
@test isapprox(isol.sol.t[end], 2.0, atol = 1e-5)
334334

335335
pprob = PyomoDynamicOptProblem(block, u0map, tspan, parammap; steps = 51)
336-
psol = solve(pprob, PyomoCollocation("ipopt"))
336+
psol = solve(pprob, PyomoCollocation("ipopt", BackwardEuler()))
337337
@test isapprox(psol.sol.t[end], 2.0, atol = 1e-5)
338338
end
339339

0 commit comments

Comments
 (0)