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