├── 1DMultigridTutorial ├── ExampleScript.m ├── Prolongate1D.m ├── README.txt ├── Restrict1D.m ├── VisualMuCycle.m ├── VisualMultigrid1D.m └── WeightedJacobi.m ├── 2DMultigrid ├── Build2DVector.m ├── BuildLaplacian2D.m ├── ExampleFMG.m ├── ExampleMucycles.m ├── FullMultiGrid.m ├── MuCycle.m ├── MultiGrid.m ├── Prolongate2D.m ├── README.txt ├── Restrict2D.m ├── WeightedJacobi.m ├── pack_matToVec.m ├── pack_vecToMat.m ├── runFMG.asv └── runFMG.m ├── 3DMultigrid ├── Build3DVector.m ├── BuildLaplacian2D.m ├── Driverv3.m ├── MuCycle.m ├── Prolongate3D.m ├── README.txt ├── Restrict3D.m ├── WeightedJacobi.m └── fd3d.m ├── LICENSE ├── NutshellMultigrid2.pdf ├── README.md └── images ├── VcycleDiagramSuperHighres.png ├── VcycleMedres.png ├── VcycleSmooshres.png ├── error2D.png ├── initialguess3D.png ├── solution2D.png └── solution3D.png /1DMultigridTutorial/ExampleScript.m: -------------------------------------------------------------------------------- 1 | %% 1-D Multigrid Code: Understanding the process 2 | %% Author: David Appelhans, Dec 22, 2011 3 | 4 | % This code was written to better help you understand the multigrid process 5 | % in the simple setting of the 1-D laplace equation with zeros boundary 6 | % conditions, 7 | % -u''(x) = f(x) in [a,b] Differential Equation 8 | % u(a) = 0, u(b) = 0. Boundary Conditions 9 | % 10 | % You can examine the process of the multigrid solve by picking a u(x) that 11 | % satisfies the boundary conditions and setting f(x) as the second 12 | % derivative of the u(x) you have picked. Now you know what the exact 13 | % solution u(x) is and you can see how close the numerical solution comes 14 | % to this analytic solution. 15 | 16 | % This code will be slow because we will generate plots at each step of the 17 | % process. 18 | %% Close all figures and clear all variables: 19 | close all 20 | clear all 21 | %% Define differential equation parameters 22 | 23 | % Specify the domain [a,b]: 24 | a = 0; b = 1.5; 25 | 26 | % Define the RHS function and the known analytic solution: 27 | k = 2; L = b-a; 28 | analyticV = @(x) sin(k*pi*x/L)'; 29 | f = @(x) (k*pi/L)^2*sin(k*pi*x/L)'; 30 | 31 | %% Define discretization parameters 32 | % For this example I will be using the second order central difference 33 | % formula: -u''(x_i) = (-u(x_{i+1}) + 2u(x_i) - u(x_{i-1}))/h^2 + O(h^2). 34 | % 35 | % At the end of the day this will lead to a linear algebra system Av=f 36 | % where A is a matrix, f is a vector containing the values of the f(x) 37 | % evaluated at a discrete grid of points and v is a vector of the 38 | % approximate values of u(x) evaluated at the grid points x_i. 39 | 40 | % The error from the discretization is O(h^2) meaning that our vector of 41 | % function values v_i and the exact solution evaluated on the grid, u(x_i) 42 | % may differ by about h^2. Thus there is no point in solving the 43 | % linear algebra problem Av=f beyond O(h^2) since the linear algebra 44 | % solution will only be accurate to O(h^2). 45 | 46 | % Since the boundary conditions give us the values of u(x_i) on the 47 | % boundaries, we only need to solve for the unknown function values in the 48 | % interior of the grid 49 | 50 | % Number of points to use in the numerical solution: 51 | 52 | % Specify the number of interior points to use in the numerical solution 53 | p = 4; % 9 gives 512 points 54 | nx = 2^p -1 ; %(for multigrid this should be odd) 55 | dx = (b-a)/(nx+1); % mesh spacing h=dx. 56 | 57 | %% Multigrid Paramters 58 | % Here you can change the number of grid levels, the type of multigrid 59 | % cycle, etc. 60 | 61 | % vis is a flag to visualize each step of the MuCycle. 62 | % vis = 1 for plots of the error approximations and corrections 63 | % vis = 2 for plots of the residual on each grid level. 64 | global vis 65 | vis = 1; 66 | 67 | % Specify Mu, Mu=1 vcycle, Mu = 2 wcycle 68 | global Mu 69 | Mu = 1; 70 | % Specify the number of pre and post relaxations i.e. V(nu1,nu2). 71 | nu1 = 2; 72 | nu2 = 2; 73 | % number of Mu cycles total to run: 74 | nMucycles = 1; 75 | 76 | % total number of grids: 77 | global ngrids 78 | ngrids = p-1; 79 | %% 80 | 81 | [v] = VisualMultigrid1D(a,b,nx,nMucycles,nu1,nu2,analyticV,f); 82 | 83 | %% Test weighted Jacobi: 84 | % clear all 85 | % close all 86 | % % Specify the domain [a,b]: 87 | % a = 0; b = 1.5; 88 | % 89 | % % Define the RHS function and the known analytic solution: 90 | % k = 0; L = b-a; 91 | % exactV = @(x) sin(k*pi*x/L)'; 92 | % f = @(x) (k*pi/L)^2*sin(k*pi*x/L)'; 93 | % 94 | % p = 7; % 9 gives 512 points 95 | % nx = 2^p -1 ; %(for multigrid this should be odd) 96 | % dx = (b-a)/(nx+1); % mesh spacing h=dx. 97 | % 98 | % xgrid = a+dx:dx:b-dx; 99 | % ffunc = f(xgrid); 100 | % ex = ones(nx,1); %array of ones 101 | % A = spdiags([-ex 2*ex -ex], -1:1, nx, nx)/dx^2; 102 | % vinitial = rand(nx,1); 103 | % vfinal = WeightedJacobi(vinitial,ffunc,A,10000,2/3); 104 | % figure 105 | % plot(xgrid,vinitial,xgrid,vfinal,'--.') 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /1DMultigridTutorial/Prolongate1D.m: -------------------------------------------------------------------------------- 1 | function [vfine] = Prolongate1D(vcoarse,nx) 2 | %% Takes a vector vcoarse and interpolates it to a fine grid vector vfine 3 | 4 | 5 | % the n in the multigrid tutorial book is n = nx+1; 6 | % the nx you send in here is the x dimension of vcoarse, do not confuse it 7 | % with the larger x dimension of vfine. 8 | % I use ghost zero points, so grow the vector 9 | vCoarse = zeros(1,nx+2); 10 | % 1st unpack the vector: 11 | vCoarse(2:nx+1) = vcoarse; 12 | % Now allocate the fine grid 13 | vfine = zeros((nx+1)*2-1,1); 14 | % Do the interpolation: 15 | vfine(2:2:end-1) = vCoarse(2:end-1); 16 | vfine(1:2:end) = 0.5*(vCoarse(1:end-1) + vCoarse(2:end)); -------------------------------------------------------------------------------- /1DMultigridTutorial/README.txt: -------------------------------------------------------------------------------- 1 | $$$$$$$$ Written by David Appelhans Jan 2012 $$$$$$$ 2 | 3 | Open ExampleScript.m, it walks you through the setup parameters for a sample 4 | 5 | problem. Follow along with the code for a detailed explanation of each step of 6 | 7 | the multigrid algorithm, including explanations of the math not just the code. 8 | 9 | There are almost more lines of comments than lines of code. -------------------------------------------------------------------------------- /1DMultigridTutorial/Restrict1D.m: -------------------------------------------------------------------------------- 1 | function [vcoarse] = Restrict1D(vfine,nx) 2 | %% Takes a vector vfine and restricts it to a coarse grid vector vcoarse 3 | 4 | % the n in the multigrid tutorial book is n = nx+1; 5 | 6 | % Now allocate the coarse grid 7 | vcoarse = zeros((length(vfine)+1)/2-1,1); 8 | 9 | % Do the restriction: 10 | for i = 1:(nx+1)/2-1 11 | vcoarse(i) = 1/4*(vfine(2*i-1) + 2*vfine(2*i)+ vfine(2*i+1)); 12 | end 13 | -------------------------------------------------------------------------------- /1DMultigridTutorial/VisualMuCycle.m: -------------------------------------------------------------------------------- 1 | function [pv] = VisualMuCycle(pA,pv,pf,px,pAnalytic,pNumeric,nx,nu1,nu2,i) 2 | %% Mu cycle scheme. i tells what level the scheme is solving on. 3 | % Mu = 1 is a V cycle, Mu = 2 is a W cycle. 4 | global ngrids 5 | global Mu 6 | global vis 7 | 8 | 9 | % If I'm not on the coarsest grid, relax and take the problem down a level 10 | if i ~= ngrids 11 | if vis == 1 12 | pNumeric{i} = pA{i}\pf{i}; 13 | figure 14 | plot(px{i},pAnalytic{i},'-',px{i},pNumeric{i},'.',px{i},pv{i},'x'); 15 | title(['Exact error and approximate error before prerelaxation on grid ',num2str(i),'h']) 16 | legend('Exact Analytic Error','Numerical Error (direct solve)','Error Approximation (iterative)'); 17 | end 18 | if vis == 2 19 | figure 20 | plot(px{i},pf{i}-pA{i}*pv{i},'--.'); 21 | title(['Residual before prerelaxation on grid level ',num2str(i),'h']) 22 | end 23 | % Relax nu1 times on my current problem: 24 | % Jacobi weighting: 2/3 is ideal for 1-D. 25 | w = 2/3; 26 | [pv{i}] = WeightedJacobi(pv{i},pf{i},pA{i},nu1,w); 27 | if vis == 1 28 | figure 29 | plot(px{i},pAnalytic{i},'-',px{i},pNumeric{i},'.',px{i},pv{i},'x'); 30 | title(['Exact error and approximate error after prerelaxation on grid ',num2str(i),'h']) 31 | legend('Exact Analytic Error','Numerical Error (direct solve)','Error Approximation (iterative)'); 32 | end 33 | if vis == 2 34 | figure 35 | plot(px{i},pf{i}-pA{i}*pv{i},'--.'); 36 | title(['Residual after prerelaxation on grid level ',num2str(i),'h']) 37 | end 38 | % compute residual and restrict it to coarse grid: (it replaces coarse f). 39 | pf{i+1} = Restrict1D( pf{i}-pA{i}*pv{i} ,nx(i)); 40 | if vis == 2 41 | figure 42 | plot(px{i+1},pf{i+1},'--.'); 43 | title([num2str(i),'h grid residual restricted to grid level ',num2str(i+1),'and used as RHS']) 44 | end 45 | % make the initual guess for the error on the coarser grid 0. 46 | pv{i+1} = zeros(nx(i+1),1); 47 | % compute the exact analytic error in our new approximation to the error 48 | % and restrict it to the coarser grid 49 | pAnalytic{i+1} = Restrict1D(pAnalytic{i}-pv{i},nx(i)); 50 | % Call Mucycle scheme recursively mu times: 51 | for j=1:Mu 52 | pv = VisualMuCycle(pA,pv,pf,px,pAnalytic,pNumeric,nx,nu1,nu2,i+1); 53 | end 54 | % Prolongate back to the fine grid and add correction: 55 | pv{i} = pv{i} + Prolongate1D(pv{i+1},nx(i+1)); 56 | if vis == 1 57 | figure 58 | plot(px{i},pAnalytic{i},px{i},pNumeric{i},'.',px{i},pv{i},'x'); 59 | title(['Corrected solution (of error equation) on grid level ',num2str(i),'h, before postrelaxation']) 60 | legend('Exact Analytic Error','Numerical Error (direct solve)','Error Approximation (iterative)'); 61 | end 62 | if vis == 2 63 | figure 64 | plot(px{i},pf{i}-pA{i}*pv{i},'--.'); 65 | title(['Residual before post relaxation on grid level ',num2str(i),'h']) 66 | end 67 | % relax nu2 times after the correction: 68 | [pv{i}] = WeightedJacobi(pv{i},pf{i},pA{i},nu2,w); 69 | if vis == 1 70 | figure 71 | plot(px{i},pAnalytic{i},px{i},pNumeric{i},'.',px{i},pv{i},'x'); 72 | title(['Corrected solution (of error equation) on grid level ',num2str(i),'h, after postrelaxation']) 73 | legend('Exact Analytic Error','Numerical Error (direct solve)','Error Approximation (iterative)'); 74 | end 75 | if vis == 2 76 | figure 77 | plot(px{i},pf{i}-pA{i}*pv{i},'--.'); 78 | title(['Residual after post relaxation on grid level ',num2str(i),'h']) 79 | end 80 | else 81 | %if I'm already on the coarsest grid: 82 | 83 | % Solve thoroughly: (bunch of jacobi cycles are sufficient on very coarse grid) 84 | [pv{i}] = WeightedJacobi(pv{i},pf{i},pA{i},20,2/3); 85 | % pv{i} = pA{i}\pf{i}; %direct solve on coarsest grid is fast too, 86 | % and can be use instead of jacobi. 87 | if vis == 1 88 | pNumeric{i} = pA{i}\pf{i}; 89 | figure 90 | plot(px{i},pAnalytic{i},px{i},pNumeric{i},'.',px{i},pv{i},'x'); 91 | title(['Exact error and approximate error after solve on coarsest grid level (',num2str(i),'h)']) 92 | legend('Exact Analytic Error','Numerical Error (direct solve)','Error Approximation'); 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /1DMultigridTutorial/VisualMultigrid1D.m: -------------------------------------------------------------------------------- 1 | function [v] = VisualMultigrid1D(a,b,nxfine,nMucycles,nu1,nu2,analyticU,f) 2 | %% 3 | 4 | global vis 5 | global ngrids 6 | 7 | % Define the initial guess 8 | guess = @(x) rand(length(x),1); 9 | 10 | %initialize a cell to hold the object on different grids 11 | pA = cell(ngrids,1); %matrix operator 12 | pf = cell(ngrids,1); % (RHS) 13 | pv = cell(ngrids,1); % (unknowns) 14 | px = cell(ngrids,1); %grid 15 | pAnalytic = cell(ngrids,1); % the analytic solution 16 | pNumeric = cell(ngrids,1); %The exact solution to the linear algebra problem 17 | 18 | % Define arrays of gird parameters on each level 19 | i = 0:ngrids-1; 20 | nx = (nxfine+1)./(2.^i)-1; 21 | dx = (b-a)./(nx+1); 22 | 23 | % Build the matrices and grid on each level: 24 | for i=1:ngrids 25 | % make the sparse second derivative matrix. 26 | ex = ones(nx(i),1); %array of ones 27 | pA{i} = spdiags([-ex 2*ex -ex], -1:1, nx(i), nx(i))/dx(i)^2; 28 | px{i} = a+dx(i):dx(i):b-dx(i); %only need interior grid points 29 | end 30 | 31 | % Initial approximation to the solution is just a starting guess (random). 32 | v = guess(px{1}); 33 | 34 | % The analyticU is the analytic solution evaluated on the grid 35 | % Similarly numericU is the gauss elim solution to the linear algebra problem: 36 | numericU = pA{1}\f(px{1}); %SLOW, just for educational purpose. 37 | 38 | if vis == 1 %Plot the initial guess and the exact numeric and analytic solutions 39 | figure 40 | plot(px{1},analyticU(px{1}),'-',px{1},numericU,'.',px{1},v,'x'); 41 | title('Initial Iterative Approximation (random guess)') 42 | legend('Analytic Solution','Numerical Solution (direct solve)','Numerical Solution (initial guess)'); 43 | end 44 | 45 | 46 | % Run the Mu cycles: (solving the error equation, Ae=r) 47 | 48 | %To understand the process and the subsequent graphs, it is convenient to 49 | %solve for the error instead of the solution directly. So pv holds the 50 | %approximation for the error in the initial random guess. 51 | 52 | residual = f(px{1})-pA{1}*v; %compute residual 53 | pv{1} = zeros(nx(1),1); %make 0 error guess (equivalent to random initial guess). 54 | pf{1} = residual; 55 | pAnalytic{1} = analyticU(px{1})-v; %the exact error difference with the analtyic solution 56 | 57 | level = 1; 58 | for i=1:nMucycles 59 | pv = VisualMuCycle(pA,pv,pf,px,pAnalytic,pNumeric,nx,nu1,nu2,level); 60 | end 61 | v = v + pv{1}; %correction to the initial random guess, u=v+e. 62 | if vis == 1 %Plot the final solution and the exact solution 63 | figure 64 | plot(px{1},analyticU(px{1}),'-',px{1},numericU,'.',px{1},v,'x'); 65 | title('Final Iterative Approximation After 1 Multigrid V-cycle') 66 | legend('Analytic Solution','Numerical Solution (direct solve)','Numerical Solution (Multigrid solve)'); 67 | end -------------------------------------------------------------------------------- /1DMultigridTutorial/WeightedJacobi.m: -------------------------------------------------------------------------------- 1 | function [v] = WeightedJacobi(v,f,A,n_iterations,w) 2 | %% Performs weighted jacobi iterations to solve Av=f. 3 | D = diag(diag(A)); 4 | offD = A-D; 5 | display('size(v)') 6 | size(v) 7 | display('size(f)') 8 | size(f) 9 | 10 | for i = 1:n_iterations 11 | v = (1-w)*v + w*(f - offD*v)./diag(A); 12 | end -------------------------------------------------------------------------------- /2DMultigrid/Build2DVector.m: -------------------------------------------------------------------------------- 1 | function [ftab] = Build2DVector(f,nx,ny,ax,bx,ay,by) 2 | %% Is used to construct a vector ftab from a function 2D function handle f. 3 | dx = (bx-ax)/(nx+1); 4 | dy = (by-ay)/(ny+1); 5 | xtabfull = ax:dx:bx; 6 | ytabfull = ay:dy:by; 7 | xtab = xtabfull(2:end-1); 8 | ytab = ytabfull(2:end-1); 9 | % Define the initial guess and the RHS f: 10 | [Xmat,Ymat] = meshgrid(xtab,ytab); 11 | fmat = f(Xmat,Ymat); 12 | ftab = pack_matToVec(fmat); -------------------------------------------------------------------------------- /2DMultigrid/BuildLaplacian2D.m: -------------------------------------------------------------------------------- 1 | function [A] = BuildLaplacian2D(nx,ny,dx,dy) 2 | %% Returns the sparse matrix for 2D finite diffence laplacian: 3 | Ix = speye(nx); 4 | Iy = speye(ny); 5 | ex = ones(nx,1); 6 | ey = ones(ny,1); 7 | % make the sparse second derivative matrix. 8 | Bhx = spdiags([-ex 2*ex -ex], -1:1, nx, nx)/dx^2; 9 | Bhy = spdiags([-ey 2*ey -ey], -1:1, ny, ny)/dy^2; 10 | % Create the laplacian: 11 | A = kron(Iy,Bhx) + kron(Bhy,Ix); 12 | -------------------------------------------------------------------------------- /2DMultigrid/ExampleFMG.m: -------------------------------------------------------------------------------- 1 | close all 2 | clear all 3 | % Specify Simulation Parameters: 4 | 5 | % Specify the domain: 6 | ax = 0; bx = 1; 7 | ay = 0; by = 1; 8 | 9 | % Define the RHS function and the known analytic solution: 10 | f = @(x,y) 2*((1-6*x.^2).*y.^2.*(1-y.^2)+(1-6*y.^2).*x.^2.*(1-x.^2)); 11 | exactV = @(x,y) (x.^2 -x.^4).*(y.^4 - y.^2); 12 | 13 | % Specify Mu, Mu=1 vcycle, Mu = 2 wcycle 14 | global Mu 15 | Mu = 1; 16 | % Specify the number of pre and post relaxations i.e. V(nu1,nu2). 17 | nu1 = 2; 18 | nu2 = 2; 19 | % number of Mu cycles at each grid level in FMG: 20 | ncycles = 1; 21 | 22 | 23 | global ngrids 24 | k = 5; % 9 gives 512x512 grids 25 | 26 | % Specify the total number of interior fine points, (should be odd): 27 | nx = 2^k -1 ; %best to just use nx=ny for now, there might be a bug. 28 | ny = 2^k -1 ; 29 | dx = (bx-ax)/(nx+1); 30 | dy = dx; 31 | ngrids = k; 32 | % pv is a cell array that holds the solution (or error) at each grid level, 33 | % pv{1} is the solution on the finest grid, i.e. the answer we are after 34 | pv = cell(ngrids,1); 35 | 36 | % Call FullMultigrid routine: 37 | pv = runFMG(ax,bx,ay,by,nx,ny,ncycles,nu1,nu2,f); 38 | % Compute the L2 error: 39 | [Xmat,Ymat] = meshgrid(ax+dx:dx:bx-dx,ay+dy:dy:by-dy); 40 | exact = pack_matToVec(exactV(Xmat,Ymat)); 41 | 42 | L2error = sqrt(dx^2*sum((exact-pv{1}).^2)); 43 | display(['The L2 error is ', num2str(L2error)]) 44 | 45 | 46 | %% Plot the Solution and pointwise error: 47 | 48 | i = 1; 49 | xtabfull = ax:dx:bx; 50 | ytabfull = ay:dy:by; 51 | Vnewmat = zeros(nx+2,ny+2); 52 | Vnewmat(2:end-1,2:end-1) = pack_vecToMat(pv{i},nx); 53 | [Xmat,Ymat] = meshgrid(xtabfull,ytabfull); 54 | figure 55 | mesh(Xmat,Ymat,Vnewmat) 56 | title(['Solution after FMG with', ' V(',int2str(nu1),',',int2str(nu2),') cycles'], 'Fontsize', 18); 57 | set(gca,'FontSize', 14, 'FontName', 'Times New Roman'); 58 | zlabel('V') 59 | xlabel('x') 60 | ylabel('y') 61 | % Plot the pointwise error: 62 | figure 63 | errorMat = zeros(nx+2,ny+2); 64 | errorMat(2:end-1,2:end-1) = pack_vecToMat(abs(pv{i}-exact),nx(i)); 65 | mesh(Xmat,Ymat,errorMat) 66 | title(['Pointwise error after FMG with', ' V(',int2str(nu1),',',int2str(nu2),') cycles'], 'Fontsize', 18); 67 | zlabel('|u_{*}-u_{fmg}|') 68 | xlabel('x') 69 | ylabel('y') 70 | 71 | -------------------------------------------------------------------------------- /2DMultigrid/ExampleMucycles.m: -------------------------------------------------------------------------------- 1 | close all 2 | clear all 3 | % Specify Simulation Parameters: 4 | 5 | % Specify the domain: 6 | ax = 0; bx = 1; 7 | ay = 0; by = 1; 8 | 9 | % Define the RHS function and the known analytic solution: 10 | f = @(x,y) 2*((1-6*x.^2).*y.^2.*(1-y.^2)+(1-6*y.^2).*x.^2.*(1-x.^2)); 11 | exactV = @(x,y) (x.^2 -x.^4).*(y.^4 - y.^2); 12 | 13 | % Specify Mu, Mu=1 vcycle, Mu = 2 wcycle 14 | global Mu 15 | Mu = 1; 16 | % Specify the number of pre and post relaxations i.e. V(nu1,nu2). 17 | nu1 = 2; 18 | nu2 = 2; 19 | % number of Mu cycles total to run: 20 | nMucycles = 10; 21 | 22 | % Problem size: 23 | k = 5; % 9 gives 512x512 grids 24 | % Specify the total number of interior fine points, (should be odd): 25 | nx = 2^k -1 ; %best to just use nx=ny for now, there might be a bug. 26 | ny = 2^k -1 ; 27 | dx = (bx-ax)/(nx+1); 28 | dy = dx; 29 | 30 | % total number of grids: 31 | global ngrids 32 | ngrids = k; 33 | 34 | % pv is a cell array that holds the solution (or error) at each grid level, 35 | % pv{1} is the solution on the finest grid, i.e. the answer we are after 36 | pv = cell(ngrids,1); 37 | 38 | % initialize arrays that hold errors and error ratios (rho). 39 | errornorm = zeros(nMucycles,1); 40 | resnorm = zeros(nMucycles,1); 41 | resratio = zeros(nMucycles-1,1); 42 | errorratio = zeros(nMucycles-1,1); 43 | % Call the Multigrid routine which will run V or W cycles, not FMG: 44 | [pv,resnorm,errornorm] = MultiGrid(ax,bx,ay,by,nx,ny,nMucycles,nu1,nu2,exactV,f); 45 | 46 | %% Display the convergence: 47 | display('residual norm = ') 48 | fprintf(1,'%6.2E\n', resnorm) 49 | resratio = resnorm(2:end)./resnorm(1:end-1); 50 | display('r ratio = ') 51 | fprintf(1,'%6.2f\n', resratio) 52 | display('error norm = ') 53 | fprintf(1,'%6.2E\n', errornorm) 54 | errorratio = errornorm(2:end)./errornorm(1:end-1); 55 | display('e ratio = ') 56 | fprintf(1,'%6.2f\n', errorratio) 57 | 58 | %% Plot the Solution and convergence: 59 | 60 | i = 1; 61 | xtabfull = ax:dx:bx; 62 | ytabfull = ay:dy:by; 63 | Vnewmat = zeros(nx+2,ny+2); 64 | Vnewmat(2:end-1,2:end-1) = pack_vecToMat(pv{i},nx); 65 | [Xmat,Ymat] = meshgrid(xtabfull,ytabfull); 66 | figure 67 | mesh(Xmat,Ymat,Vnewmat) 68 | title(['After ', int2str(nMucycles), ' V(',int2str(nu1),',',int2str(nu2),') cycles'], 'Fontsize', 18); 69 | set(gca,'FontSize', 14, 'FontName', 'Times New Roman'); 70 | zlabel('V') 71 | xlabel('x') 72 | ylabel('y') 73 | % Plot the error and residual reduction: 74 | figure 75 | semilogy(1:nMucycles,errornorm) 76 | title('Error Norm Reduction', 'Fontsize', 18); 77 | set(gca,'FontSize', 14, 'FontName', 'Times New Roman'); 78 | xlabel('V-cycle') 79 | ylabel('||e||_2') 80 | figure 81 | semilogy(1:nMucycles,resnorm) 82 | title('Residual Norm Reduction', 'Fontsize', 18); 83 | set(gca,'FontSize', 14, 'FontName', 'Times New Roman'); 84 | xlabel('V-cycle') 85 | ylabel('||r||_2') 86 | -------------------------------------------------------------------------------- /2DMultigrid/FullMultiGrid.m: -------------------------------------------------------------------------------- 1 | function [pv] = FullMultiGrid(pA,pv,pf,nx,ncycles,nu1,nu2,w,i) 2 | %% Author David Appelhans 2011 3 | global ngrids 4 | % if I'm not on the coarsest grid 5 | if i~= ngrids 6 | pv = FullMultiGrid(pA,pv,pf,nx,ncycles,nu1,nu2,w,i+1); 7 | pv{i} = Prolongate2D(pv{i+1},nx(i+1)); 8 | else 9 | % If i'm on the coarsest grid just guess whatever and then V-cycle 10 | pv{i} = rand(nx(i)^2,1); 11 | end 12 | % Solve on the current grid 13 | for j = 1:ncycles 14 | pv = MuCycle(pA,pv,pf,nx,nu1,nu2,w,i); 15 | end 16 | 17 | -------------------------------------------------------------------------------- /2DMultigrid/MuCycle.m: -------------------------------------------------------------------------------- 1 | function [pv] = MuCycle(pA,pv,pf,nx,nu1,nu2,w,i) 2 | %% Mu cycle scheme. i tells what level the scheme is solving on. 3 | % Mu = 1 is a V cycle, Mu = 2 is a W cycle. 4 | global ngrids 5 | global Mu 6 | % Relax nu1 times on my current problem: 7 | [pv{i}] = WeightedJacobi(pv{i},pf{i},pA{i},nu1,w); 8 | % If I'm not on the coarsest grid, take the residual to a finer grid: 9 | if i ~= ngrids 10 | % compute residual and restrict it to coarse grid: (it replaces coarse f). 11 | pf{i+1} = Restrict2D( pf{i}-pA{i}*pv{i} ,nx(i)); 12 | % make the initual guess for the error on the coarser grid 0. 13 | pv{i+1} = zeros(nx(i+1)^2,1); 14 | % Call Mucycle scheme recursively mu times: 15 | for j=1:Mu 16 | pv = MuCycle(pA,pv,pf,nx,nu1,nu2,w,i+1); 17 | end 18 | % Prolongate back to the fine grid and add correction: 19 | pv{i} = pv{i} + Prolongate2D(pv{i+1},nx(i+1)); 20 | else 21 | %if I'm already on the coarsest grid: 22 | pv{i} = pA{i}\pf{i}; 23 | % Solve thoroughly: (20 jacobi cycles) 24 | % [pv{i}] = WeightedJacobi(pv{i},pf{i},pA{i},20,w); 25 | end 26 | % relax nu2 times on fine grid: 27 | [pv{i}] = WeightedJacobi(pv{i},pf{i},pA{i},nu2,w); -------------------------------------------------------------------------------- /2DMultigrid/MultiGrid.m: -------------------------------------------------------------------------------- 1 | function [pv,resnorm,errornorm] = MultiGrid(ax,bx,ay,by,nxfine,nyfine,nMucycles,nu1,nu2,exactV,f) 2 | %% Author: David Appelhans 2011 3 | %% solves the finite difference discretization of square 2-D Poisson. 4 | % Specify the number of grids (must be 1 for correct matrix generation. 5 | % CREDIT: Bengt Fornberg at Colorado University 6 | %----------------------------------------------------------------------- 7 | % 8 | %This was developed when it was noticed kron generates a full matrix and 9 | %then makes it sparse. the approach taken is to generate the appropriate 10 | %diagonals for the neighbor interactions in the x, y and z directions, and 11 | %then use spdiags to generate the matrix in one call. 12 | % 13 | %the following relation is used for neighbor l to create nz by nz blocks of 14 | %ny by ny of blocks of nx by nx matrices (which is a bad description). 15 | % 16 | %x direction: on diagonal l, 17 | %have repeating blocks of nx-|l| of the coefficient value and |l| zeros 18 | % 19 | %y direction: on diagonal l*nx 20 | %have repeating blocks of nx*(ny-|l|) of the coefficient and nx*|l| zeros 21 | % 22 | %z direction: on diagonal l*nx*ny 23 | %nx*ny*(nz-|l|) of the coefficient are sufficient 24 | % 25 | %note the generated matrix of diagonals, V_diag, actually pads out the 26 | %diagonals to length nx*ny*nz, and flips ordering (rather then regenerate 27 | %the diagonal) because on 'above neighbors' (l > 0) spdiags starts from one 28 | %end of the diagonal collumn, and an 'below neighbors' (l < 0) spdiags 29 | %starts from the other end. 30 | % 31 | 32 | if degree <= 2 33 | 34 | nxyz = nx*ny*nz; 35 | X1 = kron( ones(ny*nz,1), [ -1*ones(nx-1,1) ; 0 ]); 36 | Y1 = kron( ones(nz,1), [ -1*ones(nx*(ny-1),1) ; zeros(nx,1) ] ); 37 | V_diag = [ -1*ones(nxyz,1) flipud(Y1) flipud(X1) 6*ones(nxyz,1) X1 Y1 -1*ones(nxyz,1) ]; 38 | N_diag = [ nx*ny nx 1 0 -1 -1*nx -1*nx*ny ]; 39 | A= spdiags( V_diag, N_diag, nxyz, nxyz); 40 | 41 | elseif degree <= 4 42 | 43 | nxy = nx*ny; 44 | nxyz = nx*ny*nz; 45 | X1 = kron( ones(ny*nz,1), [ (-4/3 * ones(nx-1,1)) ; 0 ]); 46 | X2 = kron( ones(ny*nz,1), [ (1/12 * ones(nx-2,1)) ; zeros(min(2,nx),1) ]); 47 | Y1 = kron( ones(nz,1), [ (-4/3 * ones(nx*(ny-1),1)) ; zeros(nx,1) ] ); 48 | Y2 = kron( ones(nz,1), [ (1/12 * ones(nx*(ny-2),1)) ; zeros(min(2*nx,nx*ny),1) ] ); 49 | V_diag = [ 1/12*ones(nxyz,1) -4/3*ones(nxyz,1) flipud(Y2) flipud(Y1) flipud(X2) flipud(X1) ... 50 | 15/2*ones(nxyz,1) X1 X2 Y1 Y2 -4/3*ones(nxyz,1) 1/12*ones(nxyz,1) ]; 51 | N_diag = [ 2*nx*ny nx*ny 2*nx nx 2 1 ... 52 | 0 -1 -2 -1*nx -2*nx -1*nx*ny -2*nx*ny ]; 53 | A = spdiags( V_diag, N_diag, nxyz, nxyz); 54 | 55 | elseif degree <= 6 56 | 57 | nxy = nx*ny; 58 | nxyz = nxy*nz; 59 | X1 = kron( ones(ny*nz,1), [ (-3/2 * ones(nx-1,1)) ; zeros(min(1,nx),1) ]); 60 | X2 = kron( ones(ny*nz,1), [ (3/20 * ones(nx-2,1)) ; zeros(min(2,nx),1) ]); 61 | X3 = kron( ones(ny*nz,1), [ (-1/90 * ones(nx-3,1)) ; zeros(min(3,nx),1) ]); 62 | Y1 = kron( ones(nz,1), [ (-3/2 * ones(nx*(ny-1),1)) ; zeros(min(nx,nxy),1) ] ); 63 | Y2 = kron( ones(nz,1), [ (3/20 * ones(nx*(ny-2),1)) ; zeros(min(2*nx,nxy),1) ] ); 64 | Y3 = kron( ones(nz,1), [ (-1/90 * ones(nx*(ny-3),1)) ; zeros(min(3*nx,nxy),1) ] ); 65 | V_diag = [ -1/90*ones(nxyz,1) 3/20*ones(nxyz,1) -3/2*ones(nxyz,1) flipud(Y3) flipud(Y2) flipud(Y1) flipud(X3) flipud(X2) flipud(X1) ... 66 | 49/6*ones(nxyz,1) X1 X2 X3 Y1 Y2 Y3 -3/2*ones(nxyz,1) 3/20*ones(nxyz,1) -1/90*ones(nxyz,1) ]; 67 | N_diag = [ 3*nx*ny 2*nx*ny nx*ny 3*nx 2*nx nx 3 2 1 ... 68 | 0 -1 -2 -3 -1*nx -2*nx -3*nx -1*nx*ny -2*nx*ny -3*nx*ny ]; 69 | A = spdiags( V_diag, N_diag, nxyz, nxyz); 70 | 71 | else 72 | 73 | nxy = nx*ny; 74 | nxyz = nxy*nz; 75 | X1 = kron( ones(ny*nz,1), [ (-8/5 * ones(nx-1,1)) ; zeros(min(1,nx),1) ]); 76 | X2 = kron( ones(ny*nz,1), [ (1/5 * ones(nx-2,1)) ; zeros(min(2,nx),1) ]); 77 | X3 = kron( ones(ny*nz,1), [ (-8/315 * ones(nx-3,1)) ; zeros(min(3,nx),1) ]); 78 | X4 = kron( ones(ny*nz,1), [ (1/560 * ones(nx-4,1)) ; zeros(min(4,nx),1) ]); 79 | Y1 = kron( ones(nz,1), [ (-8/5 * ones(nx*(ny-1),1)) ; zeros(min(nxy,nx),1) ] ); 80 | Y2 = kron( ones(nz,1), [ (1/5 * ones(nx*(ny-2),1)) ; zeros(min(nxy,2*nx),1) ] ); 81 | Y3 = kron( ones(nz,1), [ (-8/315 * ones(nx*(ny-3),1)) ; zeros(min(nxy,3*nx),1) ] ); 82 | Y4 = kron( ones(nz,1), [ (1/560 * ones(nx*(ny-4),1)) ; zeros(min(nxy,4*nx),1) ] ); 83 | V_diag = [ 1/560*ones(nxyz,1) -8/315*ones(nxyz,1) 1/5*ones(nxyz,1) -8/5*ones(nxyz,1) flipud(Y4) flipud(Y3) flipud(Y2) flipud(Y1) flipud(X4) flipud(X3) flipud(X2) flipud(X1) ... 84 | 205/24*ones(nxyz,1) X1 X2 X3 X4 Y1 Y2 Y3 Y4 -8/5*ones(nxyz,1) 1/5*ones(nxyz,1) -8/315*ones(nxyz,1) 1/560*ones(nxyz,1) ]; 85 | N_diag = [ 4*nx*ny 3*nx*ny 2*nx*ny nx*ny 4*nx 3*nx 2*nx nx 4 3 2 1 ... 86 | 0 -1 -2 -3 -4 -1*nx -2*nx -3*nx -4*nx -1*nx*ny -2*nx*ny -3*nx*ny -4*nx*ny ]; 87 | A = spdiags( V_diag, N_diag, nxyz, nxyz); 88 | 89 | end 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 David Appelhans 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NutshellMultigrid2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappelha/MultiGridMatlab/c28eaa64b688f328d2c33bf4b0a9e338e25580da/NutshellMultigrid2.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MultiGridMatlab 2 | 3 | The purpose of this repository is to provide Matlab code for geometric multigrid that is easy to understand and learn from. It is perfect for students because it was written by a graduate student. 4 | 5 | Start with the ![1D Multigrid Tutorial](1DMultigridTutorial/) to understand how multigrid works. This is ideal for the beginner to walk through, with visualizations every step of the way. 6 | 7 | Writting or enhancing a code like ![2D Multigrid](2DMultigrid/.) would make a great class project. Perhaps extend 2D to 3D for a short assignment, write in another language, or add some advection for more difficulty. 8 | 9 | This short ![PDF](NutshellMultigrid2.pdf) explains the math behind the code. 10 | 11 | ![alt tag](images/VcycleMedres.png) 12 | -------------------------------------------------------------------------------- /images/VcycleDiagramSuperHighres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappelha/MultiGridMatlab/c28eaa64b688f328d2c33bf4b0a9e338e25580da/images/VcycleDiagramSuperHighres.png -------------------------------------------------------------------------------- /images/VcycleMedres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappelha/MultiGridMatlab/c28eaa64b688f328d2c33bf4b0a9e338e25580da/images/VcycleMedres.png -------------------------------------------------------------------------------- /images/VcycleSmooshres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappelha/MultiGridMatlab/c28eaa64b688f328d2c33bf4b0a9e338e25580da/images/VcycleSmooshres.png -------------------------------------------------------------------------------- /images/error2D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappelha/MultiGridMatlab/c28eaa64b688f328d2c33bf4b0a9e338e25580da/images/error2D.png -------------------------------------------------------------------------------- /images/initialguess3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappelha/MultiGridMatlab/c28eaa64b688f328d2c33bf4b0a9e338e25580da/images/initialguess3D.png -------------------------------------------------------------------------------- /images/solution2D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappelha/MultiGridMatlab/c28eaa64b688f328d2c33bf4b0a9e338e25580da/images/solution2D.png -------------------------------------------------------------------------------- /images/solution3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dappelha/MultiGridMatlab/c28eaa64b688f328d2c33bf4b0a9e338e25580da/images/solution3D.png --------------------------------------------------------------------------------