├── README.md └── poisson2d_MG.f90 /README.md: -------------------------------------------------------------------------------- 1 | # 5.06.Multigrid2D 2 | V-cycle Multigrid method for 2D Poisson Equation 3 | 4 | 5 | ![residual](https://cloud.githubusercontent.com/assets/15114859/10856500/27fbcbd0-7f16-11e5-96af-89f90d1cfa5e.png) 6 | ![residual_log](https://cloud.githubusercontent.com/assets/15114859/10856503/299b622a-7f16-11e5-989e-769513abe097.png) 7 | ![field](https://cloud.githubusercontent.com/assets/15114859/10856504/2aa99ede-7f16-11e5-840f-220a99dc9485.png) 8 | -------------------------------------------------------------------------------- /poisson2d_MG.f90: -------------------------------------------------------------------------------- 1 | !-----------------------------------------------------------------------------! 2 | ! MAE 5093 DOCUMENTATION | Engineering Numerical Analysis 3 | !-----------------------------------------------------------------------------! 4 | ! >>> V-cycle Multigrid method for solving 2D Poisson equation 5 | ! d2u/dx2 + d2u/dy2 = f(x,y) 6 | ! Drichlet b.c. 7 | 8 | !-----------------------------------------------------------------------------! 9 | ! References: 10 | ! * Fundamentals of Engineering Numerical Analysis by P. Moin (2012) 11 | ! * Numerical Recipes: The Art of Scientific Computing, 2nd Edition (1992) 12 | !-----------------------------------------------------------------------------! 13 | ! Written by Omer San 14 | ! CFDLab, Oklahoma State University, cfdlab.osu@gmail.com 15 | ! www.cfdlab.org 16 | ! 17 | ! Last updated: Oct. 29, 2015 18 | !-----------------------------------------------------------------------------! 19 | 20 | program poisson2d 21 | implicit none 22 | integer::i,j,nx,ny,isolver 23 | real*8,dimension(:,:),allocatable ::u,f,ue,e 24 | real*8,dimension(:),allocatable ::x,y 25 | real*8 ::dx,dy,tol,rms,x0,xL,y0,yL 26 | 27 | !Domain 28 | x0 =-1.0d0 !left 29 | xL = 1.0d0 !right 30 | 31 | y0 =-1.0d0 !bottom 32 | yL = 1.0d0 !up 33 | 34 | !number of points 35 | nx = 128 !number of grid points in x (i.e., should be power of 2) 36 | ny = nx !number of grid points in y 37 | 38 | !grid spacing (spatial) 39 | dx = (xL-x0)/dfloat(nx) 40 | dy = (yL-y0)/dfloat(ny) 41 | 42 | !spatial coordinates 43 | allocate(x(0:nx)) 44 | do i=0,nx 45 | x(i) = x0 + dfloat(i)*dx 46 | end do 47 | 48 | allocate(y(0:ny)) 49 | do j=0,ny 50 | y(j) = y0 + dfloat(j)*dy 51 | end do 52 | 53 | 54 | 55 | 56 | 57 | !Tolerance 58 | tol= 1.0d-4 59 | 60 | 61 | allocate(u(0:nx,0:ny)) 62 | allocate(f(0:nx,0:ny)) 63 | allocate(e(0:nx,0:ny)) 64 | allocate(ue(0:nx,0:ny)) 65 | 66 | !---------------------------------------------! 67 | !Exact solution (test case from Moin's textbook): 68 | !---------------------------------------------! 69 | do j=0,ny 70 | do i=0,nx 71 | f(i,j) =-2.0d0*(2.0d0-x(i)*x(i)-y(j)*y(j)) 72 | ue(i,j)= (x(i)*x(i)-1.0d0)*(y(j)*y(j)-1.0d0) 73 | end do 74 | end do 75 | 76 | 77 | !Numerical solution: 78 | do i=0,nx 79 | do j=0,ny 80 | u(i,j)=0.0d0 81 | end do 82 | end do 83 | 84 | !Boundary conditions has to satisfy exact solution 85 | do i=0,nx 86 | u(i,0) = ue(i,0) 87 | u(i,ny) = ue(i,ny) 88 | end do 89 | 90 | do j=0,ny 91 | u(0,j) = ue(0,j) 92 | u(nx,j) = ue(nx,j) 93 | end do 94 | 95 | 96 | open(19,file='output.txt') 97 | 98 | !----------------------! 99 | !Solver: 100 | !----------------------! 101 | isolver = 1 102 | if (isolver.eq.0) then !Gauss-Seidel scheme 103 | call GS(nx,ny,dx,dy,f,u,tol) 104 | else 105 | call MG5(nx,ny,dx,dy,f,u,tol) 106 | end if 107 | 108 | 109 | !----------------------! 110 | !Error analysis: 111 | !----------------------! 112 | do i=0,nx 113 | do j=0,ny 114 | e(i,j) = dabs(u(i,j)-ue(i,j)) 115 | end do 116 | end do 117 | 118 | 119 | !L-2 Norm: 120 | call l2norm(nx,ny,e,rms) 121 | 122 | write(*,*)"L2-norm =",rms 123 | write(19,*)"L2-norm =",rms 124 | 125 | !maximum norm 126 | write(*,*)"Max-norm =",maxval(e) 127 | write(19,*)"Max-norm =",maxval(e) 128 | close(19) 129 | 130 | !Plot field 131 | open(10,file='field.plt') 132 | write(10,*) 'variables ="x","y","f","u","ue"' 133 | write(10,*)'zone f=point i=',nx+1,',j=',ny+1 134 | do j=0,ny 135 | do i=0,nx 136 | write(10,*) x(i),y(j),f(i,j),u(i,j),ue(i,j) 137 | end do 138 | end do 139 | close(10) 140 | 141 | 142 | end 143 | 144 | 145 | 146 | 147 | !---------------------------------------------------------------------------! 148 | !Relaxation formula for Poisson equation 149 | !Uses GS relaxation 150 | !Works for Drichlet boundary conditions (Boundary points never updated) 151 | !---------------------------------------------------------------------------! 152 | SUBROUTINE relax(nx,ny,dx,dy,f,u) 153 | implicit none 154 | integer::nx,ny 155 | real*8 ::dx,dy 156 | real*8, dimension(0:nx,0:ny)::u,f 157 | real*8 ::a 158 | integer::i,j 159 | 160 | a = -2.0d0/(dx*dx) - 2.0d0/(dy*dy) 161 | 162 | do i=1,nx-1 163 | do j=1,ny-1 164 | u(i,j) = (1.0d0/a)*(f(i,j) & 165 | - (u(i+1,j)+u(i-1,j))/(dx*dx) & 166 | - (u(i,j+1)+u(i,j-1))/(dy*dy) ) 167 | end do 168 | end do 169 | 170 | return 171 | end 172 | 173 | 174 | 175 | !---------------------------------------------------------------------------! 176 | !Residual formula for Poisson equation 177 | !Works for Drichlet boundary conditions (Boundary points never updated) 178 | !---------------------------------------------------------------------------! 179 | SUBROUTINE resid(nx,ny,dx,dy,f,u,r) 180 | implicit none 181 | integer::nx,ny 182 | real*8 ::dx,dy 183 | real*8, dimension(0:nx,0:ny)::u,f,r 184 | integer::i,j 185 | 186 | do i=1,nx-1 187 | do j=1,ny-1 188 | r(i,j) = f(i,j) - (u(i+1,j) - 2.0d0*u(i,j) + u(i-1,j))/(dx*dx) & 189 | - (u(i,j+1) - 2.0d0*u(i,j) + u(i,j-1))/(dy*dy) 190 | end do 191 | end do 192 | 193 | !Boundary conditions for residuals 194 | do i=0,nx 195 | r(i,0) = 0.0d0 196 | r(i,ny) = 0.0d0 197 | end do 198 | 199 | do j=0,ny 200 | r(0,j) = 0.0d0 201 | r(nx,j) = 0.0d0 202 | end do 203 | 204 | return 205 | end 206 | 207 | !---------------------------------------------------------------------------! 208 | !Compute L2-norm for an array 209 | !---------------------------------------------------------------------------! 210 | SUBROUTINE l2norm(nx,ny,r,rms) 211 | implicit none 212 | integer::Nx,Ny 213 | real*8, dimension(0:nx,0:ny)::r 214 | integer::i,j 215 | real*8 ::rms 216 | 217 | rms=0.0d0 218 | do i=1,nx-1 219 | do j=1,ny-1 220 | rms = rms + r(i,j)*r(i,j) 221 | end do 222 | end do 223 | rms= dsqrt(rms/dfloat((nx-1)*(ny-1))) 224 | 225 | return 226 | end 227 | 228 | 229 | !---------------------------------------------------------------------------! 230 | !Restriction operators 231 | !---------------------------------------------------------------------------! 232 | SUBROUTINE rest(nxf,nyf,nxh,nyh,r,f) 233 | implicit none 234 | integer::nxf,nyf,nxh,nyh 235 | real*8, dimension(0:nxf,0:nyf)::r !on higher grid 236 | real*8, dimension(0:nxh,0:nyh)::f !on lower grid 237 | integer::i,j 238 | integer::ireo 239 | 240 | ireo = 3 241 | 242 | if (ireo.eq.1) then !simply injection 243 | 244 | do i=1,nxh-1 245 | do j=1,nyh-1 246 | f(i,j) = r(2*i,2*j) 247 | end do 248 | end do 249 | 250 | else if (ireo.eq.2) then !half-weight 251 | 252 | do i=1,nxh-1 253 | do j=1,nyh-1 254 | f(i,j) = 1.0d0/8.0d0*( 4.0d0*r(2*i,2*j) & 255 | + 1.0d0*(r(2*i+1,2*j)+r(2*i-1,2*j)+r(2*i,2*j+1)+r(2*i,2*j-1)) ) 256 | end do 257 | end do 258 | 259 | 260 | else !full-weight (trapezoidal) 261 | 262 | do i=1,nxh-1 263 | do j=1,nyh-1 264 | f(i,j) = 1.0d0/16.0d0*( 4.0d0*r(2*i,2*j) & 265 | + 2.0d0*(r(2*i+1,2*j)+r(2*i-1,2*j)+r(2*i,2*j+1)+r(2*i,2*j-1)) & 266 | + 1.0d0*(r(2*i+1,2*j+1)+r(2*i-1,2*j-1)+r(2*i-1,2*j+1)+r(2*i+1,2*j-1))) 267 | end do 268 | end do 269 | 270 | end if 271 | 272 | 273 | !update boundaries 274 | do i=0,nxh 275 | f(i,0) = r(2*i,0) 276 | f(i,nyh) = r(2*i,nyf) 277 | end do 278 | 279 | do j=0,nyh 280 | f(0,j) = r(0,2*j) 281 | f(nxh,j) = r(nxf,2*j) 282 | end do 283 | 284 | 285 | return 286 | end 287 | 288 | 289 | !---------------------------------------------------------------------------! 290 | !Prolongation operator 291 | !bilinear interpolation 292 | !---------------------------------------------------------------------------! 293 | SUBROUTINE prol(nxh,nyh,nxf,nyf,u,p) 294 | implicit none 295 | integer::nxf,nyf,nxh,nyh 296 | real*8, dimension(0:nxf,0:nyf)::p !on higher grid 297 | real*8, dimension(0:nxh,0:nyh)::u !on lower grid 298 | integer::i,j 299 | 300 | 301 | do i=0,nxh-1 302 | do j=0,nyh-1 303 | p(2*i,2*j) = u(i,j) 304 | p(2*i+1,2*j) = 1.0d0/2.0d0*(u(i,j)+u(i+1,j)) 305 | p(2*i,2*j+1) = 1.0d0/2.0d0*(u(i,j)+u(i,j+1)) 306 | p(2*i+1,2*j+1)= 1.0d0/4.0d0*(u(i,j)+u(i,j+1)+u(i+1,j)+u(i+1,j+1)) 307 | end do 308 | end do 309 | 310 | do j=0,nyh 311 | p(nxf,2*j) = u(nxh,j) 312 | end do 313 | do i=0,nxh 314 | p(2*i,nyf) = u(i,nyh) 315 | end do 316 | 317 | return 318 | end 319 | 320 | 321 | 322 | !---------------------------------------------------------------------------! 323 | !Gauss Seidel scheme (1 level) 324 | !---------------------------------------------------------------------------! 325 | SUBROUTINE GS(nx,ny,dx,dy,f,u,tol) 326 | implicit none 327 | integer::nx,ny 328 | real*8 ::dx,dy 329 | real*8 ::tol 330 | real*8,dimension(0:nx,0:ny)::u,f 331 | real*8,dimension(:,:),allocatable:: r 332 | real*8 ::rms0,rms 333 | integer::k,ke,wl,nI 334 | 335 | nI = 100000 !maximum number of iteration 336 | 337 | allocate(r(0:nx,0:ny)) 338 | 339 | !Compute initial resitual: 340 | call resid(nx,ny,dx,dy,f,u,r) 341 | 342 | !and its l2 norm: 343 | call l2norm(nx,ny,r,rms0) 344 | 345 | open(66,file='residual.plt') 346 | write(66,*) 'variables ="k","rms","rms/rms0"' 347 | 348 | do k=1,nI 349 | 350 | call relax(nx,ny,dx,dy,f,u) 351 | call resid(nx,ny,dx,dy,f,u,r) 352 | 353 | ! Check for convergence on smallest grid 354 | call l2norm(nx,ny,r,rms) 355 | if (rms/rms0.le.tol) goto 10 356 | 357 | ! Write residual history 358 | write(66,*) k,rms,rms/rms0 359 | write(*,*) k,rms,rms/rms0 360 | end do 361 | 362 | 10 continue 363 | close(66) 364 | 365 | deallocate(r) 366 | 367 | ke=k 368 | 369 | !work load (total number of operations) 370 | wl = ke*(nx*ny) 371 | 372 | write(19,*)"outer number of iteration = ",ke 373 | write(19,*)"normalized workload = ",dfloat(wl)/dfloat(nx*ny) 374 | write(*,*)"outer number of iteration = ",ke 375 | write(*,*)"normalized workload = ",dfloat(wl)/dfloat(nx*ny) 376 | 377 | return 378 | end 379 | 380 | 381 | 382 | 383 | !---------------------------------------------------------------------------! 384 | !Multigrid scheme (5 level) 385 | !Full-weighting is used as restriction operator 386 | !Bilinear interpolation procedure is used as prolongation operator 387 | !---------------------------------------------------------------------------! 388 | SUBROUTINE MG5(nx,ny,dx,dy,f,u,tol) 389 | implicit none 390 | integer::nx,ny 391 | real*8 ::dx,dy 392 | real*8 ::tol 393 | integer::nI,v1,v2,v3 394 | real*8,dimension(0:nx,0:ny)::u,f 395 | real*8,dimension(:,:),allocatable:: r,r2,r3,r4,r5 396 | real*8,dimension(:,:),allocatable:: p,p2,p3,p4 397 | real*8,dimension(:,:),allocatable:: u2,u3,u4,u5 398 | real*8,dimension(:,:),allocatable:: f2,f3,f4,f5 399 | real*8 ::dx2,dy2,dx3,dy3,dx4,dy4,dx5,dy5,rms0,rms,rmsc 400 | integer::i,j,k,ke,me,wl,m,nx2,ny2,nx3,ny3,nx4,ny4,nx5,ny5 401 | 402 | nI = 100000 !maximum number of outer iteration 403 | v1 = 2 !number of relaxation for restriction in V-cycle 404 | v2 = 2 !number of relaxation for prolongation in V-cycle 405 | v3 = 100 !number of relaxation at coarsest level 406 | 407 | 408 | dx2=dx*2.0d0 409 | dy2=dy*2.0d0 410 | 411 | dx3=dx*4.0d0 412 | dy3=dy*4.0d0 413 | 414 | dx4=dx*8.0d0 415 | dy4=dy*8.0d0 416 | 417 | dx5=dx*16.0d0 418 | dy5=dy*16.0d0 419 | 420 | nx2=nx/2 421 | ny2=ny/2 422 | 423 | nx3=nx/4 424 | ny3=ny/4 425 | 426 | nx4=nx/8 427 | ny4=ny/8 428 | 429 | nx5=nx/16 430 | ny5=ny/16 431 | 432 | me = 0 433 | 434 | if (nx5.lt.2.or.ny5.lt.2) then 435 | write(*,*)"5 level is high for this grid.." 436 | stop 437 | end if 438 | 439 | allocate(r (0:nx ,0:ny)) 440 | allocate(p (0:nx ,0:ny)) 441 | 442 | allocate(u2(0:nx2,0:ny2)) 443 | allocate(f2(0:nx2,0:ny2)) 444 | allocate(r2(0:nx2,0:ny2)) 445 | allocate(p2(0:nx2,0:ny2)) 446 | 447 | allocate(u3(0:nx3,0:ny3)) 448 | allocate(f3(0:nx3,0:ny3)) 449 | allocate(r3(0:nx3,0:ny3)) 450 | allocate(p3(0:nx3,0:ny3)) 451 | 452 | allocate(u4(0:nx4,0:ny4)) 453 | allocate(f4(0:nx4,0:ny4)) 454 | allocate(r4(0:nx4,0:ny4)) 455 | allocate(p4(0:nx4,0:ny4)) 456 | 457 | allocate(u5(0:nx5,0:ny5)) 458 | allocate(f5(0:nx5,0:ny5)) 459 | allocate(r5(0:nx5,0:ny5)) 460 | 461 | !Compute initial resitual: 462 | call resid(nx,ny,dx,dy,f,u,r) 463 | !and its l2 norm: 464 | call l2norm(nx,ny,r,rms0) 465 | 466 | open(66,file='residual.plt') 467 | write(66,*) 'variables ="k","rms","rms/rms0"' 468 | 469 | 470 | do k=1,nI 471 | 472 | !1.Relax v1 times 473 | do m=1,v1 474 | call relax(nx,ny,dx,dy,f,u) 475 | end do 476 | 477 | ! Compute residual 478 | call resid(nx,ny,dx,dy,f,u,r) 479 | 480 | ! Check for convergence on finest grid 481 | call l2norm(nx,ny,r,rms) 482 | write(66,*) k,rms,rms/rms0 483 | write(*,*) k,rms,rms/rms0 484 | if (rms/rms0.le.tol) goto 10 485 | 486 | !1r.Restriction 487 | call rest(nx,ny,nx2,ny2,r,f2) 488 | 489 | !Set zero 490 | do i=0,nx2 491 | do j=0,ny2 492 | u2(i,j)=0.0d0 493 | end do 494 | end do 495 | 496 | 497 | !2.Relax v1 times 498 | do m=1,v1 499 | call relax(nx2,ny2,dx2,dy2,f2,u2) 500 | end do 501 | 502 | ! Compute residual 503 | call resid(nx2,ny2,dx2,dy2,f2,u2,r2) 504 | 505 | !2r.Restriction 506 | call rest(nx2,ny2,nx3,ny3,r2,f3) 507 | 508 | 509 | !Set zero 510 | do i=0,nx3 511 | do j=0,ny3 512 | u3(i,j)=0.0d0 513 | end do 514 | end do 515 | 516 | 517 | !3.Relax v1 times 518 | do m=1,v1 519 | call relax(nx3,ny3,dx3,dy3,f3,u3) 520 | end do 521 | 522 | ! Compute residual 523 | call resid(nx3,ny3,dx3,dy3,f3,u3,r3) 524 | 525 | 526 | !3r.Restriction 527 | call rest(nx3,ny3,nx4,ny4,r3,f4) 528 | 529 | 530 | !Set zero 531 | do i=0,nx4 532 | do j=0,ny4 533 | u4(i,j)=0.0d0 534 | end do 535 | end do 536 | 537 | !4.Relax v1 times 538 | do m=1,v1 539 | call relax(nx4,ny4,dx4,dy4,f4,u4) 540 | end do 541 | 542 | 543 | ! Compute residual 544 | call resid(nx4,ny4,dx4,dy4,f4,u4,r4) 545 | 546 | 547 | !4r.Restriction 548 | call rest(nx4,ny4,nx5,ny5,r4,f5) 549 | 550 | 551 | !Set zero 552 | do i=0,nx5 553 | do j=0,ny5 554 | u5(i,j)=0.0d0 555 | end do 556 | end do 557 | 558 | !5.Relax v3 times (or it can be solved exactly) 559 | !call initial residual: 560 | call resid(nx5,ny5,dx5,dy5,f5,u5,r5) 561 | call l2norm(nx5,ny5,r5,rmsc) 562 | do m=1,v3 563 | call relax(nx5,ny5,dx5,dy5,f5,u5) 564 | call resid(nx5,ny5,dx5,dy5,f5,u5,r5) 565 | ! Check for convergence on smallest grid 566 | call l2norm(nx5,ny5,r5,rms) 567 | if (rms/rmsc.le.tol) goto 11 568 | end do 569 | 11 continue 570 | 571 | me = me + m 572 | 573 | !4p.Prolongation 574 | call prol(nx5,ny5,nx4,ny4,u5,p4) 575 | 576 | 577 | !Correct 578 | do i=1,nx4-1 579 | do j=1,ny4-1 580 | u4(i,j) = u4(i,j) + p4(i,j) 581 | end do 582 | end do 583 | 584 | !4.Relax v2 times 585 | do m=1,v2 586 | call relax(nx4,ny4,dx4,dy4,f4,u4) 587 | end do 588 | 589 | !3p.Prolongation 590 | call prol(nx4,ny4,nx3,ny3,u4,p3) 591 | 592 | !Correct 593 | do i=1,nx3-1 594 | do j=1,ny3-1 595 | u3(i,j) = u3(i,j) + p3(i,j) 596 | end do 597 | end do 598 | 599 | !3.Relax v2 times 600 | do m=1,v2 601 | call relax(nx3,ny3,dx3,dy3,f3,u3) 602 | end do 603 | 604 | !2p.Prolongation 605 | call prol(nx3,ny3,nx2,ny2,u3,p2) 606 | 607 | 608 | !Correct 609 | do i=1,nx2-1 610 | do j=1,ny2-1 611 | u2(i,j) = u2(i,j) + p2(i,j) 612 | end do 613 | end do 614 | 615 | !2.Relax v2 times 616 | do m=1,v2 617 | call relax(nx2,ny2,dx2,dy2,f2,u2) 618 | end do 619 | 620 | !1p.Prolongation 621 | call prol(nx2,ny2,nx,ny,u2,p) 622 | 623 | 624 | !Correct 625 | do i=1,nx-1 626 | do j=1,ny-1 627 | u(i,j) = u(i,j) + p(i,j) 628 | end do 629 | end do 630 | 631 | 632 | !1.Relax v2 times 633 | do m=1,v2 634 | call relax(nx,ny,dx,dy,f,u) 635 | end do 636 | 637 | end do ! Outer iteration loop 638 | 639 | 10 continue 640 | 641 | close(66) 642 | 643 | 644 | deallocate(r,p,u2,f2,r2,p2,u3,f3,r3,p3,u4,f4,r4,p4,u5,f5,r5) 645 | 646 | ke=k 647 | 648 | !work load (total number of operations) 649 | wl = ke*(v1+v2)*(nx*ny + nx2*ny2 + nx3*ny3 + nx4*ny4) + me*(nx5*ny5) 650 | 651 | write(19,*)"outer number of iteration = ",ke 652 | write(19,*)"normalized workload = ",dfloat(wl)/dfloat(nx*ny) 653 | write(*,*)"outer number of iteration = ",ke 654 | write(*,*)"normalized workload = ",dfloat(wl)/dfloat(nx*ny) 655 | 656 | return 657 | end 658 | 659 | 660 | --------------------------------------------------------------------------------