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