|
3 | 3 | import scipy.sparse.linalg as LA |
4 | 4 | import scipy.sparse as sp |
5 | 5 |
|
| 6 | +# |
| 7 | +# Runge-Kutta IMEX methods of order 1 to 3 |
| 8 | +# |
| 9 | +class rk_imex: |
| 10 | + |
| 11 | + def __init__(self, M_fast, M_slow, order): |
| 12 | + assert np.shape(M_fast)[0]==np.shape(M_fast)[1], "A_fast must be square" |
| 13 | + assert np.shape(M_slow)[0]==np.shape(M_slow)[1], "A_slow must be square" |
| 14 | + assert np.shape(M_fast)[0]==np.shape(M_slow)[0], "A_fast and A_slow must be of the same size" |
| 15 | + |
| 16 | + assert order in [1,2,3,4], "Order must be 1, 2, 3 or 4" |
| 17 | + self.order = order |
| 18 | + |
| 19 | + if self.order==1: |
| 20 | + self.A = np.array([[0,0],[0,1]]) |
| 21 | + self.A_hat = np.array([[0,0],[1,0]]) |
| 22 | + self.b = np.array([0,1]) |
| 23 | + self.b_hat = np.array([1,0]) |
| 24 | + self.nstages = 2 |
| 25 | + |
| 26 | + elif self.order==2: |
| 27 | + self.A = np.array([[0,0],[0,0.5]]) |
| 28 | + self.A_hat = np.array([[0,0],[0.5,0]]) |
| 29 | + self.b = np.array([0,1]) |
| 30 | + self.b_hat = np.array([0,1]) |
| 31 | + self.nstages = 2 |
| 32 | + |
| 33 | + elif self.order==3: |
| 34 | + # parameter from Pareschi and Russo, J. Sci. Comp. 2005 |
| 35 | + alpha = 0.24169426078821 |
| 36 | + beta = 0.06042356519705 |
| 37 | + eta = 0.12915286960590 |
| 38 | + self.A_hat = np.array([ [0,0,0,0], [0,0,0,0], [0,1.0,0,0], [0, 1.0/4.0, 1.0/4.0, 0] ]) |
| 39 | + self.A = np.array([ [alpha, 0, 0, 0], [-alpha, alpha, 0, 0], [0, 1.0-alpha, alpha, 0], [beta, eta, 0.5-beta-eta-alpha, alpha] ]) |
| 40 | + self.b_hat = np.array([0, 1.0/6.0, 1.0/6.0, 2.0/3.0]) |
| 41 | + self.b = self.b_hat |
| 42 | + self.nstages = 4 |
| 43 | + |
| 44 | + elif self.order==4: |
| 45 | + |
| 46 | + self.A_hat = np.array([ [0,0,0,0,0,0], |
| 47 | + [1./2,0,0,0,0,0], |
| 48 | + [13861./62500.,6889./62500.,0,0,0,0], |
| 49 | + [-116923316275./2393684061468.,-2731218467317./15368042101831.,9408046702089./11113171139209.,0,0,0], |
| 50 | + [-451086348788./2902428689909.,-2682348792572./7519795681897.,12662868775082./11960479115383.,3355817975965./11060851509271.,0,0], |
| 51 | + [647845179188./3216320057751.,73281519250./8382639484533.,552539513391./3454668386233.,3354512671639./8306763924573.,4040./17871.,0]]) |
| 52 | + self.A = np.array([[0,0,0,0,0,0], |
| 53 | + [1./4,1./4,0,0,0,0], |
| 54 | + [8611./62500.,-1743./31250.,1./4,0,0,0], |
| 55 | + [5012029./34652500.,-654441./2922500.,174375./388108.,1./4,0,0], |
| 56 | + [15267082809./155376265600.,-71443401./120774400.,730878875./902184768.,2285395./8070912.,1./4,0], |
| 57 | + [82889./524892.,0,15625./83664.,69875./102672.,-2260./8211,1./4]]) |
| 58 | + self.b = np.array([82889./524892.,0,15625./83664.,69875./102672.,-2260./8211,1./4]) |
| 59 | + self.b_hat = np.array([4586570599./29645900160.,0,178811875./945068544.,814220225./1159782912.,-3700637./11593932.,61727./225920.]) |
| 60 | + self.nstages = 6 |
| 61 | + |
| 62 | + self.M_fast = sp.csc_matrix(M_fast) |
| 63 | + self.M_slow = sp.csc_matrix(M_slow) |
| 64 | + self.ndof = np.shape(M_fast)[0] |
| 65 | + |
| 66 | + self.stages = np.zeros((self.nstages, self.ndof), dtype='complex') |
| 67 | + |
| 68 | + def timestep(self, u0, dt): |
| 69 | + |
| 70 | + # Solve for stages |
| 71 | + for i in range(0,self.nstages): |
| 72 | + |
| 73 | + # Construct RHS |
| 74 | + rhs = np.copy(u0) |
| 75 | + for j in range(0,i): |
| 76 | + rhs += dt*self.A_hat[i,j]*(self.f_slow(self.stages[j,:])) + dt*self.A[i,j]*(self.f_fast(self.stages[j,:])) |
| 77 | + |
| 78 | + # Solve for stage i |
| 79 | + if self.A[i,i] == 0: |
| 80 | + # Avoid call to spsolve with identity matrix |
| 81 | + self.stages[i,:] = np.copy(rhs) |
| 82 | + else: |
| 83 | + self.stages[i,:] = self.f_fast_solve( rhs, dt*self.A[i,i] ) |
| 84 | + |
| 85 | + # Update |
| 86 | + for i in range(0,self.nstages): |
| 87 | + u0 += dt*self.b_hat[i]*(self.f_slow(self.stages[i,:])) + dt*self.b[i]*(self.f_fast(self.stages[i,:])) |
| 88 | + |
| 89 | + return u0 |
| 90 | + |
| 91 | + def f_slow(self, u): |
| 92 | + return self.M_slow.dot(u) |
| 93 | + |
| 94 | + def f_fast(self, u): |
| 95 | + return self.M_fast.dot(u) |
| 96 | + |
| 97 | + def f_fast_solve(self, rhs, alpha): |
| 98 | + L = sp.eye(self.ndof) - alpha*self.M_fast |
| 99 | + return LA.spsolve(L, rhs) |
| 100 | + |
6 | 101 | # |
7 | 102 | # Trapezoidal rule |
8 | 103 | # |
|
0 commit comments