Consider the initial value problem given below where the behaviour of the system is studied with respect to given set of inputs and initial condition. Lets make it parameteric by adding a scalar parameter to the ode and setting its value to zero.
ODE 1¶
The problem can be solved in 2 ways using Simulate.
eliminate controls from the ode
supply controls evaluated on the grid points
Elimination¶
Firstly, the ode needs to modelled for which we call the
get_symbols()function. The arguments are the number of states, controls, parameters and intervals for the grid. This also has an additional option to model the ode using symbolics of the SX or MX class. MX is usually preferred due to its generality over SX. Theget_symbols()return a dictionary with required symbolics to construct the ode. This dictionary will keep updating as we pass it through each function.Using the symbolics stored in the dictionary, the ode is constructed. The ode will be passed to the
scale_ode()method where, a time domain transformation takes place. In the scaled ode, the initial and final time are simply parameters.Post domain transformation, the numerical solver for integration and its options must be set. Currently, CVODES, rk4, IRK methods are supported. For detailed options list, refer to integrator section of CasADi API. Calling the
integrator()creates an integrator object for the scaled ode.Lastly,
simulate()is called where the initial state, initial and final time, control and parameter if any is passed. The system is integrated and results are stored. This can be accessed using keys ‘x_res’,’u_res’ and ‘t_grid’ if required.
import simulatetraj as a
import casadi as cs
sym_dict=a.get_symbols(n_x=2,n_u=0,n_p=1,N=25)
t=sym_dict['t']
x=sym_dict['x']
u=sym_dict['u']
p=sym_dict['p']
f=cs.vertcat((1-x[1]**2)*x[0]-x[1]+0.2*t-1,x[0])+p
_=a.scale_ode(sym_dict=sym_dict,f=f)
_=a.integrator(sym_dict=sym_dict,options=None)
a.simulate(t0=0,tf=10,x0=cs.DM([0,0]),p=cs.DM(0),sym_dict=sym_dict)
a.plot_sol(sym_dict=sym_dict)
without control elimination¶
The control input is approximated as a piecewise constant function.
import simulatetraj as a
import casadi as cs
sym_dict=a.get_symbols(n_x=2,n_u=1,n_p=1,N=25)
t=sym_dict['t']
x=sym_dict['x']
u=sym_dict['u']
p=sym_dict['p']
f=cs.vertcat((1-x[1]**2)*x[0]-x[1]+u,x[0])+p
_=a.scale_ode(sym_dict=sym_dict,f=f)
_=a.integrator(sym_dict=sym_dict,options=None)
a.simulate(t0=0,tf=10,x0=cs.DM([0,0]),u=cs.linspace(-1,1,25).T,p=cs.DM(0),sym_dict=sym_dict)
a.plot_sol(sym_dict=sym_dict)
ODE 2¶
In this example, the derivative of the square function is integrated.
sym_dict=get_symbols(n_x=1,n_u=0,n_p=0,N=100000)
t=sym_dict['t']
f=2*t
_=scale_ode(sym_dict=sym_dict,f=f)
_=integrator(sym_dict=sym_dict,options=None)
x0=cs.DM([0])
t0,tf=0,10
simulate(t0=t0,tf=tf,x0=x0,sym_dict=sym_dict)
plot_sol(sym_dict=sym_dict)
print('Global error x(N+1) for xdot=2t:',sym_dict['x_res'][:,-1]-100)
Global error x(N+1) for xdot=2t: DM(1.49132e-08)
Lotka voltera/prey-predator model¶
sym_dict=get_symbols(n_x=2,n_u=0,n_p=2,N=1000)
x=sym_dict['x']
p=sym_dict['p']
f=cs.vertcat(x[0]-p[0]*x[0]*x[1],-x[1]+p[1]*x[0]*x[1])
_=scale_ode(sym_dict=sym_dict,f=f)
_=integrator(sym_dict=sym_dict,options=None)
x0=cs.DM([20,20])
t0,tf=0,15
p=cs.DM([0.01,0.02])
simulate(t0=t0,tf=tf,x0=x0,p=p,sym_dict=sym_dict)
plot_sol(sym_dict=sym_dict)
plt.plot(sym_dict['x_res'][0,:].full().flatten(),sym_dict['x_res'][1,:].full().flatten(),'o')
plt.show()