Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gmres.f90
Go to the documentation of this file.
1! Copyright (c) 2020-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!
34module gmres
35 !$ use omp_lib
36 use krylov, only : ksp_t, ksp_monitor_t
37 use precon, only : pc_t
38 use ax_product, only : ax_t
39 use num_types, only : rp, xp
40 use field, only : field_t
41 use coefs, only : coef_t
42 use gather_scatter, only : gs_t, gs_op_add
46 use matrix, only : matrix_t
48 use math, only : glsc3, rzero, copy, sub2, cmult2, abscmp
49 use neko_config, only : neko_blk_size
51 use mpi_f08, only : mpi_allreduce, mpi_in_place, mpi_sum
52 implicit none
53 private
54
60 type, public, extends(ksp_t) :: gmres_t
62 integer :: lgmres = 30
64 real(kind=rp), pointer :: w(:) => null()
66 real(kind=rp), pointer :: r(:) => null()
68 type(matrix_t), pointer :: z => null()
70 type(matrix_t), pointer :: v => null()
72 real(kind=xp), allocatable :: h(:,:)
74 real(kind=xp), allocatable :: s(:)
76 real(kind=xp), allocatable :: gam(:)
78 real(kind=xp), allocatable :: c(:)
80 real(kind=xp), allocatable :: hp(:,:)
81 contains
83 procedure, pass(this) :: init => gmres_init
85 procedure, pass(this) :: free => gmres_free
87 procedure, pass(this) :: solve => gmres_solve
89 procedure, pass(this) :: solve_coupled => gmres_solve_coupled
90 end type gmres_t
91
92contains
93
95 subroutine gmres_init(this, n, max_iter, M, rel_tol, abs_tol, monitor)
96 class(gmres_t), target, intent(inout) :: this
97 integer, intent(in) :: n
98 integer, intent(in) :: max_iter
99 class(pc_t), optional, intent(in), target :: M
100 real(kind=rp), optional, intent(in) :: rel_tol
101 real(kind=rp), optional, intent(in) :: abs_tol
102 logical, optional, intent(in) :: monitor
103 integer :: nthrds
104
105 call this%free()
106
107 if (present(m)) then
108 this%M => m
109 end if
110
111 allocate(this%c(this%lgmres))
112 allocate(this%s(this%lgmres))
113 allocate(this%gam(this%lgmres + 1))
114
115 allocate(this%h(this%lgmres, this%lgmres))
116
117 nthrds = 1
118 !$ nthrds = omp_get_max_threads()
119 allocate(this%hp(this%lgmres, nthrds))
120
121 if (present(rel_tol) .and. present(abs_tol) .and. present(monitor)) then
122 call this%ksp_init(max_iter, rel_tol, abs_tol, monitor = monitor)
123 else if (present(rel_tol) .and. present(abs_tol)) then
124 call this%ksp_init(max_iter, rel_tol, abs_tol)
125 else if (present(monitor) .and. present(abs_tol)) then
126 call this%ksp_init(max_iter, abs_tol = abs_tol, monitor = monitor)
127 else if (present(rel_tol) .and. present(monitor)) then
128 call this%ksp_init(max_iter, rel_tol, monitor = monitor)
129 else if (present(rel_tol)) then
130 call this%ksp_init(max_iter, rel_tol = rel_tol)
131 else if (present(abs_tol)) then
132 call this%ksp_init(max_iter, abs_tol = abs_tol)
133 else if (present(monitor)) then
134 call this%ksp_init(max_iter, monitor = monitor)
135 else
136 call this%ksp_init(max_iter)
137 end if
138
139 end subroutine gmres_init
140
142 subroutine gmres_free(this)
143 class(gmres_t), intent(inout) :: this
144
145 call this%ksp_free()
146
147 nullify(this%w)
148
149 if (allocated(this%c)) then
150 deallocate(this%c)
151 end if
152
153 nullify(this%r)
154 nullify(this%z)
155
156 if (allocated(this%h)) then
157 deallocate(this%h)
158 end if
159
160 nullify(this%v)
161
162 if (allocated(this%s)) then
163 deallocate(this%s)
164 end if
165
166
167 if (allocated(this%gam)) then
168 deallocate(this%gam)
169 end if
170
171 if (allocated(this%hp)) then
172 deallocate(this%hp)
173 end if
174
175 nullify(this%M)
176
177 end subroutine gmres_free
178
180 function gmres_solve(this, Ax, x, f, n, coef, bc_projector, gs_h, niter) &
181 result(ksp_results)
182 class(gmres_t), intent(inout) :: this
183 class(ax_t), intent(in) :: ax
184 type(field_t), intent(inout) :: x
185 integer, intent(in) :: n
186 real(kind=rp), dimension(n), intent(in) :: f
187 type(coef_t), intent(inout) :: coef
188 class(scalar_bc_projector_t), intent(inout) :: bc_projector
189 type(gs_t), intent(inout) :: gs_h
190 type(ksp_monitor_t) :: ksp_results
191 integer, optional, intent(in) :: niter
192 integer :: iter, max_iter
193 integer :: i, j, k, l, ierr, tid, nthrds
194 real(kind=xp) :: w_plus(neko_blk_size), x_plus(neko_blk_size)
195 real(kind=xp) :: hl(this%lgmres)
196 real(kind=xp) :: alpha, lr, alpha2, norm_fac, tmp, acc
197 real(kind=rp) :: temp, rnorm
198 logical :: conv
199 integer :: temp_indices(4)
200
201 conv = .false.
202 iter = 0
203 rnorm = 0.0_rp
204
205 if (present(niter)) then
206 max_iter = niter
207 else
208 max_iter = this%max_iter
209 end if
210
211 nthrds = 1
212 !$ nthrds = omp_get_max_threads()
213
214 call neko_scratch_registry%request(this%w, temp_indices(1), n, .false.)
215 call neko_scratch_registry%request(this%r, temp_indices(2), n, .false.)
216 call neko_scratch_registry%request(this%z, temp_indices(3), &
217 n, this%lgmres, .false.)
218 call neko_scratch_registry%request(this%v, temp_indices(4), &
219 n, this%lgmres, .false.)
220
221 associate(w => this%w, c => this%c, r => this%r, z => this%z%x, &
222 h => this%h, v => this%v%x, s => this%s, gam => this%gam, &
223 hp => this%hp)
224
225 norm_fac = 1.0_rp / sqrt(coef%volume)
226 call rzero(x%x, n)
227 gam = 0.0_xp
228 s = 1.0_xp
229 c = 1.0_xp
230 h = 0.0_xp
231 call this%monitor_start('GMRES')
232 do while (.not. conv .and. iter .lt. max_iter)
233
234 if (iter .eq. 0) then
235 call copy(r, f, n)
236 else
237 call copy(r, f, n)
238 call ax%compute(w, x%x, coef, x%msh, x%Xh)
239 call gs_h%op(w, n, gs_op_add)
240 call bc_projector%apply(w, n)
241 call sub2(r, w, n)
242 end if
243
244 gam(1) = sqrt(glsc3(r, r, coef%mult, n))
245 if (iter .eq. 0) then
246 ksp_results%res_start = gam(1) * norm_fac
247 end if
248
249 if (abscmp(gam(1), 0.0_xp)) exit
250
251 rnorm = 0.0_rp
252 temp = 1.0_rp / gam(1)
253 call cmult2(v(1,1), r, temp, n)
254 do j = 1, this%lgmres
255 iter = iter+1
256
257 call this%M%solve(z(1,j), v(1,j), n)
258
259 call ax%compute(w, z(1,j), coef, x%msh, x%Xh)
260 call gs_h%op(w, n, gs_op_add)
261 call bc_projector%apply(w, n)
262
263 ! Classical Gram-Schmidt orthogonalization: accumulate
264 ! <w, v_l>_mult for l=1..j in a thread-private array hl,
265 ! publish it once per thread into a column of hp, merge
266 ! across threads, then one MPI_Allreduce of length j.
267 !$omp parallel private(i, k, l, tid, acc, hl)
268 tid = 1
269 !$ tid = omp_get_thread_num() + 1
270 do l = 1, j
271 hl(l) = 0.0_xp
272 end do
273 !$omp do
274 do i = 0, n-1, neko_blk_size
275 if (i + neko_blk_size .le. n) then
276 do l = 1, j
277 acc = hl(l)
278 !$omp simd reduction(+:acc)
279 do k = 1, neko_blk_size
280 acc = acc + &
281 w(i+k) * v(i+k,l) * coef%mult(i+k,1,1,1)
282 end do
283 hl(l) = acc
284 end do
285 else
286 do l = 1, j
287 do k = 1, n - i
288 hl(l) = hl(l) + &
289 w(i+k) * v(i+k,l) * coef%mult(i+k,1,1,1)
290 end do
291 end do
292 end if
293 end do
294 !$omp end do nowait
295 do l = 1, j
296 hp(l,tid) = hl(l)
297 end do
298 !$omp end parallel
299
300 ! Cross-thread merge into hp(:, 1), then one Allreduce of j.
301 do k = 2, nthrds
302 do l = 1, j
303 hp(l,1) = hp(l,1) + hp(l,k)
304 end do
305 end do
306 call mpi_allreduce(mpi_in_place, hp(1,1), j, &
307 mpi_extra_precision, mpi_sum, neko_comm, ierr)
308
309 do l = 1, j
310 h(l,j) = hp(l,1)
311 end do
312
313 ! Projection w = w - sum h(l,j) v_l, with fused <w,w>_mult
314 ! reduction for the post-projection norm. alpha2 is computed
315 ! directly (not by Pythagoras) so accuracy is preserved when
316 ! the Krylov subspace is becoming invariant.
317 alpha2 = 0.0_xp
318 !$omp parallel do private(k, l, w_plus, tmp) reduction(+:alpha2)
319 do i = 0, n-1, neko_blk_size
320 if (i + neko_blk_size .le. n) then
321 !$omp simd
322 do k = 1, neko_blk_size
323 w_plus(k) = 0.0_xp
324 end do
325 do l = 1, j
326 !$omp simd
327 do k = 1, neko_blk_size
328 w_plus(k) = w_plus(k) - h(l,j) * v(i+k,l)
329 end do
330 end do
331 !$omp simd reduction(+:alpha2)
332 do k = 1, neko_blk_size
333 w(i+k) = w(i+k) + w_plus(k)
334 alpha2 = alpha2 + w(i+k)**2 * coef%mult(i+k,1,1,1)
335 end do
336 else
337 do k = 1, n - i
338 tmp = 0.0_xp
339 do l = 1, j
340 tmp = tmp - h(l,j) * v(i+k,l)
341 end do
342 w(i+k) = w(i+k) + tmp
343 alpha2 = alpha2 + w(i+k)**2 * coef%mult(i+k,1,1,1)
344 end do
345 end if
346 end do
347 !$omp end parallel do
348 call mpi_allreduce(mpi_in_place, alpha2, 1, &
349 mpi_extra_precision, mpi_sum, neko_comm, ierr)
350 alpha = sqrt(alpha2)
351 do i = 1, j-1
352 temp = h(i,j)
353 h(i,j) = c(i)*temp + s(i) * h(i+1,j)
354 h(i+1,j) = -s(i)*temp + c(i) * h(i+1,j)
355 end do
356
357 rnorm = 0.0_rp
358 if (abscmp(alpha, 0.0_xp)) then
359 conv = .true.
360 exit
361 end if
362
363 lr = sqrt(h(j,j) * h(j,j) + alpha2)
364 temp = 1.0_rp / lr
365 c(j) = h(j,j) * temp
366 s(j) = alpha * temp
367 h(j,j) = lr
368 gam(j+1) = -s(j) * gam(j)
369 gam(j) = c(j) * gam(j)
370 rnorm = abs(gam(j+1)) * norm_fac
371 call this%monitor_iter(iter, rnorm)
372 if (rnorm .lt. this%abs_tol) then
373 conv = .true.
374 exit
375 end if
376
377 if (iter + 1 .gt. max_iter) exit
378
379 if (j .lt. this%lgmres) then
380 temp = 1.0_rp / alpha
381 call cmult2(v(1,j+1), w, temp, n)
382 end if
383
384 end do
385
386 j = min(j, this%lgmres)
387 do k = j, 1, -1
388 temp = gam(k)
389 do i = j, k+1, -1
390 temp = temp - h(k,i) * c(i)
391 end do
392 c(k) = temp / h(k,k)
393 end do
394
395 !$omp parallel do private(k, l, x_plus, tmp)
396 do i = 0, n-1, neko_blk_size
397 if (i + neko_blk_size .le. n) then
398 !$omp simd
399 do k = 1, neko_blk_size
400 x_plus(k) = 0.0_xp
401 end do
402 do l = 1, j
403 !$omp simd
404 do k = 1, neko_blk_size
405 x_plus(k) = x_plus(k) + c(l) * z(i+k,l)
406 end do
407 end do
408 !$omp simd
409 do k = 1, neko_blk_size
410 x%x(i+k,1,1,1) = x%x(i+k,1,1,1) + x_plus(k)
411 end do
412 else
413 do k = 1, n - i
414 tmp = 0.0_xp
415 do l = 1, j
416 tmp = tmp + c(l) * z(i+k,l)
417 end do
418 x%x(i+k,1,1,1) = x%x(i+k,1,1,1) + tmp
419 end do
420 end if
421 end do
422 !$omp end parallel do
423 end do
424
425 end associate
426 nullify(this%w, this%r, this%z, this%v)
427 call neko_scratch_registry%relinquish(temp_indices)
428 call this%monitor_stop()
429 ksp_results%res_final = rnorm
430 ksp_results%iter = iter
431 ksp_results%converged = this%is_converged(iter, rnorm)
432
433 end function gmres_solve
434
436 function gmres_solve_coupled(this, Ax, x, y, z, fx, fy, fz, &
437 n, coef, bc_projector, gs_h, niter) result(ksp_results)
438 class(gmres_t), intent(inout) :: this
439 class(ax_t), intent(in) :: ax
440 type(field_t), intent(inout) :: x
441 type(field_t), intent(inout) :: y
442 type(field_t), intent(inout) :: z
443 integer, intent(in) :: n
444 real(kind=rp), dimension(n), intent(in) :: fx
445 real(kind=rp), dimension(n), intent(in) :: fy
446 real(kind=rp), dimension(n), intent(in) :: fz
447 type(coef_t), intent(inout) :: coef
448 class(vector_bc_projector_t), intent(inout) :: bc_projector
449 type(gs_t), intent(inout) :: gs_h
450 type(ksp_monitor_t), dimension(3) :: ksp_results
451 integer, optional, intent(in) :: niter
452 type(scalar_bc_projector_t), pointer :: bc_x, bc_y, bc_z
453
454 call vector_bc_projector_components(bc_projector, bc_x, bc_y, bc_z)
455 ksp_results(1) = this%solve(ax, x, fx, n, coef, bc_x, gs_h, niter)
456 ksp_results(2) = this%solve(ax, y, fy, n, coef, bc_y, gs_h, niter)
457 ksp_results(3) = this%solve(ax, z, fz, n, coef, bc_z, gs_h, niter)
458
459 end function gmres_solve_coupled
460
461end module gmres
__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
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 gmres_t.
Definition gmres.f90:34
type(ksp_monitor_t) function, dimension(3) gmres_solve_coupled(this, ax, x, y, z, fx, fy, fz, n, coef, bc_projector, gs_h, niter)
Solve three independent systems with the standard GMRES method.
Definition gmres.f90:438
subroutine gmres_free(this)
Free a standard GMRES solver.
Definition gmres.f90:143
type(ksp_monitor_t) function gmres_solve(this, ax, x, f, n, coef, bc_projector, gs_h, niter)
Solve a linear system with the standard GMRES method.
Definition gmres.f90:182
subroutine gmres_init(this, n, max_iter, m, rel_tol, abs_tol, monitor)
Initialise a standard GMRES solver.
Definition gmres.f90:96
Implements the base abstract type for Krylov solvers plus helper types.
Definition krylov.f90:34
Definition math.f90:60
subroutine, public cmult2(a, b, c, n)
Multiplication by constant c .
Definition math.f90:523
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
subroutine, public rzero(a, n)
Zero a real vector.
Definition math.f90:239
subroutine, public sub2(a, b, n)
Vector substraction .
Definition math.f90:987
Defines a matrix.
Definition matrix.f90:34
Build configurations.
integer, parameter neko_blk_size
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.
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:44
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:136
Gather-scatter kernel.
CPU implementation of the preconditioned standard GMRES method.
Definition gmres.f90:60
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:44
Projector for scalar boundary conditions.
Abstract type for resolving vector boundary conditions.