Neko 1.99.2
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
tree_amg_multigrid.f90
Go to the documentation of this file.
1! Copyright (c) 2024, 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!
45 use num_types, only: rp
46 use utils, only : neko_error, neko_warning
47 use math, only : add2, rzero, glsc2, sub3, col2
50 use comm
51 use mpi_f08, only: mpi_allreduce, mpi_min, mpi_in_place, mpi_integer
52 use coefs, only : coef_t
53 use mesh, only : mesh_t
54 use space, only : space_t
55 use ax_product, only: ax_t
56 use bc_list, only : bc_list_t
57 use gather_scatter, only : gs_t, gs_op_add
62 use logger, only : neko_log, log_size
66 use, intrinsic :: iso_c_binding
67 implicit none
68 private
69
70 type :: tamg_wrk_t
71 real(kind=rp), allocatable :: r(:)
72 real(kind=rp), allocatable :: rc(:)
73 real(kind=rp), allocatable :: tmp(:)
74 type(c_ptr) :: r_d = c_null_ptr
75 type(c_ptr) :: rc_d = c_null_ptr
76 type(c_ptr) :: tmp_d = c_null_ptr
77 end type tamg_wrk_t
78
80 type, public :: tamg_solver_t
81 type(tamg_hierarchy_t), allocatable :: amg
82 type(amg_cheby_t), allocatable :: smoo(:)
83 type(tamg_wrk_t), allocatable :: wrk(:)
84 !type(amg_jacobi_t), allocatable :: jsmoo(:)
85 integer :: nlvls
86 integer :: max_iter
87 contains
88 procedure, pass(this) :: init => tamg_mg_init
89 procedure, pass(this) :: solve => tamg_mg_solve
90 procedure, pass(this) :: free =>tamg_mg_free
91 end type tamg_solver_t
92
93contains
94
104 subroutine tamg_mg_init(this, ax, Xh, coef, msh, gs_h, nlvls, blst, &
105 max_iter, cheby_degree)
106 class(tamg_solver_t), intent(inout), target :: this
107 class(ax_t), target, intent(in) :: ax
108 type(space_t), target, intent(in) :: Xh
109 type(coef_t), target, intent(in) :: coef
110 type(mesh_t), target, intent(in) :: msh
111 type(gs_t), target, intent(in) :: gs_h
112 type(bc_list_t), target, intent(in) :: blst
113 integer, intent(in) :: nlvls
114 integer, intent(in) :: max_iter
115 integer, intent(in) :: cheby_degree
116 integer :: lvl, n, mlvl, target_num_aggs
117 integer, allocatable :: agg_nhbr(:,:), nhbr_tmp(:,:)
118 character(len=LOG_SIZE) :: log_buf
119 integer :: glb_min_target_aggs
120 logical :: use_greedy_agg
121
122 call neko_log%section('AMG')
123
124 write(log_buf, '(A28,I2,A8)') 'Creating AMG hierarchy with', &
125 nlvls, 'levels.'
126 call neko_log%message(log_buf)
127
128 allocate( this%amg )
129 call this%amg%init(ax, xh, coef, msh, gs_h, nlvls, blst)
130
131 ! Aggregation
132 use_greedy_agg = .true.
133 ! Create level 1 (neko elements are level 0)
134 call aggregate_finest_level(this%amg, xh%lx, xh%ly, xh%lz, msh%nelv)
135
136 ! Create the remaining levels
137 allocate( agg_nhbr, source = msh%facet_neigh )
138 do mlvl = 2, nlvls-1
139 ! estimate number of aggregates
140 if (use_greedy_agg) then
141 target_num_aggs = this%amg%lvl(mlvl-1)%nnodes / 8
142 else
143 target_num_aggs = this%amg%lvl(mlvl-1)%nnodes / 2
144 end if
145
146 glb_min_target_aggs = target_num_aggs
147 call mpi_allreduce(mpi_in_place, glb_min_target_aggs, 1, &
148 mpi_integer, mpi_min, neko_comm)
149 if (glb_min_target_aggs .lt. 4 ) then
150 call neko_warning( &
151 "TAMG: Too many levels. Not enough DOFs for coarsest grid.")
152 this%amg%nlvls = mlvl
153 exit
154 end if
155
156 if (use_greedy_agg) then
157 call print_preagg_info( mlvl, glb_min_target_aggs, 1)
158 call aggregate_greedy( this%amg, mlvl, target_num_aggs, agg_nhbr, nhbr_tmp)
159 else
160 call print_preagg_info( mlvl, glb_min_target_aggs, 2)
161 call aggregate_pairs( this%amg, mlvl, target_num_aggs, agg_nhbr, nhbr_tmp)
162 end if
163
164 agg_nhbr = nhbr_tmp
165 deallocate( nhbr_tmp )
166 end do
167 deallocate( agg_nhbr )
168
169 ! Create the end point
170 call aggregate_end(this%amg, this%amg%nlvls)
171
172 this%max_iter = max_iter
173
174 this%nlvls = this%amg%nlvls
175 if (this%nlvls .gt. this%amg%nlvls) then
176 call neko_error( &
177 "Requested number multigrid levels &
178 & is greater than the initialized AMG levels")
179 end if
180
181 ! Initialize relaxation methods
182 allocate(this%smoo(0:(this%amg%nlvls)))
183 do lvl = 0, this%amg%nlvls-1
184 n = this%amg%lvl(lvl+1)%fine_lvl_dofs
185 call this%smoo(lvl)%init(n, lvl, cheby_degree)
186 end do
187
188 ! Allocate work space on each level
189 allocate(this%wrk(this%amg%nlvls))
190 do lvl = 1, this%amg%nlvls
191 n = this%amg%lvl(lvl)%fine_lvl_dofs
192 allocate( this%wrk(lvl)%r(n) )
193 allocate( this%wrk(lvl)%rc(n) )
194 allocate( this%wrk(lvl)%tmp(n) )
195 if (neko_bcknd_device .eq. 1) then
196 call device_map( this%wrk(lvl)%r, this%wrk(lvl)%r_d, n)
197 call device_map( this%wrk(lvl)%rc, this%wrk(lvl)%rc_d, n)
198 call device_map( this%wrk(lvl)%tmp, this%wrk(lvl)%tmp_d, n)
199 end if
200 end do
201
202 !allocate(this%jsmoo(0:(this%amg%nlvls)))
203 !do lvl = 0, this%amg%nlvls-1
204 ! n = this%amg%lvl(lvl+1)%fine_lvl_dofs
205 ! call this%jsmoo(lvl)%init(n ,lvl, cheby_degree)
206 !end do
207
208 ! Create index mapping between levels
209 call fill_lvl_map(this%amg)
210
211 call neko_log%end_section()
212
213 end subroutine tamg_mg_init
214
216 subroutine tamg_mg_free(this)
217 class(tamg_solver_t), intent(inout), target :: this
218 integer :: i
219 if (allocated(this%amg)) then
220 call this%amg%free()
221 deallocate(this%amg)
222 end if
223 if (allocated(this%smoo)) then
224 do i = 1, size(this%smoo)
225 call this%smoo(i)%free()
226 end do
227 deallocate(this%smoo)
228 end if
229 if (allocated(this%wrk)) then
230 do i = 1, size(this%wrk)
231 if (neko_bcknd_device .eq. 1) then
232 call device_free(this%wrk(i)%r_d)
233 call device_free(this%wrk(i)%rc_d)
234 call device_free(this%wrk(i)%tmp_d)
235 end if
236 if (allocated(this%wrk(i)%r)) deallocate(this%wrk(i)%r)
237 if (allocated(this%wrk(i)%rc)) deallocate(this%wrk(i)%rc)
238 if (allocated(this%wrk(i)%tmp)) deallocate(this%wrk(i)%tmp)
239 end do
240 end if
241 end subroutine tamg_mg_free
242
243
248 subroutine tamg_mg_solve(this, z, r, n)
249 integer, intent(in) :: n
250 class(tamg_solver_t), intent(inout) :: this
251 real(kind=rp), dimension(n), intent(inout) :: z
252 real(kind=rp), dimension(n), intent(inout) :: r
253 type(c_ptr) :: z_d
254 type(c_ptr) :: r_d
255 integer :: iter, max_iter
256 logical :: zero_initial_guess
257
258 max_iter = this%max_iter
259
260 if (neko_bcknd_device .eq. 1) then
261 z_d = device_get_ptr(z)
262 r_d = device_get_ptr(r)
263 ! Zero out the initial guess becuase we do not handle null spaces very well...
264 call device_rzero(z_d, n)
265 zero_initial_guess = .true.
266 ! Call the amg cycle
267 do iter = 1, max_iter
268 call tamg_mg_cycle_d(z, r, z_d, r_d, n, 0, this%amg, this, &
269 zero_initial_guess)
270 zero_initial_guess = .false.
271 end do
272 else
273 ! Zero out the initial guess becuase we do not handle null spaces very well...
274 call rzero(z, n)
275 zero_initial_guess = .true.
276 ! Call the amg cycle
277 do iter = 1, max_iter
278 call tamg_mg_cycle(z, r, n, 0, this%amg, this, &
279 zero_initial_guess)
280 zero_initial_guess = .false.
281 end do
282 end if
283 end subroutine tamg_mg_solve
284
285
293 recursive subroutine tamg_mg_cycle(x, b, n, lvl, amg, mgstuff, &
294 zero_initial_guess)
295 integer, intent(in) :: n
296 real(kind=rp), intent(inout) :: x(n)
297 real(kind=rp), intent(inout) :: b(n)
298 type(tamg_hierarchy_t), intent(inout) :: amg
299 type(tamg_solver_t), intent(inout) :: mgstuff
300 logical, intent(in) :: zero_initial_guess
301 integer, intent(in) :: lvl
302 real(kind=rp) :: r(n)
303 real(kind=rp) :: rc(n)
304 real(kind=rp) :: tmp(n)
305 integer :: iter, num_iter
306 integer :: max_lvl
307 integer :: i, cyt
308 max_lvl = mgstuff%nlvls-1
309 !!----------!!
310 !! SMOOTH !!
311 !!----------!!
312 call mgstuff%smoo(lvl)%solve(x, b, n, amg, &
313 zero_initial_guess)
314 if (lvl .eq. max_lvl) then
315 return
316 end if
317 !!----------!!
318 !! Residual !!
319 !!----------!!
320 call calc_resid(r, x, b, amg, lvl, n)
321 !!----------!!
322 !! Restrict !!
323 !!----------!!
324 call amg%interp_f2c(rc, r, lvl+1)
325 !!-------------------!!
326 !! Call Coarse solve !!
327 !!-------------------!!
328 call rzero(tmp, n)
329 call tamg_mg_cycle(tmp, rc, amg%lvl(lvl+1)%nnodes, lvl+1, amg, mgstuff, &
330 .true.)
331 !!----------!!
332 !! Project !!
333 !!----------!!
334 call amg%interp_c2f(r, tmp, lvl+1)
335 !!----------!!
336 !! Correct !!
337 !!----------!!
338 call add2(x, r, n)
339 !!----------!!
340 !! SMOOTH !!
341 !!----------!!
342 call mgstuff%smoo(lvl)%solve(x,b, n, amg)
343 end subroutine tamg_mg_cycle
344
352 recursive subroutine tamg_mg_cycle_d(x, b, x_d, b_d, n, lvl, amg, mgstuff, &
353 zero_initial_guess)
354 integer, intent(in) :: n
355 real(kind=rp), intent(inout) :: x(n)
356 real(kind=rp), intent(inout) :: b(n)
357 type(c_ptr) :: x_d
358 type(c_ptr) :: b_d
359 type(tamg_hierarchy_t), intent(inout) :: amg
360 type(tamg_solver_t), intent(inout) :: mgstuff
361 logical, intent(in) :: zero_initial_guess
362 integer, intent(in) :: lvl
363 integer :: iter, num_iter
364 integer :: max_lvl
365 integer :: i, cyt
366 max_lvl = mgstuff%nlvls-1
367 !!----------!!
368 !! SMOOTH !!
369 !!----------!!
370 call mgstuff%smoo(lvl)%device_solve(x, b, x_d, b_d, n, amg, &
371 zero_initial_guess)
372 if (lvl .eq. max_lvl) then
373 return
374 end if
375 associate( r => mgstuff%wrk(lvl+1)%r, r_d => mgstuff%wrk(lvl+1)%r_d, &
376 rc => mgstuff%wrk(lvl+1)%rc, rc_d => mgstuff%wrk(lvl+1)%rc_d, &
377 tmp => mgstuff%wrk(lvl+1)%tmp, tmp_d => mgstuff%wrk(lvl+1)%tmp_d )
378 !!----------!!
379 !! Residual !!
380 !!----------!!
381 call amg%device_matvec(r, x, r_d, x_d, lvl)
382 call device_sub3(r_d, b_d, r_d, n)
383 !!----------!!
384 !! Restrict !!
385 !!----------!!
386 call amg%interp_f2c_d(rc_d, r_d, lvl+1)
387 !!-------------------!!
388 !! Call Coarse solve !!
389 !!-------------------!!
390 call device_rzero(tmp_d, n)
391 call tamg_mg_cycle_d(tmp, rc, tmp_d, rc_d, &
392 amg%lvl(lvl+1)%nnodes, lvl+1, amg, mgstuff, .true.)
393 !!----------!!
394 !! Project !!
395 !!----------!!
396 call amg%interp_c2f_d(r_d, tmp_d, lvl+1, r)
397 !!----------!!
398 !! Correct !!
399 !!----------!!
400 call device_add2(x_d, r_d, n)
401 !!----------!!
402 !! SMOOTH !!
403 !!----------!!
404 call mgstuff%smoo(lvl)%device_solve(x, b, x_d, b_d, n, amg)
405 end associate
406 end subroutine tamg_mg_cycle_d
407
408
416 subroutine calc_resid(r, x, b, amg, lvl, n)
417 integer, intent(in) :: n
418 real(kind=rp), intent(inout) :: r(n)
419 real(kind=rp), intent(inout) :: x(n)
420 real(kind=rp), intent(inout) :: b(n)
421 type(tamg_hierarchy_t), intent(inout) :: amg
422 integer, intent(in) :: lvl
423 integer :: i
424 call amg%matvec(r, x, lvl)
425 call sub3(r, b, r, n)
426 end subroutine calc_resid
427
428
429 subroutine print_preagg_info(lvl, nagg, agg_type)
430 integer, intent(in) :: lvl, nagg, agg_type
431 character(len=LOG_SIZE) :: log_buf
432 !TODO: calculate min and max agg size
433 if (agg_type .eq. 1) then
434 write(log_buf, '(A8,I2,A31)') '-- level', lvl, &
435 '-- Calling Greedy Aggregation'
436 else if (agg_type .eq. 2) then
437 write(log_buf, '(A8,I2,A33)') '-- level', lvl, &
438 '-- Calling Pairwise Aggregation'
439 else
440 write(log_buf, '(A8,I2,A31)') '-- level', lvl, &
441 '-- UNKNOWN Aggregation'
442 end if
443 call neko_log%message(log_buf)
444 write(log_buf, '(A33,I6)') 'Target Aggregates:', nagg
445 call neko_log%message(log_buf)
446 end subroutine print_preagg_info
447
448 subroutine print_resid_info(r, x, b, r_d, x_d, b_d, amg, lvl, n)
449 integer, intent(in) :: lvl, n
450 real(kind=rp), intent(inout) :: r(n)
451 real(kind=rp), intent(inout) :: x(n)
452 real(kind=rp), intent(inout) :: b(n)
453 type(c_ptr) :: r_d
454 type(c_ptr) :: x_d
455 type(c_ptr) :: b_d
456 type(tamg_hierarchy_t), intent(inout) :: amg
457 real(kind=rp) :: val
458 character(len=LOG_SIZE) :: log_buf
459
460 call amg%device_matvec(r, x, r_d, x_d, lvl)
461 call device_sub3(r_d, b_d, r_d, n)
462 val = device_glsc2(r_d, r_d, n)
463
464 write(log_buf, '(A33,I6,F12.6)') 'tAMG resid:', lvl, val
465 call neko_log%message(log_buf)
466 end subroutine print_resid_info
467
470 subroutine fill_lvl_map(amg)
471 type(tamg_hierarchy_t), intent(inout) :: amg
472 integer :: i, j, k, l, nid, n
473 do j = 1, amg%lvl(1)%nnodes
474 do k = 1, amg%lvl(1)%nodes(j)%ndofs
475 nid = amg%lvl(1)%nodes(j)%dofs(k)
476 amg%lvl(1)%map_finest2lvl(nid) = amg%lvl(1)%nodes(j)%gid
477 end do
478 end do
479 n = size(amg%lvl(1)%map_finest2lvl)
480 do l = 2, amg%nlvls
481 do i = 1, n
482 nid = amg%lvl(l-1)%map_finest2lvl(i)
483 do j = 1, amg%lvl(l)%nnodes
484 do k = 1, amg%lvl(l)%nodes(j)%ndofs
485 if (nid .eq. amg%lvl(l)%nodes(j)%dofs(k)) then
486 amg%lvl(l)%map_finest2lvl(i) = amg%lvl(l)%nodes(j)%gid
487 end if
488 end do
489 end do
490 end do
491 end do
492 if (neko_bcknd_device .eq. 1) then
493 do l = 1, amg%nlvls
494 amg%lvl(l)%map_finest2lvl(0) = n
495 call device_memcpy( amg%lvl(l)%map_finest2lvl, &
496 amg%lvl(l)%map_finest2lvl_d, n, &
497 host_to_device, .true.)
498 call device_memcpy( amg%lvl(l)%map_f2c, &
499 amg%lvl(l)%map_f2c_d, amg%lvl(l)%fine_lvl_dofs+1, &
500 host_to_device, .true.)
501 end do
502 end if
503 end subroutine fill_lvl_map
504end module tree_amg_multigrid
__device__ T solve(const T u, const T y, const T guess, const T nu, const T kappa, const T B)
Return the device pointer for an associated Fortran array.
Definition device.F90:101
Map a Fortran array to a device (allocate and associate)
Definition device.F90:77
Copy data between host and device (or device and device)
Definition device.F90:71
Defines a Matrix-vector product.
Definition ax.f90:34
Defines a list of bc_t.
Definition bc_list.f90:34
Coefficients.
Definition coef.f90:34
Definition comm.F90:1
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:43
subroutine, public device_sub3(a_d, b_d, c_d, n, strm)
Vector subtraction .
subroutine, public device_add2(a_d, b_d, n, strm)
Vector addition .
subroutine, public device_rzero(a_d, n, strm)
Zero a real vector.
subroutine, public device_col2(a_d, b_d, n, strm)
Vector multiplication .
real(kind=rp) function, public device_glsc2(a_d, b_d, n, strm)
Weighted inner product .
Device abstraction, common interface for various accelerators.
Definition device.F90:34
integer, parameter, public host_to_device
Definition device.F90:47
subroutine, public device_free(x_d)
Deallocate memory on the device.
Definition device.F90:219
Gather-scatter.
Logging routines.
Definition log.f90:34
type(log_t), public neko_log
Global log stream.
Definition log.f90:76
integer, parameter, public log_size
Definition log.f90:46
Definition math.f90:60
real(kind=rp) function, public glsc2(a, b, n)
Weighted inner product .
Definition math.f90:1048
subroutine, public sub3(a, b, c, n)
Vector subtraction .
Definition math.f90:781
subroutine, public add2(a, b, n)
Vector addition .
Definition math.f90:726
subroutine, public col2(a, b, n)
Vector multiplication .
Definition math.f90:854
subroutine, public rzero(a, n)
Zero a real vector.
Definition math.f90:205
Defines a mesh.
Definition mesh.f90:34
Build configurations.
integer, parameter neko_bcknd_device
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Defines a function space.
Definition space.f90:34
Implements an aggregation for TreeAMG hierarchy structure.
subroutine aggregate_end(tamg, lvl_id)
Aggregate all dofs to a single point to form a tree-like structure.
subroutine aggregate_pairs(tamg, lvl_id, max_aggs, facet_neigh, agg_nhbr)
Aggregates pairs of dofs based on adjacent dofs.
subroutine aggregate_greedy(tamg, lvl_id, max_aggs, facet_neigh, agg_nhbr)
Aggregates dofs based on adjacent dofs.
subroutine aggregate_finest_level(tamg, lx, ly, lz, ne)
Aggregaiton on finest level Aggregates all dofs in an element into a single aggregate.
Implements multigrid using the TreeAMG hierarchy structure. USE:
recursive subroutine tamg_mg_cycle(x, b, n, lvl, amg, mgstuff, zero_initial_guess)
Recrsive multigrid cycle for the TreeAMG solver object.
subroutine calc_resid(r, x, b, amg, lvl, n)
Wrapper function to calculate residyal.
subroutine tamg_mg_free(this)
free tree amg solver object
recursive subroutine tamg_mg_cycle_d(x, b, x_d, b_d, n, lvl, amg, mgstuff, zero_initial_guess)
Recrsive multigrid cycle for the TreeAMG solver object on device.
subroutine fill_lvl_map(amg)
Create index mapping between levels and directly to finest level.
subroutine print_resid_info(r, x, b, r_d, x_d, b_d, amg, lvl, n)
subroutine tamg_mg_solve(this, z, r, n)
Solver function for the TreeAMG solver object.
subroutine print_preagg_info(lvl, nagg, agg_type)
subroutine tamg_mg_init(this, ax, xh, coef, msh, gs_h, nlvls, blst, max_iter, cheby_degree)
Initialization of the TreeAMG multigrid solver.
Implements smoothers for use with TreeAMG matrix vector product.
Implements the base type for TreeAMG hierarchy structure.
Definition tree_amg.f90:34
subroutine, public tamg_node_init(node, gid, ndofs)
Initialization of a TreeAMG tree node.
Definition tree_amg.f90:255
subroutine, public tamg_lvl_init(tamg_lvl, lvl, nnodes, ndofs)
Initialization of a TreeAMG level.
Definition tree_amg.f90:186
Utilities.
Definition utils.f90:35
subroutine, public neko_warning(warning_msg)
Reports a warning to standard output.
Definition utils.f90:346
Base type for a matrix-vector product providing .
Definition ax.f90:43
A list of allocatable `bc_t`. Follows the standard interface of lists.
Definition bc_list.f90:48
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:56
The function space for the SEM solution fields.
Definition space.f90:63
Type for a TreeAMG hierarchy.
Definition tree_amg.f90:87
Type for the TreeAMG solver.
Type for Chebyshev iteration using TreeAMG matvec.