MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
PetscConvergence.F90
Go to the documentation of this file.
2 #include <petsc/finclude/petscksp.h>
3  use petscksp
4  use kindmodule, only: i4b, dp
5  use constantsmodule, only: dprec, dzero
6  use listmodule
9  implicit none
10  private
11 
12  public :: petsc_check_convergence
13  public :: kspsetconvergencetest
14 
15  ! TODO_MJR: this could be smaller, find a bound
16  real(dp), private, parameter :: rnorm_l2_tol = dprec
17 
18  !< Context for the custom convergence check
19  type, public :: petsccnvgctxtype
20  vec :: x_old !< x vector from the previous iteration
21  vec :: delta_x !< delta in x w.r.t. previous iteration
22  vec :: residual !< the unpreconditoned residual vector (a la IMS)
23  integer(I4B) :: icnvg_ims !< IMS convergence number: 1 => converged, -1 => forces next Picard iter
24  integer(I4B) :: icnvgopt !< convergence option from IMS settings
25  real(dp) :: dvclose !< dep. variable closure criterion
26  real(dp) :: rclose !< residual closure criterion
27  integer(I4B) :: max_its !< maximum number of inner iterations
28  real(dp) :: rnorm_l2_init !< the initial L2 norm for (b - Ax)
29  type(convergencesummarytype), pointer :: cnvg_summary => null() !< detailed convergence information
30  real(dp) :: t_convergence_check !< the time spent convergence checking
31  contains
32  procedure :: create
33  procedure :: destroy
34  end type petsccnvgctxtype
35 
36  ! passing our context into PETSc requires an explicit interface
37  ! on KSPSetConvergenceTest, it is defined here:
38  interface
39  subroutine cnvgcheckfunc(ksp, n, rnorm, flag, context, ierr)
40  import tksp, petsccnvgctxtype
41  type(tksp) :: ksp
42  petscint :: n
43  petscreal :: rnorm
44  kspconvergedreason :: flag
45  class(petsccnvgctxtype), pointer :: context
46  petscerrorcode :: ierr
47  end subroutine
48 
49  subroutine cnvgdestroyfunc(context, ierr)
50  import petsccnvgctxtype
51  class(petsccnvgctxtype), pointer :: context
52  petscerrorcode :: ierr
53  end subroutine
54 
55  subroutine kspsetconvergencetest(ksp, check_convergence, context, &
56  destroy, ierr)
57  import tksp, cnvgcheckfunc, petsccnvgctxtype, cnvgdestroyfunc
58  type(tksp) :: ksp
59  procedure(cnvgcheckfunc) :: check_convergence
60  class(petsccnvgctxtype), pointer :: context
61  procedure(cnvgdestroyfunc) :: destroy
62  petscerrorcode :: ierr
63  end subroutine
64  end interface
65 
66 contains
67 
68  subroutine create(this, mat, settings, summary)
69  class(petsccnvgctxtype) :: this
70  mat, pointer :: mat
71  type(imslinearsettingstype), pointer :: settings
72  type(convergencesummarytype), pointer :: summary
73  ! local
74  petscerrorcode :: ierr
75 
76  this%icnvg_ims = 0
77  this%icnvgopt = settings%icnvgopt
78  this%dvclose = settings%dvclose
79  this%rclose = settings%rclose
80  this%max_its = settings%iter1
81  this%cnvg_summary => summary
82  call matcreatevecs(mat, this%x_old, petsc_null_vec, ierr)
83  chkerrq(ierr)
84  call matcreatevecs(mat, this%delta_x, petsc_null_vec, ierr)
85  chkerrq(ierr)
86  call matcreatevecs(mat, this%residual, petsc_null_vec, ierr)
87  chkerrq(ierr)
88 
89  end subroutine create
90 
91  !> @brief Routine to check the convergence. This is called
92  !< from within PETSc.
93  subroutine petsc_check_convergence(ksp, n, rnorm_L2, flag, context, ierr)
94  ksp :: ksp !< Iterative context
95  petscint :: n !< Iteration number
96  petscreal :: rnorm_l2 !< 2-norm (preconditioned) residual value
97  kspconvergedreason :: flag !< Converged reason
98  class(petsccnvgctxtype), pointer :: context !< context
99  petscerrorcode :: ierr !< error
100  ! local
101  petscreal, parameter :: min_one = -1.0
102  petscreal, dimension(:), pointer :: local_dx, local_res
103  petscreal :: xnorm_inf_ims, rnorm_inf_ims, rnorm_l2_ims
104  petscreal :: dvmax_model, rmax_model
105  petscint :: idx_dv, idx_r
106  vec :: x, rhs
107  mat :: amat
108  type(convergencesummarytype), pointer :: summary
109  petscint :: iter_cnt
110  petscint :: i, j, istart, iend
111 
112  summary => context%cnvg_summary
113 
114  ! NB: KSPBuildResidual needs to have its vector destroyed
115  ! to avoid a memory leak, KSPBuildSolution doesn't...
116  call kspbuildsolution(ksp, petsc_null_vec, x, ierr)
117  chkerrq(ierr)
118 
119  call kspgetrhs(ksp, rhs, ierr)
120  chkerrq(ierr)
121 
122  call kspgetoperators(ksp, amat, petsc_null_mat, ierr)
123  chkerrq(ierr)
124 
125  call matmult(amat, x, context%residual, ierr)
126  chkerrq(ierr)
127 
128  ! y = x + beta y (i.e. r = b - A*x)
129  call vecaypx(context%residual, -1.0_dp, rhs, ierr)
130  chkerrq(ierr)
131 
132  call vecnorm(context%residual, norm_2, rnorm_l2_ims, ierr)
133  chkerrq(ierr)
134 
135  ! n == 0 is before the iteration starts
136  if (n == 0) then
137  context%rnorm_L2_init = rnorm_l2_ims
138  if (rnorm_l2 < rnorm_l2_tol) then
139  ! exact solution found
140  flag = ksp_converged_happy_breakdown
141  else
142  call veccopy(x, context%x_old, ierr)
143  chkerrq(ierr)
144  flag = ksp_converged_iterating
145  end if
146  ! early return
147  return
148  end if
149 
150  ! increment iteration counter
151  summary%iter_cnt = summary%iter_cnt + 1
152  iter_cnt = summary%iter_cnt
153 
154  if (summary%nitermax > 1) then
155  summary%itinner(iter_cnt) = n
156  do i = 1, summary%convnmod
157  summary%convdvmax(i, iter_cnt) = dzero
158  summary%convlocdv(i, iter_cnt) = 0
159  summary%convrmax(i, iter_cnt) = dzero
160  summary%convlocr(i, iter_cnt) = 0
161  end do
162  end if
163 
164  call vecwaxpy(context%delta_x, min_one, context%x_old, x, ierr)
165  chkerrq(ierr)
166 
167  call vecnorm(context%delta_x, norm_infinity, xnorm_inf_ims, ierr)
168  chkerrq(ierr)
169 
170  rnorm_inf_ims = 0.0
171  if (context%icnvgopt == 0 .or. context%icnvgopt == 1) then
172  call vecnorm(context%residual, norm_infinity, rnorm_inf_ims, ierr)
173  chkerrq(ierr)
174  end if
175 
176  call veccopy(x, context%x_old, ierr)
177  chkerrq(ierr)
178 
179  ! get dv and dr per local model (readonly!)
180  call vecgetarrayreadf90(context%delta_x, local_dx, ierr)
181  chkerrq(ierr)
182  call vecgetarrayreadf90(context%residual, local_res, ierr)
183  chkerrq(ierr)
184  do i = 1, summary%convnmod
185  ! reset
186  dvmax_model = dzero
187  idx_dv = 0
188  rmax_model = dzero
189  idx_r = 0
190  ! get first and last model index
191  istart = summary%model_bounds(i)
192  iend = summary%model_bounds(i + 1) - 1
193  do j = istart, iend
194  if (abs(local_dx(j)) > abs(dvmax_model)) then
195  dvmax_model = local_dx(j)
196  idx_dv = j
197  end if
198  if (abs(local_res(j)) > abs(rmax_model)) then
199  rmax_model = local_res(j)
200  idx_r = j
201  end if
202  end do
203  if (summary%nitermax > 1) then
204  summary%convdvmax(i, iter_cnt) = dvmax_model
205  summary%convlocdv(i, iter_cnt) = idx_dv
206  summary%convrmax(i, iter_cnt) = rmax_model
207  summary%convlocr(i, iter_cnt) = idx_r
208  end if
209  end do
210  call vecrestorearrayf90(context%delta_x, local_dx, ierr)
211  chkerrq(ierr)
212  call vecrestorearrayf90(context%residual, local_res, ierr)
213  chkerrq(ierr)
214 
215  if (rnorm_l2 < rnorm_l2_tol) then
216  ! exact solution, set to 'converged'
217  flag = ksp_converged_happy_breakdown
218  else
219  ! IMS check on convergence
220  flag = apply_check(context, n, xnorm_inf_ims, rnorm_inf_ims, rnorm_l2_ims)
221  end if
222 
223  if (flag == ksp_converged_iterating) then
224  ! not yet converged, max. iters reached? Then stop.
225  if (n == context%max_its) then
226  flag = ksp_diverged_its
227  end if
228  end if
229 
230  end subroutine petsc_check_convergence
231 
232  !> @brief Apply the IMS convergence check
233  !<
234  function apply_check(ctx, nit, dvmax, rnorm_inf, rnorm_L2) result(flag)
235  use tdismodule, only: kstp
237  class(petsccnvgctxtype) :: ctx
238  integer(I4B) :: nit !< iteration number
239  real(dp) :: dvmax !< infinity norm of dep. var. change
240  real(dp) :: rnorm_inf !< infinity norm of residual change
241  real(dp) :: rnorm_l2 !< L2-norm of residual change
242  kspconvergedreason :: flag !< the convergence status
243  ! local
244  real(dp) :: epfact
245  real(dp) :: rcnvg
246 
247  ! Set to 'not converged'
248  flag = ksp_converged_iterating
249  ctx%icnvg_ims = 0
250 
251  epfact = ims_base_epfact(ctx%icnvgopt, kstp)
252 
253  if (ctx%icnvgopt == 2 .or. &
254  ctx%icnvgopt == 3 .or. &
255  ctx%icnvgopt == 4) then
256  rcnvg = rnorm_l2
257  else
258  rcnvg = rnorm_inf
259  end if
260  call ims_base_testcnvg(ctx%icnvgopt, ctx%icnvg_ims, nit, &
261  dvmax, rcnvg, ctx%rnorm_L2_init, &
262  epfact, ctx%dvclose, ctx%rclose)
263 
264  if (ctx%icnvg_ims /= 0) then
265  ! Set to 'converged'
266  flag = ksp_converged_happy_breakdown
267  end if
268 
269  end function apply_check
270 
271  subroutine destroy(this)
272  class(petsccnvgctxtype) :: this
273  ! local
274  integer(I4B) :: ierr
275 
276  call vecdestroy(this%x_old, ierr)
277  chkerrq(ierr)
278  call vecdestroy(this%delta_x, ierr)
279  chkerrq(ierr)
280  call vecdestroy(this%residual, ierr)
281  chkerrq(ierr)
282 
283  end subroutine destroy
284 
285 end module petscconvergencemodule
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:64
real(dp), parameter dprec
real constant machine precision
Definition: Constants.f90:119
subroutine destroy(this)
Cleanup.
This module contains the IMS linear accelerator subroutines.
subroutine ims_base_testcnvg(Icnvgopt, Icnvg, Iiter, Dvmax, Rmax, Rmax0, Epfact, Dvclose, Rclose)
@ brief Test for solver convergence
real(dp) function ims_base_epfact(icnvgopt, kstp)
Function returning EPFACT.
This module defines variable data types.
Definition: kind.f90:8
real(dp), parameter, private rnorm_l2_tol
Context for the custom convergence check.
function apply_check(ctx, nit, dvmax, rnorm_inf, rnorm_L2)
Apply the IMS convergence check.
subroutine, public petsc_check_convergence(ksp, n, rnorm_L2, flag, context, ierr)
Routine to check the convergence. This is called.
subroutine create(this, mat, settings, summary)
integer(i4b), pointer, public kstp
current time step number
Definition: tdis.f90:24
This structure stores the generic convergence info for a solution.
x vector from the previous iteration