Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
bicgstab.f90
Go to the documentation of this file.
1! Copyright (c) 2021-2026, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
35 use num_types, only: rp, xp
37 use precon, only : pc_t
38 use ax_product, only : ax_t
39 use field, only : field_t
40 use coefs, only : coef_t
41 use gather_scatter, only : gs_t, gs_op_add
46 use math, only : glsc3, copy, neko_eps, add2s2, p_update
47 use utils, only : neko_error
49 use mpi_f08, only : mpi_allreduce, mpi_in_place, mpi_sum
50 use, intrinsic :: ieee_arithmetic, only : ieee_is_finite
51 implicit none
52 private
53
59 type, public, extends(ksp_t) :: bicgstab_t
61 real(kind=rp), pointer :: p(:) => null()
63 real(kind=rp), pointer :: p_hat(:) => null()
65 real(kind=rp), pointer :: r(:) => null()
67 real(kind=rp), pointer :: s_hat(:) => null()
69 real(kind=rp), pointer :: t(:) => null()
71 real(kind=rp), pointer :: v(:) => null()
72 contains
74 procedure, pass(this) :: init => bicgstab_init
76 procedure, pass(this) :: free => bicgstab_free
78 procedure, pass(this) :: solve => bicgstab_solve
80 procedure, pass(this) :: solve_coupled => bicgstab_solve_coupled
81 end type bicgstab_t
82
83contains
84
93 subroutine bicgstab_init(this, n, max_iter, M, rel_tol, abs_tol, monitor)
94 class(bicgstab_t), target, intent(inout) :: this
95 class(pc_t), optional, intent(in), target :: M
96 integer, intent(in) :: n
97 integer, intent(in) :: max_iter
98 real(kind=rp), optional, intent(in) :: rel_tol
99 real(kind=rp), optional, intent(in) :: abs_tol
100 logical, optional, intent(in) :: monitor
101
102
103 call this%free()
104
105 if (present(m)) then
106 this%M => m
107 end if
108
109 if (present(rel_tol) .and. present(abs_tol) .and. present(monitor)) then
110 call this%ksp_init(max_iter, rel_tol, abs_tol, monitor = monitor)
111 else if (present(rel_tol) .and. present(abs_tol)) then
112 call this%ksp_init(max_iter, rel_tol, abs_tol)
113 else if (present(monitor) .and. present(abs_tol)) then
114 call this%ksp_init(max_iter, abs_tol = abs_tol, monitor = monitor)
115 else if (present(rel_tol) .and. present(monitor)) then
116 call this%ksp_init(max_iter, rel_tol, monitor = monitor)
117 else if (present(rel_tol)) then
118 call this%ksp_init(max_iter, rel_tol = rel_tol)
119 else if (present(abs_tol)) then
120 call this%ksp_init(max_iter, abs_tol = abs_tol)
121 else if (present(monitor)) then
122 call this%ksp_init(max_iter, monitor = monitor)
123 else
124 call this%ksp_init(max_iter)
125 end if
126
127 end subroutine bicgstab_init
128
130 subroutine bicgstab_free(this)
131 class(bicgstab_t), intent(inout) :: this
132
133 call this%ksp_free()
134
135 nullify(this%M)
136 nullify(this%p, this%p_hat, this%r, this%s_hat, this%t, this%v)
137
138 end subroutine bicgstab_free
139
153 function bicgstab_solve(this, Ax, x, f, n, coef, bc_projector, gs_h, niter) &
154 result(ksp_results)
155 class(bicgstab_t), intent(inout) :: this
156 class(ax_t), intent(in) :: ax
157 type(field_t), intent(inout) :: x
158 integer, intent(in) :: n
159 real(kind=rp), dimension(n), intent(in) :: f
160 type(coef_t), intent(inout) :: coef
161 class(scalar_bc_projector_t), intent(inout) :: bc_projector
162 type(gs_t), intent(inout) :: gs_h
163 type(ksp_monitor_t) :: ksp_results
164 integer, optional, intent(in) :: niter
165 integer :: iter, max_iter, i, ierr
166 real(kind=rp) :: rnorm, rtr, norm_fac, gamma
167 real(kind=rp) :: r_norm, s_norm, shadow_norm, t_norm, v_norm
168 ! s^T s, f^T v, v^T v, s^T t, t^T t
169 real(kind=rp) :: sts, ftv, vtv, stt, ttt
170 real(kind=rp) :: beta, alpha, omega, rho_1, rho_2
171 ! Extra-precision accumulator for the fused residual reductions
172 real(kind=xp) :: res_sum
173 integer :: temp_indices(6)
174
175 if (present(niter)) then
176 max_iter = niter
177 else
178 max_iter = this%max_iter
179 end if
180 norm_fac = 1.0_rp / sqrt(coef%volume)
181
182 call neko_scratch_registry%request(this%p, temp_indices(1), n, .false.)
183 call neko_scratch_registry%request(this%p_hat, temp_indices(2), n, .false.)
184 call neko_scratch_registry%request(this%r, temp_indices(3), n, .false.)
185 call neko_scratch_registry%request(this%s_hat, temp_indices(4), n, .false.)
186 call neko_scratch_registry%request(this%t, temp_indices(5), n, .false.)
187 call neko_scratch_registry%request(this%v, temp_indices(6), n, .false.)
188
189 associate(p => this%p, p_hat => this%p_hat, r => this%r, &
190 s_hat => this%s_hat, t => this%t, v => this%v)
191
192 res_sum = 0.0_xp
193 !$omp parallel do reduction(+:res_sum)
194 do i = 1, n
195 x%x(i,1,1,1) = 0.0_rp
196 r(i) = f(i)
197 res_sum = res_sum + (r(i) * coef%mult(i,1,1,1) * r(i))
198 end do
199 !$omp end parallel do
200
201 call mpi_allreduce(mpi_in_place, res_sum, 1, &
202 mpi_extra_precision, mpi_sum, neko_comm, ierr)
203 rtr = res_sum
204
205 ! The implementation deliberately starts from x = 0, so f is both the
206 ! initial residual and the fixed shadow residual used by BiCGStab.
207 r_norm = bicgstab_sqrt(rtr, 'initial residual')
208 shadow_norm = r_norm
209 rnorm = r_norm * norm_fac
210 gamma = rnorm * this%rel_tol
211 ksp_results%res_start = rnorm
212 ksp_results%res_final = rnorm
213 ksp_results%iter = 0
214
215 ! Avoid entering the recurrence if the zero initial guess already meets
216 ! either stopping criterion. Apart from saving work, this prevents a
217 ! small right-hand side from being mistaken for a rho breakdown.
218 if (r_norm .le. 0.0_rp .or. rnorm .lt. this%abs_tol .or. &
219 rnorm .lt. gamma) then
220 ksp_results%converged = .true.
221 nullify(this%p, this%p_hat, this%r, this%s_hat, this%t, this%v)
222 call neko_scratch_registry%relinquish_host_array(temp_indices)
223 return
224 end if
225
226 call this%monitor_start('BiCGStab')
227 do iter = 1, max_iter
228
229 rho_1 = glsc3(f, coef%mult, r, n)
230
231 ! BiCGStab breaks down if the residual becomes orthogonal to the
232 ! shadow residual. Test the inner product relative to the norms of
233 ! both vectors so that uniformly scaling the system does not change
234 ! the decision.
235 call bicgstab_check_inner_product(rho_1, shadow_norm, r_norm, &
236 'rho inner product')
237
238 if (iter .eq. 1) then
239 call copy(p, r, n)
240 else
241 beta = (rho_1 / rho_2) * (alpha / omega)
242 if (.not. ieee_is_finite(beta)) then
243 call neko_error('BiCGStab failure: non-finite beta')
244 end if
245 call p_update(p, r, v, beta, omega, n)
246 end if
247
248 call this%M%solve(p_hat, p, n)
249 call ax%compute(v, p_hat, coef, x%msh, x%Xh)
250 call gs_h%op(v, n, gs_op_add)
251 call bc_projector%apply(v, n)
252
253 ! The alpha denominator is another BiCG breakdown point. Computing it
254 ! together with ||v|| permits a scale-aware orthogonality check without
255 ! an additional global synchronisation.
256 call bicgstab_product_and_norm(ftv, vtv, f, v, coef%mult, n)
257 v_norm = bicgstab_sqrt(vtv, 'operator result v')
258 call bicgstab_check_inner_product(ftv, shadow_norm, &
259 v_norm, 'alpha denominator')
260 alpha = rho_1 / ftv
261 if (.not. ieee_is_finite(alpha)) then
262 call neko_error('BiCGStab failure: non-finite alpha')
263 end if
264
265 ! The previous residual is no longer needed after p has been formed,
266 ! so store the intermediate residual in r.
267 res_sum = 0.0_xp
268 !$omp parallel do reduction(+:res_sum)
269 do i = 1, n
270 r(i) = r(i) - alpha * v(i)
271 res_sum = res_sum + r(i) * coef%mult(i,1,1,1) * r(i)
272 end do
273 !$omp end parallel do
274
275 call mpi_allreduce(mpi_in_place, res_sum, 1, &
276 mpi_extra_precision, mpi_sum, neko_comm, ierr)
277 sts = res_sum
278
279 s_norm = bicgstab_sqrt(sts, 'intermediate residual')
280 rnorm = s_norm * norm_fac
281 if (rnorm .lt. this%abs_tol .or. rnorm .lt. gamma) then
282 call add2s2(x%x, p_hat, alpha, n)
283 call this%monitor_iter(iter, rnorm)
284 exit
285 end if
286
287 call this%M%solve(s_hat, r, n)
288 call ax%compute(t, s_hat, coef, x%msh, x%Xh)
289 call gs_h%op(t, n, gs_op_add)
290 call bc_projector%apply(t, n)
291
292 call bicgstab_product_and_norm(stt, ttt, r, t, coef%mult, n)
293 t_norm = bicgstab_sqrt(ttt, 'operator result t')
294 if (t_norm .le. 0.0_rp) then
295 call neko_error('BiCGStab breakdown: zero omega denominator')
296 end if
297 if (.not. ieee_is_finite(stt)) then
298 call neko_error('BiCGStab failure: non-finite omega numerator')
299 end if
300 omega = stt / ttt
301 if (.not. ieee_is_finite(omega)) then
302 call neko_error('BiCGStab failure: non-finite omega')
303 end if
304
305 res_sum = 0.0_xp
306 !$omp parallel do reduction(+:res_sum)
307 do i = 1, n
308 x%x(i,1,1,1) = x%x(i,1,1,1) + alpha * p_hat(i) + omega * s_hat(i)
309 r(i) = r(i) - omega * t(i)
310 res_sum = res_sum + r(i) * coef%mult(i,1,1,1) * r(i)
311 end do
312 !$omp end parallel do
313
314 call mpi_allreduce(mpi_in_place, res_sum, 1, &
315 mpi_extra_precision, mpi_sum, neko_comm, ierr)
316 rtr = res_sum
317
318 r_norm = bicgstab_sqrt(rtr, 'recursive residual')
319 rnorm = r_norm * norm_fac
320 call this%monitor_iter(iter, rnorm)
321 if (rnorm .lt. this%abs_tol .or. rnorm .lt. gamma) then
322 exit
323 end if
324
325 ! A negative omega is valid. Breakdown occurs only when the local
326 ! minimal-residual step stagnates, i.e. when t and s are numerically
327 ! orthogonal.
328 call bicgstab_check_inner_product(stt, t_norm, s_norm, &
329 'omega numerator')
330 rho_2 = rho_1
331
332 end do
333 call this%monitor_stop()
334 ksp_results%res_final = rnorm
335 ksp_results%iter = iter
336 ksp_results%converged = this%is_converged(iter, rnorm)
337 end associate
338 nullify(this%p, this%p_hat, this%r, this%s_hat, this%t, this%v)
339 call neko_scratch_registry%relinquish_host_array(temp_indices)
340 end function bicgstab_solve
341
350 subroutine bicgstab_check_inner_product(inner_product, norm_a, norm_b, &
351 quantity)
352 real(kind=rp), intent(in) :: inner_product
353 real(kind=rp), intent(in) :: norm_a
354 real(kind=rp), intent(in) :: norm_b
355 character(len=*), intent(in) :: quantity
356 real(kind=rp) :: large_norm, small_norm
357
358 if (.not. ieee_is_finite(inner_product)) then
359 call neko_error('BiCGStab failure: non-finite ' // trim(quantity))
360 end if
361
362 large_norm = max(norm_a, norm_b)
363 small_norm = min(norm_a, norm_b)
364 if (large_norm .le. 0.0_rp .or. &
365 abs(inner_product) / large_norm .le. neko_eps * small_norm) then
366 call neko_error('BiCGStab breakdown: near-zero ' // trim(quantity))
367 end if
368
369 end subroutine bicgstab_check_inner_product
370
381 subroutine bicgstab_product_and_norm(product, norm_squared, a, b, mult, n)
382 integer, intent(in) :: n
383 real(kind=rp), intent(out) :: product
384 real(kind=rp), intent(out) :: norm_squared
385 real(kind=rp), dimension(n), intent(in) :: a
386 real(kind=rp), dimension(n), intent(in) :: b
387 real(kind=rp), dimension(n), intent(in) :: mult
388 real(kind=xp) :: product_sum, norm_sum
389 real(kind=xp) :: reductions(2)
390 integer :: i, ierr
391
392 product_sum = 0.0_xp
393 norm_sum = 0.0_xp
394 !$omp parallel do reduction(+:product_sum,norm_sum)
395 do i = 1, n
396 product_sum = product_sum + a(i) * mult(i) * b(i)
397 norm_sum = norm_sum + b(i) * mult(i) * b(i)
398 end do
399 !$omp end parallel do
400
401 reductions(1) = product_sum
402 reductions(2) = norm_sum
403 call mpi_allreduce(mpi_in_place, reductions, 2, mpi_extra_precision, &
404 mpi_sum, neko_comm, ierr)
405 product = reductions(1)
406 norm_squared = reductions(2)
407
408 end subroutine bicgstab_product_and_norm
409
414 function bicgstab_sqrt(value, quantity) result(root)
415 real(kind=rp), intent(in) :: value
416 character(len=*), intent(in) :: quantity
417 real(kind=rp) :: root
418
419 if (.not. ieee_is_finite(value) .or. value .lt. 0.0_rp) then
420 call neko_error('BiCGStab failure: invalid ' // trim(quantity) // &
421 ' norm')
422 end if
423 root = sqrt(value)
424
425 end function bicgstab_sqrt
426
446 function bicgstab_solve_coupled(this, Ax, x, y, z, fx, fy, fz, &
447 n, coef, bc_projector, gs_h, niter) result(ksp_results)
448 class(bicgstab_t), intent(inout) :: this
449 class(ax_t), intent(in) :: ax
450 type(field_t), intent(inout) :: x
451 type(field_t), intent(inout) :: y
452 type(field_t), intent(inout) :: z
453 integer, intent(in) :: n
454 real(kind=rp), dimension(n), intent(in) :: fx
455 real(kind=rp), dimension(n), intent(in) :: fy
456 real(kind=rp), dimension(n), intent(in) :: fz
457 type(coef_t), intent(inout) :: coef
458 class(vector_bc_projector_t), intent(inout) :: bc_projector
459 type(gs_t), intent(inout) :: gs_h
460 type(ksp_monitor_t), dimension(3) :: ksp_results
461 integer, optional, intent(in) :: niter
462 type(scalar_bc_projector_t), pointer :: bc_x, bc_y, bc_z
463
464 call vector_bc_projector_components(bc_projector, bc_x, bc_y, bc_z)
465 ksp_results(1) = this%solve(ax, x, fx, n, coef, bc_x, gs_h, niter)
466 ksp_results(2) = this%solve(ax, y, fy, n, coef, bc_y, gs_h, niter)
467 ksp_results(3) = this%solve(ax, z, fz, n, coef, bc_z, gs_h, niter)
468
469 end function bicgstab_solve_coupled
470
471end module bicgstab
__device__ T solve(const T u, const T y, const T guess, const T nu, const T kappa, const T B)
Defines a Matrix-vector product.
Definition ax.f90:34
Provides a CPU implementation of the BiCGStab method.
Definition bicgstab.f90:34
real(kind=rp) function bicgstab_sqrt(value, quantity)
Return the square root of a valid squared norm.
Definition bicgstab.f90:415
subroutine bicgstab_product_and_norm(product, norm_squared, a, b, mult, n)
Compute a weighted inner product and squared norm in one reduction.
Definition bicgstab.f90:382
subroutine bicgstab_init(this, n, max_iter, m, rel_tol, abs_tol, monitor)
Initialise a CPU BiCGStab solver.
Definition bicgstab.f90:94
subroutine bicgstab_free(this)
Free a CPU BiCGStab solver.
Definition bicgstab.f90:131
type(ksp_monitor_t) function bicgstab_solve(this, ax, x, f, n, coef, bc_projector, gs_h, niter)
Solve a linear system with the CPU BiCGStab method.
Definition bicgstab.f90:155
subroutine bicgstab_check_inner_product(inner_product, norm_a, norm_b, quantity)
Check an inner product for a BiCGStab breakdown.
Definition bicgstab.f90:352
type(ksp_monitor_t) function, dimension(3) bicgstab_solve_coupled(this, ax, x, y, z, fx, fy, fz, n, coef, bc_projector, gs_h, niter)
Solve three independent systems with the CPU BiCGStab method.
Definition bicgstab.f90:448
Coefficients.
Definition coef.f90:34
Definition comm.F90:1
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
type(mpi_datatype), public mpi_extra_precision
Definition comm.F90:55
Defines a field.
Definition field.f90:34
Gather-scatter.
Implements the base abstract type for Krylov solvers plus helper types.
Definition krylov.f90:34
integer, parameter, public ksp_max_iter
Maximum number of iters.
Definition krylov.f90:52
Definition math.f90:60
real(kind=rp) function, public glsc3(a, b, c, n)
Weighted inner product .
Definition math.f90:1326
subroutine, public copy(a, b, n)
Copy a vector .
Definition math.f90:295
real(kind=rp), parameter, public neko_eps
Machine epsilon .
Definition math.f90:70
subroutine, public add2s2(a, b, c1, n)
Vector addition with scalar multiplication (multiplication on second argument)
Definition math.f90:1037
subroutine, public p_update(a, b, c, c1, c2, n)
Returns .
Definition math.f90:1271
integer, parameter, public xp
Definition num_types.f90:16
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Krylov preconditioner.
Definition precon.f90:34
Implements scalar_projector_t.
Defines a registry for storing and requesting temporary objects This can be used when you have a func...
type(scratch_registry_t), target, public neko_scratch_registry
Global scratch registry.
Utilities.
Definition utils.f90:35
Implements boundary condition projectors for vector fields. Two types concrete types are provided: se...
subroutine, public vector_bc_projector_components(this, x, y, z)
Access the component scalar projectors from a segregated vector projector.
Base type for a matrix-vector product providing .
Definition ax.f90:43
CPU implementation of the right-preconditioned BiCGStab method.
Definition bicgstab.f90:59
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:135
Gather-scatter kernel.
Type for storing initial and final residuals in a Krylov solver.
Definition krylov.f90:57
Base abstract type for a canonical Krylov method, solving .
Definition krylov.f90:74
Defines a canonical Krylov preconditioner.
Definition precon.f90:40
Projector for scalar boundary conditions.
Abstract type for resolving vector boundary conditions.
#define max(a, b)
Definition tensor.cu:40