Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Goddard 3D does not compile #369

Open
horasio opened this issue Oct 1, 2024 · 7 comments
Open

[Bug] Goddard 3D does not compile #369

horasio opened this issue Oct 1, 2024 · 7 comments
Assignees
Labels
bug Something isn't working

Comments

@horasio
Copy link

horasio commented Oct 1, 2024

Dear CT community,

Describe the bug
If the F0() function for the dynamics just returns a vector of zeros, then it compiles.
But if I copy the velocity state into the time-deriv (x[4] in the line below marked as "BUG HERE") then the code does not compile, apparently in some automatic differentiation part.
To Reproduce
Execute the following:

module Goddard3D

using OptimalControl
#using NLPModelsIpopt
#using OrdinaryDiffEq  # to get the Flow function from OptimalControl
#using NonlinearSolve  # interface to NLE solvers
#using MINPACK         # NLE solver: use to solve the shooting equation
using Plots           # to plot the solution

t0 = 0      # initial time
r0 = [0.999949994, 0.0001, 0.01]      # initial altitude
v0 = [0,0,0]      # initial speed, v = 1 ==> 7910 m/s (orbital velocity at sea level)
m0 = 1      # initial mass
x0 = [ r0..., v0..., m0 ]
rf = [1.01,0,0] # r[1] = 1 ==> 6378 km

@def ocp begin # definition of the optimal control problem
    tf  R, variable
    t  [ t0, tf ], time
    x  R⁷, state
    u  R³, control

    r = x[1:3]
    v = x[4:6]
    m = x[7]

    x(t0) == x0
    r(tf) == rf,         (1)
    u(t)' * u(t)  1.0
    r(t)' * r(t)  1.0
    #0 ≤ v(t) ≤ vmax

    (t) == F0(x(t)) + F1(x(t), u(t))

    m(tf)  max

end;

# Dynamics
const Cd = 310
const Tmax = 3.5
const β = 500
const b = 7

F0(x) = begin
    return [x[4],0,0,0,0,0,0] ###  <---- ************** BUG HERE
    #r = x[1:3]
    #v = x[4:6]
    #m = x[7]
    #rnorm2 = r' * r
    #rnorm = sqrt(rnorm2)
    #rnormalized = (1/rnorm) * r
    #vnorm = sqrt(v' * v)
    #D = ( Cd * vnorm * exp(-β*(rnorm - 1)) ) * v # Drag force
    #return [v..., (-D/m - (1/rnorm2)*rnormalized)..., 0 ]
end

F1(x,u) = begin
    m = x[7]
    unorm = sqrt(u' * u)
    t = (Tmax / m) * u
    return [ 0,0,0, t..., -b*Tmax*unorm ]
end

# DIRECT METHOD

function run_direct()
    direct_sol = solve(ocp, grid_size=100)
    plt = plot(direct_sol, size=(900, 900))
end

# INDIRECT SHOOTING METHOD

end # module

Goddard3D.run_direct()

Expected behavior
Should compile

Julia 1.10.5 on MacOS Ventura 13.7 up-to-date packages

@horasio horasio added the bug Something isn't working label Oct 1, 2024
@ocots
Copy link
Member

ocots commented Oct 1, 2024

Actually, even for this simple problem, there is a bug:

using OptimalControl
using NLPModelsIpopt

ocp = @def begin
    t  [0, 1], time
    x  R², state
    u  R, control
    x(0) == [ -1, 0 ]
    x(1) == [ 0, 0 ]
    (t) == [ x₂(t), 0 ] + [ 0, u(t) ] # this compiles: ẋ(t) == [ x₂(t), u(t) ]
    ( 0.5u(t)^2 )  min
end

sol = solve(ocp)

We get the error:

ERROR: LoadError: Cannot determine ordering of Dual tags ForwardDiff.Tag{ReverseDiff.var"#130#133"{typeof(+), ForwardDiff.Dual{ForwardDiff.Tag{ADNLPModels.var"#ψ#74"{CTDirect.var"#34#36"{CTDirect.DOCP}}, Float64}, Float64, 1}}, ForwardDiff.Dual{ForwardDiff.Tag{ADNLPModels.var"#ψ#74"{CTDirect.var"#34#36"{CTDirect.DOCP}}, Float64}, Float64, 1}} and ForwardDiff.Tag{ADNLPModels.var"#ψ#74"{CTDirect.var"#34#36"{CTDirect.DOCP}}, Float64}

This compiles:

(t) == [ x₂(t), u(t) ]
# or 
(t) == [ x₂(t), 0 ] + [ x₂(t), u(t) ]
# or
(t) == [ x₂(t), 0 ] + [ x₁(t), u(t) ]
# or 
(t) == [ x₂(t), 0 ] + u(t) * [ 0, 1 ]

@ocots
Copy link
Member

ocots commented Oct 1, 2024

This for instance compiles but there is still an issue:

module Goddard3D

using OptimalControl
using NLPModelsIpopt
#using OrdinaryDiffEq  # to get the Flow function from OptimalControl
#using NonlinearSolve  # interface to NLE solvers
#using MINPACK         # NLE solver: use to solve the shooting equation
using Plots           # to plot the solution

t0 = 0                              # initial time
r0 = [0.999949994, 0.0001, 0.01]    # initial altitude
v0 = [0, 0, 0]                      # initial speed, v = 1 ==> 7910 m/s (orbital velocity at sea level)
m0 = 1                              # initial mass
x0 = [r0..., v0..., m0]
rf = [1.01, 0, 0]                   # r[1] = 1 ==> 6378 km

ocp = @def begin # definition of the optimal control problem

    tf  R, variable
    t  [ t0, tf ], time
    x  R⁷, state
    u  R³, control

    r = x[1:3]
    v = x[4:6]
    m = x[7]

    x(t0) == x0
    r(tf) == rf

    u(t)' * u(t)  1
    r(t)' * r(t)  1

    (t) == F(x(t), u(t))

    m(tf)  max

end;

# Dynamics
const Cd = 310
const Tmax = 3.5
const β = 500
const b = 7

mysqrt(x) = sqrt(x + 1e-6)

F(x, u) = begin

    r = x[1:3]
    v = x[4:6]
    m = x[7]

    rnorm2 = r' * r
    rnorm = mysqrt(rnorm2)
    rnormalized = (1/rnorm) * r
    vnorm = mysqrt(v' * v)
    D = ( Cd * vnorm * exp(-β*(rnorm - 1)) ) * v # Drag force= v
    v̇ = -D/m - (1/rnorm2)*rnormalized + (Tmax / m) * u
    ṁ = -b*Tmax*mysqrt(u' * u)

    return [ṙ..., v̇..., ṁ]

end    

# DIRECT METHOD

function run_direct()
    direct_sol = solve(ocp; grid_size=100)
    plt = plot(direct_sol, size=(900, 900))
end

# INDIRECT SHOOTING METHOD

end # module

Goddard3D.run_direct()

returns

julia> include("goddard.jl");
WARNING: replacing module Goddard3D.
This is Ipopt version 3.14.16, running with linear solver MUMPS 5.7.3.

Number of nonzeros in equality constraint Jacobian...:     7510
Number of nonzeros in inequality constraint Jacobian.:      606
Number of nonzeros in Lagrangian Hessian.............:     4747

Total number of variables............................:     1011
                     variables with only lower bounds:        0
                variables with lower and upper bounds:        0
                     variables with only upper bounds:        0
Total number of equality constraints.................:      710
Total number of inequality constraints...............:      202
        inequality constraints with only lower bounds:      101
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:      101

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0 -1.0000000e-01 7.98e+137 1.00e+00   0.0 0.00e+00    -  0.00e+00 0.00e+00   0
MUMPS returned INFO(1) = -9 and requires more memory, reallocating.  Attempt 1
  Increasing icntl[13] from 1000 to 2000.
MUMPS returned INFO(1) = -9 and requires more memory, reallocating.  Attempt 2
  Increasing icntl[13] from 2000 to 4000.
MUMPS returned INFO(1) = -9 and requires more memory, reallocating.  Attempt 3
  Increasing icntl[13] from 4000 to 8000.
WARNING: Problem in step computation; switching to emergency mode.
   1r-1.0000000e-01 7.98e+137 9.99e+02 129.9 0.00e+00  20.0 0.00e+00 0.00e+00R  1
Restoration phase failed with unexpected solverreturn status 9

Number of Iterations....: 1

                                   (scaled)                 (unscaled)
Objective...............:  -1.0000000000000001e-01   -1.0000000000000001e-01
Dual infeasibility......:   1.0000000000000000e+00    1.0000000000000000e+00
Constraint violation....:  7.9803725102132858e+129   7.9803725102132855e+137
Variable bound violation:   0.0000000000000000e+00    0.0000000000000000e+00
Complementarity.........:   9.7000000999999991e-01    9.7000000999999991e-01
Overall NLP error.......:  7.9803725102132858e+129   7.9803725102132855e+137


Number of objective function evaluations             = 2
Number of objective gradient evaluations             = 2
Number of equality constraint evaluations            = 2
Number of inequality constraint evaluations          = 2
Number of equality constraint Jacobian evaluations   = 2
Number of inequality constraint Jacobian evaluations = 2
Number of Lagrangian Hessian evaluations             = 1
Total seconds in IPOPT                               = 1.988

EXIT: Restoration Failed!

@ocots
Copy link
Member

ocots commented Oct 1, 2024

@jbcaillau, @PierreMartinon Any idea?

@jbcaillau
Copy link
Member

@jbcaillau, @PierreMartinon Any idea?

Hi, thanks @horasio for the feedback and @ocots for the tests. Two things (at least 🤞🏾):

  • there is a known issue with constants and (seemingly) AD, see Pb with zero in dyn rhs? CTDirect.jl#190 : we have not taken the time to delve into it but it is a good occasion (though there are simple workarounds, it is not acceptable)
  • there seems to be a specific issue with this example (0 iteration, you must be kidding me 🥲)

@horasio norm (unsquared) is not differentiable, so it is always advisable to smooth it

@ocots I see, however, that changing to mysqrt is not enough; to be continued. have you tried to change the linear solver, replacing MUMPS by, e.g., MA27/57?

@PierreMartinon
Copy link
Member

Hi everyone @horasio @jbcaillau @ocots !
I see JB already answered concerning the AD problem.
As for the restoration error, I strongly suspect this is due to the (lack of) initial guess in the solve call: default initial guess is constant 0.1, however for this problem this is not good since the altitude should always be greater than 1. Can you try

direct_sol = solve(ocp; grid_size=100, init=(state=[1.01,0,0,0,0,0,1],))

@horasio
Copy link
Author

horasio commented Oct 15, 2024

Thank you everyone,
Using the smoothed sqrt as suggested by @ocots I get the same EXIT: Restoration Failed!.
By adding an init=(state=x0,)), as suggested by @PierreMartinon , Ipopt progresses: 13 iterations and EXIT: Converged to a point of local infeasibility. Problem may be infeasible.
Is there a way, in init=... to provide a more precise warm start ?
In my code for the Goddard problem without control-toolbox (JuMP+Ipopt) I do not use smoothed sqrt and still do not have divide-by-zero errors and I think it's thanks to the warm start, that moved the trajectory away from zero at all times.

@PierreMartinon
Copy link
Member

Thank you everyone, Using the smoothed sqrt as suggested by @ocots I get the same EXIT: Restoration Failed!. By adding an init=(state=x0,)), as suggested by @PierreMartinon , Ipopt progresses: 13 iterations and EXIT: Converged to a point of local infeasibility. Problem may be infeasible. Is there a way, in init=... to provide a more precise warm start ? In my code for the Goddard problem without control-toolbox (JuMP+Ipopt) I do not use smoothed sqrt and still do not have divide-by-zero errors and I think it's thanks to the warm start, that moved the trajectory away from zero at all times.

Hi @horasio, yes, you can find more details on the initial guess here:
https://control-toolbox.org/OptimalControl.jl/stable/tutorial-initial-guess.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants