xref: /petsc/src/snes/tutorials/ex5f90t.F90 (revision ceeae6b5899f2879f7a95602f98efecbe51ff614)
1!
2!  Description: Solves a nonlinear system in parallel with SNES.
3!  We solve the  Bratu (SFI - solid fuel ignition) problem in a 2D rectangular
4!  domain, using distributed arrays (DMDAs) to partition the parallel grid.
5!  The command line options include:
6!    -par <parameter>, where <parameter> indicates the nonlinearity of the problem
7!       problem SFI:  <parameter> = Bratu parameter (0 <= par <= 6.81)
8!
9!
10!  --------------------------------------------------------------------------
11!
12!  Solid Fuel Ignition (SFI) problem.  This problem is modeled by
13!  the partial differential equation
14!
15!          -Laplacian u - lambda*exp(u) = 0,  0 < x,y < 1,
16!
17!  with boundary conditions
18!
19!           u = 0  for  x = 0, x = 1, y = 0, y = 1.
20!
21!  A finite difference approximation with the usual 5-point stencil
22!  is used to discretize the boundary value problem to obtain a nonlinear
23!  system of equations.
24!
25!  The uniprocessor version of this code is snes/tutorials/ex4f.F
26!
27!  --------------------------------------------------------------------------
28!  The following define must be used before including any PETSc include files
29!  into a module or interface. This is because they can't handle declarations
30!  in them
31!
32#include <petsc/finclude/petscdmda.h>
33#include <petsc/finclude/petscsnes.h>
34#include <petsc/finclude/petscsys.h>
35#include <petsc/finclude/petscmat.h>
36module ex5f90tmodule
37  use petscdmda
38  type userctx
39    type(tDM) da
40    PetscInt xs, xe, xm, gxs, gxe, gxm
41    PetscInt ys, ye, ym, gys, gye, gym
42    PetscInt mx, my
43    PetscMPIInt rank
44    PetscReal lambda
45  end type userctx
46
47contains
48! ---------------------------------------------------------------------
49!
50!  FormFunction - Evaluates nonlinear function, F(x).
51!
52!  Input Parameters:
53!  snes - the SNES context
54!  X - input vector
55!  dummy - optional user-defined context, as set by SNESSetFunction()
56!          (not used here)
57!
58!  Output Parameter:
59!  F - function vector
60!
61!  Notes:
62!  This routine serves as a wrapper for the lower-level routine
63!  "FormFunctionLocal", where the actual computations are
64!  done using the standard Fortran style of treating the local
65!  vector data as a multidimensional array over the local mesh.
66!  This routine merely handles ghost point scatters and accesses
67!  the local vector data via VecGetArray() and VecRestoreArray().
68!
69  subroutine FormFunction(snesIn, X, F, user, ierr)
70    use petscsnes
71    use petscdmda
72
73!  Input/output variables:
74    type(tSNES) snesIn
75    type(tVec) X, F
76    PetscErrorCode ierr
77    type(userctx) user
78
79!  Declarations for use with local arrays:
80    PetscScalar, pointer :: lx_v(:), lf_v(:)
81    type(tVec) localX
82
83!  Scatter ghost points to local vector, using the 2-step process
84!     DMGlobalToLocalBegin(), DMGlobalToLocalEnd().
85!  By placing code between these two statements, computations can
86!  be done while messages are in transition.
87    PetscCall(DMGetLocalVector(user%da, localX, ierr))
88    PetscCall(DMGlobalToLocalBegin(user%da, X, INSERT_VALUES, localX, ierr))
89    PetscCall(DMGlobalToLocalEnd(user%da, X, INSERT_VALUES, localX, ierr))
90
91!  Get a pointer to vector data.
92!    - VecGetArray90() returns a pointer to the data array.
93!    - You MUST call VecRestoreArray() when you no longer need access to
94!      the array.
95
96    PetscCall(VecGetArray(localX, lx_v, ierr))
97    PetscCall(VecGetArray(F, lf_v, ierr))
98
99!  Compute function over the locally owned part of the grid
100    PetscCall(FormFunctionLocal(lx_v, lf_v, user, ierr))
101
102!  Restore vectors
103    PetscCall(VecRestoreArray(localX, lx_v, ierr))
104    PetscCall(VecRestoreArray(F, lf_v, ierr))
105
106!  Insert values into global vector
107
108    PetscCall(DMRestoreLocalVector(user%da, localX, ierr))
109    PetscCall(PetscLogFlops(11.0d0*user%ym*user%xm, ierr))
110
111!      PetscCall(VecView(X,PETSC_VIEWER_STDOUT_WORLD,ierr))
112!      PetscCall(VecView(F,PETSC_VIEWER_STDOUT_WORLD,ierr))
113  end subroutine formfunction
114end module ex5f90tmodule
115
116module f90moduleinterfacest
117  use ex5f90tmodule
118
119  Interface SNESSetApplicationContext
120    Subroutine SNESSetApplicationContext(snesIn, ctx, ierr)
121      use petscsnes
122      use ex5f90tmodule
123      type(tSNES) snesIn
124      type(userctx) ctx
125      PetscErrorCode ierr
126    End Subroutine
127  End Interface SNESSetApplicationContext
128
129  Interface SNESGetApplicationContext
130    Subroutine SNESGetApplicationContext(snesIn, ctx, ierr)
131      use petscsnes
132      use ex5f90tmodule
133      type(tSNES) snesIn
134      type(userctx), pointer :: ctx
135      PetscErrorCode ierr
136    End Subroutine
137  End Interface SNESGetApplicationContext
138end module f90moduleinterfacest
139
140program main
141  use petscdmda
142  use petscsnes
143  use ex5f90tmodule
144  use f90moduleinterfacest
145  implicit none
146! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
147!                   Variable declarations
148! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
149!
150!  Variables:
151!     mysnes      - nonlinear solver
152!     x, r        - solution, residual vectors
153!     J           - Jacobian matrix
154!     its         - iterations for convergence
155!     Nx, Ny      - number of preocessors in x- and y- directions
156!     matrix_free - flag - 1 indicates matrix-free version
157!
158  type(tSNES) mysnes
159  type(tVec) x, r
160  type(tMat) J
161  PetscErrorCode ierr
162  PetscInt its
163  PetscBool flg, matrix_free, set
164  PetscInt ione, nfour
165  PetscReal lambda_max, lambda_min
166  type(userctx) user
167  type(userctx), pointer:: puser
168  type(tPetscOptions) :: options
169
170!  Note: Any user-defined Fortran routines (such as FormJacobian)
171!  MUST be declared as external.
172  external FormInitialGuess, FormJacobian
173
174! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
175!  Initialize program
176! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
177  PetscCallA(PetscInitialize(ierr))
178  PetscCallMPIA(MPI_Comm_rank(PETSC_COMM_WORLD, user%rank, ierr))
179
180!  Initialize problem parameters
181  options%v = 0
182  lambda_max = 6.81
183  lambda_min = 0.0
184  user%lambda = 6.0
185  ione = 1
186  nfour = 4
187  PetscCallA(PetscOptionsGetReal(options, PETSC_NULL_CHARACTER, '-par', user%lambda, flg, ierr))
188  PetscCheckA(user%lambda < lambda_max .and. user%lambda > lambda_min, PETSC_COMM_SELF, PETSC_ERR_USER, 'Lambda provided with -par is out of range')
189
190! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
191!  Create nonlinear solver context
192! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
193  PetscCallA(SNESCreate(PETSC_COMM_WORLD, mysnes, ierr))
194
195! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
196!  Create vector data structures; set function evaluation routine
197! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
198
199!  Create distributed array (DMDA) to manage parallel grid and vectors
200
201! This really needs only the star-type stencil, but we use the box
202! stencil temporarily.
203  PetscCallA(DMDACreate2d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DMDA_STENCIL_BOX, nfour, nfour, PETSC_DECIDE, PETSC_DECIDE, ione, ione, PETSC_NULL_INTEGER_ARRAY, PETSC_NULL_INTEGER_ARRAY, user%da, ierr))
204  PetscCallA(DMSetFromOptions(user%da, ierr))
205  PetscCallA(DMSetUp(user%da, ierr))
206  PetscCallA(DMDAGetInfo(user%da, PETSC_NULL_INTEGER, user%mx, user%my, PETSC_NULL_INTEGER, PETSC_NULL_INTEGER, PETSC_NULL_INTEGER, PETSC_NULL_INTEGER, PETSC_NULL_INTEGER, PETSC_NULL_INTEGER, PETSC_NULL_DMBOUNDARYTYPE, PETSC_NULL_DMBOUNDARYTYPE, PETSC_NULL_DMBOUNDARYTYPE, PETSC_NULL_DMDASTENCILTYPE, ierr))
207
208!
209!   Visualize the distribution of the array across the processors
210!
211!     PetscCallA(DMView(user%da,PETSC_VIEWER_DRAW_WORLD,ierr))
212
213!  Extract global and local vectors from DMDA; then duplicate for remaining
214!  vectors that are the same types
215  PetscCallA(DMCreateGlobalVector(user%da, x, ierr))
216  PetscCallA(VecDuplicate(x, r, ierr))
217
218!  Get local grid boundaries (for 2-dimensional DMDA)
219  PetscCallA(DMDAGetCorners(user%da, user%xs, user%ys, PETSC_NULL_INTEGER, user%xm, user%ym, PETSC_NULL_INTEGER, ierr))
220  PetscCallA(DMDAGetGhostCorners(user%da, user%gxs, user%gys, PETSC_NULL_INTEGER, user%gxm, user%gym, PETSC_NULL_INTEGER, ierr))
221
222!  Here we shift the starting indices up by one so that we can easily
223!  use the Fortran convention of 1-based indices (rather 0-based indices).
224  user%xs = user%xs + 1
225  user%ys = user%ys + 1
226  user%gxs = user%gxs + 1
227  user%gys = user%gys + 1
228
229  user%ye = user%ys + user%ym - 1
230  user%xe = user%xs + user%xm - 1
231  user%gye = user%gys + user%gym - 1
232  user%gxe = user%gxs + user%gxm - 1
233
234  PetscCallA(SNESSetApplicationContext(mysnes, user, ierr))
235
236!  Set function evaluation routine and vector
237  PetscCallA(SNESSetFunction(mysnes, r, FormFunction, user, ierr))
238
239! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
240!  Create matrix data structure; set Jacobian evaluation routine
241! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
242
243!  Set Jacobian matrix data structure and default Jacobian evaluation
244!  routine. User can override with:
245!     -snes_fd : default finite differencing approximation of Jacobian
246!     -snes_mf : matrix-free Newton-Krylov method with no preconditioning
247!                (unless user explicitly sets preconditioner)
248!     -snes_mf_operator : form matrix used to construct the preconditioner as set by the user,
249!                         but use matrix-free approx for Jacobian-vector
250!                         products within Newton-Krylov method
251!
252!  Note:  For the parallel case, vectors and matrices MUST be partitioned
253!     accordingly.  When using distributed arrays (DMDAs) to create vectors,
254!     the DMDAs determine the problem partitioning.  We must explicitly
255!     specify the local matrix dimensions upon its creation for compatibility
256!     with the vector distribution.  Thus, the generic MatCreate() routine
257!     is NOT sufficient when working with distributed arrays.
258!
259!     Note: Here we only approximately preallocate storage space for the
260!     Jacobian.  See the users manual for a discussion of better techniques
261!     for preallocating matrix memory.
262
263  PetscCallA(PetscOptionsHasName(options, PETSC_NULL_CHARACTER, '-snes_mf', matrix_free, ierr))
264  if (.not. matrix_free) then
265    PetscCallA(DMSetMatType(user%da, MATAIJ, ierr))
266    PetscCallA(DMCreateMatrix(user%da, J, ierr))
267    PetscCallA(SNESSetJacobian(mysnes, J, J, FormJacobian, user, ierr))
268  end if
269
270! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
271!  Customize nonlinear solver; set runtime options
272! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
273!  Set runtime options (e.g., -snes_monitor -snes_rtol <rtol> -ksp_type <type>)
274  PetscCallA(SNESSetFromOptions(mysnes, ierr))
275
276!     Test Fortran90 wrapper for SNESSet/Get ApplicationContext()
277  PetscCallA(PetscOptionsGetBool(options, PETSC_NULL_CHARACTER, '-test_appctx', flg, set, ierr))
278  if (flg) then
279    PetscCallA(SNESGetApplicationContext(mysnes, puser, ierr))
280  end if
281
282! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
283!  Evaluate initial guess; then solve nonlinear system.
284! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
285!  Note: The user should initialize the vector, x, with the initial guess
286!  for the nonlinear solver prior to calling SNESSolve().  In particular,
287!  to employ an initial guess of zero, the user should explicitly set
288!  this vector to zero by calling VecSet().
289
290  PetscCallA(FormInitialGuess(mysnes, x, ierr))
291  PetscCallA(SNESSolve(mysnes, PETSC_NULL_VEC, x, ierr))
292  PetscCallA(SNESGetIterationNumber(mysnes, its, ierr))
293  if (user%rank == 0) then
294    write (6, 100) its
295  end if
296100 format('Number of SNES iterations = ', i5)
297
298! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
299!  Free work space.  All PETSc objects should be destroyed when they
300!  are no longer needed.
301! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
302  if (.not. matrix_free) PetscCallA(MatDestroy(J, ierr))
303  PetscCallA(VecDestroy(x, ierr))
304  PetscCallA(VecDestroy(r, ierr))
305  PetscCallA(SNESDestroy(mysnes, ierr))
306  PetscCallA(DMDestroy(user%da, ierr))
307
308  PetscCallA(PetscFinalize(ierr))
309end
310
311! ---------------------------------------------------------------------
312!
313!  FormInitialGuess - Forms initial approximation.
314!
315!  Input Parameters:
316!  X - vector
317!
318!  Output Parameter:
319!  X - vector
320!
321!  Notes:
322!  This routine serves as a wrapper for the lower-level routine
323!  "InitialGuessLocal", where the actual computations are
324!  done using the standard Fortran style of treating the local
325!  vector data as a multidimensional array over the local mesh.
326!  This routine merely handles ghost point scatters and accesses
327!  the local vector data via VecGetArray() and VecRestoreArray().
328!
329subroutine FormInitialGuess(mysnes, X, ierr)
330  use petscsnes
331  use ex5f90tmodule
332  use f90moduleinterfacest
333!  Input/output variables:
334  type(tSNES) mysnes
335  type(userctx), pointer:: puser
336  type(tVec) X
337  PetscErrorCode ierr
338
339!  Declarations for use with local arrays:
340  PetscScalar, pointer :: lx_v(:)
341
342  ierr = 0
343  PetscCallA(SNESGetApplicationContext(mysnes, puser, ierr))
344!  Get a pointer to vector data.
345!    - VecGetArray90() returns a pointer to the data array.
346!    - You MUST call VecRestoreArray() when you no longer need access to
347!      the array.
348
349  PetscCallA(VecGetArray(X, lx_v, ierr))
350
351!  Compute initial guess over the locally owned part of the grid
352  PetscCallA(InitialGuessLocal(puser, lx_v, ierr))
353
354!  Restore vector
355  PetscCallA(VecRestoreArray(X, lx_v, ierr))
356
357!  Insert values into global vector
358
359end
360
361! ---------------------------------------------------------------------
362!
363!  InitialGuessLocal - Computes initial approximation, called by
364!  the higher level routine FormInitialGuess().
365!
366!  Input Parameter:
367!  x - local vector data
368!
369!  Output Parameters:
370!  x - local vector data
371!  ierr - error code
372!
373!  Notes:
374!  This routine uses standard Fortran-style computations over a 2-dim array.
375!
376subroutine InitialGuessLocal(user, x, ierr)
377  use petscsys
378  use ex5f90tmodule
379!  Input/output variables:
380  type(userctx) user
381  PetscScalar x(user%xs:user%xe, user%ys:user%ye)
382  PetscErrorCode ierr
383
384!  Local variables:
385  PetscInt i, j
386  PetscScalar temp1, temp, hx, hy
387  PetscScalar one
388
389!  Set parameters
390
391  ierr = 0
392  one = 1.0
393  hx = one/(PetscIntToReal(user%mx - 1))
394  hy = one/(PetscIntToReal(user%my - 1))
395  temp1 = user%lambda/(user%lambda + one)
396
397  do j = user%ys, user%ye
398    temp = PetscIntToReal(min(j - 1, user%my - j))*hy
399    do i = user%xs, user%xe
400      if (i == 1 .or. j == 1 .or. i == user%mx .or. j == user%my) then
401        x(i, j) = 0.0
402      else
403        x(i, j) = temp1*sqrt(min(PetscIntToReal(min(i - 1, user%mx - i)*hx), PetscIntToReal(temp)))
404      end if
405    end do
406  end do
407
408end
409
410! ---------------------------------------------------------------------
411!
412!  FormFunctionLocal - Computes nonlinear function, called by
413!  the higher level routine FormFunction().
414!
415!  Input Parameter:
416!  x - local vector data
417!
418!  Output Parameters:
419!  f - local vector data, f(x)
420!  ierr - error code
421!
422!  Notes:
423!  This routine uses standard Fortran-style computations over a 2-dim array.
424!
425subroutine FormFunctionLocal(x, f, user, ierr)
426  use petscsys
427  use ex5f90tmodule
428!  Input/output variables:
429  type(userctx) user
430  PetscScalar x(user%gxs:user%gxe, user%gys:user%gye)
431  PetscScalar f(user%xs:user%xe, user%ys:user%ye)
432  PetscErrorCode ierr
433
434!  Local variables:
435  PetscScalar two, one, hx, hy, hxdhy, hydhx, sc
436  PetscScalar u, uxx, uyy
437  PetscInt i, j
438
439  one = 1.0
440  two = 2.0
441  hx = one/PetscIntToReal(user%mx - 1)
442  hy = one/PetscIntToReal(user%my - 1)
443  sc = hx*hy*user%lambda
444  hxdhy = hx/hy
445  hydhx = hy/hx
446
447!  Compute function over the locally owned part of the grid
448
449  do j = user%ys, user%ye
450    do i = user%xs, user%xe
451      if (i == 1 .or. j == 1 .or. i == user%mx .or. j == user%my) then
452        f(i, j) = x(i, j)
453      else
454        u = x(i, j)
455        uxx = hydhx*(two*u - x(i - 1, j) - x(i + 1, j))
456        uyy = hxdhy*(two*u - x(i, j - 1) - x(i, j + 1))
457        f(i, j) = uxx + uyy - sc*exp(u)
458      end if
459    end do
460  end do
461  ierr = 0
462end
463
464! ---------------------------------------------------------------------
465!
466!  FormJacobian - Evaluates Jacobian matrix.
467!
468!  Input Parameters:
469!  snes     - the SNES context
470!  x        - input vector
471!  dummy    - optional user-defined context, as set by SNESSetJacobian()
472!             (not used here)
473!
474!  Output Parameters:
475!  jac      - Jacobian matrix
476!  jac_prec - optionally different matrix used to construct the preconditioner (not used here)
477!
478!  Notes:
479!  This routine serves as a wrapper for the lower-level routine
480!  "FormJacobianLocal", where the actual computations are
481!  done using the standard Fortran style of treating the local
482!  vector data as a multidimensional array over the local mesh.
483!  This routine merely accesses the local vector data via
484!  VecGetArray() and VecRestoreArray().
485!
486!  Notes:
487!  Due to grid point reordering with DMDAs, we must always work
488!  with the local grid points, and then transform them to the new
489!  global numbering with the "ltog" mapping
490!  We cannot work directly with the global numbers for the original
491!  uniprocessor grid!
492!
493!  Two methods are available for imposing this transformation
494!  when setting matrix entries:
495!    (A) MatSetValuesLocal(), using the local ordering (including
496!        ghost points!)
497!        - Set matrix entries using the local ordering
498!          by calling MatSetValuesLocal()
499!    (B) MatSetValues(), using the global ordering
500!        - Use DMGetLocalToGlobalMapping() then
501!          ISLocalToGlobalMappingGetIndices() to extract the local-to-global map
502!        - Then apply this map explicitly yourself
503!        - Set matrix entries using the global ordering by calling
504!          MatSetValues()
505!  Option (A) seems cleaner/easier in many cases, and is the procedure
506!  used in this example.
507!
508subroutine FormJacobian(mysnes, X, jac, jac_prec, user, ierr)
509  use petscsnes
510  use ex5f90tmodule
511!  Input/output variables:
512  type(tSNES) mysnes
513  type(tVec) X
514  type(tMat) jac, jac_prec
515  type(userctx) user
516  PetscErrorCode ierr
517
518!  Declarations for use with local arrays:
519  PetscScalar, pointer :: lx_v(:)
520  type(tVec) localX
521
522!  Scatter ghost points to local vector, using the 2-step process
523!     DMGlobalToLocalBegin(), DMGlobalToLocalEnd()
524!  Computations can be done while messages are in transition,
525!  by placing code between these two statements.
526
527  PetscCallA(DMGetLocalVector(user%da, localX, ierr))
528  PetscCallA(DMGlobalToLocalBegin(user%da, X, INSERT_VALUES, localX, ierr))
529  PetscCallA(DMGlobalToLocalEnd(user%da, X, INSERT_VALUES, localX, ierr))
530
531!  Get a pointer to vector data
532  PetscCallA(VecGetArray(localX, lx_v, ierr))
533
534!  Compute entries for the locally owned part of the Jacobian preconditioner.
535  PetscCallA(FormJacobianLocal(lx_v, jac_prec, user, ierr))
536
537!  Assemble matrix, using the 2-step process:
538!     MatAssemblyBegin(), MatAssemblyEnd()
539!  Computations can be done while messages are in transition,
540!  by placing code between these two statements.
541
542  PetscCallA(MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY, ierr))
543!      if (jac .ne. jac_prec) then
544  PetscCallA(MatAssemblyBegin(jac_prec, MAT_FINAL_ASSEMBLY, ierr))
545!      endif
546  PetscCallA(VecRestoreArray(localX, lx_v, ierr))
547  PetscCallA(DMRestoreLocalVector(user%da, localX, ierr))
548  PetscCallA(MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY, ierr))
549!      if (jac .ne. jac_prec) then
550  PetscCallA(MatAssemblyEnd(jac_prec, MAT_FINAL_ASSEMBLY, ierr))
551!      endif
552
553!  Tell the matrix we will never add a new nonzero location to the
554!  matrix. If we do it will generate an error.
555
556  PetscCallA(MatSetOption(jac, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE, ierr))
557
558end
559
560! ---------------------------------------------------------------------
561!
562!  FormJacobianLocal - Computes Jacobian matrix used to compute the preconditioner,
563!  called by the higher level routine FormJacobian().
564!
565!  Input Parameters:
566!  x        - local vector data
567!
568!  Output Parameters:
569!  jac_prec - Jacobian matrix used to compute the preconditioner
570!  ierr     - error code
571!
572!  Notes:
573!  This routine uses standard Fortran-style computations over a 2-dim array.
574!
575!  Notes:
576!  Due to grid point reordering with DMDAs, we must always work
577!  with the local grid points, and then transform them to the new
578!  global numbering with the "ltog" mapping
579!  We cannot work directly with the global numbers for the original
580!  uniprocessor grid!
581!
582!  Two methods are available for imposing this transformation
583!  when setting matrix entries:
584!    (A) MatSetValuesLocal(), using the local ordering (including
585!        ghost points!)
586!        - Set matrix entries using the local ordering
587!          by calling MatSetValuesLocal()
588!    (B) MatSetValues(), using the global ordering
589!        - Set matrix entries using the global ordering by calling
590!          MatSetValues()
591!  Option (A) seems cleaner/easier in many cases, and is the procedure
592!  used in this example.
593!
594subroutine FormJacobianLocal(x, jac_prec, user, ierr)
595  use petscmat
596  use ex5f90tmodule
597!  Input/output variables:
598  type(userctx) user
599  PetscScalar x(user%gxs:user%gxe, user%gys:user%gye)
600  type(tMat) jac_prec
601  PetscErrorCode ierr
602
603!  Local variables:
604  PetscInt row, col(5), i, j
605  PetscInt ione, ifive
606  PetscScalar two, one, hx, hy, hxdhy
607  PetscScalar hydhx, sc, v(5)
608
609!  Set parameters
610  ione = 1
611  ifive = 5
612  one = 1.0
613  two = 2.0
614  hx = one/PetscIntToReal(user%mx - 1)
615  hy = one/PetscIntToReal(user%my - 1)
616  sc = hx*hy
617  hxdhy = hx/hy
618  hydhx = hy/hx
619
620!  Compute entries for the locally owned part of the Jacobian.
621!   - Currently, all PETSc parallel matrix formats are partitioned by
622!     contiguous chunks of rows across the processors.
623!   - Each processor needs to insert only elements that it owns
624!     locally (but any non-local elements will be sent to the
625!     appropriate processor during matrix assembly).
626!   - Here, we set all entries for a particular row at once.
627!   - We can set matrix entries either using either
628!     MatSetValuesLocal() or MatSetValues(), as discussed above.
629!   - Note that MatSetValues() uses 0-based row and column numbers
630!     in Fortran as well as in C.
631
632  do j = user%ys, user%ye
633    row = (j - user%gys)*user%gxm + user%xs - user%gxs - 1
634    do i = user%xs, user%xe
635      row = row + 1
636!           boundary points
637      if (i == 1 .or. j == 1 .or. i == user%mx .or. j == user%my) then
638        col(1) = row
639        v(1) = one
640        PetscCallA(MatSetValuesLocal(jac_prec, ione, [row], ione, col, v, INSERT_VALUES, ierr))
641!           interior grid points
642      else
643        v(1) = -hxdhy
644        v(2) = -hydhx
645        v(3) = two*(hydhx + hxdhy) - sc*user%lambda*exp(x(i, j))
646        v(4) = -hydhx
647        v(5) = -hxdhy
648        col(1) = row - user%gxm
649        col(2) = row - 1
650        col(3) = row
651        col(4) = row + 1
652        col(5) = row + user%gxm
653        PetscCallA(MatSetValuesLocal(jac_prec, ione, [row], ifive, col, v, INSERT_VALUES, ierr))
654      end if
655    end do
656  end do
657end
658
659!/*TEST
660!
661!   test:
662!      nsize: 4
663!      args: -snes_mf -pc_type none -da_processors_x 4 -da_processors_y 1 -snes_monitor_short -ksp_gmres_cgs_refinement_type refine_always
664!
665!TEST*/
666