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