Skip to content

Commit 5ba7681

Browse files
authored
feat(par): add convergence check based on petsc L2 norm (MODFLOW-ORG#1970)
* marked this, and petsc preconditioning, as development feature * modify print summary to reflect new config setting
1 parent a1af64c commit 5ba7681

File tree

2 files changed

+148
-90
lines changed

2 files changed

+148
-90
lines changed

src/Solution/PETSc/PetscConvergence.F90

Lines changed: 116 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,29 @@ module PetscConvergenceModule
33
use petscksp
44
use KindModule, only: I4B, DP
55
use ConstantsModule, only: DPREC, DZERO
6+
use SimModule, only: store_error
7+
use SimVariablesModule, only: errmsg
68
use ListModule
79
use ConvergenceSummaryModule
810
use ImsLinearSettingsModule
911
implicit none
1012
private
1113

12-
public :: petsc_check_convergence
14+
public :: petsc_cnvg_check
1315
public :: KSPSetConvergenceTest
1416

1517
! TODO_MJR: this could be smaller, find a bound
1618
real(DP), private, parameter :: RNORM_L2_TOL = DPREC
1719

18-
!< Context for the custom convergence check
20+
! Context for the custom convergence check
1921
type, public :: PetscCnvgCtxType
2022
Vec :: x_old !< x vector from the previous iteration
2123
Vec :: delta_x !< delta in x w.r.t. previous iteration
2224
Vec :: residual !< the unpreconditoned residual vector (a la IMS)
2325
integer(I4B) :: icnvg_ims !< IMS convergence number: 1 => converged, -1 => forces next Picard iter
24-
integer(I4B) :: icnvgopt !< convergence option from IMS settings
26+
integer(I4B) :: icnvgopt !< convergence option:
27+
!! 0,1,2,3,4,.. for equivalent IMS settings,
28+
!! 100,... for PETSc specific settings
2529
real(DP) :: dvclose !< dep. variable closure criterion
2630
real(DP) :: rclose !< residual closure criterion
2731
integer(I4B) :: max_its !< maximum number of inner iterations
@@ -88,9 +92,9 @@ subroutine create(this, mat, settings, summary)
8892

8993
end subroutine create
9094

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)
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)
9498
KSP :: ksp !< Iterative context
9599
PetscInt :: n !< Iteration number
96100
PetscReal :: rnorm_L2 !< 2-norm (preconditioned) residual value
@@ -99,15 +103,9 @@ subroutine petsc_check_convergence(ksp, n, rnorm_L2, flag, context, ierr)
99103
PetscErrorCode :: ierr !< error
100104
! local
101105
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
106+
PetscReal :: xnorm_inf, rnorm0, rnorm_inf_ims, rnorm_L2_ims
107+
Vec :: x, res
108108
type(ConvergenceSummaryType), pointer :: summary
109-
PetscInt :: iter_cnt
110-
PetscInt :: i, j, istart, iend
111109

112110
summary => context%cnvg_summary
113111

@@ -116,25 +114,25 @@ subroutine petsc_check_convergence(ksp, n, rnorm_L2, flag, context, ierr)
116114
call KSPBuildSolution(ksp, PETSC_NULL_VEC, x, ierr)
117115
CHKERRQ(ierr)
118116

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)
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)
130120
CHKERRQ(ierr)
131121

132-
call VecNorm(context%residual, NORM_2, rnorm_L2_ims, ierr)
133-
CHKERRQ(ierr)
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
134132

135133
! n == 0 is before the iteration starts
136134
if (n == 0) then
137-
context%rnorm_L2_init = rnorm_L2_ims
135+
context%rnorm_L2_init = rnorm0
138136
if (rnorm_L2 < RNORM_L2_TOL) then
139137
! exact solution found
140138
flag = KSP_CONVERGED_HAPPY_BREAKDOWN
@@ -144,80 +142,45 @@ subroutine petsc_check_convergence(ksp, n, rnorm_L2, flag, context, ierr)
144142
flag = KSP_CONVERGED_ITERATING
145143
end if
146144
! early return
145+
call VecDestroy(res, ierr)
146+
CHKERRQ(ierr)
147147
return
148148
end if
149149

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-
164150
call VecWAXPY(context%delta_x, min_one, context%x_old, x, ierr)
165151
CHKERRQ(ierr)
166152

167-
call VecNorm(context%delta_x, NORM_INFINITY, xnorm_inf_ims, ierr)
153+
call VecNorm(context%delta_x, NORM_INFINITY, xnorm_inf, ierr)
168154
CHKERRQ(ierr)
169155

170-
rnorm_inf_ims = 0.0
156+
rnorm_inf_ims = huge(rnorm_inf_ims)
171157
if (context%icnvgopt == 0 .or. context%icnvgopt == 1) then
172-
call VecNorm(context%residual, NORM_INFINITY, rnorm_inf_ims, ierr)
158+
call VecNorm(res, NORM_INFINITY, rnorm_inf_ims, ierr)
173159
CHKERRQ(ierr)
174160
end if
175161

176162
call VecCopy(x, context%x_old, ierr)
177163
CHKERRQ(ierr)
178164

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)
165+
! fill the summary for reporting
166+
call fill_cnvg_summary(summary, context%delta_x, res, n)
214167

215168
if (rnorm_L2 < RNORM_L2_TOL) then
216169
! exact solution, set to 'converged'
217170
flag = KSP_CONVERGED_HAPPY_BREAKDOWN
218-
else
171+
else if (context%icnvgopt < 100) then
219172
! IMS check on convergence
220-
flag = apply_check(context, n, xnorm_inf_ims, rnorm_inf_ims, rnorm_L2_ims)
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.)
221184
end if
222185

223186
if (flag == KSP_CONVERGED_ITERATING) then
@@ -227,7 +190,10 @@ subroutine petsc_check_convergence(ksp, n, rnorm_L2, flag, context, ierr)
227190
end if
228191
end if
229192

230-
end subroutine petsc_check_convergence
193+
call VecDestroy(res, ierr)
194+
CHKERRQ(ierr)
195+
196+
end subroutine petsc_cnvg_check
231197

232198
!> @brief Apply the IMS convergence check
233199
!<
@@ -268,6 +234,73 @@ function apply_check(ctx, nit, dvmax, rnorm_inf, rnorm_L2) result(flag)
268234

269235
end function apply_check
270236

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+
271304
subroutine destroy(this)
272305
class(PetscCnvgCtxType) :: this
273306
! local

src/Solution/PETSc/PetscSolver.F90

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module PetscSolverModule
1414
use ImsLinearSettingsModule
1515
use SimVariablesModule, only: iout, simulation_mode
1616
use SimModule, only: store_error, store_warning
17+
use DevFeatureModule, only: dev_feature
1718

1819
implicit none
1920
private
@@ -28,6 +29,7 @@ module PetscSolverModule
2829

2930
type(ImsLinearSettingsType), pointer :: linear_settings => null() !< pointer to linear settings from IMS
3031
logical(LGP) :: use_ims_pc !< when true, use custom IMS-style preconditioning
32+
logical(LGP) :: use_ims_cnvgopt !< when true, use IMS convergence check in PETSc solve
3133
KSPType :: ksp_type !< the KSP solver type (CG, BCGS, ...)
3234
PCType :: pc_type !< the (global) preconditioner type, should be parallel
3335
PCType :: sub_pc_type !< the block preconditioner type (for the subdomain)
@@ -99,7 +101,8 @@ subroutine petsc_initialize(this, matrix, linear_settings, convergence_summary)
99101

100102
call this%petsc_check_settings(linear_settings)
101103

102-
this%use_ims_pc = .true. ! default is true, override with .petscrc
104+
this%use_ims_cnvgopt = .true. ! use IMS convergence check, override with .petscrc
105+
this%use_ims_pc = .true. ! use IMS preconditioning, override with .petscrc
103106
allocate (this%pc_context)
104107
call this%pc_context%create(this%matrix, linear_settings)
105108

@@ -187,10 +190,19 @@ subroutine get_options_mf6(this)
187190
! local
188191
PetscErrorCode :: ierr
189192
logical(LGP) :: found
193+
logical(LGP) :: use_petsc_pc, use_petsc_cnvg
190194

195+
use_petsc_pc = .false.
191196
call PetscOptionsGetBool(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, &
192-
'-ims_pc', this%use_ims_pc, found, ierr)
197+
'-use_petsc_pc', use_petsc_pc, found, ierr)
193198
CHKERRQ(ierr)
199+
this%use_ims_pc = .not. use_petsc_pc
200+
201+
use_petsc_cnvg = .false.
202+
call PetscOptionsGetBool(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, &
203+
'-use_petsc_cnvg', use_petsc_cnvg, found, ierr)
204+
CHKERRQ(ierr)
205+
this%use_ims_cnvgopt = .not. use_petsc_cnvg
194206

195207
end subroutine get_options_mf6
196208

@@ -216,6 +228,8 @@ subroutine create_ksp(this)
216228
if (this%use_ims_pc) then
217229
call this%set_ims_pc()
218230
else
231+
call dev_feature('PETSc preconditioning is under development, install the &
232+
&nightly build or compile from source with IDEVELOPMODE = 1.')
219233
call this%set_petsc_pc()
220234
end if
221235

@@ -251,10 +265,10 @@ subroutine set_petsc_pc(this)
251265
CHKERRQ(ierr)
252266
call PCSetType(sub_pc, this%sub_pc_type, ierr)
253267
CHKERRQ(ierr)
254-
call PCSetFromOptions(sub_pc, ierr)
255-
CHKERRQ(ierr)
256268
call PCFactorSetLevels(sub_pc, this%linear_settings%level, ierr)
257269
CHKERRQ(ierr)
270+
call PCSetFromOptions(sub_pc, ierr)
271+
CHKERRQ(ierr)
258272

259273
end subroutine set_petsc_pc
260274

@@ -302,8 +316,14 @@ subroutine create_convergence_check(this, convergence_summary)
302316

303317
call this%petsc_ctx%create(this%mat_petsc, this%linear_settings, &
304318
convergence_summary)
319+
if (.not. this%use_ims_cnvgopt) then
320+
! use PETSc residual L2 norm for convergence
321+
call dev_feature('Using PETSc convergence is under development, install &
322+
&the nightly build or compile from source with IDEVELOPMODE = 1.')
323+
this%petsc_ctx%icnvgopt = 100
324+
end if
305325

306-
call KSPSetConvergenceTest(this%ksp_petsc, petsc_check_convergence, &
326+
call KSPSetConvergenceTest(this%ksp_petsc, petsc_cnvg_check, &
307327
this%petsc_ctx, PETSC_NULL_FUNCTION, ierr)
308328
CHKERRQ(ierr)
309329

@@ -405,8 +425,13 @@ subroutine petsc_print_summary(this)
405425
"Dep. var. closure criterion: ", trim(adjustl(dvclose_str))
406426
write (iout, '(1x,a,a)') &
407427
"Residual closure criterion: ", trim(adjustl(rclose_str))
408-
write (iout, '(1x,a,i0)') &
409-
"Residual convergence option: ", this%linear_settings%icnvgopt
428+
if (this%use_ims_cnvgopt) then
429+
write (iout, '(1x,a,i0)') &
430+
"Residual convergence option: ", this%linear_settings%icnvgopt
431+
else
432+
write (iout, '(1x,a)') &
433+
"Residual convergence option: PETSc L2 norm"
434+
end if
410435
write (iout, '(1x,a,a)') &
411436
"Relaxation factor MILU(T): ", trim(adjustl(relax_str))
412437
write (iout, '(1x,a,i0)') &

0 commit comments

Comments
 (0)