Skip to content

Commit 851d308

Browse files
committed
starting to work on custom constraint for LinModel
1 parent 4c645f3 commit 851d308

File tree

4 files changed

+114
-65
lines changed

4 files changed

+114
-65
lines changed

src/controller/construct.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ function setconstraint!(
291291
JuMP.delete(optim, optim[:linconstraint])
292292
JuMP.unregister(optim, :linconstraint)
293293
@constraint(optim, linconstraint, A*ΔŨvar .≤ b)
294-
setnonlincon!(mpc, model, optim)
294+
set_nonlincon!(mpc, model, optim)
295295
else
296296
i_b, i_g = init_matconstraint_mpc(
297297
model, nc,
@@ -363,7 +363,7 @@ function init_matconstraint_mpc(
363363
end
364364

365365
"By default, there is no nonlinear constraint, thus do nothing."
366-
setnonlincon!(::PredictiveController, ::SimModel, ::JuMP.GenericModel) = nothing
366+
set_nonlincon!(::PredictiveController, ::SimModel, ::JuMP.GenericModel) = nothing
367367

368368
"""
369369
default_Hp(model::LinModel)

src/controller/execute.jl

+21
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,27 @@ function predict!(Ŷ0, x̂0, x̂0next, u0, û0, mpc::PredictiveController, mod
361361
return Ŷ0, x̂end
362362
end
363363

364+
"""
365+
extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ) -> Ŷe, Ue
366+
367+
Compute the extended predictions `Ŷe` and `Ue` for the nonlinear optimization.
368+
369+
The function mutates `Ŷe`, `Ue` and `Ū` in arguments, without assuming any initial values.
370+
"""
371+
function extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ)
372+
ny, nu = model.ny, model.nu
373+
# --- extended output predictions Ŷe = [ŷ(k); Ŷ] ---
374+
Ŷe[1:ny] .= mpc.
375+
Ŷe[ny+1:end] .= Ŷ0 .+ mpc.Yop
376+
# --- extended manipulated inputs Ue = [U; u(k+Hp-1)] ---
377+
U0 =
378+
U0 .= mul!(U0, mpc.S̃, ΔŨ) .+ mpc.T_lastu0
379+
Ue[1:end-nu] .= U0 .+ mpc.Uop
380+
# u(k + Hp) = u(k + Hp - 1) since Δu(k+Hp) = 0 (because Hc ≤ Hp):
381+
Ue[end-nu+1:end] .= @views Ue[end-2nu+1:end-nu]
382+
return Ŷe, Ue
383+
end
384+
364385
"""
365386
obj_nonlinprog!( _ , _ , mpc::PredictiveController, model::LinModel, Ŷe, Ue, ΔŨ)
366387

src/controller/nonlinmpc.jl

+88-60
Original file line numberDiff line numberDiff line change
@@ -437,33 +437,7 @@ function init_optimization!(mpc::NonLinMPC, model::SimModel, optim)
437437
Jfunc, gfunc = get_optim_functions(mpc, mpc.optim)
438438
@operator(optim, J, nΔŨ, Jfunc)
439439
@objective(optim, Min, J(ΔŨvar...))
440-
ny, nx̂, Hp = model.ny, mpc.estim.nx̂, mpc.Hp
441-
if length(con.i_g) 0
442-
for i in eachindex(con.Y0min)
443-
name = Symbol("g_Y0min_$i")
444-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i]; name)
445-
end
446-
i_end_Ymin = 1Hp*ny
447-
for i in eachindex(con.Y0max)
448-
name = Symbol("g_Y0max_$i")
449-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_end_Ymin+i]; name)
450-
end
451-
i_end_Ymax = 2Hp*ny
452-
for i in eachindex(con.x̂0min)
453-
name = Symbol("g_x̂0min_$i")
454-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_end_Ymax+i]; name)
455-
end
456-
i_end_x̂min = 2Hp*ny + nx̂
457-
for i in eachindex(con.x̂0max)
458-
name = Symbol("g_x̂0max_$i")
459-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_end_x̂min+i]; name)
460-
end
461-
i_end_x̂max = 2Hp*ny + 2nx̂
462-
for i in 1:con.nc
463-
name = Symbol("g_c_$i")
464-
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_end_x̂max+i]; name)
465-
end
466-
end
440+
init_nonlincon!(mpc, model, gfunc)
467441
return nothing
468442
end
469443

@@ -506,7 +480,7 @@ function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT
506480
Ŷe, Ue = extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ)
507481
ϵ = (nϵ == 1) ? ΔŨ[end] : zero(JNT) # ϵ = 0 if nϵ == 0 (meaning no relaxation)
508482
mpc.con.gc!(gc, Ue, Ŷe, mpc.D̂e, mpc.p, ϵ)
509-
g = con_nonlinprog!(g, mpc, model, x̂0end, gc, Ŷ0, ΔŨ, ϵ)
483+
g = con_nonlinprog!(g, mpc, model, x̂0end, Ŷ0, gc, ϵ)
510484
return obj_nonlinprog!(Ȳ, Ū, mpc, model, Ŷe, Ue, ΔŨ)::T
511485
end
512486
function gfunc_i(i, ΔŨtup::NTuple{N, T}) where {N, T<:Real}
@@ -525,38 +499,87 @@ function get_optim_functions(mpc::NonLinMPC, ::JuMP.GenericModel{JNT}) where JNT
525499
Ŷe, Ue = extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ)
526500
ϵ = (nϵ == 1) ? ΔŨ[end] : zero(JNT) # ϵ = 0 if nϵ == 0 (meaning no relaxation)
527501
mpc.con.gc!(gc, Ue, Ŷe, mpc.D̂e, mpc.p, ϵ)
528-
g = con_nonlinprog!(g, mpc, model, x̂0end, gc, Ŷ0, ΔŨ, ϵ)
502+
g = con_nonlinprog!(g, mpc, model, x̂0end, Ŷ0, gc, ϵ)
529503
end
530504
return g[i]::T
531505
end
532506
gfunc = [(ΔŨ...) -> gfunc_i(i, ΔŨ) for i in 1:ng]
533507
return Jfunc, gfunc
534508
end
535509

536-
"""
537-
extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ) -> Ŷe, Ue
538510

539-
Compute the extended predictions `Ŷe` and `Ue` for the nonlinear optimization.
511+
function init_nonlincon!(mpc::NonLinMPC, ::NonLinModel, gfunc::Vector{<:Function})
512+
optim, con = mpc.optim, mpc.con
513+
ny, nx̂, Hp, nΔŨ = mpc.estim.model.ny, mpc.estim.nx̂, mpc.Hp, length(mpc.ΔŨ)
514+
if length(con.i_g) 0
515+
i_base = 0
516+
for i in eachindex(con.Y0min)
517+
name = Symbol("g_Y0min_$i")
518+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
519+
end
520+
i_base = 1Hp*ny
521+
for i in eachindex(con.Y0max)
522+
name = Symbol("g_Y0max_$i")
523+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
524+
end
525+
i_base = 2Hp*ny
526+
for i in eachindex(con.x̂0min)
527+
name = Symbol("g_x̂0min_$i")
528+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
529+
end
530+
i_base = 2Hp*ny + nx̂
531+
for i in eachindex(con.x̂0max)
532+
name = Symbol("g_x̂0max_$i")
533+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
534+
end
535+
i_base = 2Hp*ny + 2nx̂
536+
for i in 1:con.nc
537+
name = Symbol("g_c_$i")
538+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
539+
end
540+
end
541+
return nothing
542+
end
543+
544+
function init_nonlincon!(mpc::NonLinMPC, ::LinModel, gfunc::Vector{<:Function})
545+
optim, con = mpc.optim, mpc.con
546+
nΔŨ = length(mpc.ΔŨ)
547+
if length(con.i_g) 0
548+
i_base = 0
549+
for i in 1:con.nc
550+
name = Symbol("g_c_$i")
551+
optim[name] = JuMP.add_nonlinear_operator(optim, nΔŨ, gfunc[i_base+i]; name)
552+
end
553+
end
554+
return nothing
555+
end
540556

541-
The function mutates `Ŷe`, `Ue` and `Ū` in arguments, without assuming any initial values.
542557
"""
543-
function extended_predictions!(Ŷe, Ue, Ū, mpc, model, Ŷ0, ΔŨ)
544-
ny, nu = model.ny, model.nu
545-
# --- extended output predictions Ŷe = [ŷ(k); Ŷ] ---
546-
Ŷe[1:ny] .= mpc.
547-
Ŷe[ny+1:end] .= Ŷ0 .+ mpc.Yop
548-
# --- extended manipulated inputs Ue = [U; u(k+Hp-1)] ---
549-
U0 =
550-
U0 .= mul!(U0, mpc.S̃, ΔŨ) .+ mpc.T_lastu0
551-
Ue[1:end-nu] .= U0 .+ mpc.Uop
552-
# u(k + Hp) = u(k + Hp - 1) since Δu(k+Hp) = 0 (because Hc ≤ Hp):
553-
Ue[end-nu+1:end] .= @views Ue[end-2nu+1:end-nu]
554-
return Ŷe, Ue
558+
set_nonlincon!(mpc::NonLinMPC, ::LinModel, optim)
559+
560+
Set the custom nonlinear inequality constraints for `LinModel`.
561+
"""
562+
function set_nonlincon!(
563+
mpc::NonLinMPC, ::LinModel, optim::JuMP.GenericModel{JNT}
564+
) where JNT<:Real
565+
ΔŨvar = optim[:ΔŨvar]
566+
con = mpc.con
567+
nonlin_constraints = JuMP.all_constraints(optim, JuMP.NonlinearExpr, MOI.LessThan{JNT})
568+
map(con_ref -> JuMP.delete(optim, con_ref), nonlin_constraints)
569+
for i in 1:con.nc
570+
gfunc_i = optim[Symbol("g_c_$i")]
571+
@constraint(optim, gfunc_i(ΔŨvar...) <= 0)
572+
end
573+
return nothing
555574
end
556575

557-
"Set the nonlinear constraints on the output predictions `Ŷ` and terminal states `x̂end`."
558-
function setnonlincon!(
559-
mpc::NonLinMPC, ::NonLinModel, optim::JuMP.GenericModel{JNT}
576+
"""
577+
set_nonlincon!(mpc::NonLinMPC, ::NonLinModel, optim)
578+
579+
Also set output prediction `Ŷ` and terminal state `x̂end` constraints when not a `LinModel`.
580+
"""
581+
function set_nonlincon!(
582+
mpc::NonLinMPC, ::SimModel, optim::JuMP.GenericModel{JNT}
560583
) where JNT<:Real
561584
ΔŨvar = optim[:ΔŨvar]
562585
con = mpc.con
@@ -585,21 +608,30 @@ function setnonlincon!(
585608
return nothing
586609
end
587610

588-
# TODO: MODIF THE FOLLOWING METHOD!
589-
function setnonlincon!(
590-
mpc::NonLinMPC, ::LinModel, optim::JuMP.GenericModel{JNT}
591-
) where JNT<:Real
592-
return nothing
611+
"""
612+
con_nonlinprog!(g, mpc::NonLinMPC, model::LinModel, _ , _ , gc, ϵ)
613+
614+
Nonlinear constrains for [`NonLinMPC`](@ref) when `model` is a [`LinModel`](@ref).
615+
616+
The method mutates the `g` vectors in argument and returns it. Only the custom constraints
617+
are include in the `g` vector.
618+
"""
619+
function con_nonlinprog!(g, mpc::NonLinMPC, ::LinModel, _ , _ , gc, ϵ)
620+
for i in eachindex(g)
621+
g[i] = gc[i]
622+
end
623+
return g
593624
end
594625

595626
"""
596-
con_nonlinprog!(g, mpc::NonLinMPC, model::SimModel, x̂end, gc, Ŷ0, ΔŨ, ϵ) -> g
627+
con_nonlinprog!(g, mpc::NonLinMPC, model::SimModel, x̂0end, Ŷ0, gc, ϵ) -> g
597628
598629
Nonlinear constrains for [`NonLinMPC`](@ref) when `model` is not a [`LinModel`](@ref).
599630
600-
The method mutates the `g` and `gc` vectors in argument.
631+
The method mutates the `g` vectors in argument and returns it. The output prediction,
632+
the terminal state and the custom constraints are include in the `g` vector.
601633
"""
602-
function con_nonlinprog!(g, mpc::NonLinMPC, ::SimModel, x̂0end, gc, Ŷ0, ΔŨ, ϵ)
634+
function con_nonlinprog!(g, mpc::NonLinMPC, ::SimModel, x̂0end, Ŷ0, gc, ϵ)
603635
nx̂, nŶ = length(x̂0end), length(Ŷ0)
604636
for i in eachindex(g)
605637
mpc.con.i_g[i] || continue
@@ -623,10 +655,6 @@ function con_nonlinprog!(g, mpc::NonLinMPC, ::SimModel, x̂0end, gc, Ŷ0, ΔŨ
623655
return g
624656
end
625657

626-
#TODO: MODIF THE FOLLOWING METHOD!
627-
"No nonlinear constraints if `model` is a [`LinModel`](@ref), return `g` unchanged."
628-
con_nonlinprog!(g, ::NonLinMPC, ::LinModel, _ , _ , _ , _ , _ ) = g
629-
630658
"Evaluate the economic term `E*JE` of the objective function for [`NonLinMPC`](@ref)."
631659
function obj_econ!(Ue, Ŷe, mpc::NonLinMPC, model::SimModel)
632660
E_JE = iszero(mpc.E) ? 0.0 : mpc.E*mpc.JE(Ue, Ŷe, mpc.D̂e, mpc.p)

src/estimator/mhe/construct.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ function setconstraint!(
652652
JuMP.delete(optim, optim[:linconstraint])
653653
JuMP.unregister(optim, :linconstraint)
654654
@constraint(optim, linconstraint, A*Z̃var .≤ b)
655-
setnonlincon!(estim, model, optim)
655+
set_nonlincon!(estim, model, optim)
656656
else
657657
i_b, i_g = init_matconstraint_mhe(model,
658658
i_x̃min, i_x̃max, i_X̂min, i_X̂max, i_Ŵmin, i_Ŵmax, i_V̂min, i_V̂max
@@ -714,10 +714,10 @@ function init_matconstraint_mhe(::SimModel{NT},
714714
end
715715

716716
"By default, no nonlinear constraints in the MHE, thus return nothing."
717-
setnonlincon!(::MovingHorizonEstimator, ::SimModel, ::JuMP.GenericModel) = nothing
717+
set_nonlincon!(::MovingHorizonEstimator, ::SimModel, ::JuMP.GenericModel) = nothing
718718

719719
"Set the nonlinear constraints on the output predictions `Ŷ` and terminal states `x̂end`."
720-
function setnonlincon!(
720+
function set_nonlincon!(
721721
estim::MovingHorizonEstimator, ::NonLinModel, optim::JuMP.GenericModel{JNT}
722722
) where JNT<:Real
723723
optim, con = estim.optim, estim.con

0 commit comments

Comments
 (0)