1! 2! This example shows how to avoid Fortran line lengths larger than 132 characters. 3! It avoids used of certain macros such as PetscCallA() and PetscCheckA() that 4! generate very long lines 5! 6! We recommend starting from src/snes/tutorials/ex5f90.F90 instead of this example 7! because that does not have the restricted formatting that makes this version 8! more difficult to read 9! 10! Description: This example solves a nonlinear system in parallel with SNES. 11! We solve the Bratu (SFI - solid fuel ignition) problem in a 2D rectangular 12! domain, using distributed arrays (DMDAs) to partition the parallel grid. 13! The command line options include: 14! -par <param>, where <param> indicates the nonlinearity of the problem 15! problem SFI: <parameter> = Bratu parameter (0 <= par <= 6.81) 16! 17! -------------------------------------------------------------------------- 18! 19! Solid Fuel Ignition (SFI) problem. This problem is modeled by 20! the partial differential equation 21! 22! -Laplacian u - lambda*exp(u) = 0, 0 < x,y < 1, 23! 24! with boundary conditions 25! 26! u = 0 for x = 0, x = 1, y = 0, y = 1. 27! 28! A finite difference approximation with the usual 5-point stencil 29! is used to discretize the boundary value problem to obtain a nonlinear 30! system of equations. 31! 32! -------------------------------------------------------------------------- 33 module ex5fmodule 34 use petscsnes 35 use petscdmda 36#include <petsc/finclude/petscsnes.h> 37#include <petsc/finclude/petscdm.h> 38#include <petsc/finclude/petscdmda.h> 39 PetscInt xs,xe,xm,gxs,gxe,gxm 40 PetscInt ys,ye,ym,gys,gye,gym 41 PetscInt mx,my 42 PetscMPIInt rank,size 43 PetscReal lambda 44 end module ex5fmodule 45 46 program main 47 use ex5fmodule 48 implicit none 49 50! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 51! Variable declarations 52! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 53! 54! Variables: 55! snes - nonlinear solver 56! x, r - solution, residual vectors 57! its - iterations for convergence 58! 59! See additional variable declarations in the file ex5f.h 60! 61 SNES snes 62 Vec x,r 63 PetscInt its,i1,i4 64 PetscErrorCode ierr 65 PetscReal lambda_max,lambda_min 66 PetscBool flg 67 DM da 68 69! Note: Any user-defined Fortran routines (such as FormJacobianLocal) 70! MUST be declared as external. 71 72 external FormInitialGuess 73 external FormFunctionLocal,FormJacobianLocal 74 external MySNESConverged 75 76! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 77! Initialize program 78! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 79 80 call PetscInitialize(ierr) 81 CHKERRA(ierr) 82 call MPI_Comm_size(PETSC_COMM_WORLD,size,ierr) 83 CHKERRMPIA(ierr) 84 call MPI_Comm_rank(PETSC_COMM_WORLD,rank,ierr) 85 CHKERRMPIA(ierr) 86! Initialize problem parameters 87 88 i1 = 1 89 i4 = 4 90 lambda_max = 6.81 91 lambda_min = 0.0 92 lambda = 6.0 93 call PetscOptionsGetReal(PETSC_NULL_OPTIONS,PETSC_NULL_CHARACTER,'-par',lambda,PETSC_NULL_BOOL,ierr) 94 CHKERRA(ierr) 95 96! this statement is split into multiple-lines to keep lines under 132 char limit - required by 'make check' 97 if (lambda .ge. lambda_max .or. lambda .le. lambda_min) then 98 ierr = PETSC_ERR_ARG_OUTOFRANGE; 99 SETERRA(PETSC_COMM_WORLD,ierr,'Lambda') 100 endif 101 102! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 103! Create nonlinear solver context 104! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 105 106 call SNESCreate(PETSC_COMM_WORLD,snes,ierr) 107 CHKERRA(ierr) 108 109! Set convergence test routine if desired 110 111 call PetscOptionsHasName(PETSC_NULL_OPTIONS,PETSC_NULL_CHARACTER,'-my_snes_convergence',flg,ierr) 112 CHKERRA(ierr) 113 if (flg) then 114 call SNESSetConvergenceTest(snes,MySNESConverged,0,PETSC_NULL_FUNCTION,ierr) 115 CHKERRA(ierr) 116 endif 117 118! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 119! Create vector data structures; set function evaluation routine 120! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 121 122! Create distributed array (DMDA) to manage parallel grid and vectors 123 124! This really needs only the star-type stencil, but we use the box stencil 125 126 call DMDACreate2d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,DM_BOUNDARY_NONE,DMDA_STENCIL_STAR,i4,i4,PETSC_DECIDE,PETSC_DECIDE, & 127 i1,i1, PETSC_NULL_INTEGER,PETSC_NULL_INTEGER,da,ierr) 128 CHKERRA(ierr) 129 call DMSetFromOptions(da,ierr) 130 CHKERRA(ierr) 131 call DMSetUp(da,ierr) 132 CHKERRA(ierr) 133 134! Extract global and local vectors from DMDA; then duplicate for remaining 135! vectors that are the same types 136 137 call DMCreateGlobalVector(da,x,ierr) 138 CHKERRA(ierr) 139 call VecDuplicate(x,r,ierr) 140 CHKERRA(ierr) 141 142! Get local grid boundaries (for 2-dimensional DMDA) 143 144 call DMDAGetInfo(da,PETSC_NULL_INTEGER,mx,my,PETSC_NULL_INTEGER,PETSC_NULL_INTEGER,PETSC_NULL_INTEGER, & 145 PETSC_NULL_INTEGER,PETSC_NULL_INTEGER,PETSC_NULL_INTEGER,PETSC_NULL_INTEGER,PETSC_NULL_INTEGER, & 146 PETSC_NULL_INTEGER,PETSC_NULL_INTEGER,ierr) 147 CHKERRA(ierr) 148 call DMDAGetCorners(da,xs,ys,PETSC_NULL_INTEGER,xm,ym,PETSC_NULL_INTEGER,ierr) 149 CHKERRA(ierr) 150 call DMDAGetGhostCorners(da,gxs,gys,PETSC_NULL_INTEGER,gxm,gym,PETSC_NULL_INTEGER,ierr) 151 CHKERRA(ierr) 152 153! Here we shift the starting indices up by one so that we can easily 154! use the Fortran convention of 1-based indices (rather 0-based indices). 155 156 xs = xs+1 157 ys = ys+1 158 gxs = gxs+1 159 gys = gys+1 160 161 ye = ys+ym-1 162 xe = xs+xm-1 163 gye = gys+gym-1 164 gxe = gxs+gxm-1 165 166! Set function evaluation routine and vector 167 168 call DMDASNESSetFunctionLocal(da,INSERT_VALUES,FormFunctionLocal,da,ierr) 169 CHKERRA(ierr) 170 call DMDASNESSetJacobianLocal(da,FormJacobianLocal,da,ierr) 171 CHKERRA(ierr) 172 call SNESSetDM(snes,da,ierr) 173 CHKERRA(ierr) 174 175! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 176! Customize nonlinear solver; set runtime options 177! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 178 179! Set runtime options (e.g., -snes_monitor -snes_rtol <rtol> -ksp_type <type>) 180 181 call SNESSetFromOptions(snes,ierr) 182 CHKERRA(ierr) 183! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 184! Evaluate initial guess; then solve nonlinear system. 185! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 186 187! Note: The user should initialize the vector, x, with the initial guess 188! for the nonlinear solver prior to calling SNESSolve(). In particular, 189! to employ an initial guess of zero, the user should explicitly set 190! this vector to zero by calling VecSet(). 191 192 call FormInitialGuess(x,ierr) 193 CHKERRA(ierr) 194 call SNESSolve(snes,PETSC_NULL_VEC,x,ierr) 195 CHKERRA(ierr) 196 call SNESGetIterationNumber(snes,its,ierr) 197 CHKERRA(ierr) 198 if (rank .eq. 0) then 199 write(6,100) its 200 endif 201 100 format('Number of SNES iterations = ',i5) 202 203! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 204! Free work space. All PETSc objects should be destroyed when they 205! are no longer needed. 206! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 207 208 call VecDestroy(x,ierr) 209 CHKERRA(ierr) 210 call VecDestroy(r,ierr) 211 CHKERRA(ierr) 212 call SNESDestroy(snes,ierr) 213 CHKERRA(ierr) 214 call DMDestroy(da,ierr) 215 CHKERRA(ierr) 216 call PetscFinalize(ierr) 217 CHKERRA(ierr) 218 end 219 220! --------------------------------------------------------------------- 221! 222! FormInitialGuess - Forms initial approximation. 223! 224! Input Parameters: 225! X - vector 226! 227! Output Parameter: 228! X - vector 229! 230! Notes: 231! This routine serves as a wrapper for the lower-level routine 232! "ApplicationInitialGuess", where the actual computations are 233! done using the standard Fortran style of treating the local 234! vector data as a multidimensional array over the local mesh. 235! This routine merely handles ghost point scatters and accesses 236! the local vector data via VecGetArrayF90() and VecRestoreArrayF90(). 237! 238 subroutine FormInitialGuess(X,ierr) 239 use ex5fmodule 240 implicit none 241 242! Input/output variables: 243 Vec X 244 PetscErrorCode ierr 245! Declarations for use with local arrays: 246 PetscScalar, pointer :: lx_v(:) 247 248 ierr = 0 249 250! Get a pointer to vector data. 251! - For default PETSc vectors, VecGetArray() returns a pointer to 252! the data array. Otherwise, the routine is implementation dependent. 253! - You MUST call VecRestoreArray() when you no longer need access to 254! the array. 255! - Note that the Fortran interface to VecGetArray() differs from the 256! C version. See the users manual for details. 257 258 call VecGetArrayF90(X,lx_v,ierr) 259 CHKERRQ(ierr) 260 261! Compute initial guess over the locally owned part of the grid 262 263 call InitialGuessLocal(lx_v,ierr) 264 CHKERRQ(ierr) 265 266! Restore vector 267 268 call VecRestoreArrayF90(X,lx_v,ierr) 269 CHKERRQ(ierr) 270 271 end 272 273! --------------------------------------------------------------------- 274! 275! InitialGuessLocal - Computes initial approximation, called by 276! the higher level routine FormInitialGuess(). 277! 278! Input Parameter: 279! x - local vector data 280! 281! Output Parameters: 282! x - local vector data 283! ierr - error code 284! 285! Notes: 286! This routine uses standard Fortran-style computations over a 2-dim array. 287! 288 subroutine InitialGuessLocal(x,ierr) 289 use ex5fmodule 290 implicit none 291 292! Input/output variables: 293 PetscScalar x(xs:xe,ys:ye) 294 PetscErrorCode ierr 295 296! Local variables: 297 PetscInt i,j 298 PetscReal temp1,temp,one,hx,hy 299 300! Set parameters 301 302 ierr = 0 303 one = 1.0 304 hx = one/((mx-1)) 305 hy = one/((my-1)) 306 temp1 = lambda/(lambda + one) 307 308 do 20 j=ys,ye 309 temp = (min(j-1,my-j))*hy 310 do 10 i=xs,xe 311 if (i .eq. 1 .or. j .eq. 1 .or. i .eq. mx .or. j .eq. my) then 312 x(i,j) = 0.0 313 else 314 x(i,j) = temp1 * sqrt(min(min(i-1,mx-i)*hx,(temp))) 315 endif 316 10 continue 317 20 continue 318 319 end 320 321! --------------------------------------------------------------------- 322! 323! FormFunctionLocal - Computes nonlinear function, called by 324! the higher level routine FormFunction(). 325! 326! Input Parameter: 327! x - local vector data 328! 329! Output Parameters: 330! f - local vector data, f(x) 331! ierr - error code 332! 333! Notes: 334! This routine uses standard Fortran-style computations over a 2-dim array. 335! 336! 337 subroutine FormFunctionLocal(info,x,f,da,ierr) 338 use ex5fmodule 339 implicit none 340 341 DM da 342 343! Input/output variables: 344 DMDALocalInfo info(DMDA_LOCAL_INFO_SIZE) 345 PetscScalar x(gxs:gxe,gys:gye) 346 PetscScalar f(xs:xe,ys:ye) 347 PetscErrorCode ierr 348 349! Local variables: 350 PetscScalar two,one,hx,hy 351 PetscScalar hxdhy,hydhx,sc 352 PetscScalar u,uxx,uyy 353 PetscInt i,j 354 355 xs = info(DMDA_LOCAL_INFO_XS)+1 356 xe = xs+info(DMDA_LOCAL_INFO_XM)-1 357 ys = info(DMDA_LOCAL_INFO_YS)+1 358 ye = ys+info(DMDA_LOCAL_INFO_YM)-1 359 mx = info(DMDA_LOCAL_INFO_MX) 360 my = info(DMDA_LOCAL_INFO_MY) 361 362 one = 1.0 363 two = 2.0 364 hx = one/(mx-1) 365 hy = one/(my-1) 366 sc = hx*hy*lambda 367 hxdhy = hx/hy 368 hydhx = hy/hx 369 370! Compute function over the locally owned part of the grid 371 372 do 20 j=ys,ye 373 do 10 i=xs,xe 374 if (i .eq. 1 .or. j .eq. 1 .or. i .eq. mx .or. j .eq. my) then 375 f(i,j) = x(i,j) 376 else 377 u = x(i,j) 378 uxx = hydhx * (two*u - x(i-1,j) - x(i+1,j)) 379 uyy = hxdhy * (two*u - x(i,j-1) - x(i,j+1)) 380 f(i,j) = uxx + uyy - sc*exp(u) 381 endif 382 10 continue 383 20 continue 384 385 call PetscLogFlops(11.0d0*ym*xm,ierr) 386 CHKERRQ(ierr) 387 388 end 389 390! --------------------------------------------------------------------- 391! 392! FormJacobianLocal - Computes Jacobian matrix, called by 393! the higher level routine FormJacobian(). 394! 395! Input Parameters: 396! x - local vector data 397! 398! Output Parameters: 399! jac - Jacobian matrix 400! jac_prec - optionally different preconditioning matrix (not used here) 401! ierr - error code 402! 403! Notes: 404! This routine uses standard Fortran-style computations over a 2-dim array. 405! 406! Notes: 407! Due to grid point reordering with DMDAs, we must always work 408! with the local grid points, and then transform them to the new 409! global numbering with the "ltog" mapping 410! We cannot work directly with the global numbers for the original 411! uniprocessor grid! 412! 413! Two methods are available for imposing this transformation 414! when setting matrix entries: 415! (A) MatSetValuesLocal(), using the local ordering (including 416! ghost points!) 417! by calling MatSetValuesLocal() 418! (B) MatSetValues(), using the global ordering 419! - Use DMDAGetGlobalIndices() to extract the local-to-global map 420! - Then apply this map explicitly yourself 421! - Set matrix entries using the global ordering by calling 422! MatSetValues() 423! Option (A) seems cleaner/easier in many cases, and is the procedure 424! used in this example. 425! 426 subroutine FormJacobianLocal(info,x,A,jac,da,ierr) 427 use ex5fmodule 428 implicit none 429 430 DM da 431 432! Input/output variables: 433 PetscScalar x(gxs:gxe,gys:gye) 434 Mat A,jac 435 PetscErrorCode ierr 436 DMDALocalInfo info(DMDA_LOCAL_INFO_SIZE) 437 438! Local variables: 439 PetscInt row,col(5),i,j,i1,i5 440 PetscScalar two,one,hx,hy,v(5) 441 PetscScalar hxdhy,hydhx,sc 442 443! Set parameters 444 445 i1 = 1 446 i5 = 5 447 one = 1.0 448 two = 2.0 449 hx = one/(mx-1) 450 hy = one/(my-1) 451 sc = hx*hy 452 hxdhy = hx/hy 453 hydhx = hy/hx 454 455! Compute entries for the locally owned part of the Jacobian. 456! - Currently, all PETSc parallel matrix formats are partitioned by 457! contiguous chunks of rows across the processors. 458! - Each processor needs to insert only elements that it owns 459! locally (but any non-local elements will be sent to the 460! appropriate processor during matrix assembly). 461! - Here, we set all entries for a particular row at once. 462! - We can set matrix entries either using either 463! MatSetValuesLocal() or MatSetValues(), as discussed above. 464! - Note that MatSetValues() uses 0-based row and column numbers 465! in Fortran as well as in C. 466 467 do 20 j=ys,ye 468 row = (j - gys)*gxm + xs - gxs - 1 469 do 10 i=xs,xe 470 row = row + 1 471! boundary points 472 if (i .eq. 1 .or. j .eq. 1 .or. i .eq. mx .or. j .eq. my) then 473! Some f90 compilers need 4th arg to be of same type in both calls 474 col(1) = row 475 v(1) = one 476 call MatSetValuesLocal(jac,i1,row,i1,col,v,INSERT_VALUES,ierr) 477 CHKERRQ(ierr) 478! interior grid points 479 else 480 v(1) = -hxdhy 481 v(2) = -hydhx 482 v(3) = two*(hydhx + hxdhy) - sc*lambda*exp(x(i,j)) 483 v(4) = -hydhx 484 v(5) = -hxdhy 485 col(1) = row - gxm 486 col(2) = row - 1 487 col(3) = row 488 col(4) = row + 1 489 col(5) = row + gxm 490 call MatSetValuesLocal(jac,i1,row,i5,col,v, INSERT_VALUES,ierr) 491 CHKERRQ(ierr) 492 endif 493 10 continue 494 20 continue 495 call MatAssemblyBegin(jac,MAT_FINAL_ASSEMBLY,ierr) 496 CHKERRQ(ierr) 497 call MatAssemblyEnd(jac,MAT_FINAL_ASSEMBLY,ierr) 498 CHKERRQ(ierr) 499 if (A .ne. jac) then 500 call MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY,ierr) 501 CHKERRQ(ierr) 502 call MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY,ierr) 503 CHKERRQ(ierr) 504 endif 505 end 506 507! 508! Simple convergence test based on the infinity norm of the residual being small 509! 510 subroutine MySNESConverged(snes,it,xnorm,snorm,fnorm,reason,dummy,ierr) 511 use ex5fmodule 512 implicit none 513 514 SNES snes 515 PetscInt it,dummy 516 PetscReal xnorm,snorm,fnorm,nrm 517 SNESConvergedReason reason 518 Vec f 519 PetscErrorCode ierr 520 521 call SNESGetFunction(snes,f,PETSC_NULL_FUNCTION,dummy,ierr) 522 CHKERRQ(ierr) 523 call VecNorm(f,NORM_INFINITY,nrm,ierr) 524 CHKERRQ(ierr) 525 if (nrm .le. 1.e-5) reason = SNES_CONVERGED_FNORM_ABS 526 527 end 528 529!/*TEST 530! 531! build: 532! requires: !complex !single 533! 534! test: 535! nsize: 4 536! args: -snes_mf -pc_type none -da_processors_x 4 -da_processors_y 1 -snes_monitor_short \ 537! -ksp_gmres_cgs_refinement_type refine_always 538! 539! test: 540! suffix: 2 541! nsize: 4 542! args: -da_processors_x 2 -da_processors_y 2 -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always 543! 544! test: 545! suffix: 3 546! nsize: 3 547! args: -snes_fd -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always 548! 549! test: 550! suffix: 6 551! nsize: 1 552! args: -snes_monitor_short -my_snes_convergence 553! 554!TEST*/ 555