From 88457e9f1c1832feac08a3f3c432aa6d29461280 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Caillau Date: Mon, 9 Mar 2026 18:19:36 +0100 Subject: [PATCH 1/6] beam + goddard with @init: passed --- test/problems/beam.jl | 5 ++++- test/problems/goddard.jl | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/problems/beam.jl b/test/problems/beam.jl index 542d7500..5927cbee 100644 --- a/test/problems/beam.jl +++ b/test/problems/beam.jl @@ -22,7 +22,10 @@ function Beam() ∫(u(t)^2) → min end - init = (state=[0.05, 0.1], control=0.1) + init = @init ocp begin + x(t) := [0.05, 0.1] + u(t) := 0.1 + end return (ocp=ocp, obj=8.898598, name="beam", init=init) end diff --git a/test/problems/goddard.jl b/test/problems/goddard.jl index 310adcdb..03588a30 100644 --- a/test/problems/goddard.jl +++ b/test/problems/goddard.jl @@ -58,7 +58,11 @@ function Goddard(; vmax=0.1, Tmax=3.5) end # Components for a reasonable initial guess around a feasible trajectory. - init = (state=[1.01, 0.05, 0.8], control=0.5, variable=[0.1]) + init = @init goddard begin + x(t) := [1.01, 0.05, 0.8] + u(t) := 0.5 + tf := 0.1 + end return (ocp=goddard, obj=1.01257, name="goddard", init=init) end From 958959222cb177aa494f2b524353182238a74e08 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Caillau Date: Mon, 9 Mar 2026 18:37:22 +0100 Subject: [PATCH 2/6] updated canonical test with GPU --- test/suite/solve/test_canonical.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/suite/solve/test_canonical.jl b/test/suite/solve/test_canonical.jl index b883a52f..3d27f40b 100644 --- a/test/suite/solve/test_canonical.jl +++ b/test/suite/solve/test_canonical.jl @@ -9,7 +9,7 @@ module TestCanonical import Test -import OptimalControl +import OptimalControl: OptimalControl, GPU # Import du module d'affichage (DIP - dépend de l'abstraction) include(joinpath(@__DIR__, "..", "..", "helpers", "print_utils.jl")) @@ -138,8 +138,8 @@ function test_canonical() # GPU tests (only if CUDA is available) # ---------------------------------------------------------------- if is_cuda_on() - gpu_modeler = ("Exa/GPU", OptimalControl.Exa(backend=CUDA.CUDABackend())) - gpu_solver = ("MadNLP/GPU", OptimalControl.MadNLP(print_level=MadNLP.ERROR, linear_solver=MadNLPGPU.CUDSSSolver)) + gpu_modeler = ("Exa/GPU", OptimalControl.Exa{GPU}()) + gpu_solver = ("MadNLP/GPU", OptimalControl.MadNLP{GPU}(print_level=MadNLP.ERROR)) for (pname, pb) in problems Test.@testset "GPU / $pname" begin From a8212c70194e5e87ecb7e9cc8afe2775a75df504 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Caillau Date: Mon, 9 Mar 2026 18:48:02 +0100 Subject: [PATCH 3/6] added quadrotor --- test/problems/TestProblems.jl | 5 +++-- test/suite/solve/test_canonical.jl | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/problems/TestProblems.jl b/test/problems/TestProblems.jl index 2ec09eea..e9dba448 100644 --- a/test/problems/TestProblems.jl +++ b/test/problems/TestProblems.jl @@ -4,7 +4,8 @@ module TestProblems include("beam.jl") include("goddard.jl") + include("quadrotor.jl") - export Beam, Goddard + export Beam, Goddard, Quadrotor -end \ No newline at end of file +end diff --git a/test/suite/solve/test_canonical.jl b/test/suite/solve/test_canonical.jl index 3d27f40b..18417321 100644 --- a/test/suite/solve/test_canonical.jl +++ b/test/suite/solve/test_canonical.jl @@ -70,6 +70,7 @@ function test_canonical() problems = [ ("Beam", Beam()), ("Goddard", Goddard()), + ("Quadrotor", Quadrotor()), ] # ---------------------------------------------------------------- From f840becb978093be8173b8e1c661a14e979b1611 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Caillau Date: Tue, 10 Mar 2026 09:13:03 +0100 Subject: [PATCH 4/6] added quadrotor --- test/problems/quadrotor.jl | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 test/problems/quadrotor.jl diff --git a/test/problems/quadrotor.jl b/test/problems/quadrotor.jl new file mode 100644 index 00000000..b9379844 --- /dev/null +++ b/test/problems/quadrotor.jl @@ -0,0 +1,54 @@ +# Quadrotor optimal control problem definition used by tests and examples. +# +# Returns a NamedTuple with fields: +# - ocp :: the CTParser-defined optimal control problem +# - obj :: reference optimal objective value (Ipopt / MadNLP, Collocation) +# - name :: a short problem name +# - init :: NamedTuple of components for CTSolvers.initial_guess +function Quadrotor(; T=1, g=9.8, r=0.1) + + ocp = @def begin + t ∈ [0, T], time + x ∈ R⁹, state + u ∈ R⁴, control + + x(0) == zeros(9) + + ∂(x₁)(t) == x₂(t) + ∂(x₂)(t) == + u₁(t) * cos(x₇(t)) * sin(x₈(t)) * cos(x₉(t)) + u₁(t) * sin(x₇(t)) * sin(x₉(t)) + ∂(x₃)(t) == x₄(t) + ∂(x₄)(t) == + u₁(t) * cos(x₇(t)) * sin(x₈(t)) * sin(x₉(t)) - u₁(t) * sin(x₇(t)) * cos(x₉(t)) + ∂(x₅)(t) == x₆(t) + ∂(x₆)(t) == u₁(t) * cos(x₇(t)) * cos(x₈(t)) - g + ∂(x₇)(t) == u₂(t) * cos(x₇(t)) / cos(x₈(t)) + u₃(t) * sin(x₇(t)) / cos(x₈(t)) + ∂(x₈)(t) == -u₂(t) * sin(x₇(t)) + u₃(t) * cos(x₇(t)) + ∂(x₉)(t) == + u₂(t) * cos(x₇(t)) * tan(x₈(t)) + u₃(t) * sin(x₇(t)) * tan(x₈(t)) + u₄(t) + + dt1 = sin(2π * t / T) + df1 = 0 + dt3 = 2sin(4π * t / T) + df3 = 0 + dt5 = 2t / T + df5 = 2 + + 0.5∫( + (x₁(t) - dt1)^2 + + (x₃(t) - dt3)^2 + + (x₅(t) - dt5)^2 + + x₇(t)^2 + + x₈(t)^2 + + x₉(t)^2 + + r * (u₁(t)^2 + u₂(t)^2 + u₃(t)^2 + u₄(t)^2), + ) → min + end + + init = @init ocp begin + x(t) := 0.1 * ones(9) + u(t) := 0.1 * ones(4) + end + + return (ocp=ocp, obj=4.2679623758, name="quadrotor", init=init) +end From 771f1e73c73093a72437526c681d0f386a07abec Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Caillau Date: Tue, 10 Mar 2026 10:09:22 +0100 Subject: [PATCH 5/6] added transfer (not ready to be tested) --- test/problems/TestProblems.jl | 3 +- test/problems/transfer.jl | 103 +++++++++++++++++++++++++++++ test/suite/solve/test_canonical.jl | 1 + 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 test/problems/transfer.jl diff --git a/test/problems/TestProblems.jl b/test/problems/TestProblems.jl index e9dba448..694bcb91 100644 --- a/test/problems/TestProblems.jl +++ b/test/problems/TestProblems.jl @@ -5,7 +5,8 @@ module TestProblems include("beam.jl") include("goddard.jl") include("quadrotor.jl") + include("transfer.jl") - export Beam, Goddard, Quadrotor + export Beam, Goddard, Quadrotor, Transfer end diff --git a/test/problems/transfer.jl b/test/problems/transfer.jl new file mode 100644 index 00000000..275b1896 --- /dev/null +++ b/test/problems/transfer.jl @@ -0,0 +1,103 @@ +# Transfer optimal control problem definition used by tests and examples. +# +# Returns a NamedTuple with fields: +# - ocp :: the CTParser-defined optimal control problem +# - obj :: reference optimal objective value (Ipopt / MadNLP, Collocation) +# - name :: a short problem name +# - init :: NamedTuple of components for CTSolvers.initial_guess + +asqrt(x; ε=1e-9) = sqrt(sqrt(x^2+ε^2)) # Avoid issues with AD + +function F0(x) + P, ex, ey, hx, hy, L = x + pdm = asqrt(P / μ) + cl = cos(L) + sl = sin(L) + w = 1 + ex * cl + ey * sl + F = zeros(eltype(x), 6) # Use eltype to allow overloading for AD + F[6] = w^2 / (P * pdm) + return F +end + +function F1(x) + P, ex, ey, hx, hy, L = x + pdm = asqrt(P / μ) + cl = cos(L) + sl = sin(L) + F = zeros(eltype(x), 6) + F[2] = pdm * sl + F[3] = pdm * (-cl) + return F +end + +function F2(x) + P, ex, ey, hx, hy, L = x + pdm = asqrt(P / μ) + cl = cos(L) + sl = sin(L) + w = 1 + ex * cl + ey * sl + F = zeros(eltype(x), 6) + F[1] = pdm * 2 * P / w + F[2] = pdm * (cl + (ex + cl) / w) + F[3] = pdm * (sl + (ey + sl) / w) + return F +end + +function F3(x) + P, ex, ey, hx, hy, L = x + pdm = asqrt(P / μ) + cl = cos(L) + sl = sin(L) + w = 1 + ex * cl + ey * sl + pdmw = pdm / w + zz = hx * sl - hy * cl + uh = (1 + hx^2 + hy^2) / 2 + F = zeros(eltype(x), 6) + F[2] = pdmw * (-zz * ey) + F[3] = pdmw * zz * ex + F[4] = pdmw * uh * cl + F[5] = pdmw * uh * sl + F[6] = pdmw * zz + return F +end + +function Transfer(; Tmax=60) + + cTmax = 3600^2 / 1e6; T = Tmax * cTmax # Conversion from Newtons to kg x Mm / h² + mass0 = 1500 # Initial mass of the spacecraft + β = 1.42e-02 # Engine specific impulsion + μ = 5165.8620912 # Earth gravitation constant + P0 = 11.625 # Initial semilatus rectum + ex0, ey0 = 0.75, 0 # Initial eccentricity + hx0, hy0 = 6.12e-2, 0 # Initial ascending node and inclination + L0 = π # Initial longitude + Pf = 42.165 # Final semilatus rectum + exf, eyf = 0, 0 # Final eccentricity + hxf, hyf = 0, 0 # Final ascending node and inclination + + Lf = 3π # Estimation of final longitude + x0 = [P0, ex0, ey0, hx0, hy0, L0] # Initial state + xf = [Pf, exf, eyf, hxf, hyf, Lf] # Final state + + ocp = @def begin + tf ∈ R, variable + t ∈ [0, tf], time + x = (P, ex, ey, hx, hy, L) ∈ R⁶, state + u ∈ R³, control + x(0) == x0 + x[1:5](tf) == xf[1:5] + mass = mass0 - β * T * t + ẋ(t) == F0(x(t)) + T / mass * (u₁(t) * F1(x(t)) + u₂(t) * F2(x(t)) + u₃(t) * F3(x(t))) + u₁(t)^2 + u₂(t)^2 + u₃(t)^2 ≤ 1 + tf → min + end + + init = @init ocp begin + tf_i = 15 + x(t) := x0 + (xf - x0) * t / tf_i # Linear interpolation + u(t) := [0.1, 0.5, 0.] # Initial guess for the control + tf := tf_i # Initial guess for final time + end + + return (ocp=ocp, obj=14.79643132, name="transfer", init=init) +end diff --git a/test/suite/solve/test_canonical.jl b/test/suite/solve/test_canonical.jl index 18417321..a5a2c602 100644 --- a/test/suite/solve/test_canonical.jl +++ b/test/suite/solve/test_canonical.jl @@ -68,6 +68,7 @@ function test_canonical() ] problems = [ + #("Transfer", Transfer()), # debug: add later (currently issue with :exa) ("Beam", Beam()), ("Goddard", Goddard()), ("Quadrotor", Quadrotor()), From e0c98c233f2ddf2be65ecfdd09dabe1ff55becb5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Caillau Date: Wed, 11 Mar 2026 01:16:59 +0100 Subject: [PATCH 6/6] rewritten transfer (no more zeros(..., eltype(x))) --- test/problems/transfer.jl | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/test/problems/transfer.jl b/test/problems/transfer.jl index 275b1896..072b7339 100644 --- a/test/problems/transfer.jl +++ b/test/problems/transfer.jl @@ -8,14 +8,15 @@ asqrt(x; ε=1e-9) = sqrt(sqrt(x^2+ε^2)) # Avoid issues with AD +const μ = 5165.8620912 # Earth gravitation constant + function F0(x) P, ex, ey, hx, hy, L = x pdm = asqrt(P / μ) cl = cos(L) sl = sin(L) w = 1 + ex * cl + ey * sl - F = zeros(eltype(x), 6) # Use eltype to allow overloading for AD - F[6] = w^2 / (P * pdm) + F = [0, 0, 0, 0, 0, w^2 / (P * pdm)] return F end @@ -24,9 +25,7 @@ function F1(x) pdm = asqrt(P / μ) cl = cos(L) sl = sin(L) - F = zeros(eltype(x), 6) - F[2] = pdm * sl - F[3] = pdm * (-cl) + F = pdm * [0, sl, -cl, 0, 0, 0] return F end @@ -36,10 +35,7 @@ function F2(x) cl = cos(L) sl = sin(L) w = 1 + ex * cl + ey * sl - F = zeros(eltype(x), 6) - F[1] = pdm * 2 * P / w - F[2] = pdm * (cl + (ex + cl) / w) - F[3] = pdm * (sl + (ey + sl) / w) + F = pdm * [2 * P / w, cl + (ex + cl) / w, sl + (ey + sl) / w, 0, 0, 0] return F end @@ -52,12 +48,7 @@ function F3(x) pdmw = pdm / w zz = hx * sl - hy * cl uh = (1 + hx^2 + hy^2) / 2 - F = zeros(eltype(x), 6) - F[2] = pdmw * (-zz * ey) - F[3] = pdmw * zz * ex - F[4] = pdmw * uh * cl - F[5] = pdmw * uh * sl - F[6] = pdmw * zz + F = pdmw * [0, -zz * ey, zz * ex, uh * cl, uh * sl, zz] return F end @@ -66,7 +57,6 @@ function Transfer(; Tmax=60) cTmax = 3600^2 / 1e6; T = Tmax * cTmax # Conversion from Newtons to kg x Mm / h² mass0 = 1500 # Initial mass of the spacecraft β = 1.42e-02 # Engine specific impulsion - μ = 5165.8620912 # Earth gravitation constant P0 = 11.625 # Initial semilatus rectum ex0, ey0 = 0.75, 0 # Initial eccentricity hx0, hy0 = 6.12e-2, 0 # Initial ascending node and inclination @@ -74,7 +64,6 @@ function Transfer(; Tmax=60) Pf = 42.165 # Final semilatus rectum exf, eyf = 0, 0 # Final eccentricity hxf, hyf = 0, 0 # Final ascending node and inclination - Lf = 3π # Estimation of final longitude x0 = [P0, ex0, ey0, hx0, hy0, L0] # Initial state xf = [Pf, exf, eyf, hxf, hyf, Lf] # Final state