MODFLOW 6  version 6.7.0.dev2
USGS Modular Hydrologic Model
PetscSolver.F90
Go to the documentation of this file.
2 #include <petsc/finclude/petscksp.h>
3  use petscksp
4  use kindmodule, only: i4b, dp, lgp
10  use petscmatrixmodule
18  use devfeaturemodule, only: dev_feature
19 
20  implicit none
21  private
22 
23  public :: create_petsc_solver
24 
25  !< Linear solver using PETSc KSP
26  type, public, extends(linearsolverbasetype) :: petscsolvertype
27  ksp :: ksp_petsc !< the KSP linear solver object
28  class(petscmatrixtype), pointer :: matrix => null() !< the system matrix in PETSc compatible format
29  mat, pointer :: mat_petsc => null() !< the PETSc internal matrix format
30 
31  type(imslinearsettingstype), pointer :: linear_settings => null() !< pointer to linear settings from IMS
32  logical(LGP) :: use_ims_pc !< when true, use custom IMS-style preconditioning
33  logical(LGP) :: use_ims_cnvgopt !< when true, use IMS convergence check in PETSc solve
34  ksptype :: ksp_type !< the KSP solver type (CG, BCGS, ...)
35  class(petsccnvgctxtype), pointer :: petsc_ctx => null() !< context for the PETSc custom convergence check
36  type(pcshellctxtype), pointer :: pc_context => null() !< context for the custom (IMS) precondioner
37  type(convergencesummarytype), pointer :: convergence_summary => null() !< data structure wrapping the convergence data
38  character(len=LENSOLUTIONNAME + 1) :: option_prefix !< prefix for keys in petscrc database in case there are multiple solutions
39  contains
40  procedure :: initialize => petsc_initialize
41  procedure :: solve => petsc_solve
42  procedure :: print_summary => petsc_print_summary
43  procedure :: destroy => petsc_destroy
44  procedure :: create_matrix => petsc_create_matrix
45 
46  ! private
47  procedure, private :: petsc_check_settings
48  procedure, private :: get_options_mf6
49  procedure, private :: create_ksp
50  procedure, private :: create_convergence_check
51  procedure, private :: set_ims_pc
52  procedure, private :: print_vec
53  procedure, private :: print_petsc_version
54  end type petscsolvertype
55 
56 contains
57 
58  !> @brief Create a PETSc solver object
59  !<
60  function create_petsc_solver(sln_name) result(solver)
61  class(linearsolverbasetype), pointer :: solver !< Uninitialized instance of the PETSc solver
62  character(len=LENSOLUTIONNAME) :: sln_name !< the solution name
63  ! local
64  class(petscsolvertype), pointer :: petsc_solver
65  character(len=LINELENGTH) :: errmsg
66 
67  allocate (petsc_solver)
68  allocate (petsc_solver%petsc_ctx)
69  petsc_solver%option_prefix = ""
70  if (basesolutionlist%Count() > 1) then
71  petsc_solver%option_prefix = trim(sln_name)//"_"
72  end if
73 
74  solver => petsc_solver
75  solver%name = sln_name
76 
77  if (simulation_mode /= 'PARALLEL') then
78  write (errmsg, '(a,a)') 'PETSc solver not supported for run mode: ', &
79  trim(simulation_mode)
80  call store_error(errmsg, terminate=.true.)
81  end if
82 
83  end function create_petsc_solver
84 
85  !> @brief Initialize PETSc KSP solver with
86  !< options from the petsc database file
87  subroutine petsc_initialize(this, matrix, linear_settings, convergence_summary)
88  class(petscsolvertype) :: this !< This solver instance
89  class(matrixbasetype), pointer :: matrix !< The solution matrix as KSP operator
90  type(imslinearsettingstype), pointer :: linear_settings !< the settings for the linear solver from the .ims file
91  type(convergencesummarytype), pointer :: convergence_summary !< a convergence record for diagnostics
92 
93  this%linear_settings => linear_settings
94 
95  call this%print_petsc_version()
96 
97  this%mat_petsc => null()
98  select type (pm => matrix)
99  class is (petscmatrixtype)
100  this%matrix => pm
101  this%mat_petsc => pm%mat
102  end select
103 
104  call this%petsc_check_settings(linear_settings)
105 
106  this%use_ims_cnvgopt = .true. ! use IMS convergence check, override with .petscrc
107  this%use_ims_pc = .true. ! use IMS preconditioning, override with .petscrc
108  allocate (this%pc_context)
109  call this%pc_context%create(this%matrix, linear_settings)
110 
111  if (linear_settings%ilinmeth == cg_method) then
112  this%ksp_type = kspcg
113  else
114  this%ksp_type = kspbcgs
115  end if
116 
117  ! get MODFLOW options from PETSc database file
118  call this%get_options_mf6()
119 
120  ! create the solver object
121  call this%create_ksp()
122 
123  ! Create custom convergence check
124  call this%create_convergence_check(convergence_summary)
125 
126  end subroutine petsc_initialize
127 
128  subroutine petsc_check_settings(this, linear_settings)
129  class(petscsolvertype) :: this
130  type(imslinearsettingstype), pointer :: linear_settings
131  ! local
132  character(len=LINELENGTH) :: warnmsg, errmsg
133 
134  ! errors
135  if (linear_settings%ilinmeth /= cg_method .and. &
136  linear_settings%ilinmeth /= bcgs_method) then
137  write (errmsg, '(a,a)') 'PETSc: unknown linear solver method in ', &
138  this%name
139  call store_error(errmsg, terminate=.true.)
140  end if
141 
142  ! warnings
143  if (linear_settings%iord > 0) then
144  linear_settings%iord = 0
145  write (warnmsg, '(a)') 'PETSc: IMS reordering not supported'
146  call store_warning(warnmsg)
147  end if
148  if (linear_settings%iscl > 0) then
149  linear_settings%iscl = 0
150  write (warnmsg, '(a)') 'PETSc: IMS matrix scaling not supported'
151  call store_warning(warnmsg)
152  end if
153  if (linear_settings%north > 0) then
154  linear_settings%north = 0
155  write (warnmsg, '(a)') 'PETSc: IMS orthogonalization not supported'
156  call store_warning(warnmsg)
157  end if
158 
159  end subroutine petsc_check_settings
160 
161  !> @brief Print PETSc version string from shared lib
162  !<
163  subroutine print_petsc_version(this)
164  class(petscsolvertype) :: this
165  ! local
166  petscerrorcode :: ierr
167  petscint :: major, minor, subminor, release
168  character(len=128) :: petsc_version, release_str
169 
170  call petscgetversionnumber(major, minor, subminor, release, ierr)
171  chkerrq(ierr)
172 
173  if (release == 1) then
174  release_str = "(release)"
175  else
176  release_str = "(unofficial)"
177  end if
178  write (petsc_version, '(i0,a,i0,a,i0,a,a)') &
179  major, ".", minor, ".", subminor, " ", trim(release_str)
180  write (iout, '(/,1x,4a,/)') "PETSc Linear Solver will be used for ", &
181  trim(this%name), ": version ", petsc_version
182 
183  end subroutine print_petsc_version
184 
185  !> @brief Get the MODFLOW specific options from the PETSc database
186  !<
187  subroutine get_options_mf6(this)
188  class(petscsolvertype) :: this
189  ! local
190  petscerrorcode :: ierr
191  logical(LGP) :: found
192  logical(LGP) :: use_petsc_pc, use_petsc_cnvg
193 
194  use_petsc_pc = .false.
195  call petscoptionsgetbool(petsc_null_options, trim(this%option_prefix), &
196  '-use_petsc_pc', use_petsc_pc, found, ierr)
197  chkerrq(ierr)
198  this%use_ims_pc = .not. use_petsc_pc
199 
200  use_petsc_cnvg = .false.
201  call petscoptionsgetbool(petsc_null_options, trim(this%option_prefix), &
202  '-use_petsc_cnvg', use_petsc_cnvg, found, ierr)
203  chkerrq(ierr)
204  this%use_ims_cnvgopt = .not. use_petsc_cnvg
205 
206  end subroutine get_options_mf6
207 
208  !> @brief Create the PETSc KSP object
209  !<
210  subroutine create_ksp(this)
211  class(petscsolvertype) :: this !< This solver instance
212  ! local
213  petscerrorcode :: ierr
214  pc :: pc
215 
216  call kspcreate(petsc_comm_world, this%ksp_petsc, ierr)
217  chkerrq(ierr)
218 
219  ! set prefix for options database
220  call kspsetoptionsprefix(this%ksp_petsc, trim(this%option_prefix), ierr)
221  chkerrq(ierr)
222 
223  call kspsetoperators(this%ksp_petsc, this%mat_petsc, this%mat_petsc, ierr)
224  chkerrq(ierr)
225 
226  call kspsetinitialguessnonzero(this%ksp_petsc, .true., ierr)
227  chkerrq(ierr)
228 
229  call kspsettype(this%ksp_petsc, this%ksp_type, ierr)
230  chkerrq(ierr)
231 
232  if (this%use_ims_pc) then
233  call this%set_ims_pc()
234  else
235  call dev_feature('PETSc preconditioning is under development, install the &
236  &nightly build or compile from source with IDEVELOPMODE = 1.')
237  ! The PC options will be set from the .petscrc
238  ! file in the call to KSPSetFromOptions below
239  call kspgetpc(this%ksp_petsc, pc, ierr)
240  chkerrq(ierr)
241  call pcsetfromoptions(pc, ierr)
242  chkerrq(ierr)
243  end if
244 
245  call kspseterrorifnotconverged(this%ksp_petsc, .false., ierr)
246  chkerrq(ierr)
247 
248  ! finally override these options from the
249  ! optional .petscrc file
250  call kspsetfromoptions(this%ksp_petsc, ierr)
251  chkerrq(ierr)
252 
253  call kspsetup(this%ksp_petsc, ierr)
254  chkerrq(ierr)
255 
256  end subroutine create_ksp
257 
258  !> @brief Set up a custom preconditioner following the ones
259  !< we have in IMS, i.e. Modified ILU(T)
260  subroutine set_ims_pc(this)
261  class(petscsolvertype) :: this !< This solver instance
262  ! local
263  pc :: pc, sub_pc
264  ksp, dimension(1) :: sub_ksp
265  petscint :: n_local, n_first
266  petscerrorcode :: ierr
267 
268  call kspgetpc(this%ksp_petsc, pc, ierr)
269  chkerrq(ierr)
270  call pcsettype(pc, pcbjacobi, ierr)
271  chkerrq(ierr)
272  call pcsetup(pc, ierr)
273  chkerrq(ierr)
274  call pcbjacobigetsubksp(pc, n_local, n_first, sub_ksp, ierr)
275  chkerrq(ierr)
276  call kspgetpc(sub_ksp(1), sub_pc, ierr)
277  chkerrq(ierr)
278  call pcsettype(sub_pc, pcshell, ierr)
279  chkerrq(ierr)
280  call pcshellsetapply(sub_pc, pcshell_apply, ierr)
281  chkerrq(ierr)
282  call pcshellsetsetup(sub_pc, pcshell_setup, ierr)
283  chkerrq(ierr)
284  call pcshellsetcontext(sub_pc, this%pc_context, ierr)
285  chkerrq(ierr)
286 
287  end subroutine set_ims_pc
288 
289  !> @brief Create and assign a custom convergence
290  !< check for this solver
291  subroutine create_convergence_check(this, convergence_summary)
293  class(petscsolvertype) :: this !< This solver instance
294  type(convergencesummarytype), pointer :: convergence_summary
295  ! local
296  petscerrorcode :: ierr
297 
298  call this%petsc_ctx%create(this%mat_petsc, this%linear_settings, &
299  convergence_summary)
300 
301  if (.not. this%use_ims_cnvgopt) then
302  ! use PETSc residual L2 norm for convergence
303  call dev_feature('Using PETSc convergence is under development, install &
304  &the nightly build or compile from source with IDEVELOPMODE = 1.')
305  call kspsetconvergencetest(this%ksp_petsc, petsc_cnvg_check_internal, &
306  this%petsc_ctx, petsc_null_function, ierr)
307  else
308  ! IMS convergence check
309  call kspsetconvergencetest(this%ksp_petsc, petsc_cnvg_check, &
310  this%petsc_ctx, petsc_null_function, ierr)
311  end if
312  chkerrq(ierr)
313 
314  end subroutine create_convergence_check
315 
316  subroutine petsc_solve(this, kiter, rhs, x, cnvg_summary)
317  class(petscsolvertype) :: this
318  integer(I4B) :: kiter
319  class(vectorbasetype), pointer :: rhs
320  class(vectorbasetype), pointer :: x
321  type(convergencesummarytype) :: cnvg_summary
322  ! local
323  petscerrorcode :: ierr
324  class(petscvectortype), pointer :: rhs_petsc, x_petsc
325  kspconvergedreason :: cnvg_reason
326  integer :: it_number
327  character(len=LINELENGTH) :: errmsg
328 
329  rhs_petsc => null()
330  select type (rhs)
331  class is (petscvectortype)
332  rhs_petsc => rhs
333  end select
334 
335  x_petsc => null()
336  select type (x)
337  class is (petscvectortype)
338  x_petsc => x
339  end select
340 
341  this%iteration_number = 0
342  this%is_converged = 0
343  if (kiter == 1) then
344  this%petsc_ctx%cnvg_summary%iter_cnt = 0
345  end if
346 
347  ! update matrix coefficients
348  call this%matrix%update()
349  call kspsolve(this%ksp_petsc, rhs_petsc%vec_impl, x_petsc%vec_impl, ierr)
350  chkerrq(ierr)
351 
352  call kspgetiterationnumber(this%ksp_petsc, it_number, ierr)
353  chkerrq(ierr)
354  this%iteration_number = it_number
355 
356  call kspgetconvergedreason(this%ksp_petsc, cnvg_reason, ierr)
357  chkerrq(ierr)
358  if (cnvg_reason > 0) then
359  if (this%petsc_ctx%icnvg_ims == -1) then
360  ! move to next Picard iteration (e.g. with 'STRICT' option)
361  this%is_converged = 0
362  else
363  ! linear convergence reached
364  this%is_converged = 1
365  end if
366  end if
367 
368  if (cnvg_reason < 0 .and. cnvg_reason /= ksp_diverged_its) then
369  write (errmsg, '(1x,3a,i0)') "PETSc convergence failure in ", &
370  trim(this%name), ": ", cnvg_reason
371  call store_error(errmsg, terminate=.true.)
372  end if
373 
374  end subroutine petsc_solve
375 
376  subroutine petsc_print_summary(this)
377  class(petscsolvertype) :: this
378  ! local
379  character(len=128) :: ksp_str, pc_str, subpc_str, &
380  dvclose_str, rclose_str, relax_str, dtol_str
381  character(len=128) :: ksp_logfile
382  integer :: ierr
383  pc :: pc
384  petscviewer :: ksp_viewer
385 
386  if (this%use_ims_pc) then
387  call kspgettype(this%ksp_petsc, ksp_str, ierr)
388  chkerrq(ierr)
389  call kspgetpc(this%ksp_petsc, pc, ierr)
390  chkerrq(ierr)
391  call pcgettype(pc, pc_str, ierr)
392  chkerrq(ierr)
393  subpc_str = this%pc_context%ims_pc_type
394 
395  write (dvclose_str, '(e15.5)') this%linear_settings%dvclose
396  write (rclose_str, '(e15.5)') this%linear_settings%rclose
397  write (relax_str, '(e15.5)') this%linear_settings%relax
398  write (dtol_str, '(e15.5)') this%linear_settings%droptol
399 
400  write (iout, '(/,1x,a)') "PETSc linear solver settings: "
401  write (iout, '(1x,a)') repeat('-', 66)
402  write (iout, '(1x,a,a)') "Linear acceleration method: ", trim(ksp_str)
403  write (iout, '(1x,a,a)') "Preconditioner type: ", trim(pc_str)
404  write (iout, '(1x,a,a)') "Sub-preconditioner type: ", trim(subpc_str)
405  write (iout, '(1x,a,i0)') "Maximum nr. of iterations: ", &
406  this%linear_settings%iter1
407  write (iout, '(1x,a,a)') &
408  "Dep. var. closure criterion: ", trim(adjustl(dvclose_str))
409  write (iout, '(1x,a,a)') &
410  "Residual closure criterion: ", trim(adjustl(rclose_str))
411  if (this%use_ims_cnvgopt) then
412  write (iout, '(1x,a,i0)') &
413  "Residual convergence option: ", this%linear_settings%icnvgopt
414  else
415  write (iout, '(1x,a)') &
416  "Residual convergence option: PETSc L2 norm"
417  end if
418  write (iout, '(1x,a,a)') &
419  "Relaxation factor MILU(T): ", trim(adjustl(relax_str))
420  write (iout, '(1x,a,i0)') &
421  "Fill level in factorization: ", this%linear_settings%level
422  write (iout, '(1x,a,a,/)') &
423  "Drop tolerance level fill: ", trim(adjustl(dtol_str))
424  else
425  ksp_logfile = trim(this%option_prefix)//"ksp_logview.txt"
426  write (iout, '(/,1x,a)') "PETSc linear solver settings from .petscrc: "
427  write (iout, '(1x,a)') repeat('-', 66)
428  write (iout, '(1x,2a)') "see ", trim(ksp_logfile)
429 
430  ! collective write
431  call petscviewerasciiopen(petsc_comm_world, ksp_logfile, ksp_viewer, ierr);
432  chkerrq(ierr)
433  call petscviewerpushformat(ksp_viewer, petsc_viewer_ascii_info_detail, ierr)
434  chkerrq(ierr)
435  call kspview(this%ksp_petsc, ksp_viewer, ierr)
436  chkerrq(ierr)
437  call petscviewerdestroy(ksp_viewer, ierr)
438  chkerrq(ierr)
439  end if
440 
441  end subroutine petsc_print_summary
442 
443  subroutine petsc_destroy(this)
444  class(petscsolvertype) :: this
445  ! local
446  petscerrorcode :: ierr
447 
448  call kspdestroy(this%ksp_petsc, ierr)
449  chkerrq(ierr)
450 
451  ! delete context
452  call this%petsc_ctx%destroy()
453  deallocate (this%petsc_ctx)
454 
455  call this%pc_context%destroy()
456  deallocate (this%pc_context)
457 
458  end subroutine petsc_destroy
459 
460  function petsc_create_matrix(this) result(matrix)
461  class(petscsolvertype) :: this
462  class(matrixbasetype), pointer :: matrix
463  ! local
464  class(petscmatrixtype), pointer :: petsc_matrix
465 
466  allocate (petsc_matrix)
467  matrix => petsc_matrix
468 
469  end function petsc_create_matrix
470 
471  subroutine print_vec(this, vec, vec_name, kiter)
472  use tdismodule, only: nper, kstp
473  class(petscsolvertype) :: this
474  class(petscvectortype) :: vec
475  character(len=*) :: vec_name
476  integer(I4B) :: kiter
477  ! local
478  petscviewer :: viewer
479  character(len=24) :: filename
480  petscerrorcode :: ierr
481 
482  write (filename, '(2a,i0,a,i0,a,i0,a)') vec_name, '_', nper, &
483  '_', kstp, '_', kiter, '.txt'
484  call petscviewerasciiopen(petsc_comm_world, filename, viewer, ierr)
485  chkerrq(ierr)
486  call vecview(vec%vec_impl, viewer, ierr)
487  chkerrq(ierr)
488  call petscviewerdestroy(viewer, ierr)
489  chkerrq(ierr)
490 
491  end subroutine print_vec
492 
493 end module petscsolvermodule
This module contains simulation constants.
Definition: Constants.f90:9
integer(i4b), parameter linelength
maximum length of a standard line
Definition: Constants.f90:45
integer(i4b), parameter lensolutionname
maximum length of the solution name
Definition: Constants.f90:21
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
subroutine destroy(this)
Cleanup.
Disable development features in release mode.
Definition: DevFeature.f90:2
subroutine, public dev_feature(errmsg, iunit)
Terminate if in release mode (guard development features)
Definition: DevFeature.f90:21
This module contains the IMS linear accelerator subroutines.
real(dp) function ims_base_epfact(icnvgopt, kstp)
Function returning EPFACT.
integer(i4b), parameter, public cg_method
integer(i4b), parameter, public bcgs_method
This module defines variable data types.
Definition: kind.f90:8
type(listtype), public basesolutionlist
Definition: mf6lists.f90:19
subroutine, public petsc_cnvg_check_internal(ksp, n, rnorm_L2, flag, context, ierr)
Routine to check the convergence following the configuration.
subroutine, public petsc_cnvg_check(ksp, n, rnorm_L2, flag, context, ierr)
Routine to check the convergence following the configuration.
subroutine, public pcshell_setup(pc, ierr)
Set up the custom preconditioner.
subroutine, public pcshell_apply(pc, x, y, ierr)
Apply shell preconditioner.
class(linearsolverbasetype) function, pointer, public create_petsc_solver(sln_name)
Create a PETSc solver object.
Definition: PetscSolver.F90:61
subroutine print_vec(this, vec, vec_name, kiter)
subroutine petsc_check_settings(this, linear_settings)
subroutine petsc_solve(this, kiter, rhs, x, cnvg_summary)
subroutine petsc_initialize(this, matrix, linear_settings, convergence_summary)
Initialize PETSc KSP solver with.
Definition: PetscSolver.F90:88
subroutine set_ims_pc(this)
Set up a custom preconditioner following the ones.
subroutine print_petsc_version(this)
Print PETSc version string from shared lib.
class(matrixbasetype) function, pointer petsc_create_matrix(this)
subroutine get_options_mf6(this)
Get the MODFLOW specific options from the PETSc database.
subroutine create_convergence_check(this, convergence_summary)
Create and assign a custom convergence.
subroutine petsc_destroy(this)
subroutine petsc_print_summary(this)
subroutine create_ksp(this)
Create the PETSc KSP object.
This module contains simulation methods.
Definition: Sim.f90:10
subroutine, public store_warning(msg, substring)
Store warning message.
Definition: Sim.f90:236
subroutine, public store_error(msg, terminate)
Store an error message.
Definition: Sim.f90:92
This module contains simulation variables.
Definition: SimVariables.f90:9
character(len=linelength) simulation_mode
integer(i4b) nr_procs
integer(i4b) iout
file unit number for simulation output
integer(i4b) proc_id
integer(i4b), pointer, public kstp
current time step number
Definition: tdis.f90:24
integer(i4b), pointer, public nper
number of stress period
Definition: tdis.f90:21
This structure stores the generic convergence info for a solution.
x vector from the previous iteration