├── .gitattributes ├── .gitignore ├── GS_Iter.m ├── Jacobi_Iter.m ├── Multigrid1D_Vcycle_GenMat.m ├── Multigrid2D_Vcycle_GenMat.m ├── Multigrid3D_Vcycle_GenMat.m ├── Multigrid_Solver.m ├── Multigrid_Vcycle.m ├── Poisson1D_3pt_GenMat.m ├── Poisson2D_5pt_GenMat.m ├── Poisson3D_7pt_GenMat.m ├── README.md ├── images ├── 3DStencil.png ├── CG.png ├── CoefMatrix.png ├── MG_VWF.png └── Multigrid.png ├── test_Poisson1D.m ├── test_Poisson2D.m └── test_Poisson3D.m /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /GS_Iter.m: -------------------------------------------------------------------------------- 1 | function [x, iter_cnt] = GS_Iter(A, b, iter_eps, max_iter, x0) 2 | if size(A, 1) ~= size(A, 2) 3 | x = -1; 4 | iter_cnt = 0; 5 | return; 6 | end 7 | 8 | n = size(A, 1); 9 | iter_cnt = 0; 10 | err = 1; 11 | 12 | if (nargin < 3) iter_eps = 1e-10; end 13 | if (nargin < 4) max_iter = 1000; end 14 | if (nargin < 5) 15 | x = zeros(n, 1) + iter_eps; 16 | else 17 | x = x0; 18 | end 19 | 20 | while ((err >= iter_eps) && (iter_cnt < max_iter)) 21 | x0 = x; 22 | 23 | for i = 1 : n 24 | A_ii = A(i, i); 25 | x(i) = b(i) - A(i, :) * x + A_ii * x(i); 26 | x(i) = x(i) / A_ii; 27 | end 28 | 29 | iter_cnt = iter_cnt + 1; 30 | err = max(abs(x0 - x)); 31 | end 32 | end -------------------------------------------------------------------------------- /Jacobi_Iter.m: -------------------------------------------------------------------------------- 1 | function [x, iter_cnt] = Jacobi_Iter(A, b, iter_eps, max_iter, x0) 2 | if size(A, 1) ~= size(A, 2) 3 | x = -1; 4 | iter_cnt = 0; 5 | return; 6 | end 7 | 8 | n = size(A, 1); 9 | iter_cnt = 0; 10 | 11 | if (nargin < 3) iter_eps = 1e-10; end 12 | if (nargin < 4) max_iter = 1000; end 13 | if (nargin < 5) 14 | x = zeros(n, 1); 15 | else 16 | x = x0; 17 | end 18 | 19 | L = -tril(A, -1); 20 | U = -triu(A, 1); 21 | D = diag(diag(A)); 22 | B = inv(D) * (L + U); 23 | g = inv(D) * b; 24 | 25 | err = 1; 26 | while ((err >= iter_eps) && (iter_cnt < max_iter)) 27 | x0 = x; 28 | x = B * x0 + g; 29 | iter_cnt = iter_cnt + 1; 30 | err = max(abs(x0 - x)); 31 | end 32 | end -------------------------------------------------------------------------------- /Multigrid1D_Vcycle_GenMat.m: -------------------------------------------------------------------------------- 1 | function [A_list, R_list, max_level] = Multigrid1D_Vcycle_GenMat(A, direct_n) 2 | n = size(A, 1); 3 | 4 | A_list = {}; 5 | R_list = {}; 6 | 7 | level = 1; 8 | A_list(level) = {A}; 9 | 10 | while (n > direct_n) 11 | coarse_n = floor((n - 1) / 2); 12 | 13 | % Construct full-weighted restriction operator 14 | R = sparse(coarse_n, n); 15 | for i = 1 : coarse_n 16 | col = 2 * i - 1; 17 | R(i, col) = 0.25; 18 | R(i, col + 1) = 0.5; 19 | R(i, col + 2) = 0.25; 20 | end 21 | 22 | R_list(level) = {R}; 23 | 24 | P = 2 * R'; 25 | A = R * A * P; 26 | 27 | level = level + 1; 28 | A_list(level) = {A}; 29 | n = coarse_n; 30 | end 31 | 32 | max_level = level; 33 | end -------------------------------------------------------------------------------- /Multigrid2D_Vcycle_GenMat.m: -------------------------------------------------------------------------------- 1 | function [A_list, R_list, max_level] = Multigrid2D_Vcycle_GenMat(A, direct_N) 2 | N = size(A, 1); 3 | n = floor(sqrt(N)); 4 | coarse_n = floor((n - 1) / 2); 5 | coarse_N = coarse_n * coarse_n; 6 | 7 | A_list = {}; 8 | R_list = {}; 9 | 10 | level = 1; 11 | A_list(level) = {A}; 12 | 13 | while (N > direct_N) 14 | coarse_n = floor((n - 1) / 2); 15 | coarse_N = coarse_n * coarse_n; 16 | 17 | % Construct restriction operators 18 | R = sparse(coarse_N, N); 19 | k = 0; 20 | for jy = 2 : 2 : n 21 | for ix = 2 : 2 : n 22 | k = k + 1; 23 | fine_grid_k = (jy - 1) * n + ix; 24 | 25 | R(k, fine_grid_k - n - 1) = 0.0625; 26 | R(k, fine_grid_k - n ) = 0.125; 27 | R(k, fine_grid_k - n + 1) = 0.0625; 28 | 29 | R(k, fine_grid_k - 1) = 0.125; 30 | R(k, fine_grid_k ) = 0.25; 31 | R(k, fine_grid_k + 1) = 0.125; 32 | 33 | R(k, fine_grid_k + n - 1) = 0.0625; 34 | R(k, fine_grid_k + n ) = 0.125; 35 | R(k, fine_grid_k + n + 1) = 0.0625; 36 | end 37 | end 38 | 39 | R_list(level) = {R}; 40 | 41 | P = 4 * R'; 42 | A = R * A * P; 43 | 44 | level = level + 1; 45 | A_list(level) = {A}; 46 | n = coarse_n; 47 | N = coarse_N; 48 | end 49 | max_level = level; 50 | end -------------------------------------------------------------------------------- /Multigrid3D_Vcycle_GenMat.m: -------------------------------------------------------------------------------- 1 | function [A_list, R_list, max_level] = Multigrid3D_Vcycle_GenMat(A, direct_N) 2 | N = size(A, 1); 3 | n = round(N^(1 / 3)); 4 | 5 | A_list = {}; 6 | R_list = {}; 7 | 8 | level = 1; 9 | A_list(level) = {A}; 10 | 11 | while (N > direct_N) 12 | coarse_n = floor((n - 1) / 2); 13 | coarse_N = coarse_n * coarse_n * coarse_n; 14 | 15 | % Construct restriction operators 16 | R = sparse(coarse_N, N); 17 | k = 0; 18 | n2 = n * n; 19 | for kz = 2 : 2 : n 20 | for jy = 2 : 2 : n 21 | for ix = 2 : 2 : n 22 | k = k + 1; 23 | fine_grid_k = (kz - 1) * n2 + (jy - 1) * n + ix; 24 | 25 | % Plane z = kz 26 | R(k, fine_grid_k - n - 1) = 1 / 32; 27 | R(k, fine_grid_k - n ) = 1 / 16; 28 | R(k, fine_grid_k - n + 1) = 1 / 32; 29 | 30 | R(k, fine_grid_k - 1) = 1 / 16; 31 | R(k, fine_grid_k ) = 1 / 8; 32 | R(k, fine_grid_k + 1) = 1 / 16; 33 | 34 | R(k, fine_grid_k + n - 1) = 1 / 32; 35 | R(k, fine_grid_k + n ) = 1 / 16; 36 | R(k, fine_grid_k + n + 1) = 1 / 32; 37 | 38 | % Plane z = kz - 1 39 | R(k, fine_grid_k - n2 - n - 1) = 1 / 64; 40 | R(k, fine_grid_k - n2 - n ) = 1 / 32; 41 | R(k, fine_grid_k - n2 - n + 1) = 1 / 64; 42 | 43 | R(k, fine_grid_k - n2 - 1) = 1 / 32; 44 | R(k, fine_grid_k - n2 ) = 1 / 16; 45 | R(k, fine_grid_k - n2 + 1) = 1 / 32; 46 | 47 | R(k, fine_grid_k - n2 + n - 1) = 1 / 64; 48 | R(k, fine_grid_k - n2 + n ) = 1 / 32; 49 | R(k, fine_grid_k - n2 + n + 1) = 1 / 64; 50 | 51 | % Plane z = kz + 1 52 | R(k, fine_grid_k + n2 - n - 1) = 1 / 64; 53 | R(k, fine_grid_k + n2 - n ) = 1 / 32; 54 | R(k, fine_grid_k + n2 - n + 1) = 1 / 64; 55 | 56 | R(k, fine_grid_k + n2 - 1) = 1 / 32; 57 | R(k, fine_grid_k + n2 ) = 1 / 16; 58 | R(k, fine_grid_k + n2 + 1) = 1 / 32; 59 | 60 | R(k, fine_grid_k + n2 + n - 1) = 1 / 64; 61 | R(k, fine_grid_k + n2 + n ) = 1 / 32; 62 | R(k, fine_grid_k + n2 + n + 1) = 1 / 64; 63 | end 64 | end 65 | end 66 | 67 | R_list(level) = {R}; 68 | 69 | P = 8 * R'; 70 | A = R * A * P; 71 | 72 | level = level + 1; 73 | A_list(level) = {A}; 74 | n = coarse_n; 75 | N = coarse_N; 76 | end 77 | max_level = level; 78 | end -------------------------------------------------------------------------------- /Multigrid_Solver.m: -------------------------------------------------------------------------------- 1 | function [x, vcycle_cnt, res_norm] = Multigrid_Solver(A, b, dim, smoother, pre_steps, pos_steps, rn_tol) 2 | % Multigrid solver for A * x = b on n-dimension cude grid 3 | % A : The inital coefficient matrix 4 | % b : The right hand side 5 | % dim : The dimension of the grid (1, 2 or 3) 6 | % smoother : Function handle for a iterative method as a smoother 7 | % pre_steps : Number of iterations in the pre-smoothing 8 | % pos_steps : Number of iterations in the post-smoothing 9 | % rn_tol : The tolerance of the relative residual norm 10 | if (nargin < 4) smoother = @GS_Iter; end 11 | if (nargin < 5) pre_steps = 1; end 12 | if (nargin < 6) pos_steps = 1; end 13 | if (nargin < 7) rn_tol = 1e-10; end 14 | 15 | n = size(A, 1); 16 | x = zeros(n, 1); 17 | rn = norm(b); 18 | vcycle_cnt = 0; 19 | res_norm(1) = rn; 20 | rn_stop = rn * rn_tol; 21 | 22 | if (dim == 1) 23 | PR_coef = 2; 24 | direct_n = 16; 25 | end 26 | if (dim == 2) 27 | PR_coef = 4; 28 | direct_n = 7 * 7; 29 | end 30 | if (dim == 3) 31 | PR_coef = 8; 32 | direct_n = 3 * 3 * 3; 33 | end 34 | 35 | % Generate coefficient matrices and restriction operators of each level at once 36 | tic; 37 | if (dim == 1) 38 | [A_list, R_list, max_level] = Multigrid1D_Vcycle_GenMat(A, direct_n); 39 | end 40 | if (dim == 2) 41 | [A_list, R_list, max_level] = Multigrid2D_Vcycle_GenMat(A, direct_n); 42 | end 43 | if (dim == 3) 44 | [A_list, R_list, max_level] = Multigrid3D_Vcycle_GenMat(A, direct_n); 45 | end 46 | gm_t = toc; 47 | % Repeat V-cycle until converge 48 | tic; 49 | while (rn > rn_stop) 50 | x = Multigrid_Vcycle(1, A_list, R_list, b, x, direct_n, PR_coef, smoother, pre_steps, pos_steps); 51 | r = b - A * x; 52 | rn = norm(r, 2); 53 | vcycle_cnt = vcycle_cnt + 1; 54 | res_norm(vcycle_cnt + 1) = rn; 55 | end 56 | vcyc_t = toc; 57 | 58 | fprintf('Matrices generating wall-time = %f (s)\n', gm_t); 59 | fprintf('V-cycle solver wall-time = %f (s)\n', vcyc_t); 60 | fprintf('Performed V-cycles = %d\n', vcycle_cnt); 61 | fprintf('||b - A * x||_2 = %e\n', rn); 62 | end -------------------------------------------------------------------------------- /Multigrid_Vcycle.m: -------------------------------------------------------------------------------- 1 | function x = Multigrid_Vcycle(level, A_list, R_list, b, x0, direct_n, PR_coef, smoother, pre_steps, pos_steps) 2 | % Multigrid V-clcye 3 | % level : The current level (initial is 1) 4 | % A_list : The array of coefficient matrices on each level 5 | % R_list : The array of restriction operators on each level 6 | % b : The right hand side 7 | % x0 : Initial guess 8 | % direct_n : Threshold for directly solving A_list(level) * x = b 9 | % PR_coef : The coefficient constant between restriction and prolongation 10 | % smoother : Function handle for a iterative method as a smoother 11 | % pre_steps : Number of iterations in the pre-smoothing 12 | % pos_steps : Number of iterations in the post-smoothing 13 | if (nargin < 8) smoother = @GS_Iter; end 14 | if (nargin < 9) pre_steps = 1; end 15 | if (nargin < 10) pos_steps = 1; end 16 | 17 | % Load coefficient matrix 18 | A = cell2mat(A_list(level)); 19 | 20 | % If the problem is small enough, solve it directly 21 | n = size(b, 1); 22 | if (n <= direct_n) 23 | x = A \ b; 24 | return; 25 | end 26 | 27 | % Pre-smoothing 28 | x = smoother(A, b, 1e-14, pre_steps, x0); 29 | 30 | % Load restriction operator and construct interpolation operator 31 | R = cell2mat(R_list(level)); 32 | P = R' * PR_coef; 33 | coarse_n = size(R, 1); 34 | 35 | % Compute residual and transfer to coarse grid 36 | r = b - A * x; 37 | r_H = R * r; 38 | 39 | % Solve coarse grid problem recursively 40 | x0 = zeros(coarse_n, 1); 41 | e_H = Multigrid_Vcycle(level + 1, A_list, R_list, r_H, x0, direct_n, PR_coef, smoother, pre_steps, pos_steps); 42 | 43 | % Transfer error to fine grid and correct 44 | x = x + P * e_H; 45 | 46 | % Post-smoothing 47 | x = smoother(A, b, 1e-14, pos_steps, x); 48 | end -------------------------------------------------------------------------------- /Poisson1D_3pt_GenMat.m: -------------------------------------------------------------------------------- 1 | function A = Laplace1D_GenMat(n) 2 | A = sparse(n, n); 3 | 4 | A(1, 1) = 2.0; 5 | A(1, 2) = -1.0; 6 | for i = 2 : n - 1 7 | A(i, i - 1) = -1.0; 8 | A(i, i) = 2.0; 9 | A(i, i + 1) = -1.0; 10 | end 11 | A(n, n) = 2.0; 12 | A(n, n - 1) = -1.0; 13 | end -------------------------------------------------------------------------------- /Poisson2D_5pt_GenMat.m: -------------------------------------------------------------------------------- 1 | function A = Poisson2D_5pt_GenMat(p) 2 | % Form the coefficient matrix for 2D Poisson equation with 5-point difference 3 | % Each edge has 2^p-1 inner grid points and 2 boundary points 4 | n = 2^p - 1; % Number of inner grid points on an edge 5 | N = n * n; % Number of inner grid points on (0,1)*(0,1) 6 | 7 | A = sparse(N, N); 8 | for k = 1 : N 9 | ix = mod((k - 1), n) + 1; 10 | jy = floor((k - 1) / n) + 1; 11 | 12 | A(k, k) = 4; 13 | if (ix < n) A(k, k + 1) = -1; end 14 | if (ix > 1) A(k, k - 1) = -1; end 15 | if (jy < n) A(k, k + n) = -1; end 16 | if (jy > 1) A(k, k - n) = -1; end 17 | end 18 | end -------------------------------------------------------------------------------- /Poisson3D_7pt_GenMat.m: -------------------------------------------------------------------------------- 1 | function A = Poisson3D_7pt_GenMat(p) 2 | % Form the coefficient matrix for 3D Poisson equation with 7-point difference 3 | % Each edge has 2^p-1 inner grid points and 2 boundary points 4 | n = 2^p - 1; % Number of inner grid points on an edge 5 | N = n * n * n; % Number of inner grid points on (0,1)*(0,1)*(0,1) 6 | n2 = n * n; 7 | 8 | A = sparse(N, N); 9 | for kz = 1 : n 10 | for jy = 1 : n 11 | for ix = 1 : n 12 | k = (kz - 1) * n2 + (jy - 1) * n + ix; 13 | A(k, k) = 6; 14 | if (ix < n) A(k, k + 1) = -1; end 15 | if (ix > 1) A(k, k - 1) = -1; end 16 | if (jy < n) A(k, k + n) = -1; end 17 | if (jy > 1) A(k, k - n) = -1; end 18 | if (kz < n) A(k, k + n2) = -1; end 19 | if (kz > 1) A(k, k - n2) = -1; end 20 | end 21 | end 22 | end 23 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Poisson Equation Solver with Finite Difference Method and Multigrid 2 | 3 | Yet another "byproduct" of my course CSE 6644 / MATH 6644. :) 4 | 5 | Using finite difference method to discrete Poisson equation in 1D, 2D, 3D and use multigrid method to accelerate the solving of the linear system. I use center difference for the second order derivative. The right hand side is set as random numbers. 6 | 7 | Use `test_Poisson{1,2,3}D(p)` to test the solver. `p` indicates that on each the edge of each dimension, there are $2^p-1$ inner grid points and $2$ boundary points, and the rank of the coefficient matrix is $(2^p-1)^d$, where $d \in \{1, 2, 3\}$ is the dimension of the problem. Full weighting restriction/prolongation operators and Galerkin coarse grid $A^H = R A P$, where $R$ is restriction operator and $P = 2^d R^T$ is prolongation operator. 8 | 9 | The `Multigrid_Solver()` will first call `Multigrid{1,2,3}D_Vcycle_GenMat()` to generate the coefficient matrices and restriction operators on each level and store them, then it will call `Multigrid_Vcycle()` to perform V-cycle computation until the relative residual norm is smaller than the given threshold. `Multigrid_Solver()` use Gauss-Seidel method as the default pre- and post-smoother, with 1 pre- and post-smoothing. 10 | 11 | Here is a brief comparison: 12 | 13 | ![CG](images/CG.png) 14 | 15 | ![Multigrid](images/Multigrid.png) -------------------------------------------------------------------------------- /images/3DStencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnigmaHuang/Poisson_FDM_Multigrid/e1b27dd2f5dd1a2028060588ac1b88f677c6190e/images/3DStencil.png -------------------------------------------------------------------------------- /images/CG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnigmaHuang/Poisson_FDM_Multigrid/e1b27dd2f5dd1a2028060588ac1b88f677c6190e/images/CG.png -------------------------------------------------------------------------------- /images/CoefMatrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnigmaHuang/Poisson_FDM_Multigrid/e1b27dd2f5dd1a2028060588ac1b88f677c6190e/images/CoefMatrix.png -------------------------------------------------------------------------------- /images/MG_VWF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnigmaHuang/Poisson_FDM_Multigrid/e1b27dd2f5dd1a2028060588ac1b88f677c6190e/images/MG_VWF.png -------------------------------------------------------------------------------- /images/Multigrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnigmaHuang/Poisson_FDM_Multigrid/e1b27dd2f5dd1a2028060588ac1b88f677c6190e/images/Multigrid.png -------------------------------------------------------------------------------- /test_Poisson1D.m: -------------------------------------------------------------------------------- 1 | function test_Poisson1D(p) 2 | n = 2^p - 1; 3 | rng(n); 4 | fprintf('Using %d initial grid points\n', n); 5 | A = Poisson1D_3pt_GenMat(n); 6 | b = rand(n, 1) - 0.5; 7 | [x, vc_cnt] = Multigrid_Solver(A, b, 1); 8 | end -------------------------------------------------------------------------------- /test_Poisson2D.m: -------------------------------------------------------------------------------- 1 | function test_Poisson2D(p) 2 | n = 2^p - 1; % Number of inner grid points on an edge 3 | N = n * n; % Number of inner grid points on (0,1)*(0,1) 4 | rng(n); 5 | fprintf('Using %d * %d square initial grid\n', n, n); 6 | A = Poisson2D_5pt_GenMat(p); 7 | b = rand(N, 1) - 0.5; 8 | [x, vc_cnt] = Multigrid_Solver(A, b, 2); 9 | end -------------------------------------------------------------------------------- /test_Poisson3D.m: -------------------------------------------------------------------------------- 1 | function test_Poisson3D(p) 2 | n = 2^p - 1; % Number of inner grid points on an edge 3 | N = n * n * n; % Number of inner grid points on (0,1)*(0,1)*(0,1) 4 | rng(n); 5 | fprintf('Using %d * %d * %d initial cube\n', n, n, n); 6 | A = Poisson3D_7pt_GenMat(p); 7 | b = rand(N, 1) - 0.5; 8 | [x, vc_cnt] = Multigrid_Solver(A, b, 3); 9 | end --------------------------------------------------------------------------------