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 use utils, only : neko_error
53 use json_module, only : json_file
55 use logger, only : neko_log, log_size
56 implicit none
57 private
58
60 integer, public, parameter :: gmres_space_size = 30
61
67 type, public, extends(ksp_t) :: gmres_t
69 integer :: lgmres = gmres_space_size
71 real(kind=rp), pointer :: w(:) => null()
73 real(kind=rp), pointer :: r(:) => null()
75 type(matrix_t), pointer :: z => null()
77 type(matrix_t), pointer :: v => null()
79 real(kind=xp), allocatable :: h(:,:)
81 real(kind=xp), allocatable :: s(:)
83 real(kind=xp), allocatable :: gam(:)
85 real(kind=xp), allocatable :: c(:)
87 real(kind=xp), allocatable :: hp(:,:)
88 contains
90 procedure, pass(this) :: init => gmres_init
92 procedure, pass(this) :: init_from_json => gmres_init_from_json
94 procedure, pass(this) :: free => gmres_free
96 procedure, pass(this) :: solve => gmres_solve
98 procedure, pass(this) :: solve_coupled => gmres_solve_coupled
99 end type gmres_t
100
101contains
102
104 subroutine gmres_init(this, n, max_iter, M, rel_tol, abs_tol, monitor)
105 class(gmres_t), target, intent(inout) :: this
106 integer, intent(in) :: n
107 integer, intent(in) :: max_iter
108 class(pc_t), optional, intent(in), target :: M
109 real(kind=rp), optional, intent(in) :: rel_tol
110 real(kind=rp), optional, intent(in) :: abs_tol
111 logical, optional, intent(in) :: monitor
112 integer :: nthrds
113
114 call this%free()
115
116 if (present(m)) then
117 this%M => m
118 end if
119
120 allocate(this%c(this%lgmres))
121 allocate(this%s(this%lgmres))
122 allocate(this%gam(this%lgmres + 1))
123
124 allocate(this%h(this%lgmres, this%lgmres))
125
126 nthrds = 1
127 !$ nthrds = omp_get_max_threads()
128 allocate(this%hp(this%lgmres, nthrds))
129
130 if (present(rel_tol) .and. present(abs_tol) .and. present(monitor)) then
131 call this%ksp_init(max_iter, rel_tol, abs_tol, monitor = monitor)
132 else if (present(rel_tol) .and. present(abs_tol)) then
133 call this%ksp_init(max_iter, rel_tol, abs_tol)
134 else if (present(monitor) .and. present(abs_tol)) then
135 call this%ksp_init(max_iter, abs_tol = abs_tol, monitor = monitor)
136 else if (present(rel_tol) .and. present(monitor)) then
137 call this%ksp_init(max_iter, rel_tol, monitor = monitor)
138 else if (present(rel_tol)) then
139 call this%ksp_init(max_iter, rel_tol = rel_tol)
140 else if (present(abs_tol)) then
141 call this%ksp_init(max_iter, abs_tol = abs_tol)
142 else if (present(monitor)) then
143 call this%ksp_init(max_iter, monitor = monitor)
144 else
145 call this%ksp_init(max_iter)
146 end if
147
148 end subroutine gmres_init
149
153 subroutine gmres_init_from_json(this, n, json, M)
154 class(gmres_t), target, intent(inout) :: this
155 integer, intent(in) :: n
156 type(json_file), intent(inout) :: json
157 class(pc_t), optional, intent(in), target :: M
158 character(len=LOG_SIZE) :: log_buf
159
160 call json_get_or_lookup_or_default(json, 'space_size', &
161 this%lgmres, gmres_space_size)
162 if (this%lgmres .lt. 1) then
163 call neko_error('GMRES space_size must be at least 1')
164 end if
165 write(log_buf, '(A,I0)') 'GMRES space: ', this%lgmres
166 call neko_log%message(log_buf)
167
168 call this%ksp_init_from_json(n, json, m = m)
169
170 end subroutine gmres_init_from_json
171
173 subroutine gmres_free(this)
174 class(gmres_t), intent(inout) :: this
175
176 call this%ksp_free()
177
178 nullify(this%w)
179
180 if (allocated(this%c)) then
181 deallocate(this%c)
182 end if
183
184 nullify(this%r)
185 nullify(this%z)
186
187 if (allocated(this%h)) then
188 deallocate(this%h)
189 end if
190
191 nullify(this%v)
192
193 if (allocated(this%s)) then
194 deallocate(this%s)
195 end if
196
197
198 if (allocated(this%gam)) then
199 deallocate(this%gam)
200 end if
201
202 if (allocated(this%hp)) then
203 deallocate(this%hp)
204 end if
205
206 nullify(this%M)
207
208 end subroutine gmres_free
209
211 function gmres_solve(this, Ax, x, f, n, coef, bc_projector, gs_h, niter) &
212 result(ksp_results)
213 class(gmres_t), intent(inout) :: this
214 class(ax_t), intent(in) :: ax
215 type(field_t), intent(inout) :: x
216 integer, intent(in) :: n
217 real(kind=rp), dimension(n), intent(in) :: f
218 type(coef_t), intent(inout) :: coef
219 class(scalar_bc_projector_t), intent(inout) :: bc_projector
220 type(gs_t), intent(inout) :: gs_h
221 type(ksp_monitor_t) :: ksp_results
222 integer, optional, intent(in) :: niter
223 integer :: iter, max_iter
224 integer :: i, j, k, l, ierr, tid, nthrds
225 real(kind=xp) :: w_plus(neko_blk_size), x_plus(neko_blk_size)
226 real(kind=xp) :: hl(this%lgmres)
227 real(kind=xp) :: alpha, lr, alpha2, norm_fac, tmp, acc
228 real(kind=rp) :: temp, rnorm
229 logical :: conv
230 integer :: temp_indices(4)
231
232 conv = .false.
233 iter = 0
234 rnorm = 0.0_rp
235
236 if (present(niter)) then
237 max_iter = niter
238 else
239 max_iter = this%max_iter
240 end if
241
242 nthrds = 1
243 !$ nthrds = omp_get_max_threads()
244
245 call neko_scratch_registry%request(this%w, temp_indices(1), n, .false.)
246 call neko_scratch_registry%request(this%r, temp_indices(2), n, .false.)
247 call neko_scratch_registry%request(this%z, temp_indices(3), &
248 n, this%lgmres, .false.)
249 call neko_scratch_registry%request(this%v, temp_indices(4), &
250 n, this%lgmres, .false.)
251
252 associate(w => this%w, c => this%c, r => this%r, z => this%z%x, &
253 h => this%h, v => this%v%x, s => this%s, gam => this%gam, &
254 hp => this%hp)
255
256 norm_fac = 1.0_rp / sqrt(coef%volume)
257 call rzero(x%x, n)
258 gam = 0.0_xp
259 s = 1.0_xp
260 c = 1.0_xp
261 h = 0.0_xp
262 call this%monitor_start('GMRES')
263 do while (.not. conv .and. iter .lt. max_iter)
264
265 if (iter .eq. 0) then
266 call copy(r, f, n)
267 else
268 call copy(r, f, n)
269 call ax%compute(w, x%x, coef, x%msh, x%Xh)
270 call gs_h%op(w, n, gs_op_add)
271 call bc_projector%apply(w, n)
272 call sub2(r, w, n)
273 end if
274
275 gam(1) = sqrt(glsc3(r, r, coef%mult, n))
276 if (iter .eq. 0) then
277 ksp_results%res_start = gam(1) * norm_fac
278 end if
279
280 if (abscmp(gam(1), 0.0_xp)) exit
281
282 rnorm = 0.0_rp
283 temp = 1.0_rp / gam(1)
284 call cmult2(v(1,1), r, temp, n)
285 do j = 1, this%lgmres
286 iter = iter+1
287
288 call this%M%solve(z(1,j), v(1,j), n)
289
290 call ax%compute(w, z(1,j), coef, x%msh, x%Xh)
291 call gs_h%op(w, n, gs_op_add)
292 call bc_projector%apply(w, n)
293
294 ! Classical Gram-Schmidt orthogonalization: accumulate
295 ! <w, v_l>_mult for l=1..j in a thread-private array hl,
296 ! publish it once per thread into a column of hp, merge
297 ! across threads, then one MPI_Allreduce of length j.
298 !$omp parallel private(i, k, l, tid, acc, hl)
299 tid = 1
300 !$ tid = omp_get_thread_num() + 1
301 do l = 1, j
302 hl(l) = 0.0_xp
303 end do
304 !$omp do
305 do i = 0, n-1, neko_blk_size
306 if (i + neko_blk_size .le. n) then
307 do l = 1, j
308 acc = hl(l)
309 !$omp simd reduction(+:acc)
310 do k = 1, neko_blk_size
311 acc = acc + &
312 w(i+k) * v(i+k,l) * coef%mult(i+k,1,1,1)
313 end do
314 hl(l) = acc
315 end do
316 else
317 do l = 1, j
318 do k = 1, n - i
319 hl(l) = hl(l) + &
320 w(i+k) * v(i+k,l) * coef%mult(i+k,1,1,1)
321 end do
322 end do
323 end if
324 end do
325 !$omp end do nowait
326 do l = 1, j
327 hp(l,tid) = hl(l)
328 end do
329 !$omp end parallel
330
331 ! Cross-thread merge into hp(:, 1), then one Allreduce of j.
332 do k = 2, nthrds
333 do l = 1, j
334 hp(l,1) = hp(l,1) + hp(l,k)
335 end do
336 end do
337 call mpi_allreduce(mpi_in_place, hp(1,1), j, &
338 mpi_extra_precision, mpi_sum, neko_comm, ierr)
339
340 do l = 1, j
341 h(l,j) = hp(l,1)
342 end do
343
344 ! Projection w = w - sum h(l,j) v_l, with fused <w,w>_mult
345 ! reduction for the post-projection norm. alpha2 is computed
346 ! directly (not by Pythagoras) so accuracy is preserved when
347 ! the Krylov subspace is becoming invariant.
348 alpha2 = 0.0_xp
349 !$omp parallel do private(k, l, w_plus, tmp) reduction(+:alpha2)
350 do i = 0, n-1, neko_blk_size
351 if (i + neko_blk_size .le. n) then
352 !$omp simd
353 do k = 1, neko_blk_size
354 w_plus(k) = 0.0_xp
355 end do
356 do l = 1, j
357 !$omp simd
358 do k = 1, neko_blk_size
359 w_plus(k) = w_plus(k) - h(l,j) * v(i+k,l)
360 end do
361 end do
362 !$omp simd reduction(+:alpha2)
363 do k = 1, neko_blk_size
364 w(i+k) = w(i+k) + w_plus(k)
365 alpha2 = alpha2 + w(i+k)**2 * coef%mult(i+k,1,1,1)
366 end do
367 else
368 do k = 1, n - i
369 tmp = 0.0_xp
370 do l = 1, j
371 tmp = tmp - h(l,j) * v(i+k,l)
372 end do
373 w(i+k) = w(i+k) + tmp
374 alpha2 = alpha2 + w(i+k)**2 * coef%mult(i+k,1,1,1)
375 end do
376 end if
377 end do
378 !$omp end parallel do
379 call mpi_allreduce(mpi_in_place, alpha2, 1, &
380 mpi_extra_precision, mpi_sum, neko_comm, ierr)
381 alpha = sqrt(alpha2)
382 do i = 1, j-1
383 temp = h(i,j)
384 h(i,j) = c(i)*temp + s(i) * h(i+1,j)
385 h(i+1,j) = -s(i)*temp + c(i) * h(i+1,j)
386 end do
387
388 rnorm = 0.0_rp
389 if (abscmp(alpha, 0.0_xp)) then
390 conv = .true.
391 exit
392 end if
393
394 lr = sqrt(h(j,j) * h(j,j) + alpha2)
395 temp = 1.0_rp / lr
396 c(j) = h(j,j) * temp
397 s(j) = alpha * temp
398 h(j,j) = lr
399 gam(j+1) = -s(j) * gam(j)
400 gam(j) = c(j) * gam(j)
401 rnorm = abs(gam(j+1)) * norm_fac
402 call this%monitor_iter(iter, rnorm)
403 if (rnorm .lt. this%abs_tol) then
404 conv = .true.
405 exit
406 end if
407
408 if (iter + 1 .gt. max_iter) exit
409
410 if (j .lt. this%lgmres) then
411 temp = 1.0_rp / alpha
412 call cmult2(v(1,j+1), w, temp, n)
413 end if
414
415 end do
416
417 j = min(j, this%lgmres)
418 do k = j, 1, -1
419 temp = gam(k)
420 do i = j, k+1, -1
421 temp = temp - h(k,i) * c(i)
422 end do
423 c(k) = temp / h(k,k)
424 end do
425
426 !$omp parallel do private(k, l, x_plus, tmp)
427 do i = 0, n-1, neko_blk_size
428 if (i + neko_blk_size .le. n) then
429 !$omp simd
430 do k = 1, neko_blk_size
431 x_plus(k) = 0.0_xp
432 end do
433 do l = 1, j
434 !$omp simd
435 do k = 1, neko_blk_size
436 x_plus(k) = x_plus(k) + c(l) * z(i+k,l)
437 end do
438 end do
439 !$omp simd
440 do k = 1, neko_blk_size
441 x%x(i+k,1,1,1) = x%x(i+k,1,1,1) + x_plus(k)
442 end do
443 else
444 do k = 1, n - i
445 tmp = 0.0_xp
446 do l = 1, j
447 tmp = tmp + c(l) * z(i+k,l)
448 end do
449 x%x(i+k,1,1,1) = x%x(i+k,1,1,1) + tmp
450 end do
451 end if
452 end do
453 !$omp end parallel do
454 end do
455
456 end associate
457 nullify(this%w, this%r, this%z, this%v)
458 call neko_scratch_registry%relinquish(temp_indices)
459 call this%monitor_stop()
460 ksp_results%res_final = rnorm
461 ksp_results%iter = iter
462 ksp_results%converged = this%is_converged(iter, rnorm)
463
464 end function gmres_solve
465
467 function gmres_solve_coupled(this, Ax, x, y, z, fx, fy, fz, &
468 n, coef, bc_projector, gs_h, niter) result(ksp_results)
469 class(gmres_t), intent(inout) :: this
470 class(ax_t), intent(in) :: ax
471 type(field_t), intent(inout) :: x
472 type(field_t), intent(inout) :: y
473 type(field_t), intent(inout) :: z
474 integer, intent(in) :: n
475 real(kind=rp), dimension(n), intent(in) :: fx
476 real(kind=rp), dimension(n), intent(in) :: fy
477 real(kind=rp), dimension(n), intent(in) :: fz
478 type(coef_t), intent(inout) :: coef
479 class(vector_bc_projector_t), intent(inout) :: bc_projector
480 type(gs_t), intent(inout) :: gs_h
481 type(ksp_monitor_t), dimension(3) :: ksp_results
482 integer, optional, intent(in) :: niter
483 type(scalar_bc_projector_t), pointer :: bc_x, bc_y, bc_z
484
485 call vector_bc_projector_components(bc_projector, bc_x, bc_y, bc_z)
486 ksp_results(1) = this%solve(ax, x, fx, n, coef, bc_x, gs_h, niter)
487 ksp_results(2) = this%solve(ax, y, fy, n, coef, bc_y, gs_h, niter)
488 ksp_results(3) = this%solve(ax, z, fz, n, coef, bc_z, gs_h, niter)
489
490 end function gmres_solve_coupled
491
492end 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
subroutine gmres_init_from_json(this, n, json, m)
Initialise a standard GMRES solver from a JSON solver block. Reads space_size (default GMRES_SPACE_SI...
Definition gmres.f90:154
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:469
subroutine gmres_free(this)
Free a standard GMRES solver.
Definition gmres.f90:174
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:213
integer, parameter, public gmres_space_size
Default Krylov space size (restart length) of the GMRES solvers.
Definition gmres.f90:60
subroutine gmres_init(this, n, max_iter, m, rel_tol, abs_tol, monitor)
Initialise a standard GMRES solver.
Definition gmres.f90:105
Utilities for retrieving parameters from the case files.
Implements the base abstract type for Krylov solvers plus helper types.
Definition krylov.f90:34
Logging routines.
Definition log.f90:34
type(log_t), public neko_log
Global log stream.
Definition log.f90:92
integer, parameter, public log_size
Definition log.f90:46
Definition math.F90:60
subroutine, public cmult2(a, b, c, n)
Multiplication by constant c .
Definition math.F90:533
real(kind=rp) function, public glsc3(a, b, c, n)
Weighted inner product .
Definition math.F90:1336
subroutine, public copy(a, b, n)
Copy a vector .
Definition math.F90:305
subroutine, public rzero(a, n)
Zero a real vector.
Definition math.F90:249
subroutine, public sub2(a, b, n)
Vector substraction .
Definition math.F90:997
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.
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: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:67
Type for storing initial and final residuals in a Krylov solver.
Definition krylov.f90:60
Base abstract type for a canonical Krylov method, solving .
Definition krylov.f90:77
Defines a canonical Krylov preconditioner.
Definition precon.f90:44
Projector for scalar boundary conditions.
Abstract type for resolving vector boundary conditions.