Reputation: 70
I am trying to set up an implicit solver to a pendulum F and dF are defined as:
def func(y,t):
### Simplified the Function to remove friction since it canceled out
x,v = y[:3],y[3:6]
grav = np.array([0., 0., -9.8 ])
lambd = (grav.dot(x)+v.dot(v))/x.dot(x)
return np.concatenate([v, grav - lambd*x] )
def dF_matrix(y):
n=y.size
dF=np.zeros((6,6))
xp=np.array([y[3],y[4],y[5]])[np.newaxis]
mass=1.
F1=0.
F2=0.
F3=-mass*9.8
F=np.array([F1,F2,F3])[np.newaxis]
phix=2.*y[0]
phiy=2.*y[1]
phiz=2.*y[2]
G=np.array([phix,phiy,phiz])[np.newaxis]
H=2.*np.eye(3)
lambd=(mass*np.dot(xp,np.dot(H,xp.T))+np.dot(F,G.T))/np.dot(G,G.T)
dF[0,3]=1
dF[1,4]=1
dF[2,5]=1
dF[3,0]=(y[0]*F1+2*lambd)/mass
dF[3,1]=(y[0]*F2)/mass
dF[3,2]=(y[0]*F3)/mass
dF[3,3]=phix*y[3]
dF[3,4]=phix*y[4]
dF[3,5]=phix*y[5]
dF[4,0]=(y[1]*F1)/mass
dF[4,1]=(y[1]*F2+2*lambd)/mass
dF[4,2]=(y[1]*F3)/mass
dF[4,3]=phiy*y[3]
dF[4,4]=phiy*y[4]
dF[4,5]=phiy*y[5]
dF[5,0]=(y[2]*F1)/mass
dF[5,1]=(y[2]*F2)/mass
dF[5,2]=(y[2]*F3+2*lambd)/mass
dF[5,3]=phiz*y[3]
dF[5,4]=phiz*y[4]
dF[5,5]=phiz*y[5]
return dF
I am trying to set up an implicit solver that uses the function and the dF in order to guess values in order to create a more accurate solver for my pendulum function.
the error that I get is the following
Traceback (most recent call last): line 206, in test_function(backwards_Euler) line 186, in test_function y1 = test_function(func, y0, t) line 166, in backwards_Euler F = np.asarray(y[i] + dt * function(zold, time[i+1])-zold) line 13, in func lambd = (grav.dot(x)+v.dot(v))/x.dot(x)
ValueError: shapes (3,6) and (3,6) not aligned: 6 (dim 1) != 3 (dim 0)
I think it is because one of the calculations I am doing is diverging to infinity but I cannot figure out the issue that might be causing this.
Here is my current backward Euler function:
### Backwards Euler Method
def backwards_Euler(function, y_matrix, time):
y = np.zeros((np.size(time), np.size(y_matrix)))
y[0, :] = y_matrix
dt = time[1] - time[0]
for i in range(len(time-1)):
err = 1
zold = y[i] + dt*function(y[i],time[i]) ## guess with forward euler
I = 0
while err > 10**(-10) and I < 5:
F = y[i] + dt * function(zold, time[i+1])-zold ## Here is where my error occurs
dF = dt*dF_matrix(y[i+1])-1
znew = zold - F/dF
zold = znew
I+=1
y[i+1]=znew
return y
Here is how I am testing the function although currently, I have no output
# initial condition
y0 = np.array([0.0, 1.0, 0.0, 0.8, 0.0, 1.2])
def test_function(test_function):
print(test_function.__name__ + "...")
nt = 2500
time_start = process_time()
# time points
t = np.linspace(0, 25, nt)
# solve ODE
y1 = test_function(func, y0, t)
time_elapsed = (process_time() - time_start)
print('elapsed time', time_elapsed)
# compute residual:
r = y1[:, 0] ** 2 + y1[:, 1] ** 2 + y1[:, 2] ** 2 - 1
rmax1 = np.max(np.abs(r))
print('error', rmax1)
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot3D(y1[:, 0], y1[:, 1], y1[:, 2], 'gray')
plt.show()
test_function(backwards_Euler)
Upvotes: 1
Views: 6483
Reputation: 25992
No, the error is that the matrix shapes in some matrix operations don't match. The immediate cause is that x.dot(x)
works when x
is a simple array, but not for two-dimensional arrays. Then the rules of matrix multiplication have to be strictly obeyed.
However, there is no reason that there is a matrix in place of x
, you do not call the function in a "vectorized" fashion. The true reason is that python is not Matlab, the division in
znew = zold - F/dF
does not do what you think it does. Instead it does elementwise division, repeating the smaller object to fill the shape of the larger, which also happens with the subtraction, so that znew
in the end has the same shape as the matrix dF
, with the observed results in the next ODE function evaluation.
You need to explicitly invoke a linear solver, the standard one in in np.linalg
(there are others there and in scipy.sparse.linalg
using other matrix factorizations) is simply called solve(A,b)
and computes the solution of A*x=b
.
znew = zold - np.linalg.solve(dF, F)
Implicit Euler gives a diverging solution, the length of the pendulum increases rapidly. Applying these methods to the similar implicit trapezoidal method, which is also Adams-Moulton 2nd order, gives the code
def Adams_Moulton_2nd(function, y_init, time):
# solve F(z)=0 with simplified Newton, where
# F = y + 0.5*dt*(f(y,t)+f(z,t+dt)) - z
# is the defect of the implicit trapezium method. Using the derivative
# dF_z = 0.5*dt*df_z(z,t+dt) - I
# the non-linear solver step is solution of the linear system
# dF_z*dz = -F
y = np.zeros((np.size(time), np.size(y_init)))
y[0, :] = y_init
Id = np.eye(len(y_init))
dt = time[1] - time[0]
dt4 = dt**4
for i in range(len(time)-1):
f_0 = function(y[i],time[i])
norm_0 = sum(f_0**2)**0.5 + 1e-6
z = y[i] + dt*f_0 ## guess with forward euler
dF = 0.5*dt*dF_matrix(z, t[i+1]) - Id
for I in range(5):
F = y[i] + 0.5*dt * (f_0+function(z, time[i+1])) - z
dz = - np.linalg.solve(dF,F)
if max(abs(dz)) < norm_0*dt4: break
z += dz
y[i+1]=z
return y### Adams_Moulton_2nd
Upvotes: 1