|
| 1 | +from scipy.sparse import diags |
| 2 | +import scipy.linalg |
| 3 | +import numpy as np, sys |
| 4 | +#np.set_printoptions(precision=3) |
| 5 | + |
| 6 | +#solve |
| 7 | +#du/dt = D d2u/dx^2 + a du/dx + f(u) |
| 8 | +#forward in time for a length of time T e.g. [0,T] |
| 9 | +#on an interval of length L e.g. [0,L] |
| 10 | +#formulae of http://georg.io/2013/12/Crank_Nicolson_Convection_Diffusion |
| 11 | + |
| 12 | +#Can easily modify the code to make f be a function of x or t too, |
| 13 | +#but would then actually care about their values. |
| 14 | + |
| 15 | +#f should be a vectorized function or just return a constant |
| 16 | +#u is the initial condition u(0,x) |
| 17 | + |
| 18 | +#BC order is the derivative of u which is assumed 0 at the edge |
| 19 | +#0 means Dirichlet (so you just assume there's a fixed 0 beyond |
| 20 | +# the edge, so the edge equation is just like the bulk |
| 21 | +# -i.e. the diagonals are constant |
| 22 | +#1 means Neumann (like georg.io) |
| 23 | +#2 means second deriv is 0 |
| 24 | +# so the imaginary point is like u[-1]-u[0]=u[0]-u[1] or u[-1]=2*u[0]-u[1] |
| 25 | + |
| 26 | +#Or: -1 means Dirichlet where the edges are not fixed at 0 but |
| 27 | +#at whatever the corresponding edge value in the IC is. |
| 28 | + |
| 29 | +def crank_nicholson(u, L, T, n_timesteps, D, a = 0, |
| 30 | + f = lambda u: 0, leftBCOrder=2, rightBCOrder=2): |
| 31 | + nx = np.shape(u)[0] |
| 32 | + dx = L/(nx-1.) |
| 33 | + dt = T/(n_timesteps+0.0) |
| 34 | + sigma = D*dt/(2.*dx*dx) |
| 35 | + rho = a*dt/(4.*dx) |
| 36 | + |
| 37 | + A=np.zeros((3,nx)) |
| 38 | + A[0,:]=-(sigma+rho) |
| 39 | + A[2,:]=rho-sigma |
| 40 | + A[1,:]=1+2*sigma |
| 41 | + A[1,0]=(1,1+2*sigma,1+sigma+rho,1+2*rho)[leftBCOrder+1] |
| 42 | + A[1,-1]=(1,1+2*sigma,1+sigma-rho,1-2*rho)[rightBCOrder+1] |
| 43 | + A[0,1]=(0,-(sigma+rho),-(sigma+rho),-2*rho)[leftBCOrder+1] |
| 44 | + A[2,-2]=(0,rho-sigma,rho-sigma,2*rho)[rightBCOrder+1] |
| 45 | + if 0: |
| 46 | + dense_A=diags([A[2,:-1],A[1,:],A[0,1:]],[-1,0,1]).toarray() |
| 47 | + print (dense_A) |
| 48 | + |
| 49 | + B_dia=(1-2*sigma)*np.ones(nx) |
| 50 | + B_top=(sigma+rho)*np.ones(nx-1) |
| 51 | + B_bot=(sigma-rho)*np.ones(nx-1) |
| 52 | + B_dia[0] =(1,1-2*sigma,1-sigma-rho,1-2*rho)[leftBCOrder+1] |
| 53 | + B_dia[-1]=(1,1-2*sigma,1-sigma+rho,1+2*rho)[rightBCOrder+1] |
| 54 | + B_top[0]=(0,sigma+rho,sigma+rho,2*rho)[leftBCOrder+1] |
| 55 | + B_bot[-1]=(0,sigma-rho,sigma-rho,-2*rho)[rightBCOrder+1] |
| 56 | + #B=diags([sigma-rho,B_dia,sigma+rho],[-1,0,1],shape=(nx,nx)) |
| 57 | + B=diags([B_bot,B_dia,B_top],[-1,0,1],shape=(nx,nx)) |
| 58 | + #print(B.toarray()) |
| 59 | + |
| 60 | + for idx in range(n_timesteps): |
| 61 | + rhs=B.dot(u)+dt*f(u) |
| 62 | + u = scipy.linalg.solve_banded((1,1),A,rhs) |
| 63 | + return u |
| 64 | + |
| 65 | +if __name__=="__main__": |
| 66 | + n=100 |
| 67 | + u=np.concatenate([np.zeros(n//2),np.ones(n//2)]) |
| 68 | + u=np.arange(n)/10. |
| 69 | + u=u[::-1] |
| 70 | + #u=np.ones(n) |
| 71 | + u_source=(np.arange(n)-n//2)/(n/2.) |
| 72 | + u=np.abs(u_source) |
| 73 | + #u=[min(i,0.3) for i in u] |
| 74 | + D=0.01 |
| 75 | + nt=100 |
| 76 | + L=1 |
| 77 | + T=10 |
| 78 | + out=crank_nicholson(u,L,T, nt,D,0,lambda u:0) |
| 79 | + out2=crank_nicholson(u,L,T, nt,D,-0.003) |
| 80 | + #print(out2-out) |
| 81 | + from matplotlib import pyplot as plt |
| 82 | + x=list(range(n)) |
| 83 | + plt.plot(x,out,x,out2) |
| 84 | + plt.show() |
0 commit comments