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
45 use math, only : glsc3, copy, neko_eps, add2s2, p_update
46 use utils, only : neko_error
48 use mpi_f08, only : mpi_allreduce, mpi_in_place, mpi_sum
49 use, intrinsic :: ieee_arithmetic, only : ieee_is_finite
50 implicit none
51 private
52
58 type, public, extends(ksp_t) :: bicgstab_t
60 real(kind=rp), allocatable :: p(:)
62 real(kind=rp), allocatable :: p_hat(:)
64 real(kind=rp), allocatable :: r(:)
66 real(kind=rp), allocatable :: s(:)
68 real(kind=rp), allocatable :: s_hat(:)
70 real(kind=rp), allocatable :: t(:)
72 real(kind=rp), allocatable :: v(:)
73 contains
75 procedure, pass(this) :: init => bicgstab_init
77 procedure, pass(this) :: free => bicgstab_free
79 procedure, pass(this) :: solve => bicgstab_solve
81 procedure, pass(this) :: solve_coupled => bicgstab_solve_coupled
82 end type bicgstab_t
83
84contains
85
94 subroutine bicgstab_init(this, n, max_iter, M, rel_tol, abs_tol, monitor)
95 class(bicgstab_t), target, intent(inout) :: this
96 class(pc_t), optional, intent(in), target :: M
97 integer, intent(in) :: n
98 integer, intent(in) :: max_iter
99 real(kind=rp), optional, intent(in) :: rel_tol
100 real(kind=rp), optional, intent(in) :: abs_tol
101 logical, optional, intent(in) :: monitor
102
103
104 call this%free()
105
106 allocate(this%p(n))
107 allocate(this%p_hat(n))
108 allocate(this%r(n))
109 allocate(this%s(n))
110 allocate(this%s_hat(n))
111 allocate(this%t(n))
112 allocate(this%v(n))
113 if (present(m)) then
114 this%M => m
115 end if
116
117 if (present(rel_tol) .and. present(abs_tol) .and. present(monitor)) then
118 call this%ksp_init(max_iter, rel_tol, abs_tol, monitor = monitor)
119 else if (present(rel_tol) .and. present(abs_tol)) then
120 call this%ksp_init(max_iter, rel_tol, abs_tol)
121 else if (present(monitor) .and. present(abs_tol)) then
122 call this%ksp_init(max_iter, abs_tol = abs_tol, monitor = monitor)
123 else if (present(rel_tol) .and. present(monitor)) then
124 call this%ksp_init(max_iter, rel_tol, monitor = monitor)
125 else if (present(rel_tol)) then
126 call this%ksp_init(max_iter, rel_tol = rel_tol)
127 else if (present(abs_tol)) then
128 call this%ksp_init(max_iter, abs_tol = abs_tol)
129 else if (present(monitor)) then
130 call this%ksp_init(max_iter, monitor = monitor)
131 else
132 call this%ksp_init(max_iter)
133 end if
134
135 end subroutine bicgstab_init
136
138 subroutine bicgstab_free(this)
139 class(bicgstab_t), intent(inout) :: this
140
141 call this%ksp_free()
142
143 if (allocated(this%v)) then
144 deallocate(this%v)
145 end if
146
147 if (allocated(this%r)) then
148 deallocate(this%r)
149 end if
150
151 if (allocated(this%t)) then
152 deallocate(this%t)
153 end if
154
155 if (allocated(this%p)) then
156 deallocate(this%p)
157 end if
158
159 if (allocated(this%p_hat)) then
160 deallocate(this%p_hat)
161 end if
162
163 if (allocated(this%s)) then
164 deallocate(this%s)
165 end if
166
167 if (allocated(this%s_hat)) then
168 deallocate(this%s_hat)
169 end if
170
171 nullify(this%M)
172
173
174 end subroutine bicgstab_free
175
189 function bicgstab_solve(this, Ax, x, f, n, coef, bc_projector, gs_h, niter) &
190 result(ksp_results)
191 class(bicgstab_t), intent(inout) :: this
192 class(ax_t), intent(in) :: ax
193 type(field_t), intent(inout) :: x
194 integer, intent(in) :: n
195 real(kind=rp), dimension(n), intent(in) :: f
196 type(coef_t), intent(inout) :: coef
197 class(scalar_bc_projector_t), intent(inout) :: bc_projector
198 type(gs_t), intent(inout) :: gs_h
199 type(ksp_monitor_t) :: ksp_results
200 integer, optional, intent(in) :: niter
201 integer :: iter, max_iter, i, ierr
202 real(kind=rp) :: rnorm, rtr, norm_fac, gamma
203 real(kind=rp) :: r_norm, s_norm, shadow_norm, t_norm, v_norm
204 ! s^T s, f^T v, v^T v, s^T t, t^T t
205 real(kind=rp) :: sts, ftv, vtv, stt, ttt
206 real(kind=rp) :: beta, alpha, omega, rho_1, rho_2
207 ! Extra-precision accumulator for the fused residual reductions
208 real(kind=xp) :: res_sum
209
210 if (present(niter)) then
211 max_iter = niter
212 else
213 max_iter = this%max_iter
214 end if
215 norm_fac = 1.0_rp / sqrt(coef%volume)
216
217 associate(r => this%r, t => this%t, s => this%s, v => this%v, &
218 p => this%p, s_hat => this%s_hat, p_hat => this%p_hat)
219
220 res_sum = 0.0_xp
221 !$omp parallel do reduction(+:res_sum)
222 do i = 1, n
223 x%x(i,1,1,1) = 0.0_rp
224 r(i) = f(i)
225 res_sum = res_sum + (r(i) * coef%mult(i,1,1,1) * r(i))
226 end do
227 !$omp end parallel do
228
229 call mpi_allreduce(mpi_in_place, res_sum, 1, &
230 mpi_extra_precision, mpi_sum, neko_comm, ierr)
231 rtr = res_sum
232
233 ! The implementation deliberately starts from x = 0, so f is both the
234 ! initial residual and the fixed shadow residual used by BiCGStab.
235 r_norm = bicgstab_sqrt(rtr, 'initial residual')
236 shadow_norm = r_norm
237 rnorm = r_norm * norm_fac
238 gamma = rnorm * this%rel_tol
239 ksp_results%res_start = rnorm
240 ksp_results%res_final = rnorm
241 ksp_results%iter = 0
242
243 ! Avoid entering the recurrence if the zero initial guess already meets
244 ! either stopping criterion. Apart from saving work, this prevents a
245 ! small right-hand side from being mistaken for a rho breakdown.
246 if (r_norm .le. 0.0_rp .or. rnorm .lt. this%abs_tol .or. &
247 rnorm .lt. gamma) then
248 ksp_results%converged = .true.
249 return
250 end if
251
252 call this%monitor_start('BiCGStab')
253 do iter = 1, max_iter
254
255 rho_1 = glsc3(f, coef%mult, r, n)
256
257 ! BiCGStab breaks down if the residual becomes orthogonal to the
258 ! shadow residual. Test the inner product relative to the norms of
259 ! both vectors so that uniformly scaling the system does not change
260 ! the decision.
261 call bicgstab_check_inner_product(rho_1, shadow_norm, r_norm, &
262 'rho inner product')
263
264 if (iter .eq. 1) then
265 call copy(p, r, n)
266 else
267 beta = (rho_1 / rho_2) * (alpha / omega)
268 if (.not. ieee_is_finite(beta)) then
269 call neko_error('BiCGStab failure: non-finite beta')
270 end if
271 call p_update(p, r, v, beta, omega, n)
272 end if
273
274 call this%M%solve(p_hat, p, n)
275 call ax%compute(v, p_hat, coef, x%msh, x%Xh)
276 call gs_h%op(v, n, gs_op_add)
277 call bc_projector%apply(v, n)
278
279 ! The alpha denominator is another BiCG breakdown point. Computing it
280 ! together with ||v|| permits a scale-aware orthogonality check without
281 ! an additional global synchronisation.
282 call bicgstab_product_and_norm(ftv, vtv, f, v, coef%mult, n)
283 v_norm = bicgstab_sqrt(vtv, 'operator result v')
284 call bicgstab_check_inner_product(ftv, shadow_norm, &
285 v_norm, 'alpha denominator')
286 alpha = rho_1 / ftv
287 if (.not. ieee_is_finite(alpha)) then
288 call neko_error('BiCGStab failure: non-finite alpha')
289 end if
290
291 res_sum = 0.0_xp
292 !$omp parallel do reduction(+:res_sum)
293 do i = 1, n
294 s(i) = r(i) - alpha * v(i)
295 res_sum = res_sum + s(i) * coef%mult(i,1,1,1) * s(i)
296 end do
297 !$omp end parallel do
298
299 call mpi_allreduce(mpi_in_place, res_sum, 1, &
300 mpi_extra_precision, mpi_sum, neko_comm, ierr)
301 sts = res_sum
302
303 s_norm = bicgstab_sqrt(sts, 'intermediate residual')
304 rnorm = s_norm * norm_fac
305 if (rnorm .lt. this%abs_tol .or. rnorm .lt. gamma) then
306 call add2s2(x%x, p_hat, alpha, n)
307 call this%monitor_iter(iter, rnorm)
308 exit
309 end if
310
311 call this%M%solve(s_hat, s, n)
312 call ax%compute(t, s_hat, coef, x%msh, x%Xh)
313 call gs_h%op(t, n, gs_op_add)
314 call bc_projector%apply(t, n)
315
316 call bicgstab_product_and_norm(stt, ttt, s, t, coef%mult, n)
317 t_norm = bicgstab_sqrt(ttt, 'operator result t')
318 if (t_norm .le. 0.0_rp) then
319 call neko_error('BiCGStab breakdown: zero omega denominator')
320 end if
321 if (.not. ieee_is_finite(stt)) then
322 call neko_error('BiCGStab failure: non-finite omega numerator')
323 end if
324 omega = stt / ttt
325 if (.not. ieee_is_finite(omega)) then
326 call neko_error('BiCGStab failure: non-finite omega')
327 end if
328
329 res_sum = 0.0_xp
330 !$omp parallel do reduction(+:res_sum)
331 do i = 1, n
332 x%x(i,1,1,1) = x%x(i,1,1,1) + alpha * p_hat(i) + omega * s_hat(i)
333 r(i) = s(i) - omega * t(i)
334 res_sum = res_sum + r(i) * coef%mult(i,1,1,1) * r(i)
335 end do
336 !$omp end parallel do
337
338 call mpi_allreduce(mpi_in_place, res_sum, 1, &
339 mpi_extra_precision, mpi_sum, neko_comm, ierr)
340 rtr = res_sum
341
342 r_norm = bicgstab_sqrt(rtr, 'recursive residual')
343 rnorm = r_norm * norm_fac
344 call this%monitor_iter(iter, rnorm)
345 if (rnorm .lt. this%abs_tol .or. rnorm .lt. gamma) then
346 exit
347 end if
348
349 ! A negative omega is valid. Breakdown occurs only when the local
350 ! minimal-residual step stagnates, i.e. when t and s are numerically
351 ! orthogonal.
352 call bicgstab_check_inner_product(stt, t_norm, s_norm, &
353 'omega numerator')
354 rho_2 = rho_1
355
356 end do
357 end associate
358 call this%monitor_stop()
359 ksp_results%res_final = rnorm
360 ksp_results%iter = iter
361 ksp_results%converged = this%is_converged(iter, rnorm)
362 end function bicgstab_solve
363
372 subroutine bicgstab_check_inner_product(inner_product, norm_a, norm_b, &
373 quantity)
374 real(kind=rp), intent(in) :: inner_product
375 real(kind=rp), intent(in) :: norm_a
376 real(kind=rp), intent(in) :: norm_b
377 character(len=*), intent(in) :: quantity
378 real(kind=rp) :: large_norm, small_norm
379
380 if (.not. ieee_is_finite(inner_product)) then
381 call neko_error('BiCGStab failure: non-finite ' // trim(quantity))
382 end if
383
384 large_norm = max(norm_a, norm_b)
385 small_norm = min(norm_a, norm_b)
386 if (large_norm .le. 0.0_rp .or. &
387 abs(inner_product) / large_norm .le. neko_eps * small_norm) then
388 call neko_error('BiCGStab breakdown: near-zero ' // trim(quantity))
389 end if
390
391 end subroutine bicgstab_check_inner_product
392
403 subroutine bicgstab_product_and_norm(product, norm_squared, a, b, mult, n)
404 integer, intent(in) :: n
405 real(kind=rp), intent(out) :: product
406 real(kind=rp), intent(out) :: norm_squared
407 real(kind=rp), dimension(n), intent(in) :: a
408 real(kind=rp), dimension(n), intent(in) :: b
409 real(kind=rp), dimension(n), intent(in) :: mult
410 real(kind=xp) :: product_sum, norm_sum
411 real(kind=xp) :: reductions(2)
412 integer :: i, ierr
413
414 product_sum = 0.0_xp
415 norm_sum = 0.0_xp
416 !$omp parallel do reduction(+:product_sum,norm_sum)
417 do i = 1, n
418 product_sum = product_sum + a(i) * mult(i) * b(i)
419 norm_sum = norm_sum + b(i) * mult(i) * b(i)
420 end do
421 !$omp end parallel do
422
423 reductions(1) = product_sum
424 reductions(2) = norm_sum
425 call mpi_allreduce(mpi_in_place, reductions, 2, mpi_extra_precision, &
426 mpi_sum, neko_comm, ierr)
427 product = reductions(1)
428 norm_squared = reductions(2)
429
430 end subroutine bicgstab_product_and_norm
431
436 function bicgstab_sqrt(value, quantity) result(root)
437 real(kind=rp), intent(in) :: value
438 character(len=*), intent(in) :: quantity
439 real(kind=rp) :: root
440
441 if (.not. ieee_is_finite(value) .or. value .lt. 0.0_rp) then
442 call neko_error('BiCGStab failure: invalid ' // trim(quantity) // &
443 ' norm')
444 end if
445 root = sqrt(value)
446
447 end function bicgstab_sqrt
448
468 function bicgstab_solve_coupled(this, Ax, x, y, z, fx, fy, fz, &
469 n, coef, bc_projector, gs_h, niter) result(ksp_results)
470 class(bicgstab_t), intent(inout) :: this
471 class(ax_t), intent(in) :: ax
472 type(field_t), intent(inout) :: x
473 type(field_t), intent(inout) :: y
474 type(field_t), intent(inout) :: z
475 integer, intent(in) :: n
476 real(kind=rp), dimension(n), intent(in) :: fx
477 real(kind=rp), dimension(n), intent(in) :: fy
478 real(kind=rp), dimension(n), intent(in) :: fz
479 type(coef_t), intent(inout) :: coef
480 class(vector_bc_projector_t), intent(inout) :: bc_projector
481 type(gs_t), intent(inout) :: gs_h
482 type(ksp_monitor_t), dimension(3) :: ksp_results
483 integer, optional, intent(in) :: niter
484 type(scalar_bc_projector_t), pointer :: bc_x, bc_y, bc_z
485
486 call vector_bc_projector_components(bc_projector, bc_x, bc_y, bc_z)
487 ksp_results(1) = this%solve(ax, x, fx, n, coef, bc_x, gs_h, niter)
488 ksp_results(2) = this%solve(ax, y, fy, n, coef, bc_y, gs_h, niter)
489 ksp_results(3) = this%solve(ax, z, fz, n, coef, bc_z, gs_h, niter)
490
491 end function bicgstab_solve_coupled
492
493end 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:437
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:404
subroutine bicgstab_init(this, n, max_iter, m, rel_tol, abs_tol, monitor)
Initialise a CPU BiCGStab solver.
Definition bicgstab.f90:95
subroutine bicgstab_free(this)
Free a CPU BiCGStab solver.
Definition bicgstab.f90:139
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:191
subroutine bicgstab_check_inner_product(inner_product, norm_a, norm_b, quantity)
Check an inner product for a BiCGStab breakdown.
Definition bicgstab.f90:374
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:470
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:1290
subroutine, public copy(a, b, n)
Copy a vector .
Definition math.f90:294
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:1001
subroutine, public p_update(a, b, c, c1, c2, n)
Returns .
Definition math.f90:1235
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.
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:58
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:93
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