MODFLOW 6  version 6.6.0.dev0
USGS Modular Hydrologic Model
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 simmodule, only: store_error
7  use simvariablesmodule, only: errmsg
8  use listmodule
11  implicit none
12  private
13 
14  public :: petsc_cnvg_check
15  public :: kspsetconvergencetest
16 
17  ! TODO_MJR: this could be smaller, find a bound
18  real(dp), private, parameter :: rnorm_l2_tol = dprec
19 
20  ! Context for the custom convergence check
21  type, public :: petsccnvgctxtype
22  vec :: x_old !< x vector from the previous iteration
23  vec :: delta_x !< delta in x w.r.t. previous iteration
24  vec :: residual !< the unpreconditoned residual vector (a la IMS)
25  integer(I4B) :: icnvg_ims !< IMS convergence number: 1 => converged, -1 => forces next Picard iter
26  integer(I4B) :: icnvgopt !< convergence option:
27  !! 0,1,2,3,4,.. for equivalent IMS settings,
28  !! 100,... for PETSc specific settings
29  real(dp) :: dvclose !< dep. variable closure criterion
30  real(dp) :: rclose !< residual closure criterion
31  integer(I4B) :: max_its !< maximum number of inner iterations
32  real(dp) :: rnorm_l2_init !< the initial L2 norm for (b - Ax)
33  type(convergencesummarytype), pointer :: cnvg_summary => null() !< detailed convergence information
34  real(dp) :: t_convergence_check !< the time spent convergence checking
35  contains
36  procedure :: create
37  procedure :: destroy
38  end type petsccnvgctxtype
39 
40  ! passing our context into PETSc requires an explicit interface
41  ! on KSPSetConvergenceTest, it is defined here:
42  interface
43  subroutine cnvgcheckfunc(ksp, n, rnorm, flag, context, ierr)
44  import tksp, petsccnvgctxtype
45  type(tksp) :: ksp
46  petscint :: n
47  petscreal :: rnorm
48  kspconvergedreason :: flag
49  class(petsccnvgctxtype), pointer :: context
50  petscerrorcode :: ierr
51  end subroutine
52 
53  subroutine cnvgdestroyfunc(context, ierr)
54  import petsccnvgctxtype
55  class(petsccnvgctxtype), pointer :: context
56  petscerrorcode :: ierr
57  end subroutine
58 
59  subroutine kspsetconvergencetest(ksp, check_convergence, context, &
60  destroy, ierr)
61  import tksp, cnvgcheckfunc, petsccnvgctxtype, cnvgdestroyfunc
62  type(tksp) :: ksp
63  procedure(cnvgcheckfunc) :: check_convergence
64  class(petsccnvgctxtype), pointer :: context
65  procedure(cnvgdestroyfunc) :: destroy
66  petscerrorcode :: ierr
67  end subroutine
68  end interface
69 
70 contains
71 
72  subroutine create(this, mat, settings, summary)
73  class(petsccnvgctxtype) :: this
74  mat, pointer :: mat
75  type(imslinearsettingstype), pointer :: settings
76  type(convergencesummarytype), pointer :: summary
77  ! local
78  petscerrorcode :: ierr
79 
80  this%icnvg_ims = 0
81  this%icnvgopt = settings%icnvgopt
82  this%dvclose = settings%dvclose
83  this%rclose = settings%rclose
84  this%max_its = settings%iter1
85  this%cnvg_summary => summary
86  call matcreatevecs(mat, this%x_old, petsc_null_vec, ierr)
87  chkerrq(ierr)
88  call matcreatevecs(mat, this%delta_x, petsc_null_vec, ierr)
89  chkerrq(ierr)
90  call matcreatevecs(mat, this%residual, petsc_null_vec, ierr)
91  chkerrq(ierr)
92 
93  end subroutine create
94 
95  !> @brief Routine to check the convergence following the configuration
96  !< of IMS. (called back from the PETSc solver)
97  subroutine petsc_cnvg_check(ksp, n, rnorm_L2, flag, context, ierr)
98  ksp :: ksp !< Iterative context
99  petscint :: n !< Iteration number
100  petscreal :: rnorm_l2 !< 2-norm (preconditioned) residual value
101  kspconvergedreason :: flag !< Converged reason
102  class(petsccnvgctxtype), pointer :: context !< context
103  petscerrorcode :: ierr !< error
104  ! local
105  petscreal, parameter :: min_one = -1.0
106  petscreal :: xnorm_inf, rnorm0, rnorm_inf_ims, rnorm_l2_ims
107  vec :: x, res
108  type(convergencesummarytype), pointer :: summary
109 
110  summary => context%cnvg_summary
111 
112  ! NB: KSPBuildResidual needs to have its vector destroyed
113  ! to avoid a memory leak, KSPBuildSolution doesn't...
114  call kspbuildsolution(ksp, petsc_null_vec, x, ierr)
115  chkerrq(ierr)
116 
117  ! for CG the KSPBuildResidual returns the work vector directly,
118  ! but BCGS (and possibly others) will do the matrix multiplication
119  call kspbuildresidual(ksp, petsc_null_vec, petsc_null_vec, res, ierr)
120  chkerrq(ierr)
121 
122  rnorm0 = huge(rnorm0)
123  if (context%icnvgopt == 2 .or. &
124  context%icnvgopt == 3 .or. &
125  context%icnvgopt == 4) then
126  call vecnorm(res, norm_2, rnorm_l2_ims, ierr)
127  rnorm0 = rnorm_l2_ims
128  chkerrq(ierr)
129  else if (context%icnvgopt == 100) then
130  rnorm0 = rnorm_l2
131  end if
132 
133  ! n == 0 is before the iteration starts
134  if (n == 0) then
135  context%rnorm_L2_init = rnorm0
136  if (rnorm_l2 < rnorm_l2_tol) then
137  ! exact solution found
138  flag = ksp_converged_happy_breakdown
139  else
140  call veccopy(x, context%x_old, ierr)
141  chkerrq(ierr)
142  flag = ksp_converged_iterating
143  end if
144  ! early return
145  call vecdestroy(res, ierr)
146  chkerrq(ierr)
147  return
148  end if
149 
150  call vecwaxpy(context%delta_x, min_one, context%x_old, x, ierr)
151  chkerrq(ierr)
152 
153  call vecnorm(context%delta_x, norm_infinity, xnorm_inf, ierr)
154  chkerrq(ierr)
155 
156  rnorm_inf_ims = huge(rnorm_inf_ims)
157  if (context%icnvgopt == 0 .or. context%icnvgopt == 1) then
158  call vecnorm(res, norm_infinity, rnorm_inf_ims, ierr)
159  chkerrq(ierr)
160  end if
161 
162  call veccopy(x, context%x_old, ierr)
163  chkerrq(ierr)
164 
165  ! fill the summary for reporting
166  call fill_cnvg_summary(summary, context%delta_x, res, n)
167 
168  if (rnorm_l2 < rnorm_l2_tol) then
169  ! exact solution, set to 'converged'
170  flag = ksp_converged_happy_breakdown
171  else if (context%icnvgopt < 100) then
172  ! IMS check on convergence
173  flag = apply_check(context, n, xnorm_inf, rnorm_inf_ims, rnorm_l2_ims)
174  else if (context%icnvgopt == 100) then
175  ! use PETSc rnorm directly
176  flag = ksp_converged_iterating
177  if (xnorm_inf < context%dvclose .and. rnorm_l2 < context%rclose) then
178  flag = ksp_converged_happy_breakdown
179  end if
180  else
181  ! invalid option somehow
182  write (errmsg, '(a,i0)') "Invalid convergence option: ", context%icnvgopt
183  call store_error(errmsg, .true.)
184  end if
185 
186  if (flag == ksp_converged_iterating) then
187  ! not yet converged, max. iters reached? Then stop.
188  if (n == context%max_its) then
189  flag = ksp_diverged_its
190  end if
191  end if
192 
193  call vecdestroy(res, ierr)
194  chkerrq(ierr)
195 
196  end subroutine petsc_cnvg_check
197 
198  !> @brief Apply the IMS convergence check
199  !<
200  function apply_check(ctx, nit, dvmax, rnorm_inf, rnorm_L2) result(flag)
201  use tdismodule, only: kstp
203  class(petsccnvgctxtype) :: ctx
204  integer(I4B) :: nit !< iteration number
205  real(dp) :: dvmax !< infinity norm of dep. var. change
206  real(dp) :: rnorm_inf !< infinity norm of residual change
207  real(dp) :: rnorm_l2 !< L2-norm of residual change
208  kspconvergedreason :: flag !< the convergence status
209  ! local
210  real(dp) :: epfact
211  real(dp) :: rcnvg
212 
213  ! Set to 'not converged'
214  flag = ksp_converged_iterating
215  ctx%icnvg_ims = 0
216 
217  epfact = ims_base_epfact(ctx%icnvgopt, kstp)
218 
219  if (ctx%icnvgopt == 2 .or. &
220  ctx%icnvgopt == 3 .or. &
221  ctx%icnvgopt == 4) then
222  rcnvg = rnorm_l2
223  else
224  rcnvg = rnorm_inf
225  end if
226  call ims_base_testcnvg(ctx%icnvgopt, ctx%icnvg_ims, nit, &
227  dvmax, rcnvg, ctx%rnorm_L2_init, &
228  epfact, ctx%dvclose, ctx%rclose)
229 
230  if (ctx%icnvg_ims /= 0) then
231  ! Set to 'converged'
232  flag = ksp_converged_happy_breakdown
233  end if
234 
235  end function apply_check
236 
237  !> @brief Fill the convergence summary from the context
238  !<
239  subroutine fill_cnvg_summary(summary, dx, res, n)
240  type(convergencesummarytype), pointer :: summary !< the convergence summary
241  vec :: dx !< the vector with changes in x
242  vec :: res !< the residual vector
243  petscint :: n !< the PETSc iteration number
244  ! local
245  petscreal, dimension(:), pointer :: local_dx, local_res
246  petscreal :: dvmax_model, rmax_model
247  petscerrorcode :: ierr
248  petscint :: idx_dv, idx_r
249  petscint :: i, j, istart, iend
250  petscint :: iter_cnt
251 
252  ! increment iteration counter
253  summary%iter_cnt = summary%iter_cnt + 1
254  iter_cnt = summary%iter_cnt
255 
256  if (summary%nitermax > 1) then
257  summary%itinner(iter_cnt) = n
258  do i = 1, summary%convnmod
259  summary%convdvmax(i, iter_cnt) = dzero
260  summary%convlocdv(i, iter_cnt) = 0
261  summary%convrmax(i, iter_cnt) = dzero
262  summary%convlocr(i, iter_cnt) = 0
263  end do
264  end if
265 
266  ! get dv and dr per local model (readonly!)
267  call vecgetarrayreadf90(dx, local_dx, ierr)
268  chkerrq(ierr)
269  call vecgetarrayreadf90(res, local_res, ierr)
270  chkerrq(ierr)
271  do i = 1, summary%convnmod
272  ! reset
273  dvmax_model = dzero
274  idx_dv = 0
275  rmax_model = dzero
276  idx_r = 0
277  ! get first and last model index
278  istart = summary%model_bounds(i)
279  iend = summary%model_bounds(i + 1) - 1
280  do j = istart, iend
281  if (abs(local_dx(j)) > abs(dvmax_model)) then
282  dvmax_model = local_dx(j)
283  idx_dv = j
284  end if
285  if (abs(local_res(j)) > abs(rmax_model)) then
286  rmax_model = local_res(j)
287  idx_r = j
288  end if
289  end do
290  if (summary%nitermax > 1) then
291  summary%convdvmax(i, iter_cnt) = dvmax_model
292  summary%convlocdv(i, iter_cnt) = idx_dv
293  summary%convrmax(i, iter_cnt) = rmax_model
294  summary%convlocr(i, iter_cnt) = idx_r
295  end if
296  end do
297  call vecrestorearrayf90(dx, local_dx, ierr)
298  chkerrq(ierr)
299  call vecrestorearrayf90(res, local_res, ierr)
300  chkerrq(ierr)
301 
302  end subroutine fill_cnvg_summary
303 
304  subroutine destroy(this)
305  class(petsccnvgctxtype) :: this
306  ! local
307  integer(I4B) :: ierr
308 
309  call vecdestroy(this%x_old, ierr)
310  chkerrq(ierr)
311  call vecdestroy(this%delta_x, ierr)
312  chkerrq(ierr)
313  call vecdestroy(this%residual, ierr)
314  chkerrq(ierr)
315 
316  end subroutine destroy
317 
318 end module petscconvergencemodule
This module contains simulation constants.
Definition: Constants.f90:9
real(dp), parameter dzero
real constant zero
Definition: Constants.f90:65
real(dp), parameter dprec
real constant machine precision
Definition: Constants.f90:120
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
function apply_check(ctx, nit, dvmax, rnorm_inf, rnorm_L2)
Apply the IMS convergence check.
subroutine create(this, mat, settings, summary)
subroutine fill_cnvg_summary(summary, dx, res, n)
Fill the convergence summary from the context.
subroutine, public petsc_cnvg_check(ksp, n, rnorm_L2, flag, context, ierr)
Routine to check the convergence following the configuration.
This module contains simulation methods.
Definition: Sim.f90:10
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=maxcharlen) errmsg
error message string
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