Neko 1.99.6
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gather_scatter.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!
38 use gs_device, only : gs_device_t
39 use gs_sx, only : gs_sx_t
40 use gs_cpu, only : gs_cpu_t
45 use gs_mpi, only : gs_mpi_t
47 use gs_shmem, only : gs_shmem_t
48 use gs_caf, only : gs_caf_t
49 use gs_utofu, only : gs_utofu_t
53 use mesh, only : mesh_t
54 use comm, only : pe_rank, pe_size, neko_comm
55 use mpi_f08, only : mpi_reduce, mpi_allreduce, mpi_barrier, mpi_in_place, &
56 mpi_wtime, mpi_sum, mpi_integer, mpi_integer8
57 use dofmap, only : dofmap_t
58 use field, only : field_t
59 use num_types, only : rp, dp, i2, i8, c_rp
61 use stack, only : stack_i4_t, stack_i8_t
63 use math, only : sort
64 use utils, only : neko_error, linear_index
65 use logger, only : neko_log, log_size
70 use, intrinsic :: iso_c_binding, only : c_ptr, c_null_ptr, c_intptr_t, &
71 c_sizeof, c_associated, c_size_t
72 !$ use omp_lib, only : omp_get_thread_num
73 implicit none
74 private
75
82 type, public :: gs_t
83 real(kind=rp), allocatable :: local_gs(:)
84 integer, allocatable :: local_dof_gs(:)
85 integer, allocatable :: local_gs_dof(:)
86 integer, allocatable :: local_blk_len(:)
87 integer, allocatable :: local_blk_off(:)
88 real(kind=rp), allocatable :: shared_gs(:)
92 real(kind=rp), allocatable :: shared_gs_v(:)
93 type(c_ptr) :: shared_gs_v_d = c_null_ptr
94 integer, allocatable :: shared_dof_gs(:)
95 integer, allocatable :: shared_gs_dof(:)
96 integer, allocatable :: shared_blk_len(:)
97 integer, allocatable :: shared_blk_off(:)
98 type(dofmap_t), pointer ::dofmap
99 type(htable_i8_t) :: shared_dofs
100 integer :: nlocal
101 integer :: nshared
102 integer :: nlocal_blks
103 integer :: nshared_blks
104 integer :: local_facet_offset
105 integer :: shared_facet_offset
106 class(gs_bcknd_t), allocatable :: bcknd
107 class(gs_comm_t), allocatable :: comm
108 contains
109 procedure, private, pass(gs) :: gs_op_fld
110 procedure, private, pass(gs) :: gs_op_r4
111 procedure, pass(gs) :: gs_op_vector
112 procedure, pass(gs) :: gs_op_r3
113 procedure, pass(gs) :: gs_op_vector3
114 procedure, pass(gs) :: init => gs_init
115 procedure, pass(gs) :: free => gs_free
118 end type gs_t
119
120 ! Expose available gather-scatter operation
122
123 ! Expose available gather-scatter backends
125
126 ! Expose available gather-scatter comm. backends
129
130contains
131
136 subroutine gs_init(gs, dofmap, bcknd, comm_bcknd)
137 class(gs_t), intent(inout) :: gs
138 type(dofmap_t), target, intent(inout) :: dofmap
139 character(len=LOG_SIZE) :: log_buf
140 character(len=20) :: bcknd_str
141 integer, optional :: bcknd, comm_bcknd
142 integer :: i, j, ierr, bcknd_, comm_bcknd_
143 integer(i8) :: glb_nshared, glb_nlocal
144 logical :: use_device_mpi, use_device_nccl, use_device_shmem, use_host_mpi
145 logical :: use_host_shmem
146 logical :: use_caf
147 logical :: use_neighbour
148 logical :: use_utofu
149 real(kind=rp), allocatable :: tmp(:)
150 type(c_ptr) :: tmp_d = c_null_ptr
151 integer :: strtgy(4) = [int(b'00'), int(b'01'), int(b'10'), int(b'11')]
152 integer :: avg_strtgy, env_len
153 character(len=255) :: env_strtgy, env_gscomm
154 real(kind=dp) :: strtgy_time(4)
155
156 call gs%free()
157
158 call neko_log%section('Gather-Scatter')
159 ! Currently this uses the dofmap which also contains geometric information
160 ! Only connectivity/numbering of points is technically necessary for gs
161 gs%dofmap => dofmap
162
163 use_device_mpi = .false.
164 use_device_nccl = .false.
165 use_device_shmem = .false.
166 use_host_mpi = .false.
167 use_host_shmem = .false.
168 use_caf = .false.
169 use_neighbour = .false.
170 use_utofu = .false.
171
172 ! Check if a comm-backend is requested via env. variables
173 call get_environment_variable("NEKO_GS_COMM", env_gscomm, env_len)
174 if (env_len .gt. 0) then
175 if (env_gscomm(1:env_len) .eq. "MPI") then
176 use_host_mpi = .true.
177 else if (env_gscomm(1:env_len) .eq. "MPIGPU") then
178 use_device_mpi = .true.
179 else if (env_gscomm(1:env_len) .eq. "NCCL") then
180 use_device_nccl = .true.
181 else if (env_gscomm(1:env_len) .eq. "SHMEM") then
182 if (neko_bcknd_device .eq. 1) then
183 use_device_shmem = .true.
184 else
185 use_host_shmem = .true.
186 end if
187 else if (env_gscomm(1:env_len) .eq. "CAF") then
188 use_caf = .true.
189 else if (env_gscomm(1:env_len) .eq. "NEIGHBOUR" .or. &
190 env_gscomm(1:env_len) .eq. "NEIGHBOR") then
191 use_neighbour = .true.
192 else if (env_gscomm(1:env_len) .eq. "UTOFU") then
193 use_utofu = .true.
194 else
195 call neko_error('Unknown Gather-scatter comm. backend')
196 end if
197 end if
198
199
200 if (present(comm_bcknd)) then
201 comm_bcknd_ = comm_bcknd
202 else if (use_host_mpi) then
203 comm_bcknd_ = gs_comm_mpi
204 else if (use_device_mpi) then
205 comm_bcknd_ = gs_comm_mpigpu
206 else if (use_device_nccl) then
207 comm_bcknd_ = gs_comm_nccl
208 else if (use_device_shmem) then
209 comm_bcknd_ = gs_comm_nvshmem
210 else if (use_host_shmem) then
211 comm_bcknd_ = gs_comm_openshmem
212 else if (use_caf) then
213 comm_bcknd_ = gs_comm_caf
214 else if (use_neighbour) then
215 comm_bcknd_ = gs_comm_neighbour
216 else if (use_utofu) then
217 comm_bcknd_ = gs_comm_utofu
218 else
219 if (neko_device_mpi) then
220 comm_bcknd_ = gs_comm_mpigpu
221 use_device_mpi = .true.
222 else
223 comm_bcknd_ = gs_comm_mpi
224 end if
225 end if
226
227 select case (comm_bcknd_)
228 case (gs_comm_mpi)
229 call neko_log%message('Comm : MPI')
230 allocate(gs_mpi_t::gs%comm)
231 case (gs_comm_mpigpu)
232 call neko_log%message('Comm : Device MPI')
233 allocate(gs_device_mpi_t::gs%comm)
234 case (gs_comm_nccl)
235 call neko_log%message('Comm : NCCL')
236 allocate(gs_device_nccl_t::gs%comm)
237 case (gs_comm_nvshmem)
238 call neko_log%message('Comm : NVSHMEM')
239 allocate(gs_device_shmem_t::gs%comm)
240 case (gs_comm_openshmem)
241 call neko_log%message('Comm : OpenSHMEM')
242 allocate(gs_shmem_t::gs%comm)
243 case (gs_comm_caf)
244 call neko_log%message('Comm : CAF')
245 allocate(gs_caf_t::gs%comm)
246 case (gs_comm_neighbour)
247 call neko_log%message('Comm : MPI neigh.')
248 allocate(gs_neighbour_t::gs%comm)
249 case (gs_comm_utofu)
250 call neko_log%message('Comm : uTofu')
251 allocate(gs_utofu_t::gs%comm)
252 case default
253 call neko_error('Unknown Gather-scatter comm. backend')
254 end select
255 ! Initialize a stack for each rank containing which dofs to send/recv at
256 ! that rank
257 call gs%comm%init_dofs()
258 ! Initialize mapping between local ids and gather-scatter ids
259 ! based on the global numbering in dofmap
260 call gs_init_mapping(gs)
261 ! Setup buffers and which ranks to send/recv data from based on mapping
262 ! and initializes gs%comm (sets up gs%comm%send_dof and gs%comm%recv_dof and
263 ! recv_pe/send_pe)
264 call gs_schedule(gs)
265 ! Global number of points not needing to be sent over mpi for gs operations
266 ! "Internal points"
267 glb_nlocal = int(gs%nlocal, i8)
268 ! Global number of points needing to be communicated with other pes/ranks
269 ! "external points"
270 glb_nshared = int(gs%nshared, i8)
271 ! Can be thought of a measure of the volume of this rank (glb_nlocal) and
272 ! the surface area (glb_nshared) that is shared with other ranks
273 ! Lots of internal volume compared to surface that needs communication is
274 ! good
275
276 if (pe_rank .eq. 0) then
277 call mpi_reduce(mpi_in_place, glb_nlocal, 1, &
278 mpi_integer8, mpi_sum, 0, neko_comm, ierr)
279
280 call mpi_reduce(mpi_in_place, glb_nshared, 1, &
281 mpi_integer8, mpi_sum, 0, neko_comm, ierr)
282 else
283 call mpi_reduce(glb_nlocal, glb_nlocal, 1, &
284 mpi_integer8, mpi_sum, 0, neko_comm, ierr)
285
286 call mpi_reduce(glb_nshared, glb_nshared, 1, &
287 mpi_integer8, mpi_sum, 0, neko_comm, ierr)
288 end if
289
290 write(log_buf, '(A,I12)') 'Avg. internal: ', glb_nlocal/pe_size
291 call neko_log%message(log_buf)
292 write(log_buf, '(A,I12)') 'Avg. external: ', glb_nshared/pe_size
293 call neko_log%message(log_buf)
294
295 if (present(bcknd)) then
296 bcknd_ = bcknd
297 else
298 if (neko_bcknd_sx .eq. 1) then
299 bcknd_ = gs_bcknd_sx
300 else if (neko_bcknd_device .eq. 1) then
301 bcknd_ = gs_bcknd_dev
302 else
303 bcknd_ = gs_bcknd_cpu
304 end if
305 end if
306
307 ! Setup Gather-scatter backend
308 select case (bcknd_)
309 case (gs_bcknd_cpu)
310 allocate(gs_cpu_t::gs%bcknd)
311 bcknd_str = ' std'
312 case (gs_bcknd_dev)
313 allocate(gs_device_t::gs%bcknd)
314 if (neko_bcknd_hip .eq. 1) then
315 bcknd_str = ' hip'
316 else if (neko_bcknd_cuda .eq. 1) then
317 bcknd_str = ' cuda'
318 else if (neko_bcknd_opencl .eq. 1) then
319 bcknd_str = ' opencl'
320 else if (neko_bcknd_metal .eq. 1) then
321 bcknd_str = ' metal'
322 end if
323 case (gs_bcknd_sx)
324 allocate(gs_sx_t::gs%bcknd)
325 bcknd_str = ' sx'
326 case default
327 call neko_error('Unknown Gather-scatter backend')
328 end select
329
330 write(log_buf, '(A)') 'Backend : ' // trim(bcknd_str)
331 call neko_log%message(log_buf)
332
333
334 call gs%bcknd%init(gs%nlocal, gs%nshared, gs%nlocal_blks, gs%nshared_blks)
335
336 ! Plain base-type assignment; setting this through a
337 ! select type (gs_device_t) miscompiles with CCE 21 at -O2/-O3,
338 ! silently leaving shared points on the host so that the scatter
339 ! overwrites the unpacked halo data with the stale host buffer
340 if (use_device_mpi .or. use_device_nccl .or. use_device_shmem) then
341 gs%bcknd%shared_on_host = .false.
342 end if
343
344 if (use_device_mpi) then
345 if (pe_size .gt. 1) then
346 ! Select fastest device MPI strategy at runtime
347 select type (c => gs%comm)
348 type is (gs_device_mpi_t)
349 call get_environment_variable("NEKO_GS_STRTGY", env_strtgy, &
350 env_len)
351 if (env_len .eq. 0) then
352 allocate(tmp(dofmap%size()))
353 call device_map(tmp, tmp_d, dofmap%size())
354 tmp = 1.0_rp
355 call device_memcpy(tmp, tmp_d, dofmap%size(), &
356 host_to_device, sync = .false.)
357 call gs_op_vector(gs, tmp, dofmap%size(), gs_op_add)
358
359 do i = 1, size(strtgy)
360 c%nb_strtgy = strtgy(i)
361 call device_sync
362 call mpi_barrier(neko_comm)
363 strtgy_time(i) = mpi_wtime()
364 do j = 1, 100
365 call gs_op_vector(gs, tmp, dofmap%size(), gs_op_add)
366 end do
367 strtgy_time(i) = (mpi_wtime() - strtgy_time(i)) / 100d0
368 end do
369
370 call device_unmap(tmp, tmp_d)
371 deallocate(tmp)
372
373 c%nb_strtgy = strtgy(minloc(strtgy_time, 1))
374
375 avg_strtgy = minloc(strtgy_time, 1)
376 call mpi_allreduce(mpi_in_place, avg_strtgy, 1, &
377 mpi_integer, mpi_sum, neko_comm)
378 avg_strtgy = avg_strtgy / pe_size
379
380 write(log_buf, '(A,B0.2,A)') 'Avg. strtgy : [', &
381 strtgy(avg_strtgy), ']'
382
383 else
384 read(env_strtgy(1:env_len), *) i
385
386 if (i .lt. 1 .or. i .gt. 4) then
387 call neko_error('Invalid gs sync strtgy')
388 end if
389
390 c%nb_strtgy = strtgy(i)
391 avg_strtgy = i
392
393 write(log_buf, '(A,B0.2,A)') 'Env. strtgy : [', &
394 strtgy(avg_strtgy), ']'
395 end if
396
397 call neko_log%message(log_buf)
398
399 end select
400 end if
401 end if
402
403 call neko_log%end_section()
404
405 end subroutine gs_init
406
408 subroutine gs_free(gs)
409 class(gs_t), intent(inout) :: gs
410
411 nullify(gs%dofmap)
412
413 ! The device backend lazily maps these arrays in gather/scatter,
414 ! and its free only releases the device side; remove any stale
415 ! address table entries before deallocating the host side
416 if (allocated(gs%local_gs)) then
417 if (neko_bcknd_device .eq. 1) then
418 call device_deassociate(gs%local_gs)
419 end if
420 deallocate(gs%local_gs)
421 end if
422
423 if (allocated(gs%local_dof_gs)) then
424 if (neko_bcknd_device .eq. 1) then
425 call device_deassociate(gs%local_dof_gs)
426 end if
427 deallocate(gs%local_dof_gs)
428 end if
429
430 if (allocated(gs%local_gs_dof)) then
431 if (neko_bcknd_device .eq. 1) then
432 call device_deassociate(gs%local_gs_dof)
433 end if
434 deallocate(gs%local_gs_dof)
435 end if
436
437 if (allocated(gs%local_blk_len)) then
438 if (neko_bcknd_device .eq. 1) then
439 call device_deassociate(gs%local_blk_len)
440 end if
441 deallocate(gs%local_blk_len)
442 end if
443
444 if (allocated(gs%local_blk_off)) then
445 if (neko_bcknd_device .eq. 1) then
446 call device_deassociate(gs%local_blk_off)
447 end if
448 deallocate(gs%local_blk_off)
449 end if
450
451 if (allocated(gs%shared_gs)) then
452 if (neko_bcknd_device .eq. 1) then
453 call device_deassociate(gs%shared_gs)
454 end if
455 deallocate(gs%shared_gs)
456 end if
457
458 if (allocated(gs%shared_gs_v)) then
459 if (neko_bcknd_device .eq. 1 .and. c_associated(gs%shared_gs_v_d)) then
460 call device_unmap(gs%shared_gs_v, gs%shared_gs_v_d)
461 end if
462 deallocate(gs%shared_gs_v)
463 end if
464
465 if (allocated(gs%shared_dof_gs)) then
466 if (neko_bcknd_device .eq. 1) then
467 call device_deassociate(gs%shared_dof_gs)
468 end if
469 deallocate(gs%shared_dof_gs)
470 end if
471
472 if (allocated(gs%shared_gs_dof)) then
473 if (neko_bcknd_device .eq. 1) then
474 call device_deassociate(gs%shared_gs_dof)
475 end if
476 deallocate(gs%shared_gs_dof)
477 end if
478
479 if (allocated(gs%shared_blk_len)) then
480 if (neko_bcknd_device .eq. 1) then
481 call device_deassociate(gs%shared_blk_len)
482 end if
483 deallocate(gs%shared_blk_len)
484 end if
485
486 if (allocated(gs%shared_blk_off)) then
487 if (neko_bcknd_device .eq. 1) then
488 call device_deassociate(gs%shared_blk_off)
489 end if
490 deallocate(gs%shared_blk_off)
491 end if
492
493 gs%nlocal = 0
494 gs%nshared = 0
495 gs%nlocal_blks = 0
496 gs%nshared_blks = 0
497
498 call gs%shared_dofs%free()
499
500 if (allocated(gs%bcknd)) then
501 call gs%bcknd%free()
502 deallocate(gs%bcknd)
503 end if
504
505 if (allocated(gs%comm)) then
506 call gs%comm%free()
507 deallocate(gs%comm)
508 end if
509
510 end subroutine gs_free
511
513 subroutine gs_init_mapping(gs)
514 type(gs_t), target, intent(inout) :: gs
515 type(mesh_t), pointer :: msh
516 type(dofmap_t), pointer :: dofmap
517 type(stack_i4_t), target :: local_dof, dof_local, shared_dof, dof_shared
518 type(stack_i4_t), target :: local_face_dof, face_dof_local
519 type(stack_i4_t), target :: shared_face_dof, face_dof_shared
520 integer :: i, j, k, l, lx, ly, lz, max_id, max_sid, id, lid, dm_size
521 type(htable_i8_t) :: dm
522 type(htable_i8_t), pointer :: sdm
523
524 dofmap => gs%dofmap
525 msh => dofmap%msh
526 sdm => gs%shared_dofs
527
528 lx = dofmap%Xh%lx
529 ly = dofmap%Xh%ly
530 lz = dofmap%Xh%lz
531 dm_size = dofmap%size()/lx
532
533 call dm%init(dm_size, i)
537 call sdm%init(dofmap%size(), i)
538
539
540 call local_dof%init()
541 call dof_local%init()
542
543 call local_face_dof%init()
544 call face_dof_local%init()
545
546 call shared_dof%init()
547 call dof_shared%init()
548
549 call shared_face_dof%init()
550 call face_dof_shared%init()
551
552 !
553 ! Setup mapping for dofs points
554 !
555
556 max_id = 0
557 max_sid = 0
558 do i = 1, msh%nelv
559 ! Local id of vertices
560 lid = linear_index(1, 1, 1, i, lx, ly, lz)
561 ! Check if this dof is shared among ranks or not
562 if (dofmap%shared_dof(1, 1, 1, i)) then
563 id = gs_mapping_add_dof(sdm, dofmap%dof(1, 1, 1, i), max_sid)
564 !If add unique gather-scatter id to shared_dof stack
565 call shared_dof%push(id)
566 !If add local id to dof_shared stack
567 call dof_shared%push(lid)
568 !Now we have the mapping of local id <-> gather scatter id!
569 else
570 ! Same here, only here we know the point is local
571 ! It will as such not need to be sent to other ranks later
572 id = gs_mapping_add_dof(dm, dofmap%dof(1, 1, 1, i), max_id)
573 call local_dof%push(id)
574 call dof_local%push(lid)
575 end if
576 ! This procedure is then repeated for all vertices and edges
577 ! Facets can be treated a little bit differently since they only have one
578 ! neighbor
579
580 lid = linear_index(lx, 1, 1, i, lx, ly, lz)
581 if (dofmap%shared_dof(lx, 1, 1, i)) then
582 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, 1, 1, i), max_sid)
583 call shared_dof%push(id)
584 call dof_shared%push(lid)
585 else
586 id = gs_mapping_add_dof(dm, dofmap%dof(lx, 1, 1, i), max_id)
587 call local_dof%push(id)
588 call dof_local%push(lid)
589 end if
590
591 lid = linear_index(1, ly, 1, i, lx, ly, lz)
592 if (dofmap%shared_dof(1, ly, 1, i)) then
593 id = gs_mapping_add_dof(sdm, dofmap%dof(1, ly, 1, i), max_sid)
594 call shared_dof%push(id)
595 call dof_shared%push(lid)
596 else
597 id = gs_mapping_add_dof(dm, dofmap%dof(1, ly, 1, i), max_id)
598 call local_dof%push(id)
599 call dof_local%push(lid)
600 end if
601
602 lid = linear_index(lx, ly, 1, i, lx, ly, lz)
603 if (dofmap%shared_dof(lx, ly, 1, i)) then
604 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, ly, 1, i), max_sid)
605 call shared_dof%push(id)
606 call dof_shared%push(lid)
607 else
608 id = gs_mapping_add_dof(dm, dofmap%dof(lx, ly, 1, i), max_id)
609 call local_dof%push(id)
610 call dof_local%push(lid)
611 end if
612 if (lz .gt. 1) then
613 lid = linear_index(1, 1, lz, i, lx, ly, lz)
614 if (dofmap%shared_dof(1, 1, lz, i)) then
615 id = gs_mapping_add_dof(sdm, dofmap%dof(1, 1, lz, i), max_sid)
616 call shared_dof%push(id)
617 call dof_shared%push(lid)
618 else
619 id = gs_mapping_add_dof(dm, dofmap%dof(1, 1, lz, i), max_id)
620 call local_dof%push(id)
621 call dof_local%push(lid)
622 end if
623
624 lid = linear_index(lx, 1, lz, i, lx, ly, lz)
625 if (dofmap%shared_dof(lx, 1, lz, i)) then
626 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, 1, lz, i), max_sid)
627 call shared_dof%push(id)
628 call dof_shared%push(lid)
629 else
630 id = gs_mapping_add_dof(dm, dofmap%dof(lx, 1, lz, i), max_id)
631 call local_dof%push(id)
632 call dof_local%push(lid)
633 end if
634
635 lid = linear_index(1, ly, lz, i, lx, ly, lz)
636 if (dofmap%shared_dof(1, ly, lz, i)) then
637 id = gs_mapping_add_dof(sdm, dofmap%dof(1, ly, lz, i), max_sid)
638 call shared_dof%push(id)
639 call dof_shared%push(lid)
640 else
641 id = gs_mapping_add_dof(dm, dofmap%dof(1, ly, lz, i), max_id)
642 call local_dof%push(id)
643 call dof_local%push(lid)
644 end if
645
646 lid = linear_index(lx, ly, lz, i, lx, ly, lz)
647 if (dofmap%shared_dof(lx, ly, lz, i)) then
648 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, ly, lz, i), max_sid)
649 call shared_dof%push(id)
650 call dof_shared%push(lid)
651 else
652 id = gs_mapping_add_dof(dm, dofmap%dof(lx, ly, lz, i), max_id)
653 call local_dof%push(id)
654 call dof_local%push(lid)
655 end if
656 end if
657 end do
658
659 ! Clear local dofmap table
660 call dm%clear()
661 ! Get gather scatter ids and local ids of edges
662 if (lz .gt. 1) then
663 !
664 ! Setup mapping for dofs on edges
665 !
666 do i = 1, msh%nelv
667
668 !
669 ! dofs on edges in x-direction
670 !
671 if (dofmap%shared_dof(2, 1, 1, i)) then
672 do j = 2, lx - 1
673 id = gs_mapping_add_dof(sdm, dofmap%dof(j, 1, 1, i), max_sid)
674 call shared_dof%push(id)
675 id = linear_index(j, 1, 1, i, lx, ly, lz)
676 call dof_shared%push(id)
677 end do
678 else
679 do j = 2, lx - 1
680 id = gs_mapping_add_dof(dm, dofmap%dof(j, 1, 1, i), max_id)
681 call local_dof%push(id)
682 id = linear_index(j, 1, 1, i, lx, ly, lz)
683 call dof_local%push(id)
684 end do
685 end if
686 if (dofmap%shared_dof(2, 1, lz, i)) then
687 do j = 2, lx - 1
688 id = gs_mapping_add_dof(sdm, dofmap%dof(j, 1, lz, i), max_sid)
689 call shared_dof%push(id)
690 id = linear_index(j, 1, lz, i, lx, ly, lz)
691 call dof_shared%push(id)
692 end do
693 else
694 do j = 2, lx - 1
695 id = gs_mapping_add_dof(dm, dofmap%dof(j, 1, lz, i), max_id)
696 call local_dof%push(id)
697 id = linear_index(j, 1, lz, i, lx, ly, lz)
698 call dof_local%push(id)
699 end do
700 end if
701
702 if (dofmap%shared_dof(2, ly, 1, i)) then
703 do j = 2, lx - 1
704 id = gs_mapping_add_dof(sdm, dofmap%dof(j, ly, 1, i), max_sid)
705 call shared_dof%push(id)
706 id = linear_index(j, ly, 1, i, lx, ly, lz)
707 call dof_shared%push(id)
708 end do
709
710 else
711 do j = 2, lx - 1
712 id = gs_mapping_add_dof(dm, dofmap%dof(j, ly, 1, i), max_id)
713 call local_dof%push(id)
714 id = linear_index(j, ly, 1, i, lx, ly, lz)
715 call dof_local%push(id)
716 end do
717 end if
718 if (dofmap%shared_dof(2, ly, lz, i)) then
719 do j = 2, lx - 1
720 id = gs_mapping_add_dof(sdm, dofmap%dof(j, ly, lz, i), max_sid)
721 call shared_dof%push(id)
722 id = linear_index(j, ly, lz, i, lx, ly, lz)
723 call dof_shared%push(id)
724 end do
725 else
726 do j = 2, lx - 1
727 id = gs_mapping_add_dof(dm, dofmap%dof(j, ly, lz, i), max_id)
728 call local_dof%push(id)
729 id = linear_index(j, ly, lz, i, lx, ly, lz)
730 call dof_local%push(id)
731 end do
732 end if
733
734 !
735 ! dofs on edges in y-direction
736 !
737 if (dofmap%shared_dof(1, 2, 1, i)) then
738 do k = 2, ly - 1
739 id = gs_mapping_add_dof(sdm, dofmap%dof(1, k, 1, i), max_sid)
740 call shared_dof%push(id)
741 id = linear_index(1, k, 1, i, lx, ly, lz)
742 call dof_shared%push(id)
743 end do
744 else
745 do k = 2, ly - 1
746 id = gs_mapping_add_dof(dm, dofmap%dof(1, k, 1, i), max_id)
747 call local_dof%push(id)
748 id = linear_index(1, k, 1, i, lx, ly, lz)
749 call dof_local%push(id)
750 end do
751 end if
752 if (dofmap%shared_dof(1, 2, lz, i)) then
753 do k = 2, ly - 1
754 id = gs_mapping_add_dof(sdm, dofmap%dof(1, k, lz, i), max_sid)
755 call shared_dof%push(id)
756 id = linear_index(1, k, lz, i, lx, ly, lz)
757 call dof_shared%push(id)
758 end do
759 else
760 do k = 2, ly - 1
761 id = gs_mapping_add_dof(dm, dofmap%dof(1, k, lz, i), max_id)
762 call local_dof%push(id)
763 id = linear_index(1, k, lz, i, lx, ly, lz)
764 call dof_local%push(id)
765 end do
766 end if
767
768 if (dofmap%shared_dof(lx, 2, 1, i)) then
769 do k = 2, ly - 1
770 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, k, 1, i), max_sid)
771 call shared_dof%push(id)
772 id = linear_index(lx, k, 1, i, lx, ly, lz)
773 call dof_shared%push(id)
774 end do
775 else
776 do k = 2, ly - 1
777 id = gs_mapping_add_dof(dm, dofmap%dof(lx, k, 1, i), max_id)
778 call local_dof%push(id)
779 id = linear_index(lx, k, 1, i, lx, ly, lz)
780 call dof_local%push(id)
781 end do
782 end if
783 if (dofmap%shared_dof(lx, 2, lz, i)) then
784 do k = 2, ly - 1
785 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, k, lz, i), max_sid)
786 call shared_dof%push(id)
787 id = linear_index(lx, k, lz, i, lx, ly, lz)
788 call dof_shared%push(id)
789 end do
790 else
791 do k = 2, ly - 1
792 id = gs_mapping_add_dof(dm, dofmap%dof(lx, k, lz, i), max_id)
793 call local_dof%push(id)
794 id = linear_index(lx, k, lz, i, lx, ly, lz)
795 call dof_local%push(id)
796 end do
797 end if
798 !
799 ! dofs on edges in z-direction
800 !
801 if (dofmap%shared_dof(1, 1, 2, i)) then
802 do l = 2, lz - 1
803 id = gs_mapping_add_dof(sdm, dofmap%dof(1, 1, l, i), max_sid)
804 call shared_dof%push(id)
805 id = linear_index(1, 1, l, i, lx, ly, lz)
806 call dof_shared%push(id)
807 end do
808 else
809 do l = 2, lz - 1
810 id = gs_mapping_add_dof(dm, dofmap%dof(1, 1, l, i), max_id)
811 call local_dof%push(id)
812 id = linear_index(1, 1, l, i, lx, ly, lz)
813 call dof_local%push(id)
814 end do
815 end if
816
817 if (dofmap%shared_dof(lx, 1, 2, i)) then
818 do l = 2, lz - 1
819 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, 1, l, i), max_sid)
820 call shared_dof%push(id)
821 id = linear_index(lx, 1, l, i, lx, ly, lz)
822 call dof_shared%push(id)
823 end do
824 else
825 do l = 2, lz - 1
826 id = gs_mapping_add_dof(dm, dofmap%dof(lx, 1, l, i), max_id)
827 call local_dof%push(id)
828 id = linear_index(lx, 1, l, i, lx, ly, lz)
829 call dof_local%push(id)
830 end do
831 end if
832
833 if (dofmap%shared_dof(1, ly, 2, i)) then
834 do l = 2, lz - 1
835 id = gs_mapping_add_dof(sdm, dofmap%dof(1, ly, l, i), max_sid)
836 call shared_dof%push(id)
837 id = linear_index(1, ly, l, i, lx, ly, lz)
838 call dof_shared%push(id)
839 end do
840 else
841 do l = 2, lz - 1
842 id = gs_mapping_add_dof(dm, dofmap%dof(1, ly, l, i), max_id)
843 call local_dof%push(id)
844 id = linear_index(1, ly, l, i, lx, ly, lz)
845 call dof_local%push(id)
846 end do
847 end if
848
849 if (dofmap%shared_dof(lx, ly, 2, i)) then
850 do l = 2, lz - 1
851 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, ly, l, i), max_sid)
852 call shared_dof%push(id)
853 id = linear_index(lx, ly, l, i, lx, ly, lz)
854 call dof_shared%push(id)
855 end do
856 else
857 do l = 2, lz - 1
858 id = gs_mapping_add_dof(dm, dofmap%dof(lx, ly, l, i), max_id)
859 call local_dof%push(id)
860 id = linear_index(lx, ly, l, i, lx, ly, lz)
861 call dof_local%push(id)
862 end do
863 end if
864 end do
865 end if
866
867 ! Clear local dofmap table
868 call dm%clear()
869
870 !
871 ! Setup mapping for dofs on facets
872 !
873 ! This is for 2d
874 if (lz .eq. 1) then
875 do i = 1, msh%nelv
876
877 !
878 ! dofs on edges in x-direction
879 !
880 if (msh%facet_neigh(3, i) .ne. 0) then
881 if (dofmap%shared_dof(2, 1, 1, i)) then
882 do j = 2, lx - 1
883 id = gs_mapping_add_dof(sdm, dofmap%dof(j, 1, 1, i), max_sid)
884 call shared_face_dof%push(id)
885 id = linear_index(j, 1, 1, i, lx, ly, lz)
886 call face_dof_shared%push(id)
887 end do
888 else
889 do j = 2, lx - 1
890 id = gs_mapping_add_dof(dm, dofmap%dof(j, 1, 1, i), max_id)
891 call local_face_dof%push(id)
892 id = linear_index(j, 1, 1, i, lx, ly, lz)
893 call face_dof_local%push(id)
894 end do
895 end if
896 end if
897
898 if (msh%facet_neigh(4, i) .ne. 0) then
899 if (dofmap%shared_dof(2, ly, 1, i)) then
900 do j = 2, lx - 1
901 id = gs_mapping_add_dof(sdm, dofmap%dof(j, ly, 1, i), &
902 max_sid)
903 call shared_face_dof%push(id)
904 id = linear_index(j, ly, 1, i, lx, ly, lz)
905 call face_dof_shared%push(id)
906 end do
907
908 else
909 do j = 2, lx - 1
910 id = gs_mapping_add_dof(dm, dofmap%dof(j, ly, 1, i), &
911 max_id)
912 call local_face_dof%push(id)
913 id = linear_index(j, ly, 1, i, lx, ly, lz)
914 call face_dof_local%push(id)
915 end do
916 end if
917 end if
918
919 !
920 ! dofs on edges in y-direction
921 !
922 if (msh%facet_neigh(1, i) .ne. 0) then
923 if (dofmap%shared_dof(1, 2, 1, i)) then
924 do k = 2, ly - 1
925 id = gs_mapping_add_dof(sdm, dofmap%dof(1, k, 1, i), max_sid)
926 call shared_face_dof%push(id)
927 id = linear_index(1, k, 1, i, lx, ly, lz)
928 call face_dof_shared%push(id)
929 end do
930 else
931 do k = 2, ly - 1
932 id = gs_mapping_add_dof(dm, dofmap%dof(1, k, 1, i), max_id)
933 call local_face_dof%push(id)
934 id = linear_index(1, k, 1, i, lx, ly, lz)
935 call face_dof_local%push(id)
936 end do
937 end if
938 end if
939
940 if (msh%facet_neigh(2, i) .ne. 0) then
941 if (dofmap%shared_dof(lx, 2, 1, i)) then
942 do k = 2, ly - 1
943 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, k, 1, i), &
944 max_sid)
945 call shared_face_dof%push(id)
946 id = linear_index(lx, k, 1, i, lx, ly, lz)
947 call face_dof_shared%push(id)
948 end do
949 else
950 do k = 2, ly - 1
951 id = gs_mapping_add_dof(dm, dofmap%dof(lx, k, 1, i), &
952 max_id)
953 call local_face_dof%push(id)
954 id = linear_index(lx, k, 1, i, lx, ly, lz)
955 call face_dof_local%push(id)
956 end do
957 end if
958 end if
959 end do
960 else
961 do i = 1, msh%nelv
962
963 ! Facets in x-direction (s, t)-plane
964 if (msh%facet_neigh(1, i) .ne. 0) then
965 if (dofmap%shared_dof(1, 2, 2, i)) then
966 do l = 2, lz - 1
967 do k = 2, ly - 1
968 id = gs_mapping_add_dof(sdm, dofmap%dof(1, k, l, i), &
969 max_sid)
970 call shared_face_dof%push(id)
971 id = linear_index(1, k, l, i, lx, ly, lz)
972 call face_dof_shared%push(id)
973 end do
974 end do
975 else
976 do l = 2, lz - 1
977 do k = 2, ly - 1
978 id = gs_mapping_add_dof(dm, dofmap%dof(1, k, l, i), &
979 max_id)
980 call local_face_dof%push(id)
981 id = linear_index(1, k, l, i, lx, ly, lz)
982 call face_dof_local%push(id)
983 end do
984 end do
985 end if
986 end if
987
988 if (msh%facet_neigh(2, i) .ne. 0) then
989 if (dofmap%shared_dof(lx, 2, 2, i)) then
990 do l = 2, lz - 1
991 do k = 2, ly - 1
992 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, k, l, i), &
993 max_sid)
994 call shared_face_dof%push(id)
995 id = linear_index(lx, k, l, i, lx, ly, lz)
996 call face_dof_shared%push(id)
997 end do
998 end do
999 else
1000 do l = 2, lz - 1
1001 do k = 2, ly - 1
1002 id = gs_mapping_add_dof(dm, dofmap%dof(lx, k, l, i), &
1003 max_id)
1004 call local_face_dof%push(id)
1005 id = linear_index(lx, k, l, i, lx, ly, lz)
1006 call face_dof_local%push(id)
1007 end do
1008 end do
1009 end if
1010 end if
1011
1012 ! Facets in y-direction (r, t)-plane
1013 if (msh%facet_neigh(3, i) .ne. 0) then
1014 if (dofmap%shared_dof(2, 1, 2, i)) then
1015 do l = 2, lz - 1
1016 do j = 2, lx - 1
1017 id = gs_mapping_add_dof(sdm, dofmap%dof(j, 1, l, i), &
1018 max_sid)
1019 call shared_face_dof%push(id)
1020 id = linear_index(j, 1, l, i, lx, ly, lz)
1021 call face_dof_shared%push(id)
1022 end do
1023 end do
1024 else
1025 do l = 2, lz - 1
1026 do j = 2, lx - 1
1027 id = gs_mapping_add_dof(dm, dofmap%dof(j, 1, l, i), &
1028 max_id)
1029 call local_face_dof%push(id)
1030 id = linear_index(j, 1, l, i, lx, ly, lz)
1031 call face_dof_local%push(id)
1032 end do
1033 end do
1034 end if
1035 end if
1036
1037 if (msh%facet_neigh(4, i) .ne. 0) then
1038 if (dofmap%shared_dof(2, ly, 2, i)) then
1039 do l = 2, lz - 1
1040 do j = 2, lx - 1
1041 id = gs_mapping_add_dof(sdm, dofmap%dof(j, ly, l, i), &
1042 max_sid)
1043 call shared_face_dof%push(id)
1044 id = linear_index(j, ly, l, i, lx, ly, lz)
1045 call face_dof_shared%push(id)
1046 end do
1047 end do
1048 else
1049 do l = 2, lz - 1
1050 do j = 2, lx - 1
1051 id = gs_mapping_add_dof(dm, dofmap%dof(j, ly, l, i), &
1052 max_id)
1053 call local_face_dof%push(id)
1054 id = linear_index(j, ly, l, i, lx, ly, lz)
1055 call face_dof_local%push(id)
1056 end do
1057 end do
1058 end if
1059 end if
1060
1061 ! Facets in z-direction (r, s)-plane
1062 if (msh%facet_neigh(5, i) .ne. 0) then
1063 if (dofmap%shared_dof(2, 2, 1, i)) then
1064 do k = 2, ly - 1
1065 do j = 2, lx - 1
1066 id = gs_mapping_add_dof(sdm, dofmap%dof(j, k, 1, i), &
1067 max_sid)
1068 call shared_face_dof%push(id)
1069 id = linear_index(j, k, 1, i, lx, ly, lz)
1070 call face_dof_shared%push(id)
1071 end do
1072 end do
1073 else
1074 do k = 2, ly - 1
1075 do j = 2, lx - 1
1076 id = gs_mapping_add_dof(dm, dofmap%dof(j, k, 1, i), &
1077 max_id)
1078 call local_face_dof%push(id)
1079 id = linear_index(j, k, 1, i, lx, ly, lz)
1080 call face_dof_local%push(id)
1081 end do
1082 end do
1083 end if
1084 end if
1085
1086 if (msh%facet_neigh(6, i) .ne. 0) then
1087 if (dofmap%shared_dof(2, 2, lz, i)) then
1088 do k = 2, ly - 1
1089 do j = 2, lx - 1
1090 id = gs_mapping_add_dof(sdm, dofmap%dof(j, k, lz, i), &
1091 max_sid)
1092 call shared_face_dof%push(id)
1093 id = linear_index(j, k, lz, i, lx, ly, lz)
1094 call face_dof_shared%push(id)
1095 end do
1096 end do
1097 else
1098 do k = 2, ly - 1
1099 do j = 2, lx - 1
1100 id = gs_mapping_add_dof(dm, dofmap%dof(j, k, lz, i), &
1101 max_id)
1102 call local_face_dof%push(id)
1103 id = linear_index(j, k, lz, i, lx, ly, lz)
1104 call face_dof_local%push(id)
1105 end do
1106 end do
1107 end if
1108 end if
1109 end do
1110 end if
1111
1112
1113 call dm%free()
1114
1115 gs%nlocal = local_dof%size() + local_face_dof%size()
1116 gs%local_facet_offset = local_dof%size() + 1
1117
1118 ! Finalize local dof to gather-scatter index
1119 allocate(gs%local_dof_gs(gs%nlocal))
1120
1121 ! Add dofs on points and edges
1122
1123 ! We should use the %array() procedure, which works great for
1124 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1125 ! certain data types
1126 select type (dof_array => local_dof%data)
1127 type is (integer)
1128 j = local_dof%size()
1129 do i = 1, j
1130 gs%local_dof_gs(i) = dof_array(i)
1131 end do
1132 end select
1133 call local_dof%free()
1134
1135 ! Add dofs on faces
1136
1137 ! We should use the %array() procedure, which works great for
1138 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1139 ! certain data types
1140 select type (dof_array => local_face_dof%data)
1141 type is (integer)
1142 do i = 1, local_face_dof%size()
1143 gs%local_dof_gs(i + j) = dof_array(i)
1144 end do
1145 end select
1146 call local_face_dof%free()
1147
1148 ! Finalize local gather-scatter index to dof
1149 allocate(gs%local_gs_dof(gs%nlocal))
1150
1151 ! Add gather-scatter index on points and edges
1152
1153 ! We should use the %array() procedure, which works great for
1154 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1155 ! certain data types
1156 select type (dof_array => dof_local%data)
1157 type is (integer)
1158 j = dof_local%size()
1159 do i = 1, j
1160 gs%local_gs_dof(i) = dof_array(i)
1161 end do
1162 end select
1163 call dof_local%free()
1164
1165 ! We should use the %array() procedure, which works great for
1166 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1167 ! certain data types
1168 select type (dof_array => face_dof_local%data)
1169 type is (integer)
1170 do i = 1, face_dof_local%size()
1171 gs%local_gs_dof(i+j) = dof_array(i)
1172 end do
1173 end select
1174 call face_dof_local%free()
1175
1176 call gs_qsort_dofmap(gs%local_dof_gs, gs%local_gs_dof, &
1177 gs%nlocal, 1, gs%nlocal)
1178
1179 call gs_find_blks(gs%local_dof_gs, gs%local_blk_len, &
1180 gs%local_blk_off, gs%nlocal_blks, gs%nlocal, gs%local_facet_offset)
1181
1182 ! Allocate buffer for local gs-ops
1183 allocate(gs%local_gs(gs%nlocal))
1184
1185 gs%nshared = shared_dof%size() + shared_face_dof%size()
1186 gs%shared_facet_offset = shared_dof%size() + 1
1187
1188 ! Finalize shared dof to gather-scatter index
1189 allocate(gs%shared_dof_gs(gs%nshared))
1190
1191 ! Add shared dofs on points and edges
1192
1193 ! We should use the %array() procedure, which works great for
1194 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1195 ! certain data types
1196 select type (dof_array => shared_dof%data)
1197 type is (integer)
1198 j = shared_dof%size()
1199 do i = 1, j
1200 gs%shared_dof_gs(i) = dof_array(i)
1201 end do
1202 end select
1203 call shared_dof%free()
1204
1205 ! Add shared dofs on faces
1206
1207 ! We should use the %array() procedure, which works great for
1208 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1209 ! certain data types
1210 select type (dof_array => shared_face_dof%data)
1211 type is (integer)
1212 do i = 1, shared_face_dof%size()
1213 gs%shared_dof_gs(i + j) = dof_array(i)
1214 end do
1215 end select
1216 call shared_face_dof%free()
1217
1218 ! Finalize shared gather-scatter index to dof
1219 allocate(gs%shared_gs_dof(gs%nshared))
1220
1221 ! Add dofs on points and edges
1222
1223 ! We should use the %array() procedure, which works great for
1224 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1225 ! certain data types
1226 select type (dof_array => dof_shared%data)
1227 type is (integer)
1228 j = dof_shared%size()
1229 do i = 1, j
1230 gs%shared_gs_dof(i) = dof_array(i)
1231 end do
1232 end select
1233 call dof_shared%free()
1234
1235 ! We should use the %array() procedure, which works great for
1236 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1237 ! certain data types
1238 select type (dof_array => face_dof_shared%data)
1239 type is (integer)
1240 do i = 1, face_dof_shared%size()
1241 gs%shared_gs_dof(i + j) = dof_array(i)
1242 end do
1243 end select
1244 call face_dof_shared%free()
1245
1246 ! Allocate buffer for shared gs-ops
1247 allocate(gs%shared_gs(gs%nshared))
1248
1249 ! Compact multi-component shared buffer for the fused vector gs. On the
1250 ! device it is mapped so the fused exchange can use its device pointer.
1251 allocate(gs%shared_gs_v(max(1, gs_vec_nc * gs%nshared)))
1252 if (neko_bcknd_device .eq. 1) then
1253 call device_map(gs%shared_gs_v, gs%shared_gs_v_d, &
1254 max(1, gs_vec_nc * gs%nshared))
1255 end if
1256
1257 if (gs%nshared .gt. 0) then
1258 call gs_qsort_dofmap(gs%shared_dof_gs, gs%shared_gs_dof, &
1259 gs%nshared, 1, gs%nshared)
1260
1261 call gs_find_blks(gs%shared_dof_gs, gs%shared_blk_len, &
1262 gs%shared_blk_off, gs%nshared_blks, gs%nshared, &
1263 gs%shared_facet_offset)
1264 end if
1265
1266 contains
1267
1276 function gs_mapping_add_dof(map_, dof, max_id) result(id)
1277 type(htable_i8_t), intent(inout) :: map_
1278 integer(kind=i8), intent(inout) :: dof
1279 integer, intent(inout) :: max_id
1280 integer :: id
1281
1282 if (map_%get(dof, id) .gt. 0) then
1283 max_id = max_id + 1
1284 call map_%set(dof, max_id)
1285 id = max_id
1286 end if
1287
1288 end function gs_mapping_add_dof
1289
1291 recursive subroutine gs_qsort_dofmap(dg, gd, n, lo, hi)
1292 integer, intent(inout) :: n
1293 integer, dimension(n), intent(inout) :: dg
1294 integer, dimension(n), intent(inout) :: gd
1295 integer :: lo, hi
1296 integer :: tmp, i, j, pivot
1297
1298 i = lo - 1
1299 j = hi + 1
1300 pivot = dg((lo + hi) / 2)
1301 do
1302 do
1303 i = i + 1
1304 if (dg(i) .ge. pivot) exit
1305 end do
1306
1307 do
1308 j = j - 1
1309 if (dg(j) .le. pivot) exit
1310 end do
1311
1312 if (i .lt. j) then
1313 tmp = dg(i)
1314 dg(i) = dg(j)
1315 dg(j) = tmp
1316
1317 tmp = gd(i)
1318 gd(i) = gd(j)
1319 gd(j) = tmp
1320 else if (i .eq. j) then
1321 i = i + 1
1322 exit
1323 else
1324 exit
1325 end if
1326 end do
1327 if (lo .lt. j) call gs_qsort_dofmap(dg, gd, n, lo, j)
1328 if (i .lt. hi) call gs_qsort_dofmap(dg, gd, n, i, hi)
1329
1330 end subroutine gs_qsort_dofmap
1331
1333 subroutine gs_find_blks(dg, blk_len, blk_off, nblks, n, m)
1334 integer, intent(in) :: n
1335 integer, intent(in) :: m
1336 integer, dimension(n), intent(inout) :: dg
1337 integer, allocatable, intent(inout) :: blk_len(:)
1338 integer, allocatable, intent(inout) :: blk_off(:)
1339 integer, intent(inout) :: nblks
1340 integer :: i, j
1341 integer :: id, count
1342 type(stack_i4_t), target :: blks
1343
1344 call blks%init()
1345 i = 1
1346 do while (i .lt. m)
1347 id = dg(i)
1348 count = 1
1349 j = i
1350 do while ( j+1 .le. n .and. dg(j+1) .eq. id)
1351 j = j + 1
1352 count = count + 1
1353 end do
1354 call blks%push(count)
1355 i = j + 1
1356 end do
1357
1358 select type (blk_array => blks%data)
1359 type is (integer)
1360 nblks = blks%size()
1361 allocate(blk_len(nblks))
1362 do i = 1, nblks
1363 blk_len(i) = blk_array(i)
1364 end do
1365 allocate(blk_off(nblks))
1366 blk_off(1) = 0
1367 do i = 2, nblks
1368 blk_off(i) = blk_off(i - 1) + blk_len(i - 1)
1369 end do
1370 end select
1371 call blks%free()
1372
1373 end subroutine gs_find_blks
1374
1375 end subroutine gs_init_mapping
1376
1388 subroutine gs_schedule(gs)
1389 type(gs_t), target, intent(inout) :: gs
1390 type(htable_iter_i8_t) :: it
1391 type(stack_i4_t) :: send_pe, recv_pe
1392 type(stack_i8_t) :: cr_buf
1393 integer(i8), allocatable :: buf(:)
1394 integer(i8), pointer :: cr_data(:)
1395 integer(i8), allocatable :: rgid(:), gtmp(:)
1396 integer, allocatable :: rpeer(:), rgsid(:), rperm(:), gperm(:)
1397 integer(i8) :: gid
1398 integer :: i, j, n, owner, nrec, peer, shared_gs_id, tmp
1399 integer :: a, b, cnt, t
1400
1401 call send_pe%init()
1402 call recv_pe%init()
1403
1404 !
1405 ! Phase 1: route every local shared dof to its canonical owner.
1406 ! record = [dest=owner, len=2, gid, origin]
1407 !
1408 call cr_buf%init(max(gs%shared_dofs%num_entries(), 1) * 4)
1409 call it%init(gs%shared_dofs)
1410 do while (it%next())
1411 gid = it%key()
1412 owner = int(modulo(gid, int(pe_size, i8)))
1413 call crystal_router_pack(cr_buf, owner, [gid, int(pe_rank, i8)])
1414 end do
1415
1416 n = cr_buf%size()
1417 allocate(buf(max(n, 1)))
1418 if (n .gt. 0) then
1419 cr_data => cr_buf%array()
1420 buf(1:n) = cr_data(1:n)
1421 end if
1422 call cr_buf%free()
1423
1424 call crystal_router_transfer(buf, n)
1425
1426 !
1427 ! Phase 2: at the owner, group holders by gid and reflect, to each holder,
1428 ! every other holder of the same dof.
1429 ! reply = [dest=holder, len=2, gid, peer]
1430 !
1431 nrec = n / 4 ! every record here has the fixed form [me, 2, gid, origin]
1432 allocate(rgid(max(nrec, 1)), rgsid(max(nrec, 1)), gperm(max(nrec, 1)))
1433 do i = 1, nrec
1434 rgid(i) = buf((i - 1) * 4 + 3) ! gid
1435 rgsid(i) = int(buf((i - 1) * 4 + 4)) ! origin rank (reuse array)
1436 end do
1437 if (nrec .gt. 0) call gs_sort_i8(rgid, gperm, nrec)
1438
1439 call cr_buf%init(max(n, 1))
1440 i = 1
1441 do while (i .le. nrec)
1442 j = i
1443 do while (j .le. nrec)
1444 if (rgid(j) .ne. rgid(i)) exit
1445 j = j + 1
1446 end do
1447 ! Reflect, to each holder, every other holder of this dof.
1448 if (j - i .gt. 1) then
1449 do a = i, j - 1 ! recipient holder
1450 do b = i, j - 1 ! the other holder
1451 if (a .eq. b) cycle
1452 call crystal_router_pack(cr_buf, rgsid(gperm(a)), &
1453 [rgid(i), int(rgsid(gperm(b)), i8)])
1454 end do
1455 end do
1456 end if
1457 i = j
1458 end do
1459 deallocate(rgid, rgsid, gperm)
1460
1461 n = cr_buf%size()
1462 if (allocated(buf)) deallocate(buf)
1463 allocate(buf(max(n, 1)))
1464 if (n .gt. 0) then
1465 cr_data => cr_buf%array()
1466 buf(1:n) = cr_data(1:n)
1467 end if
1468 call cr_buf%free()
1469
1470 call crystal_router_transfer(buf, n)
1471
1472 !
1473 ! Phase 3: register each (dof, peer) for both send and receive. Order each
1474 ! peer's dof list by gid so both ranks of a pair agree on the order.
1475 !
1476 nrec = n / 4 ! replies are [me, 2, gid, peer]
1477 allocate(rgid(max(nrec, 1)), rpeer(max(nrec, 1)), rgsid(max(nrec, 1)), &
1478 rperm(max(nrec, 1)))
1479 do i = 1, nrec
1480 gid = buf((i - 1) * 4 + 3)
1481 rgid(i) = gid
1482 rpeer(i) = int(buf((i - 1) * 4 + 4))
1483 tmp = gs%shared_dofs%get(gid, shared_gs_id)
1484 rgsid(i) = shared_gs_id
1485 end do
1486
1487 ! Sort by peer; within each peer run, sort by gid and register in that order.
1488 if (nrec .gt. 0) call sort(rpeer, rperm, nrec)
1489 a = 1
1490 do while (a .le. nrec)
1491 b = a
1492 do while (b .le. nrec)
1493 if (rpeer(b) .ne. rpeer(a)) exit
1494 b = b + 1
1495 end do
1496 peer = rpeer(a)
1497 cnt = b - a
1498 allocate(gtmp(cnt), gperm(cnt))
1499 do t = 1, cnt
1500 gtmp(t) = rgid(rperm(a + t - 1))
1501 end do
1502 call gs_sort_i8(gtmp, gperm, cnt)
1503 do t = 1, cnt
1504 shared_gs_id = rgsid(rperm(a + gperm(t) - 1))
1505 call gs%comm%send_dof(peer)%push(shared_gs_id)
1506 call gs%comm%recv_dof(peer)%push(shared_gs_id)
1507 end do
1508 deallocate(gtmp, gperm)
1509 call send_pe%push(peer)
1510 call recv_pe%push(peer)
1511 a = b
1512 end do
1513 deallocate(rgid, rpeer, rgsid, rperm)
1514 if (allocated(buf)) deallocate(buf)
1515
1516 call gs%comm%init(send_pe, recv_pe)
1517
1518 call send_pe%free()
1519 call recv_pe%free()
1520
1521 !This arrays seems to take massive amounts of memory...
1522 call gs%shared_dofs%free()
1523
1524 end subroutine gs_schedule
1525
1528 subroutine gs_sort_i8(a, ind, n)
1529 integer, intent(in) :: n
1530 integer(i8), intent(inout) :: a(n)
1531 integer, intent(out) :: ind(n)
1532 integer(i8) :: aa
1533 integer :: j, ir, i, ii, l
1534
1535 do j = 1, n
1536 ind(j) = j
1537 end do
1538
1539 if (n .le. 1) return
1540
1541 l = n/2 + 1
1542 ir = n
1543 do while (.true.)
1544 if (l .gt. 1) then
1545 l = l - 1
1546 aa = a(l)
1547 ii = ind(l)
1548 else
1549 aa = a(ir)
1550 ii = ind(ir)
1551 a(ir) = a(1)
1552 ind(ir) = ind(1)
1553 ir = ir - 1
1554 if (ir .eq. 1) then
1555 a(1) = aa
1556 ind(1) = ii
1557 return
1558 end if
1559 end if
1560 i = l
1561 j = l + l
1562 do while (j .le. ir)
1563 if (j .lt. ir) then
1564 if (a(j) .lt. a(j + 1)) j = j + 1
1565 end if
1566 if (aa .lt. a(j)) then
1567 a(i) = a(j)
1568 ind(i) = ind(j)
1569 i = j
1570 j = j + j
1571 else
1572 j = ir + 1
1573 end if
1574 end do
1575 a(i) = aa
1576 ind(i) = ii
1577 end do
1578 end subroutine gs_sort_i8
1579
1581 subroutine gs_op_fld(gs, u, op, event)
1582 class(gs_t), intent(inout) :: gs
1583 type(field_t), intent(inout) :: u
1584 type(c_ptr), optional, intent(inout) :: event
1585 integer :: n, op
1586
1587 n = u%msh%nelv * u%Xh%lx * u%Xh%ly * u%Xh%lz
1588 if (present(event)) then
1589 call gs_op_vector(gs, u%x, n, op, event)
1590 else
1591 call gs_op_vector(gs, u%x, n, op)
1592 end if
1593
1594 end subroutine gs_op_fld
1595
1597 subroutine gs_op_r4(gs, u, n, op, event)
1598 class(gs_t), intent(inout) :: gs
1599 integer, intent(in) :: n
1600 real(kind=rp), contiguous, dimension(:,:,:,:), intent(inout) :: u
1601 type(c_ptr), optional, intent(inout) :: event
1602 integer :: op
1603
1604 if (present(event)) then
1605 call gs_op_vector(gs, u, n, op, event)
1606 else
1607 call gs_op_vector(gs, u, n, op)
1608 end if
1609
1610 end subroutine gs_op_r4
1611
1613 subroutine gs_op_vector(gs, u, n, op, event)
1614 class(gs_t), intent(inout) :: gs
1615 integer, intent(in) :: n
1616 real(kind=rp), dimension(n), intent(inout) :: u
1617 type(c_ptr), optional, intent(inout) :: event
1618 integer :: m, l, op, lo, so, tid
1619 type(c_ptr) :: scatter_event
1620
1621 lo = gs%local_facet_offset
1622 so = -gs%shared_facet_offset
1623 m = gs%nlocal
1624 l = gs%nshared
1625
1626 ! Capture the calling thread id before opening any parallel region; it
1627 ! is used as the MPI tag so concurrent gs ops driven from different
1628 ! threads (device path) don't collide.
1629 tid = 0
1630 !$ tid = omp_get_thread_num()
1631
1632 ! Resolve the optional event into a non-optional local before opening
1633 ! the parallel region. An absent optional dummy must not be captured by
1634 ! the region's data-sharing, otherwise the outlined region prologue
1635 ! dereferences a null descriptor (segfaults on CCE).
1636 scatter_event = c_null_ptr
1637 if (present(event)) scatter_event = event
1638
1639 !$omp parallel if (NEKO_BCKND_DEVICE .eq. 0)
1640 call profiler_start_region("gather_scatter", 5)
1641 ! Gather shared dofs
1642 if (pe_size .gt. 1 .and. n .gt. 0) then
1643 call profiler_start_region("gs_nbrecv", 13)
1644 call gs%comm%nbrecv(tid)
1645 call profiler_end_region("gs_nbrecv", 13)
1646 call profiler_start_region("gs_gather_shared", 14)
1647 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u, n, &
1648 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1649 gs%shared_blk_off, op, .true.)
1650 call profiler_end_region("gs_gather_shared", 14)
1651 call profiler_start_region("gs_nbsend", 6)
1652 call gs%comm%nbsend(gs%shared_gs, l, tid, &
1653 gs%bcknd%gather_event, gs%bcknd%gs_stream)
1654 call profiler_end_region("gs_nbsend", 6)
1655
1656 end if
1657
1658 ! Gather-scatter local dofs
1659 call profiler_start_region("gs_local", 12)
1660 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u, n, &
1661 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1662 op, .false.)
1663 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u, n, &
1664 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1665 .false., c_null_ptr)
1666 call profiler_end_region("gs_local", 12)
1667 ! Scatter shared dofs
1668 if (pe_size .gt. 1 .and. n .gt. 0) then
1669 call profiler_start_region("gs_nbwait", 7)
1670 call gs%comm%nbwait(gs%shared_gs, l, op, gs%bcknd%gs_stream)
1671 call profiler_end_region("gs_nbwait", 7)
1672 call profiler_start_region("gs_scatter_shared", 15)
1673 call gs%bcknd%scatter(gs%shared_gs, l,&
1674 gs%shared_dof_gs, u, n, &
1675 gs%shared_gs_dof, gs%nshared_blks, &
1676 gs%shared_blk_len, gs%shared_blk_off, .true., scatter_event)
1677 call profiler_end_region("gs_scatter_shared", 15)
1678 end if
1679
1680 call profiler_end_region("gather_scatter", 5)
1681 !$omp end parallel
1682 end subroutine gs_op_vector
1683
1686 subroutine gs_op_r3(gs, u1, u2, u3, n, op, event)
1687 class(gs_t), intent(inout) :: gs
1688 integer, intent(in) :: n
1689 real(kind=rp), contiguous, dimension(:,:,:,:), intent(inout) :: u1, u2, u3
1690 type(c_ptr), optional, intent(inout) :: event
1691 integer :: op
1692
1693 if (present(event)) then
1694 call gs_op_vector3(gs, u1, u2, u3, n, op, event)
1695 else
1696 call gs_op_vector3(gs, u1, u2, u3, n, op)
1697 end if
1698
1699 end subroutine gs_op_r3
1700
1710 subroutine gs_op_vector3(gs, u1, u2, u3, n, op, event)
1711 class(gs_t), intent(inout) :: gs
1712 integer, intent(in) :: n
1713 real(kind=rp), dimension(n), intent(inout) :: u1, u2, u3
1714 type(c_ptr), optional, intent(inout) :: event
1715 integer :: m, l, op, lo, so, tid
1716 integer, parameter :: nc = 3
1717 type(c_ptr) :: scatter_event
1718
1719 ! Fall back to nc independent scalar exchanges when the comm backend has
1720 ! no fused vector path.
1721 if (.not. gs%comm%vec_supported) then
1722 if (present(event)) then
1723 call gs_op_vector(gs, u1, n, op, event)
1724 call gs_op_vector(gs, u2, n, op, event)
1725 call gs_op_vector(gs, u3, n, op, event)
1726 else
1727 call gs_op_vector(gs, u1, n, op)
1728 call gs_op_vector(gs, u2, n, op)
1729 call gs_op_vector(gs, u3, n, op)
1730 end if
1731 return
1732 end if
1733
1734 lo = gs%local_facet_offset
1735 so = -gs%shared_facet_offset
1736 m = gs%nlocal
1737 l = gs%nshared
1738
1739 tid = 0
1740 !$ tid = omp_get_thread_num()
1741
1742 scatter_event = c_null_ptr
1743 if (present(event)) scatter_event = event
1744
1745 if (neko_bcknd_device .eq. 0) then
1746
1747 !$omp parallel
1748 call profiler_start_region("gather_scatter", 5)
1749
1750 ! Gather each component's shared dofs directly into its column of
1751 ! shared_gs_v (the host backends write the actual argument), then
1752 ! launch ONE fused exchange covering all nc components.
1753 if (pe_size .gt. 1 .and. n .gt. 0) then
1754 call gs%comm%nbrecv_vec(tid, nc)
1755 call gs%bcknd%gather(gs%shared_gs_v(1), l, so, gs%shared_dof_gs, &
1756 u1, n, gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1757 gs%shared_blk_off, op, .true.)
1758 call gs%bcknd%gather(gs%shared_gs_v(l + 1), l, so, &
1759 gs%shared_dof_gs, u2, n, gs%shared_gs_dof, gs%nshared_blks, &
1760 gs%shared_blk_len, gs%shared_blk_off, op, .true.)
1761 call gs%bcknd%gather(gs%shared_gs_v(2*l + 1), l, so, &
1762 gs%shared_dof_gs, u3, n, gs%shared_gs_dof, gs%nshared_blks, &
1763 gs%shared_blk_len, gs%shared_blk_off, op, .true.)
1764 call gs%comm%nbsend_vec(gs%shared_gs_v, l, nc, tid, &
1765 gs%bcknd%gather_event, gs%bcknd%gs_stream)
1766 end if
1767
1768 ! Local gather-scatter, one scalar pass per component (reuses local_gs;
1769 ! the internal barriers make the sequential reuse safe).
1770 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u1, n, &
1771 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1772 gs%local_blk_off, op, .false.)
1773 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u1, n, &
1774 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1775 gs%local_blk_off, .false., c_null_ptr)
1776 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u2, n, &
1777 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1778 gs%local_blk_off, op, .false.)
1779 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u2, n, &
1780 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1781 gs%local_blk_off, .false., c_null_ptr)
1782 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u3, n, &
1783 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1784 gs%local_blk_off, op, .false.)
1785 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u3, n, &
1786 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1787 gs%local_blk_off, .false., c_null_ptr)
1788
1789 ! Wait for the fused exchange and scatter each component back.
1790 if (pe_size .gt. 1 .and. n .gt. 0) then
1791 call gs%comm%nbwait_vec(gs%shared_gs_v, l, nc, op, &
1792 gs%bcknd%gs_stream)
1793 call gs%bcknd%scatter(gs%shared_gs_v(1), l, gs%shared_dof_gs, u1, &
1794 n, gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1795 gs%shared_blk_off, .true., scatter_event)
1796 call gs%bcknd%scatter(gs%shared_gs_v(l + 1), l, gs%shared_dof_gs, &
1797 u2, n, gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1798 gs%shared_blk_off, .true., scatter_event)
1799 call gs%bcknd%scatter(gs%shared_gs_v(2*l + 1), l, &
1800 gs%shared_dof_gs, u3, n, gs%shared_gs_dof, gs%nshared_blks, &
1801 gs%shared_blk_len, gs%shared_blk_off, .true., scatter_event)
1802 end if
1803
1804 call profiler_end_region("gather_scatter", 5)
1805 !$omp end parallel
1806
1807 else
1808
1809 call gs_op_r3_device(gs, u1, u2, u3, n, op, nc, lo, so, m, l, tid, &
1810 scatter_event)
1811
1812 end if
1813
1814 end subroutine gs_op_vector3
1815
1825 subroutine gs_op_r3_device(gs, u1, u2, u3, n, op, nc, lo, so, m, l, tid, &
1826 scatter_event)
1827 class(gs_t), intent(inout) :: gs
1828 integer, intent(in) :: n, op, nc, lo, so, m, l, tid
1829 real(kind=rp), dimension(n), intent(inout) :: u1, u2, u3
1830 type(c_ptr), intent(inout) :: scatter_event
1831 type(c_ptr) :: sgs_d, col_d, col_event
1832 integer(c_intptr_t) :: sv_addr, off_bytes
1833 integer(c_size_t) :: colbytes
1834 real(c_rp) :: rp_dummy
1835 logical :: on_host
1836
1837 on_host = .true.
1838 sgs_d = c_null_ptr
1839 select type (b => gs%bcknd)
1840 type is (gs_device_t)
1841 on_host = b%shared_on_host
1842 sgs_d = b%shared_gs_d
1843 end select
1844
1845 sv_addr = transfer(gs%shared_gs_v_d, sv_addr)
1846 colbytes = c_sizeof(rp_dummy) * int(l, c_size_t)
1847 off_bytes = int(l, c_intptr_t) * int(c_sizeof(rp_dummy), c_intptr_t)
1848
1849 ! With a host-mirrored shared buffer, each scatter below issues an
1850 ! asynchronous host-to-device copy of shared_gs; a null event makes the
1851 ! scatter sync so the next column may safely overwrite the host buffer.
1852 ! Device-resident staging is stream-ordered and carries the caller's
1853 ! event.
1854 if (on_host) then
1855 col_event = c_null_ptr
1856 else
1857 col_event = scatter_event
1858 end if
1859
1860 if (pe_size .gt. 1 .and. n .gt. 0) then
1861 call gs%comm%nbrecv_vec(tid, nc)
1862
1863 ! Gather each component into the backend's shared buffer, then stage
1864 ! it into its column of shared_gs_v.
1865 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u1, n, &
1866 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1867 gs%shared_blk_off, op, .true.)
1868 if (on_host) then
1869 ! The gather mirrored the shared buffer to the host (synchronous).
1870 gs%shared_gs_v(1:l) = gs%shared_gs(1:l)
1871 else
1872 ! shared_gs_d is created lazily on the first gather.
1873 if (.not. c_associated(sgs_d)) then
1874 select type (b => gs%bcknd)
1875 type is (gs_device_t)
1876 sgs_d = b%shared_gs_d
1877 end select
1878 end if
1879 col_d = transfer(sv_addr, col_d)
1880 call device_memcpy(col_d, sgs_d, colbytes, device_to_device, &
1881 sync = .false., strm = gs%bcknd%gs_stream)
1882 end if
1883
1884 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u2, n, &
1885 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1886 gs%shared_blk_off, op, .true.)
1887 if (on_host) then
1888 gs%shared_gs_v(l + 1:2*l) = gs%shared_gs(1:l)
1889 else
1890 col_d = transfer(sv_addr + off_bytes, col_d)
1891 call device_memcpy(col_d, sgs_d, colbytes, device_to_device, &
1892 sync = .false., strm = gs%bcknd%gs_stream)
1893 end if
1894
1895 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u3, n, &
1896 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1897 gs%shared_blk_off, op, .true.)
1898 if (on_host) then
1899 gs%shared_gs_v(2*l + 1:3*l) = gs%shared_gs(1:l)
1900 else
1901 col_d = transfer(sv_addr + 2_c_intptr_t*off_bytes, col_d)
1902 call device_memcpy(col_d, sgs_d, colbytes, device_to_device, &
1903 sync = .false., strm = gs%bcknd%gs_stream)
1904 ! Re-record the gather event so it covers the column copies above.
1905 ! Comm backends that order their per-peer packing streams on this
1906 ! event (NCCL, NVSHMEM) would otherwise race with the copies; the
1907 ! device MPI backend packs on gs_stream itself and is unaffected.
1908 call device_event_record(gs%bcknd%gather_event, gs%bcknd%gs_stream)
1909 end if
1910
1911 call gs%comm%nbsend_vec(gs%shared_gs_v, l, nc, tid, &
1912 gs%bcknd%gather_event, gs%bcknd%gs_stream)
1913 end if
1914
1915 ! Local gather-scatter per component.
1916 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u1, n, &
1917 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1918 op, .false.)
1919 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u1, n, &
1920 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1921 .false., c_null_ptr)
1922 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u2, n, &
1923 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1924 op, .false.)
1925 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u2, n, &
1926 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1927 .false., c_null_ptr)
1928 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u3, n, &
1929 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1930 op, .false.)
1931 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u3, n, &
1932 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1933 .false., c_null_ptr)
1934
1935 ! Wait for the fused exchange (reduces into shared_gs_v), then stage each
1936 ! column back into the shared buffer and scatter.
1937 if (pe_size .gt. 1 .and. n .gt. 0) then
1938 call gs%comm%nbwait_vec(gs%shared_gs_v, l, nc, op, gs%bcknd%gs_stream)
1939
1940 if (on_host) then
1941 gs%shared_gs(1:l) = gs%shared_gs_v(1:l)
1942 else
1943 col_d = transfer(sv_addr, col_d)
1944 call device_memcpy(sgs_d, col_d, colbytes, device_to_device, &
1945 sync = .false., strm = gs%bcknd%gs_stream)
1946 end if
1947 call gs%bcknd%scatter(gs%shared_gs, l, gs%shared_dof_gs, u1, n, &
1948 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1949 gs%shared_blk_off, .true., col_event)
1950
1951 if (on_host) then
1952 gs%shared_gs(1:l) = gs%shared_gs_v(l + 1:2*l)
1953 else
1954 col_d = transfer(sv_addr + off_bytes, col_d)
1955 call device_memcpy(sgs_d, col_d, colbytes, device_to_device, &
1956 sync = .false., strm = gs%bcknd%gs_stream)
1957 end if
1958 call gs%bcknd%scatter(gs%shared_gs, l, gs%shared_dof_gs, u2, n, &
1959 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1960 gs%shared_blk_off, .true., col_event)
1961
1962 if (on_host) then
1963 gs%shared_gs(1:l) = gs%shared_gs_v(2*l + 1:3*l)
1964 else
1965 col_d = transfer(sv_addr + 2_c_intptr_t*off_bytes, col_d)
1966 call device_memcpy(sgs_d, col_d, colbytes, device_to_device, &
1967 sync = .false., strm = gs%bcknd%gs_stream)
1968 end if
1969 call gs%bcknd%scatter(gs%shared_gs, l, gs%shared_dof_gs, u3, n, &
1970 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1971 gs%shared_blk_off, .true., col_event)
1972 end if
1973
1974 end subroutine gs_op_r3_device
1975
1976end module gather_scatter
recursive subroutine gs_qsort_dofmap(dg, gd, n, lo, hi)
Sort the dof lists based on the dof to gather-scatter list.
subroutine gs_find_blks(dg, blk_len, blk_off, nblks, n, m)
Find blocks sharing dofs in non-facet data.
integer function gs_mapping_add_dof(map_, dof, max_id)
Register a unique dof Takes the unique id dof and checks if it is in the htable map_ If it is we retu...
Deassociate a Fortran array from a device pointer.
Definition device.F90:107
Return the device pointer for an associated Fortran array.
Definition device.F90:113
Map a Fortran array to a device (allocate and associate)
Definition device.F90:83
Copy data between host and device (or device and device)
Definition device.F90:72
Synchronize a device or stream.
Definition device.F90:119
Unmap a Fortran array from a device (deassociate and free)
Definition device.F90:89
Definition comm.F90:1
integer, public pe_size
MPI size of communicator.
Definition comm.F90:62
integer, public pe_rank
MPI rank.
Definition comm.F90:59
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
Crystal router: scalable all-to-some personalized exchange.
subroutine, public crystal_router_transfer(buf, n)
Route packed records to their destination ranks.
subroutine, public crystal_router_pack(out, dest, body)
Append one record to a packed crystal-router buffer.
Device abstraction, common interface for various accelerators.
Definition device.F90:34
subroutine, public device_event_record(event, stream)
Record a device event.
Definition device.F90:1644
integer, parameter, public device_to_device
Definition device.F90:48
integer, parameter, public host_to_device
Definition device.F90:48
Defines a mapping of the degrees of freedom.
Definition dofmap.f90:35
Defines a field.
Definition field.f90:34
Gather-scatter.
subroutine gs_init_mapping(gs)
Setup mapping of dofs to gather-scatter operations.
subroutine gs_op_r3_device(gs, u1, u2, u3, n, op, nc, lo, so, m, l, tid, scatter_event)
Device path for the fused 3-component gs. The device backend's shared gather/scatter always operate o...
subroutine gs_schedule(gs)
Schedule shared gather-scatter operations.
subroutine gs_free(gs)
Deallocate a gather-scatter kernel.
subroutine gs_op_r4(gs, u, n, op, event)
Gather-scatter operation on a rank 4 array.
subroutine gs_op_vector3(gs, u1, u2, u3, n, op, event)
Gather-scatter operation on a 3-component vector (u1, u2, u3) with op op. When the comm backend suppo...
subroutine gs_init(gs, dofmap, bcknd, comm_bcknd)
Initialize a gather-scatter kernel.
subroutine gs_sort_i8(a, ind, n)
Heap sort for 64-bit integer arrays, returning the permutation ind. Local helper for gs_schedule (the...
subroutine gs_op_vector(gs, u, n, op, event)
Gather-scatter operation on a vector u with op op.
subroutine gs_op_r3(gs, u1, u2, u3, n, op, event)
Gather-scatter operation on a 3-component vector of rank-4 arrays (u1, u2, u3) with op op; see gs_op_...
subroutine gs_op_fld(gs, u, op, event)
Gather-scatter operation on a field u with op op.
Defines a gather-scatter backend.
Definition gs_bcknd.f90:34
integer, parameter, public gs_bcknd_cpu
Definition gs_bcknd.f90:40
integer, parameter, public gs_bcknd_sx
Definition gs_bcknd.f90:40
integer, parameter, public gs_bcknd_dev
Definition gs_bcknd.f90:40
Defines Coarray Fortran gather-scatter communication.
Definition gs_caf.F90:34
Defines a gather-scatter communication method.
Definition gs_comm.f90:34
integer, parameter, public gs_vec_nc
Maximum number of components handled by the fused vector (multi-component) halo exchange used by gs_o...
Definition gs_comm.f90:49
integer, parameter, public gs_comm_mpigpu
Definition gs_comm.f90:43
integer, parameter, public gs_comm_neighbour
Definition gs_comm.f90:43
integer, parameter, public gs_comm_mpi
Definition gs_comm.f90:43
integer, parameter, public gs_comm_nvshmem
Definition gs_comm.f90:43
integer, parameter, public gs_comm_openshmem
Definition gs_comm.f90:43
integer, parameter, public gs_comm_utofu
Definition gs_comm.f90:43
integer, parameter, public gs_comm_nccl
Definition gs_comm.f90:43
integer, parameter, public gs_comm_caf
Definition gs_comm.f90:43
Generic Gather-scatter backend for CPUs.
Definition gs_cpu.f90:34
Defines GPU aware MPI gather-scatter communication.
Defines NCCL based gather-scatter communication.
Defines GPU aware MPI gather-scatter communication.
Generic Gather-scatter backend for accelerators.
Definition gs_device.F90:34
Defines MPI gather-scatter communication.
Definition gs_mpi.f90:34
Defines gather-scatter communication using MPI neighbourhood collectives.
Defines Gather-scatter operations.
Definition gs_ops.f90:34
integer, parameter, public gs_op_add
Definition gs_ops.f90:36
integer, parameter, public gs_op_max
Definition gs_ops.f90:36
integer, parameter, public gs_op_min
Definition gs_ops.f90:36
integer, parameter, public gs_op_mul
Definition gs_ops.f90:36
Defines OpenSHMEM gather-scatter communication.
Definition gs_shmem.F90:34
Generic Gather-scatter backend for NEC Vector Engines.
Definition gs_sx.f90:34
Defines a gather-scatter backend using the native Tofu interconnect (uTofu). Each rank registers its ...
Definition gs_utofu.F90:43
Implements a hash table ADT.
Definition htable.f90:52
Logging routines.
Definition log.f90:34
type(log_t), public neko_log
Global log stream.
Definition log.f90:80
integer, parameter, public log_size
Definition log.f90:46
Definition math.f90:60
Defines a mesh.
Definition mesh.f90:34
Build configurations.
integer, parameter neko_bcknd_sx
integer, parameter neko_bcknd_hip
integer, parameter neko_bcknd_device
integer, parameter neko_bcknd_opencl
logical, parameter neko_device_mpi
integer, parameter neko_bcknd_cuda
integer, parameter neko_bcknd_metal
integer, parameter, public i2
Definition num_types.f90:5
integer, parameter, public i8
Definition num_types.f90:7
integer, parameter, public dp
Definition num_types.f90:9
integer, parameter, public c_rp
Definition num_types.f90:13
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Profiling interface.
Definition profiler.F90:34
subroutine, public profiler_start_region(name, region_id)
Started a named (name) profiler region.
Definition profiler.F90:79
subroutine, public profiler_end_region(name, region_id)
End the most recently started profiler region.
Definition profiler.F90:116
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
pure integer function, public linear_index(i, j, k, l, lx, ly, lz)
Compute the address of a (i,j,k,l) array with sizes (1:lx, 1:ly, 1:lz, :)
Definition utils.f90:289
Gather-scatter kernel.
Gather-scatter backend.
Definition gs_bcknd.f90:44
Gather-scatter communication using Coarray Fortran (F2008). Each image puts directly into the (module...
Definition gs_caf.F90:129
Gather-scatter communication method.
Definition gs_comm.f90:52
Gather-scatter backend for CPUs.
Definition gs_cpu.f90:43
Gather-scatter backend for offloading devices.
Definition gs_device.F90:48
Gather-scatter communication using device MPI. The arrays are indexed per PE like send_pe and @ recv_...
Gather-scatter communication using NCCL The arrays are indexed per PE like send_pe and @ recv_pe.
Gather-scatter communication using device SHMEM. The arrays are indexed per PE like send_pe and @ rec...
Gather-scatter communication using MPI.
Definition gs_mpi.f90:49
Gather-scatter communication using an MPI neighbourhood collective. The whole halo exchange is carrie...
Gather-scatter communication using OpenSHMEM one-sided puts with per-rank signaling for completion (O...
Definition gs_shmem.F90:91
Gather-scatter backend for NEC SX-Aurora.
Definition gs_sx.f90:43
Gather-scatter communication using one-sided uTofu puts.
Definition gs_utofu.F90:67
Integer*8 based hash table.
Definition htable.f90:112
Iterator for an integer*8 based hash table.
Definition htable.f90:195
Integer based stack.
Definition stack.f90:77
Integer*8 based stack.
Definition stack.f90:84
#define max(a, b)
Definition tensor.cu:40