# [makes] collocation_integrator.mat# Simulates a system using Gauss-Legendre collocation schemes of order 2, 4 and 6# Joel Andersson, UW Madison 2017importnumpyasnpimportcasadifromnumpy.polynomial.polynomialimportpolyvalas_polyval,polyder,polyintfromscipy.ioimportsavematdef_polymul(a,b):"""Convolution of two coefficient sequences (like MATLAB conv)."""returnnp.convolve(a,b)def_eval(coeff,x):"""Evaluate polynomial with MATLAB's highest-degree-first coefficient order."""# Convert from highest-degree-first (MATLAB) to lowest-degree-first (numpy).return_polyval(x,coeff[::-1])# Declare model variablesx1=casadi.SX.sym('x1')x2=casadi.SX.sym('x2')u=casadi.SX.sym('u')# Model equationsx1_dot=(1-x2**2)*x1-x2+ux2_dot=x1# Objective function (integral term)quad=x1**2+x2**2+u**2# Dimensionsnx=2np_=1# Test valuex_test=np.array([0.0,1.0])u_test=0.5# End timeT=10.0# Number of integrator stepsN=20# Time stepdt=T/N# Time grid for plottingtgrid=np.linspace(0,T,N+1)# Continuous-time dynamicsf=casadi.Function('f',[casadi.vertcat(x1,x2),u],[casadi.vertcat(x1_dot,x2_dot),quad],['x','p'],['ode','quad'])results={}# keyed by d# Simulate with collocation, order 1, 2 and 3fordin[1,2,3]:ifd==1:tau_root=np.array([0.0,0.5])elifd==2:tau_root=np.array([0.0,0.5-np.sqrt(3)/6,0.5+np.sqrt(3)/6])elifd==3:tau_root=np.array([0.0,0.5-np.sqrt(15)/10,0.5,0.5+np.sqrt(15)/10])else:raiseValueError('order not supported')# Degree of interpolating polynomiald=len(tau_root)-1C=np.zeros((d+1,d+1))D=np.zeros(d+1)B=np.zeros(d+1)# Construct polynomial basis (Lagrange, highest-degree-first coefficients)forjinrange(d+1):coeff=np.array([1.0])forrinrange(d+1):ifr!=j:coeff=_polymul(coeff,np.array([1.0,-tau_root[r]]))coeff=coeff/(tau_root[j]-tau_root[r])D[j]=_eval(coeff,1.0)pder=np.polyder(coeff)# numpy handles highest-degree-firstforrinrange(d+1):C[j,r]=np.polyval(pder,tau_root[r])pint=np.polyint(coeff)B[j]=np.polyval(pint,1.0)# Start with an empty nonlinear system of equationsw=[]w0=[]rhs=[]# x0, p are parameters of the Newton solverX0=casadi.MX.sym('X0',nx)P=casadi.MX.sym('P',np_)# State vector at collocation points are unknownsXj=[]forjinrange(d):Xj.append(casadi.MX.sym('X_{}'.format(j+1),nx))w.append(Xj[-1])w0.append(np.zeros(nx))# State derivatives at collocation pointsXj_dot=[]forjinrange(d):d_expr=C[0,j+1]*X0forrinrange(d):d_expr=d_expr+C[r+1,j+1]*Xj[r]Xj_dot.append(d_expr)# Evaluate f at collocation pointsode_j=[]quad_j=[]forjinrange(d):oj,qj=f(Xj[j],P)ode_j.append(oj)quad_j.append(qj)# Collocation residualsforjinrange(d):rhs.append(dt*ode_j[j]-Xj_dot[j])# State at the end of the intervalXf=D[0]*X0forjinrange(d):Xf=Xf+D[j+1]*Xj[j]# QuadratureQf=0forjinrange(d):Qf=Qf+B[j+1]*quad_j[j]*dtw_vec=casadi.vertcat(*w)w0_vec=casadi.vertcat(*w0)rhs_vec=casadi.vertcat(*rhs)rfp=casadi.Function('rfp',[w_vec,X0,P],[rhs_vec,Xf,Qf],['w0','x0','p'],['w','xf','qf'])solver=casadi.rootfinder('solver','newton',rfp)# Simulatex_sim=x_test.reshape(nx,1)q_sim=np.array([[0.0]])w_sim=np.asarray(w0_vec).flatten()forkinrange(N):sol=solver(x0=x_sim[:,-1],p=u_test,w0=w_sim)x_sim=np.hstack((x_sim,np.asarray(sol['xf'])))q_sim=np.hstack((q_sim,q_sim[:,-1:]+float(sol['qf'])))w_sim=np.asarray(sol['w']).flatten()results[d]=(x_sim,q_sim)x_gl2,q_gl2=results[1]x_gl4,q_gl4=results[2]x_gl6,q_gl6=results[3]# Compare with CVODES with high accuracyprob=dict(x=casadi.vertcat(x1,x2),p=u,ode=casadi.vertcat(x1_dot,x2_dot),quad=quad)opts=dict(grid=tgrid.tolist(),output_t0=True,reltol=1e-13,abstol=1e-13)integ=casadi.integrator('integ','cvodes',prob,opts)sol=integ(x0=x_test,p=u_test)x_cvodes=np.array(sol['xf'])q_cvodes=np.array(sol['qf'])savemat('collocation_integrator.mat',{'tgrid':tgrid,'x_gl2':x_gl2,'x_gl4':x_gl4,'x_gl6':x_gl6,'x_cvodes':x_cvodes,'q_gl2':q_gl2,'q_gl4':q_gl4,'q_gl6':q_gl6,'q_cvodes':q_cvodes})