Kenyon Mcmahon
Kenyon Mcmahon

Reputation: 21

My Python nonlinear optimisation not minimising using Gekko library

I have coded a nonlinear orbital thrust and angle optimisation problem which isn't converging. Below is the Python code and below that is a snippet of the log. Basically, each time-step has a thrust and angle control variable which is to be optimised to minimise the fuel-consumption, defined by the sum of the thrust over time. This does not however converge as expected. I have tried using scipy.optimise.minimize but that also seems to be not able to minimise the objective function. Have I set up the problem wrong? Why isn't it able to minimise the objective function? My constraints and bounds seem reasonable.

from gekko import GEKKO
import numpy as np

# Initialize Model
m = GEKKO(remote=True)

m.time = np.linspace(0,140,801)

# State Variables
r = m.Var(value = 1.0)
theta = m.Var(value = 0.0)
vr = m.Var(value = 0.0)
vt = m.Var(value = 1.0)

# Control Variables
thrust = m.MV(value = 0.005, lb = 0.0, ub = 0.01)
thrust.STATUS = 1
angle = m.MV(value = 0.0, lb = -np.pi/2, ub = np.pi/2)
angle.STATUS = 1

# Optimise fuel consumption
fuel_consumption = m.Var(value = 0.0)

# Dynamics
m.Equation(r.dt() == vr)
m.Equation(theta.dt() == vt/r)
m.Equation(vr.dt() == (vt**2)/r - 1/(r**2) + thrust*m.sin(angle))
m.Equation(vt.dt() == -(vr*vt)/r + thrust*m.cos(angle))

m.Equation(fuel_consumption.dt() == thrust)  # Accumulate thrust over time

# Boundary Conditions
m.fix(r, pos=len(m.time)-1,val = 4.0)
m.fix(theta, pos=len(m.time)-1,val = 0.0)
m.fix(vr, pos=len(m.time)-1,val = 0)
m.fix(vt, pos=len(m.time)-1,val = 0.5)

# Objective function
m.Obj(fuel_consumption)

#Set global options
m.options.IMODE = 6  # Dynamic Optimization (Simultaneous ODEs)
m.options.NODES = 3  # Collocation nodes
m.options.SOLVER = 3  # IPOPT solver

#Solve simulation
m.solve(disp=True) # solve on public server

#Results
print('')
print('Results')
print('Objective: ',m.options.OBJFCNVAL)
print('Solution: ', thrust.value)

The log is:

----------------------------------------------------------------
 APMonitor, Version 1.0.1
 APMonitor Optimization Suite
 ----------------------------------------------------------------
 
 
 --------- APM Model Size ------------
 Each time step contains
   Objects      :            0
   Constants    :            0
   Variables    :            7
   Intermediates:            0
   Connections  :            8
   Equations    :            6
   Residuals    :            6
 
 Number of state variables:          25592
 Number of total equations: -        24000
 Number of slack variables: -            0
 ---------------------------------------
 Degrees of freedom       :           1592
 
 **********************************************
 Dynamic Control with Interior Point Solver
 **********************************************
  
  
 Info: Exact Hessian

******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
 Ipopt is released as open source code under the Eclipse Public License (EPL).
         For more information visit http://projects.coin-or.org/Ipopt
******************************************************************************

This is Ipopt version 3.12.10, running with linear solver ma57.

Number of nonzeros in equality constraint Jacobian...:    67966
Number of nonzeros in inequality constraint Jacobian.:     6400
Number of nonzeros in Lagrangian Hessian.............:    11195

Total number of variables............................:    25592
                     variables with only lower bounds:     3200
                variables with lower and upper bounds:     3200
                     variables with only upper bounds:        0
Total number of equality constraints.................:    20800
Total number of inequality constraints...............:     3200
        inequality constraints with only lower bounds:     3200
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        0

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0  1.3103987e-02 3.00e+00 1.00e+00   0.0 0.00e+00    -  0.00e+00 0.00e+00   0
Reallocating memory for MA57: lfact (641403)
   1  3.8902526e+01 2.77e+00 4.18e+00  -1.3 1.47e+02    -  2.34e-01 7.51e-02f  1

......

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
 250r 2.8607519e+02 1.73e+02 9.99e+02   2.2 0.00e+00   3.1 0.00e+00 2.58e-07R  5

Number of Iterations....: 250

                                   (scaled)                 (unscaled)
Objective...............:   2.8607519277584993e+02    2.8607519277584993e+02
Dual infeasibility......:   2.1703502621114161e+00    2.1703502621114161e+00
Constraint violation....:   1.7251865116637478e+02    1.7251865116637478e+02
Complementarity.........:   3.1072980675208779e+00    3.1072980675208779e+00
Overall NLP error.......:   1.7251865116637478e+02    1.7251865116637478e+02


Number of objective function evaluations             = 323
Number of objective gradient evaluations             = 252
Number of equality constraint evaluations            = 323
Number of inequality constraint evaluations          = 323
Number of equality constraint Jacobian evaluations   = 254
Number of inequality constraint Jacobian evaluations = 254
Number of Lagrangian Hessian evaluations             = 250
Total CPU secs in IPOPT (w/o function evaluations)   =     26.390
Total CPU secs in NLP function evaluations           =     45.994

EXIT: Maximum Number of Iterations Exceeded.

I have tried changing the time scale, and using scipy minimize, but can't seem to get it to minimize.

Upvotes: 2

Views: 75

Answers (1)

John Hedengren
John Hedengren

Reputation: 14346

Try solving the problem in two stages with soften boundary conditions:

# Soft Boundary Conditions
p = np.zeros_like(m.time); p[-1]=1
final = m.Param(p)
m.Minimize(final*(r-4)**2)
m.Minimize(final*(theta-0)**2)
m.Minimize(final*(vr-0)**2)
m.Minimize(final*(vt-0.5)**2)

The first optimization is a simulation that converges the equations:

 Number of state variables:          24000
 Number of total equations: -        24000
 Number of slack variables: -            0
 ---------------------------------------
 Degrees of freedom       :              0

The second optimization converges the equations and minimizes the objective with angle.STATUS=1 and thrust.STATUS=1 and the added hard constraints:

 Number of state variables:          25592
 Number of total equations: -        24000
 Number of slack variables: -            0
 ---------------------------------------
 Degrees of freedom       :           1592

This gives a successful solution:

from gekko import GEKKO
import numpy as np

# Initialize Model
m = GEKKO(remote=True)

m.time = np.linspace(0,140,801)

# State Variables
r = m.Var(value = 1.0)
theta = m.Var(value = 0.0)
vr = m.Var(value = 0.0)
vt = m.Var(value = 1.0)

# Control Variables
thrust = m.MV(value = 0.005, lb = 0.0, ub = 0.01)
thrust.STATUS = 0
angle = m.MV(value = 0.0, lb = -np.pi/4, ub = np.pi/4)
angle.STATUS = 0

# Optimise fuel consumption
fuel_consumption = m.Var(value = 0.0)

# Dynamics
m.Equation(r.dt() == vr)
m.Equation(theta.dt() == vt/r)
m.Equation(vr.dt() == (vt**2)/r - 1/(r**2) + thrust*m.sin(angle))
m.Equation(vt.dt() == -(vr*vt)/r + thrust*m.cos(angle))

m.Equation(fuel_consumption.dt() == thrust)  # Accumulate thrust over time

# Soft Boundary Conditions
p = np.zeros_like(m.time); p[-1]=1
final = m.Param(p)
m.Minimize(final*(r-4)**2)
m.Minimize(final*(theta-0)**2)
m.Minimize(final*(vr-0)**2)
m.Minimize(final*(vt-0.5)**2)

# Objective function
m.Obj(fuel_consumption)

#Set global options
m.options.IMODE = 6  # Dynamic Optimization (Simultaneous ODEs)
m.options.NODES = 3  # Collocation nodes
m.options.SOLVER = 3  # IPOPT solver

#Solve simulation
m.solve(disp=True) # solve on public server

thrust.STATUS = 1
angle.STATUS = 1

# Hard Boundary Conditions
m.fix(theta, pos=len(m.time)-1,val = 0.0)
m.fix(vr, pos=len(m.time)-1,val = 0)
m.fix(r, pos=len(m.time)-1,val = 4.0)
m.fix(vt, pos=len(m.time)-1,val = 0.5)

m.solve(disp=True)

#Results
print('')
print('Results')
print('Objective: ',m.options.OBJFCNVAL)
print('Solution: ', thrust.value)

The main idea of solving in two stages is to help the solver find an initial feasible solution before optimizing. Hard terminal constraints can also be infeasible. A recommended approach is to try soft terminal constraints (objective-based, can be violated) in favor of obtaining a solution.

Upvotes: 1

Related Questions