Reputation: 21
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
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