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
616 subroutine gs_vec_alloc(gs)
617 class(gs_t), intent(inout) :: gs
618
619 if (.not. allocated(gs%shared_gs_v)) then
620 allocate(gs%shared_gs_v(max(1, gs_vec_nc * gs%nshared)))
621 if (neko_bcknd_device .eq. 1) then
622 call device_map(gs%shared_gs_v, gs%shared_gs_v_d, &
623 max(1, gs_vec_nc * gs%nshared))
624 end if
625 end if
626
627 if (.not. gs%comm%vec_ready) then
628 call gs%comm%init_vec()
629 gs%comm%vec_ready = .true.
630 end if
631
632 end subroutine gs_vec_alloc
633
635 subroutine gs_init_mapping(gs)
636 type(gs_t), target, intent(inout) :: gs
637 type(mesh_t), pointer :: msh
638 type(dofmap_t), pointer :: dofmap
639 type(stack_i4_t), target :: local_dof, dof_local, shared_dof, dof_shared
640 type(stack_i4_t), target :: local_face_dof, face_dof_local
641 type(stack_i4_t), target :: shared_face_dof, face_dof_shared
642 integer :: i, j, k, l, lx, ly, lz, max_id, max_sid, id, lid, dm_size
643 integer :: sdm_size
644 type(htable_i8_t) :: dm
645 type(htable_i8_t), pointer :: sdm
646
647 dofmap => gs%dofmap
648 msh => dofmap%msh
649 sdm => gs%shared_dofs
650
651 lx = dofmap%Xh%lx
652 ly = dofmap%Xh%ly
653 lz = dofmap%Xh%lz
654 dm_size = dofmap%size()/lx
655
656 ! The shared dof table only ever receives dofs the dofmap flagged as
657 ! shared, i.e. the partition surface, which is a small fraction of the
658 ! dofmap.
659 sdm_size = min(count(dofmap%shared_dof), dofmap%size() / 2) * 2
660
661 call dm%init(dm_size, i)
665 call sdm%init(max(sdm_size, 1), i)
666
667
668 call local_dof%init()
669 call dof_local%init()
670
671 call local_face_dof%init()
672 call face_dof_local%init()
673
674 call shared_dof%init()
675 call dof_shared%init()
676
677 call shared_face_dof%init()
678 call face_dof_shared%init()
679
680 !
681 ! Setup mapping for dofs points
682 !
683
684 max_id = 0
685 max_sid = 0
686 do i = 1, msh%nelv
687 ! Local id of vertices
688 lid = linear_index(1, 1, 1, i, lx, ly, lz)
689 ! Check if this dof is shared among ranks or not
690 if (dofmap%shared_dof(1, 1, 1, i)) then
691 id = gs_mapping_add_dof(sdm, dofmap%dof(1, 1, 1, i), max_sid)
692 !If add unique gather-scatter id to shared_dof stack
693 call shared_dof%push(id)
694 !If add local id to dof_shared stack
695 call dof_shared%push(lid)
696 !Now we have the mapping of local id <-> gather scatter id!
697 else
698 ! Same here, only here we know the point is local
699 ! It will as such not need to be sent to other ranks later
700 id = gs_mapping_add_dof(dm, dofmap%dof(1, 1, 1, i), max_id)
701 call local_dof%push(id)
702 call dof_local%push(lid)
703 end if
704 ! This procedure is then repeated for all vertices and edges
705 ! Facets can be treated a little bit differently since they only have one
706 ! neighbor
707
708 lid = linear_index(lx, 1, 1, i, lx, ly, lz)
709 if (dofmap%shared_dof(lx, 1, 1, i)) then
710 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, 1, 1, i), max_sid)
711 call shared_dof%push(id)
712 call dof_shared%push(lid)
713 else
714 id = gs_mapping_add_dof(dm, dofmap%dof(lx, 1, 1, i), max_id)
715 call local_dof%push(id)
716 call dof_local%push(lid)
717 end if
718
719 lid = linear_index(1, ly, 1, i, lx, ly, lz)
720 if (dofmap%shared_dof(1, ly, 1, i)) then
721 id = gs_mapping_add_dof(sdm, dofmap%dof(1, ly, 1, i), max_sid)
722 call shared_dof%push(id)
723 call dof_shared%push(lid)
724 else
725 id = gs_mapping_add_dof(dm, dofmap%dof(1, ly, 1, i), max_id)
726 call local_dof%push(id)
727 call dof_local%push(lid)
728 end if
729
730 lid = linear_index(lx, ly, 1, i, lx, ly, lz)
731 if (dofmap%shared_dof(lx, ly, 1, i)) then
732 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, ly, 1, i), max_sid)
733 call shared_dof%push(id)
734 call dof_shared%push(lid)
735 else
736 id = gs_mapping_add_dof(dm, dofmap%dof(lx, ly, 1, i), max_id)
737 call local_dof%push(id)
738 call dof_local%push(lid)
739 end if
740 if (lz .gt. 1) then
741 lid = linear_index(1, 1, lz, i, lx, ly, lz)
742 if (dofmap%shared_dof(1, 1, lz, i)) then
743 id = gs_mapping_add_dof(sdm, dofmap%dof(1, 1, lz, i), max_sid)
744 call shared_dof%push(id)
745 call dof_shared%push(lid)
746 else
747 id = gs_mapping_add_dof(dm, dofmap%dof(1, 1, lz, i), max_id)
748 call local_dof%push(id)
749 call dof_local%push(lid)
750 end if
751
752 lid = linear_index(lx, 1, lz, i, lx, ly, lz)
753 if (dofmap%shared_dof(lx, 1, lz, i)) then
754 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, 1, lz, i), max_sid)
755 call shared_dof%push(id)
756 call dof_shared%push(lid)
757 else
758 id = gs_mapping_add_dof(dm, dofmap%dof(lx, 1, lz, i), max_id)
759 call local_dof%push(id)
760 call dof_local%push(lid)
761 end if
762
763 lid = linear_index(1, ly, lz, i, lx, ly, lz)
764 if (dofmap%shared_dof(1, ly, lz, i)) then
765 id = gs_mapping_add_dof(sdm, dofmap%dof(1, ly, lz, i), max_sid)
766 call shared_dof%push(id)
767 call dof_shared%push(lid)
768 else
769 id = gs_mapping_add_dof(dm, dofmap%dof(1, ly, lz, i), max_id)
770 call local_dof%push(id)
771 call dof_local%push(lid)
772 end if
773
774 lid = linear_index(lx, ly, lz, i, lx, ly, lz)
775 if (dofmap%shared_dof(lx, ly, lz, i)) then
776 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, ly, lz, i), max_sid)
777 call shared_dof%push(id)
778 call dof_shared%push(lid)
779 else
780 id = gs_mapping_add_dof(dm, dofmap%dof(lx, ly, lz, i), max_id)
781 call local_dof%push(id)
782 call dof_local%push(lid)
783 end if
784 end if
785 end do
786
787 ! Clear local dofmap table
788 call dm%clear()
789 ! Get gather scatter ids and local ids of edges
790 if (lz .gt. 1) then
791 !
792 ! Setup mapping for dofs on edges
793 !
794 do i = 1, msh%nelv
795
796 !
797 ! dofs on edges in x-direction
798 !
799 if (dofmap%shared_dof(2, 1, 1, i)) then
800 do j = 2, lx - 1
801 id = gs_mapping_add_dof(sdm, dofmap%dof(j, 1, 1, i), max_sid)
802 call shared_dof%push(id)
803 id = linear_index(j, 1, 1, i, lx, ly, lz)
804 call dof_shared%push(id)
805 end do
806 else
807 do j = 2, lx - 1
808 id = gs_mapping_add_dof(dm, dofmap%dof(j, 1, 1, i), max_id)
809 call local_dof%push(id)
810 id = linear_index(j, 1, 1, i, lx, ly, lz)
811 call dof_local%push(id)
812 end do
813 end if
814 if (dofmap%shared_dof(2, 1, lz, i)) then
815 do j = 2, lx - 1
816 id = gs_mapping_add_dof(sdm, dofmap%dof(j, 1, lz, i), max_sid)
817 call shared_dof%push(id)
818 id = linear_index(j, 1, 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, 1, lz, i), max_id)
824 call local_dof%push(id)
825 id = linear_index(j, 1, lz, i, lx, ly, lz)
826 call dof_local%push(id)
827 end do
828 end if
829
830 if (dofmap%shared_dof(2, ly, 1, i)) then
831 do j = 2, lx - 1
832 id = gs_mapping_add_dof(sdm, dofmap%dof(j, ly, 1, i), max_sid)
833 call shared_dof%push(id)
834 id = linear_index(j, ly, 1, i, lx, ly, lz)
835 call dof_shared%push(id)
836 end do
837
838 else
839 do j = 2, lx - 1
840 id = gs_mapping_add_dof(dm, dofmap%dof(j, ly, 1, i), max_id)
841 call local_dof%push(id)
842 id = linear_index(j, ly, 1, i, lx, ly, lz)
843 call dof_local%push(id)
844 end do
845 end if
846 if (dofmap%shared_dof(2, ly, lz, i)) then
847 do j = 2, lx - 1
848 id = gs_mapping_add_dof(sdm, dofmap%dof(j, ly, lz, i), max_sid)
849 call shared_dof%push(id)
850 id = linear_index(j, ly, lz, i, lx, ly, lz)
851 call dof_shared%push(id)
852 end do
853 else
854 do j = 2, lx - 1
855 id = gs_mapping_add_dof(dm, dofmap%dof(j, ly, lz, i), max_id)
856 call local_dof%push(id)
857 id = linear_index(j, ly, lz, i, lx, ly, lz)
858 call dof_local%push(id)
859 end do
860 end if
861
862 !
863 ! dofs on edges in y-direction
864 !
865 if (dofmap%shared_dof(1, 2, 1, i)) then
866 do k = 2, ly - 1
867 id = gs_mapping_add_dof(sdm, dofmap%dof(1, k, 1, i), max_sid)
868 call shared_dof%push(id)
869 id = linear_index(1, k, 1, i, lx, ly, lz)
870 call dof_shared%push(id)
871 end do
872 else
873 do k = 2, ly - 1
874 id = gs_mapping_add_dof(dm, dofmap%dof(1, k, 1, i), max_id)
875 call local_dof%push(id)
876 id = linear_index(1, k, 1, i, lx, ly, lz)
877 call dof_local%push(id)
878 end do
879 end if
880 if (dofmap%shared_dof(1, 2, lz, i)) then
881 do k = 2, ly - 1
882 id = gs_mapping_add_dof(sdm, dofmap%dof(1, k, lz, i), max_sid)
883 call shared_dof%push(id)
884 id = linear_index(1, k, lz, i, lx, ly, lz)
885 call dof_shared%push(id)
886 end do
887 else
888 do k = 2, ly - 1
889 id = gs_mapping_add_dof(dm, dofmap%dof(1, k, lz, i), max_id)
890 call local_dof%push(id)
891 id = linear_index(1, k, lz, i, lx, ly, lz)
892 call dof_local%push(id)
893 end do
894 end if
895
896 if (dofmap%shared_dof(lx, 2, 1, i)) then
897 do k = 2, ly - 1
898 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, k, 1, i), max_sid)
899 call shared_dof%push(id)
900 id = linear_index(lx, k, 1, i, lx, ly, lz)
901 call dof_shared%push(id)
902 end do
903 else
904 do k = 2, ly - 1
905 id = gs_mapping_add_dof(dm, dofmap%dof(lx, k, 1, i), max_id)
906 call local_dof%push(id)
907 id = linear_index(lx, k, 1, i, lx, ly, lz)
908 call dof_local%push(id)
909 end do
910 end if
911 if (dofmap%shared_dof(lx, 2, lz, i)) then
912 do k = 2, ly - 1
913 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, k, lz, i), max_sid)
914 call shared_dof%push(id)
915 id = linear_index(lx, k, lz, i, lx, ly, lz)
916 call dof_shared%push(id)
917 end do
918 else
919 do k = 2, ly - 1
920 id = gs_mapping_add_dof(dm, dofmap%dof(lx, k, lz, i), max_id)
921 call local_dof%push(id)
922 id = linear_index(lx, k, lz, i, lx, ly, lz)
923 call dof_local%push(id)
924 end do
925 end if
926 !
927 ! dofs on edges in z-direction
928 !
929 if (dofmap%shared_dof(1, 1, 2, i)) then
930 do l = 2, lz - 1
931 id = gs_mapping_add_dof(sdm, dofmap%dof(1, 1, l, i), max_sid)
932 call shared_dof%push(id)
933 id = linear_index(1, 1, 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, 1, l, i), max_id)
939 call local_dof%push(id)
940 id = linear_index(1, 1, l, i, lx, ly, lz)
941 call dof_local%push(id)
942 end do
943 end if
944
945 if (dofmap%shared_dof(lx, 1, 2, i)) then
946 do l = 2, lz - 1
947 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, 1, l, i), max_sid)
948 call shared_dof%push(id)
949 id = linear_index(lx, 1, 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, 1, l, i), max_id)
955 call local_dof%push(id)
956 id = linear_index(lx, 1, l, i, lx, ly, lz)
957 call dof_local%push(id)
958 end do
959 end if
960
961 if (dofmap%shared_dof(1, ly, 2, i)) then
962 do l = 2, lz - 1
963 id = gs_mapping_add_dof(sdm, dofmap%dof(1, ly, l, i), max_sid)
964 call shared_dof%push(id)
965 id = linear_index(1, ly, l, i, lx, ly, lz)
966 call dof_shared%push(id)
967 end do
968 else
969 do l = 2, lz - 1
970 id = gs_mapping_add_dof(dm, dofmap%dof(1, ly, l, i), max_id)
971 call local_dof%push(id)
972 id = linear_index(1, ly, l, i, lx, ly, lz)
973 call dof_local%push(id)
974 end do
975 end if
976
977 if (dofmap%shared_dof(lx, ly, 2, i)) then
978 do l = 2, lz - 1
979 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, ly, l, i), max_sid)
980 call shared_dof%push(id)
981 id = linear_index(lx, ly, l, i, lx, ly, lz)
982 call dof_shared%push(id)
983 end do
984 else
985 do l = 2, lz - 1
986 id = gs_mapping_add_dof(dm, dofmap%dof(lx, ly, l, i), max_id)
987 call local_dof%push(id)
988 id = linear_index(lx, ly, l, i, lx, ly, lz)
989 call dof_local%push(id)
990 end do
991 end if
992 end do
993 end if
994
995 ! Clear local dofmap table
996 call dm%clear()
997
998 !
999 ! Setup mapping for dofs on facets
1000 !
1001 ! This is for 2d
1002 if (lz .eq. 1) then
1003 do i = 1, msh%nelv
1004
1005 !
1006 ! dofs on edges in x-direction
1007 !
1008 if (msh%facet_neigh(3, i) .ne. 0) then
1009 if (dofmap%shared_dof(2, 1, 1, i)) then
1010 do j = 2, lx - 1
1011 id = gs_mapping_add_dof(sdm, dofmap%dof(j, 1, 1, i), max_sid)
1012 call shared_face_dof%push(id)
1013 id = linear_index(j, 1, 1, i, lx, ly, lz)
1014 call face_dof_shared%push(id)
1015 end do
1016 else
1017 do j = 2, lx - 1
1018 id = gs_mapping_add_dof(dm, dofmap%dof(j, 1, 1, i), max_id)
1019 call local_face_dof%push(id)
1020 id = linear_index(j, 1, 1, i, lx, ly, lz)
1021 call face_dof_local%push(id)
1022 end do
1023 end if
1024 end if
1025
1026 if (msh%facet_neigh(4, i) .ne. 0) then
1027 if (dofmap%shared_dof(2, ly, 1, i)) then
1028 do j = 2, lx - 1
1029 id = gs_mapping_add_dof(sdm, dofmap%dof(j, ly, 1, i), &
1030 max_sid)
1031 call shared_face_dof%push(id)
1032 id = linear_index(j, ly, 1, i, lx, ly, lz)
1033 call face_dof_shared%push(id)
1034 end do
1035
1036 else
1037 do j = 2, lx - 1
1038 id = gs_mapping_add_dof(dm, dofmap%dof(j, ly, 1, i), &
1039 max_id)
1040 call local_face_dof%push(id)
1041 id = linear_index(j, ly, 1, i, lx, ly, lz)
1042 call face_dof_local%push(id)
1043 end do
1044 end if
1045 end if
1046
1047 !
1048 ! dofs on edges in y-direction
1049 !
1050 if (msh%facet_neigh(1, i) .ne. 0) then
1051 if (dofmap%shared_dof(1, 2, 1, i)) then
1052 do k = 2, ly - 1
1053 id = gs_mapping_add_dof(sdm, dofmap%dof(1, k, 1, i), max_sid)
1054 call shared_face_dof%push(id)
1055 id = linear_index(1, k, 1, i, lx, ly, lz)
1056 call face_dof_shared%push(id)
1057 end do
1058 else
1059 do k = 2, ly - 1
1060 id = gs_mapping_add_dof(dm, dofmap%dof(1, k, 1, i), max_id)
1061 call local_face_dof%push(id)
1062 id = linear_index(1, k, 1, i, lx, ly, lz)
1063 call face_dof_local%push(id)
1064 end do
1065 end if
1066 end if
1067
1068 if (msh%facet_neigh(2, i) .ne. 0) then
1069 if (dofmap%shared_dof(lx, 2, 1, i)) then
1070 do k = 2, ly - 1
1071 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, k, 1, i), &
1072 max_sid)
1073 call shared_face_dof%push(id)
1074 id = linear_index(lx, k, 1, i, lx, ly, lz)
1075 call face_dof_shared%push(id)
1076 end do
1077 else
1078 do k = 2, ly - 1
1079 id = gs_mapping_add_dof(dm, dofmap%dof(lx, k, 1, i), &
1080 max_id)
1081 call local_face_dof%push(id)
1082 id = linear_index(lx, k, 1, i, lx, ly, lz)
1083 call face_dof_local%push(id)
1084 end do
1085 end if
1086 end if
1087 end do
1088 else
1089 do i = 1, msh%nelv
1090
1091 ! Facets in x-direction (s, t)-plane
1092 if (msh%facet_neigh(1, i) .ne. 0) then
1093 if (dofmap%shared_dof(1, 2, 2, i)) then
1094 do l = 2, lz - 1
1095 do k = 2, ly - 1
1096 id = gs_mapping_add_dof(sdm, dofmap%dof(1, k, l, i), &
1097 max_sid)
1098 call shared_face_dof%push(id)
1099 id = linear_index(1, k, l, i, lx, ly, lz)
1100 call face_dof_shared%push(id)
1101 end do
1102 end do
1103 else
1104 do l = 2, lz - 1
1105 do k = 2, ly - 1
1106 id = gs_mapping_add_dof(dm, dofmap%dof(1, k, l, i), &
1107 max_id)
1108 call local_face_dof%push(id)
1109 id = linear_index(1, k, l, i, lx, ly, lz)
1110 call face_dof_local%push(id)
1111 end do
1112 end do
1113 end if
1114 end if
1115
1116 if (msh%facet_neigh(2, i) .ne. 0) then
1117 if (dofmap%shared_dof(lx, 2, 2, i)) then
1118 do l = 2, lz - 1
1119 do k = 2, ly - 1
1120 id = gs_mapping_add_dof(sdm, dofmap%dof(lx, k, l, i), &
1121 max_sid)
1122 call shared_face_dof%push(id)
1123 id = linear_index(lx, k, l, i, lx, ly, lz)
1124 call face_dof_shared%push(id)
1125 end do
1126 end do
1127 else
1128 do l = 2, lz - 1
1129 do k = 2, ly - 1
1130 id = gs_mapping_add_dof(dm, dofmap%dof(lx, k, l, i), &
1131 max_id)
1132 call local_face_dof%push(id)
1133 id = linear_index(lx, k, l, i, lx, ly, lz)
1134 call face_dof_local%push(id)
1135 end do
1136 end do
1137 end if
1138 end if
1139
1140 ! Facets in y-direction (r, t)-plane
1141 if (msh%facet_neigh(3, i) .ne. 0) then
1142 if (dofmap%shared_dof(2, 1, 2, i)) then
1143 do l = 2, lz - 1
1144 do j = 2, lx - 1
1145 id = gs_mapping_add_dof(sdm, dofmap%dof(j, 1, l, i), &
1146 max_sid)
1147 call shared_face_dof%push(id)
1148 id = linear_index(j, 1, l, i, lx, ly, lz)
1149 call face_dof_shared%push(id)
1150 end do
1151 end do
1152 else
1153 do l = 2, lz - 1
1154 do j = 2, lx - 1
1155 id = gs_mapping_add_dof(dm, dofmap%dof(j, 1, l, i), &
1156 max_id)
1157 call local_face_dof%push(id)
1158 id = linear_index(j, 1, l, i, lx, ly, lz)
1159 call face_dof_local%push(id)
1160 end do
1161 end do
1162 end if
1163 end if
1164
1165 if (msh%facet_neigh(4, i) .ne. 0) then
1166 if (dofmap%shared_dof(2, ly, 2, i)) then
1167 do l = 2, lz - 1
1168 do j = 2, lx - 1
1169 id = gs_mapping_add_dof(sdm, dofmap%dof(j, ly, l, i), &
1170 max_sid)
1171 call shared_face_dof%push(id)
1172 id = linear_index(j, ly, l, i, lx, ly, lz)
1173 call face_dof_shared%push(id)
1174 end do
1175 end do
1176 else
1177 do l = 2, lz - 1
1178 do j = 2, lx - 1
1179 id = gs_mapping_add_dof(dm, dofmap%dof(j, ly, l, i), &
1180 max_id)
1181 call local_face_dof%push(id)
1182 id = linear_index(j, ly, l, i, lx, ly, lz)
1183 call face_dof_local%push(id)
1184 end do
1185 end do
1186 end if
1187 end if
1188
1189 ! Facets in z-direction (r, s)-plane
1190 if (msh%facet_neigh(5, i) .ne. 0) then
1191 if (dofmap%shared_dof(2, 2, 1, i)) then
1192 do k = 2, ly - 1
1193 do j = 2, lx - 1
1194 id = gs_mapping_add_dof(sdm, dofmap%dof(j, k, 1, i), &
1195 max_sid)
1196 call shared_face_dof%push(id)
1197 id = linear_index(j, k, 1, i, lx, ly, lz)
1198 call face_dof_shared%push(id)
1199 end do
1200 end do
1201 else
1202 do k = 2, ly - 1
1203 do j = 2, lx - 1
1204 id = gs_mapping_add_dof(dm, dofmap%dof(j, k, 1, i), &
1205 max_id)
1206 call local_face_dof%push(id)
1207 id = linear_index(j, k, 1, i, lx, ly, lz)
1208 call face_dof_local%push(id)
1209 end do
1210 end do
1211 end if
1212 end if
1213
1214 if (msh%facet_neigh(6, i) .ne. 0) then
1215 if (dofmap%shared_dof(2, 2, lz, i)) then
1216 do k = 2, ly - 1
1217 do j = 2, lx - 1
1218 id = gs_mapping_add_dof(sdm, dofmap%dof(j, k, lz, i), &
1219 max_sid)
1220 call shared_face_dof%push(id)
1221 id = linear_index(j, k, lz, i, lx, ly, lz)
1222 call face_dof_shared%push(id)
1223 end do
1224 end do
1225 else
1226 do k = 2, ly - 1
1227 do j = 2, lx - 1
1228 id = gs_mapping_add_dof(dm, dofmap%dof(j, k, lz, i), &
1229 max_id)
1230 call local_face_dof%push(id)
1231 id = linear_index(j, k, lz, i, lx, ly, lz)
1232 call face_dof_local%push(id)
1233 end do
1234 end do
1235 end if
1236 end if
1237 end do
1238 end if
1239
1240
1241 call dm%free()
1242
1243 gs%nlocal = local_dof%size() + local_face_dof%size()
1244 gs%local_facet_offset = local_dof%size() + 1
1245
1246 ! Finalize local dof to gather-scatter index
1247 allocate(gs%local_dof_gs(gs%nlocal))
1248
1249 ! Add dofs on points and edges
1250
1251 ! We should use the %array() procedure, which works great for
1252 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1253 ! certain data types
1254 select type (dof_array => local_dof%data)
1255 type is (integer)
1256 j = local_dof%size()
1257 do i = 1, j
1258 gs%local_dof_gs(i) = dof_array(i)
1259 end do
1260 end select
1261 call local_dof%free()
1262
1263 ! Add dofs on faces
1264
1265 ! We should use the %array() procedure, which works great for
1266 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1267 ! certain data types
1268 select type (dof_array => local_face_dof%data)
1269 type is (integer)
1270 do i = 1, local_face_dof%size()
1271 gs%local_dof_gs(i + j) = dof_array(i)
1272 end do
1273 end select
1274 call local_face_dof%free()
1275
1276 ! Finalize local gather-scatter index to dof
1277 allocate(gs%local_gs_dof(gs%nlocal))
1278
1279 ! Add gather-scatter index on points and edges
1280
1281 ! We should use the %array() procedure, which works great for
1282 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1283 ! certain data types
1284 select type (dof_array => dof_local%data)
1285 type is (integer)
1286 j = dof_local%size()
1287 do i = 1, j
1288 gs%local_gs_dof(i) = dof_array(i)
1289 end do
1290 end select
1291 call dof_local%free()
1292
1293 ! We should use the %array() procedure, which works great for
1294 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1295 ! certain data types
1296 select type (dof_array => face_dof_local%data)
1297 type is (integer)
1298 do i = 1, face_dof_local%size()
1299 gs%local_gs_dof(i+j) = dof_array(i)
1300 end do
1301 end select
1302 call face_dof_local%free()
1303
1304 call gs_qsort_dofmap(gs%local_dof_gs, gs%local_gs_dof, &
1305 gs%nlocal, 1, gs%nlocal)
1306
1307 call gs_find_blks(gs%local_dof_gs, gs%local_blk_len, &
1308 gs%local_blk_off, gs%nlocal_blks, gs%nlocal, gs%local_facet_offset)
1309
1310 ! Allocate buffer for local gs-ops
1311 allocate(gs%local_gs(gs%nlocal))
1312
1313 gs%nshared = shared_dof%size() + shared_face_dof%size()
1314 gs%shared_facet_offset = shared_dof%size() + 1
1315
1316 ! Finalize shared dof to gather-scatter index
1317 allocate(gs%shared_dof_gs(gs%nshared))
1318
1319 ! Add shared dofs on points and edges
1320
1321 ! We should use the %array() procedure, which works great for
1322 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1323 ! certain data types
1324 select type (dof_array => shared_dof%data)
1325 type is (integer)
1326 j = shared_dof%size()
1327 do i = 1, j
1328 gs%shared_dof_gs(i) = dof_array(i)
1329 end do
1330 end select
1331 call shared_dof%free()
1332
1333 ! Add shared dofs on faces
1334
1335 ! We should use the %array() procedure, which works great for
1336 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1337 ! certain data types
1338 select type (dof_array => shared_face_dof%data)
1339 type is (integer)
1340 do i = 1, shared_face_dof%size()
1341 gs%shared_dof_gs(i + j) = dof_array(i)
1342 end do
1343 end select
1344 call shared_face_dof%free()
1345
1346 ! Finalize shared gather-scatter index to dof
1347 allocate(gs%shared_gs_dof(gs%nshared))
1348
1349 ! Add dofs on points and edges
1350
1351 ! We should use the %array() procedure, which works great for
1352 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1353 ! certain data types
1354 select type (dof_array => dof_shared%data)
1355 type is (integer)
1356 j = dof_shared%size()
1357 do i = 1, j
1358 gs%shared_gs_dof(i) = dof_array(i)
1359 end do
1360 end select
1361 call dof_shared%free()
1362
1363 ! We should use the %array() procedure, which works great for
1364 ! GNU, Intel and NEC, but it breaks horribly on Cray when using
1365 ! certain data types
1366 select type (dof_array => face_dof_shared%data)
1367 type is (integer)
1368 do i = 1, face_dof_shared%size()
1369 gs%shared_gs_dof(i + j) = dof_array(i)
1370 end do
1371 end select
1372 call face_dof_shared%free()
1373
1374 ! Allocate buffer for shared gs-ops
1375 allocate(gs%shared_gs(gs%nshared))
1376
1377 ! The compact multi-component shared buffer for the fused vector gs
1378 ! (gs%shared_gs_v) is allocated on the first gs_op_r3, see gs_vec_alloc.
1379
1380 if (gs%nshared .gt. 0) then
1381 call gs_qsort_dofmap(gs%shared_dof_gs, gs%shared_gs_dof, &
1382 gs%nshared, 1, gs%nshared)
1383
1384 call gs_find_blks(gs%shared_dof_gs, gs%shared_blk_len, &
1385 gs%shared_blk_off, gs%nshared_blks, gs%nshared, &
1386 gs%shared_facet_offset)
1387 end if
1388
1389 contains
1390
1399 function gs_mapping_add_dof(map_, dof, max_id) result(id)
1400 type(htable_i8_t), intent(inout) :: map_
1401 integer(kind=i8), intent(inout) :: dof
1402 integer, intent(inout) :: max_id
1403 integer :: id
1404
1405 if (map_%get(dof, id) .gt. 0) then
1406 max_id = max_id + 1
1407 call map_%set(dof, max_id)
1408 id = max_id
1409 end if
1410
1411 end function gs_mapping_add_dof
1412
1414 recursive subroutine gs_qsort_dofmap(dg, gd, n, lo, hi)
1415 integer, intent(inout) :: n
1416 integer, dimension(n), intent(inout) :: dg
1417 integer, dimension(n), intent(inout) :: gd
1418 integer :: lo, hi
1419 integer :: tmp, i, j, pivot
1420
1421 i = lo - 1
1422 j = hi + 1
1423 pivot = dg((lo + hi) / 2)
1424 do
1425 do
1426 i = i + 1
1427 if (dg(i) .ge. pivot) exit
1428 end do
1429
1430 do
1431 j = j - 1
1432 if (dg(j) .le. pivot) exit
1433 end do
1434
1435 if (i .lt. j) then
1436 tmp = dg(i)
1437 dg(i) = dg(j)
1438 dg(j) = tmp
1439
1440 tmp = gd(i)
1441 gd(i) = gd(j)
1442 gd(j) = tmp
1443 else if (i .eq. j) then
1444 i = i + 1
1445 exit
1446 else
1447 exit
1448 end if
1449 end do
1450 if (lo .lt. j) call gs_qsort_dofmap(dg, gd, n, lo, j)
1451 if (i .lt. hi) call gs_qsort_dofmap(dg, gd, n, i, hi)
1452
1453 end subroutine gs_qsort_dofmap
1454
1456 subroutine gs_find_blks(dg, blk_len, blk_off, nblks, n, m)
1457 integer, intent(in) :: n
1458 integer, intent(in) :: m
1459 integer, dimension(n), intent(inout) :: dg
1460 integer, allocatable, intent(inout) :: blk_len(:)
1461 integer, allocatable, intent(inout) :: blk_off(:)
1462 integer, intent(inout) :: nblks
1463 integer :: i, j
1464 integer :: id, count
1465 type(stack_i4_t), target :: blks
1466
1467 call blks%init()
1468 i = 1
1469 do while (i .lt. m)
1470 id = dg(i)
1471 count = 1
1472 j = i
1473 do while ( j+1 .le. n .and. dg(j+1) .eq. id)
1474 j = j + 1
1475 count = count + 1
1476 end do
1477 call blks%push(count)
1478 i = j + 1
1479 end do
1480
1481 select type (blk_array => blks%data)
1482 type is (integer)
1483 nblks = blks%size()
1484 allocate(blk_len(nblks))
1485 do i = 1, nblks
1486 blk_len(i) = blk_array(i)
1487 end do
1488 allocate(blk_off(nblks))
1489 blk_off(1) = 0
1490 do i = 2, nblks
1491 blk_off(i) = blk_off(i - 1) + blk_len(i - 1)
1492 end do
1493 end select
1494 call blks%free()
1495
1496 end subroutine gs_find_blks
1497
1498 end subroutine gs_init_mapping
1499
1511 subroutine gs_schedule(gs)
1512 type(gs_t), target, intent(inout) :: gs
1513 type(htable_iter_i8_t) :: it
1514 type(stack_i4_t) :: send_pe, recv_pe
1515 type(stack_i8_t) :: cr_buf
1516 integer(i8), allocatable :: buf(:)
1517 integer(i8), pointer :: cr_data(:)
1518 integer(i8), allocatable :: rgid(:), gtmp(:)
1519 integer, allocatable :: rpeer(:), rgsid(:), rperm(:), gperm(:)
1520 integer(i8) :: gid
1521 integer :: i, j, n, owner, nrec, peer, shared_gs_id, tmp
1522 integer :: a, b, cnt, t
1523
1524 call send_pe%init()
1525 call recv_pe%init()
1526
1527 !
1528 ! Phase 1: route every local shared dof to its canonical owner.
1529 ! record = [dest=owner, len=2, gid, origin]
1530 !
1531 call cr_buf%init(max(gs%shared_dofs%num_entries(), 1) * 4)
1532 call it%init(gs%shared_dofs)
1533 do while (it%next())
1534 gid = it%key()
1535 owner = int(modulo(gid, int(pe_size, i8)))
1536 call crystal_router_pack(cr_buf, owner, [gid, int(pe_rank, i8)])
1537 end do
1538
1539 n = cr_buf%size()
1540 allocate(buf(max(n, 1)))
1541 if (n .gt. 0) then
1542 cr_data => cr_buf%array()
1543 buf(1:n) = cr_data(1:n)
1544 end if
1545 call cr_buf%free()
1546
1547 call crystal_router_transfer(buf, n)
1548
1549 !
1550 ! Phase 2: at the owner, group holders by gid and reflect, to each holder,
1551 ! every other holder of the same dof.
1552 ! reply = [dest=holder, len=2, gid, peer]
1553 !
1554 nrec = n / 4 ! every record here has the fixed form [me, 2, gid, origin]
1555 allocate(rgid(max(nrec, 1)), rgsid(max(nrec, 1)), gperm(max(nrec, 1)))
1556 do i = 1, nrec
1557 rgid(i) = buf((i - 1) * 4 + 3) ! gid
1558 rgsid(i) = int(buf((i - 1) * 4 + 4)) ! origin rank (reuse array)
1559 end do
1560 if (nrec .gt. 0) call gs_sort_i8(rgid, gperm, nrec)
1561
1562 call cr_buf%init(max(n, 1))
1563 i = 1
1564 do while (i .le. nrec)
1565 j = i
1566 do while (j .le. nrec)
1567 if (rgid(j) .ne. rgid(i)) exit
1568 j = j + 1
1569 end do
1570 ! Reflect, to each holder, every other holder of this dof.
1571 if (j - i .gt. 1) then
1572 do a = i, j - 1 ! recipient holder
1573 do b = i, j - 1 ! the other holder
1574 if (a .eq. b) cycle
1575 call crystal_router_pack(cr_buf, rgsid(gperm(a)), &
1576 [rgid(i), int(rgsid(gperm(b)), i8)])
1577 end do
1578 end do
1579 end if
1580 i = j
1581 end do
1582 deallocate(rgid, rgsid, gperm)
1583
1584 n = cr_buf%size()
1585 if (allocated(buf)) deallocate(buf)
1586 allocate(buf(max(n, 1)))
1587 if (n .gt. 0) then
1588 cr_data => cr_buf%array()
1589 buf(1:n) = cr_data(1:n)
1590 end if
1591 call cr_buf%free()
1592
1593 call crystal_router_transfer(buf, n)
1594
1595 !
1596 ! Phase 3: register each (dof, peer) for both send and receive. Order each
1597 ! peer's dof list by gid so both ranks of a pair agree on the order.
1598 !
1599 nrec = n / 4 ! replies are [me, 2, gid, peer]
1600 allocate(rgid(max(nrec, 1)), rpeer(max(nrec, 1)), rgsid(max(nrec, 1)), &
1601 rperm(max(nrec, 1)))
1602 do i = 1, nrec
1603 gid = buf((i - 1) * 4 + 3)
1604 rgid(i) = gid
1605 rpeer(i) = int(buf((i - 1) * 4 + 4))
1606 tmp = gs%shared_dofs%get(gid, shared_gs_id)
1607 rgsid(i) = shared_gs_id
1608 end do
1609
1610 ! Sort by peer; within each peer run, sort by gid and register in that order.
1611 if (nrec .gt. 0) call sort(rpeer, rperm, nrec)
1612 a = 1
1613 do while (a .le. nrec)
1614 b = a
1615 do while (b .le. nrec)
1616 if (rpeer(b) .ne. rpeer(a)) exit
1617 b = b + 1
1618 end do
1619 peer = rpeer(a)
1620 cnt = b - a
1621 allocate(gtmp(cnt), gperm(cnt))
1622 do t = 1, cnt
1623 gtmp(t) = rgid(rperm(a + t - 1))
1624 end do
1625 call gs_sort_i8(gtmp, gperm, cnt)
1626 do t = 1, cnt
1627 shared_gs_id = rgsid(rperm(a + gperm(t) - 1))
1628 call gs%comm%send_dof(peer)%push(shared_gs_id)
1629 call gs%comm%recv_dof(peer)%push(shared_gs_id)
1630 end do
1631 deallocate(gtmp, gperm)
1632 call send_pe%push(peer)
1633 call recv_pe%push(peer)
1634 a = b
1635 end do
1636 deallocate(rgid, rpeer, rgsid, rperm)
1637 if (allocated(buf)) deallocate(buf)
1638
1639 call gs%comm%init(send_pe, recv_pe)
1640
1641 call send_pe%free()
1642 call recv_pe%free()
1643
1644 !This arrays seems to take massive amounts of memory...
1645 call gs%shared_dofs%free()
1646
1647 end subroutine gs_schedule
1648
1651 subroutine gs_sort_i8(a, ind, n)
1652 integer, intent(in) :: n
1653 integer(i8), intent(inout) :: a(n)
1654 integer, intent(out) :: ind(n)
1655 integer(i8) :: aa
1656 integer :: j, ir, i, ii, l
1657
1658 do j = 1, n
1659 ind(j) = j
1660 end do
1661
1662 if (n .le. 1) return
1663
1664 l = n/2 + 1
1665 ir = n
1666 do while (.true.)
1667 if (l .gt. 1) then
1668 l = l - 1
1669 aa = a(l)
1670 ii = ind(l)
1671 else
1672 aa = a(ir)
1673 ii = ind(ir)
1674 a(ir) = a(1)
1675 ind(ir) = ind(1)
1676 ir = ir - 1
1677 if (ir .eq. 1) then
1678 a(1) = aa
1679 ind(1) = ii
1680 return
1681 end if
1682 end if
1683 i = l
1684 j = l + l
1685 do while (j .le. ir)
1686 if (j .lt. ir) then
1687 if (a(j) .lt. a(j + 1)) j = j + 1
1688 end if
1689 if (aa .lt. a(j)) then
1690 a(i) = a(j)
1691 ind(i) = ind(j)
1692 i = j
1693 j = j + j
1694 else
1695 j = ir + 1
1696 end if
1697 end do
1698 a(i) = aa
1699 ind(i) = ii
1700 end do
1701 end subroutine gs_sort_i8
1702
1704 subroutine gs_op_fld(gs, u, op, event)
1705 class(gs_t), intent(inout) :: gs
1706 type(field_t), intent(inout) :: u
1707 type(c_ptr), optional, intent(inout) :: event
1708 integer :: n, op
1709
1710 n = u%msh%nelv * u%Xh%lx * u%Xh%ly * u%Xh%lz
1711 if (present(event)) then
1712 call gs_op_vector(gs, u%x, n, op, event)
1713 else
1714 call gs_op_vector(gs, u%x, n, op)
1715 end if
1716
1717 end subroutine gs_op_fld
1718
1720 subroutine gs_op_r4(gs, u, n, op, event)
1721 class(gs_t), intent(inout) :: gs
1722 integer, intent(in) :: n
1723 real(kind=rp), contiguous, dimension(:,:,:,:), intent(inout) :: u
1724 type(c_ptr), optional, intent(inout) :: event
1725 integer :: op
1726
1727 if (present(event)) then
1728 call gs_op_vector(gs, u, n, op, event)
1729 else
1730 call gs_op_vector(gs, u, n, op)
1731 end if
1732
1733 end subroutine gs_op_r4
1734
1736 subroutine gs_op_vector(gs, u, n, op, event)
1737 class(gs_t), intent(inout) :: gs
1738 integer, intent(in) :: n
1739 real(kind=rp), dimension(n), intent(inout) :: u
1740 type(c_ptr), optional, intent(inout) :: event
1741 integer :: m, l, op, lo, so, tid
1742 type(c_ptr) :: scatter_event
1743
1744 lo = gs%local_facet_offset
1745 so = -gs%shared_facet_offset
1746 m = gs%nlocal
1747 l = gs%nshared
1748
1749 ! Capture the calling thread id before opening any parallel region; it
1750 ! is used as the MPI tag so concurrent gs ops driven from different
1751 ! threads (device path) don't collide.
1752 tid = 0
1753 !$ tid = omp_get_thread_num()
1754
1755 ! Resolve the optional event into a non-optional local before opening
1756 ! the parallel region. An absent optional dummy must not be captured by
1757 ! the region's data-sharing, otherwise the outlined region prologue
1758 ! dereferences a null descriptor (segfaults on CCE).
1759 scatter_event = c_null_ptr
1760 if (present(event)) scatter_event = event
1761
1762 !$omp parallel if (NEKO_BCKND_DEVICE .eq. 0)
1763 call profiler_start_region("gather_scatter", 5)
1764 ! Gather shared dofs
1765 if (pe_size .gt. 1 .and. n .gt. 0) then
1766 call profiler_start_region("gs_nbrecv", 13)
1767 call gs%comm%nbrecv(tid)
1768 call profiler_end_region("gs_nbrecv", 13)
1769 call profiler_start_region("gs_gather_shared", 14)
1770 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u, n, &
1771 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1772 gs%shared_blk_off, op, .true.)
1773 call profiler_end_region("gs_gather_shared", 14)
1774 call profiler_start_region("gs_nbsend", 6)
1775 call gs%comm%nbsend(gs%shared_gs, l, tid, &
1776 gs%bcknd%gather_event, gs%bcknd%gs_stream)
1777 call profiler_end_region("gs_nbsend", 6)
1778
1779 end if
1780
1781 ! Gather-scatter local dofs
1782 call profiler_start_region("gs_local", 12)
1783 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u, n, &
1784 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1785 op, .false.)
1786 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u, n, &
1787 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1788 .false., c_null_ptr)
1789 call profiler_end_region("gs_local", 12)
1790 ! Scatter shared dofs
1791 if (pe_size .gt. 1 .and. n .gt. 0) then
1792 call profiler_start_region("gs_nbwait", 7)
1793 call gs%comm%nbwait(gs%shared_gs, l, op, gs%bcknd%gs_stream)
1794 call profiler_end_region("gs_nbwait", 7)
1795 call profiler_start_region("gs_scatter_shared", 15)
1796 call gs%bcknd%scatter(gs%shared_gs, l,&
1797 gs%shared_dof_gs, u, n, &
1798 gs%shared_gs_dof, gs%nshared_blks, &
1799 gs%shared_blk_len, gs%shared_blk_off, .true., scatter_event)
1800 call profiler_end_region("gs_scatter_shared", 15)
1801 end if
1802
1803 call profiler_end_region("gather_scatter", 5)
1804 !$omp end parallel
1805 end subroutine gs_op_vector
1806
1809 subroutine gs_op_r3(gs, u1, u2, u3, n, op, event)
1810 class(gs_t), intent(inout) :: gs
1811 integer, intent(in) :: n
1812 real(kind=rp), contiguous, dimension(:,:,:,:), intent(inout) :: u1, u2, u3
1813 type(c_ptr), optional, intent(inout) :: event
1814 integer :: op
1815
1816 if (present(event)) then
1817 call gs_op_vector3(gs, u1, u2, u3, n, op, event)
1818 else
1819 call gs_op_vector3(gs, u1, u2, u3, n, op)
1820 end if
1821
1822 end subroutine gs_op_r3
1823
1833 subroutine gs_op_vector3(gs, u1, u2, u3, n, op, event)
1834 class(gs_t), intent(inout) :: gs
1835 integer, intent(in) :: n
1836 real(kind=rp), dimension(n), intent(inout) :: u1, u2, u3
1837 type(c_ptr), optional, intent(inout) :: event
1838 integer :: m, l, op, lo, so, tid
1839 integer, parameter :: nc = 3
1840 type(c_ptr) :: scatter_event
1841
1842 ! Fall back to nc independent scalar exchanges when the comm backend has
1843 ! no fused vector path.
1844 if (.not. gs%comm%vec_supported) then
1845 if (present(event)) then
1846 call gs_op_vector(gs, u1, n, op, event)
1847 call gs_op_vector(gs, u2, n, op, event)
1848 call gs_op_vector(gs, u3, n, op, event)
1849 else
1850 call gs_op_vector(gs, u1, n, op)
1851 call gs_op_vector(gs, u2, n, op)
1852 call gs_op_vector(gs, u3, n, op)
1853 end if
1854 return
1855 end if
1856
1857 ! The fused path is the only user of the vector buffers, so they are
1858 ! allocated here rather than in the setup. A rank that skips the
1859 ! exchange below (no dofs, or a single rank run) needs none of them,
1860 ! and gs_vec_alloc communicates nothing, so ranks may disagree.
1861 if (pe_size .gt. 1 .and. n .gt. 0) call gs_vec_alloc(gs)
1862
1863 lo = gs%local_facet_offset
1864 so = -gs%shared_facet_offset
1865 m = gs%nlocal
1866 l = gs%nshared
1867
1868 tid = 0
1869 !$ tid = omp_get_thread_num()
1870
1871 scatter_event = c_null_ptr
1872 if (present(event)) scatter_event = event
1873
1874 if (neko_bcknd_device .eq. 0) then
1875
1876 !$omp parallel
1877 call profiler_start_region("gather_scatter", 5)
1878
1879 ! Gather each component's shared dofs directly into its column of
1880 ! shared_gs_v (the host backends write the actual argument), then
1881 ! launch ONE fused exchange covering all nc components.
1882 if (pe_size .gt. 1 .and. n .gt. 0) then
1883 call gs%comm%nbrecv_vec(tid, nc)
1884 call gs%bcknd%gather(gs%shared_gs_v(1), l, so, gs%shared_dof_gs, &
1885 u1, n, gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1886 gs%shared_blk_off, op, .true.)
1887 call gs%bcknd%gather(gs%shared_gs_v(l + 1), l, so, &
1888 gs%shared_dof_gs, u2, n, gs%shared_gs_dof, gs%nshared_blks, &
1889 gs%shared_blk_len, gs%shared_blk_off, op, .true.)
1890 call gs%bcknd%gather(gs%shared_gs_v(2*l + 1), l, so, &
1891 gs%shared_dof_gs, u3, n, gs%shared_gs_dof, gs%nshared_blks, &
1892 gs%shared_blk_len, gs%shared_blk_off, op, .true.)
1893 call gs%comm%nbsend_vec(gs%shared_gs_v, l, nc, tid, &
1894 gs%bcknd%gather_event, gs%bcknd%gs_stream)
1895 end if
1896
1897 ! Local gather-scatter, one scalar pass per component (reuses local_gs;
1898 ! the internal barriers make the sequential reuse safe).
1899 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u1, n, &
1900 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1901 gs%local_blk_off, op, .false.)
1902 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u1, n, &
1903 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1904 gs%local_blk_off, .false., c_null_ptr)
1905 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u2, n, &
1906 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1907 gs%local_blk_off, op, .false.)
1908 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u2, n, &
1909 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1910 gs%local_blk_off, .false., c_null_ptr)
1911 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u3, n, &
1912 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1913 gs%local_blk_off, op, .false.)
1914 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u3, n, &
1915 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1916 gs%local_blk_off, .false., c_null_ptr)
1917
1918 ! Wait for the fused exchange and scatter each component back.
1919 if (pe_size .gt. 1 .and. n .gt. 0) then
1920 call gs%comm%nbwait_vec(gs%shared_gs_v, l, nc, op, &
1921 gs%bcknd%gs_stream)
1922 call gs%bcknd%scatter(gs%shared_gs_v(1), l, gs%shared_dof_gs, u1, &
1923 n, gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1924 gs%shared_blk_off, .true., scatter_event)
1925 call gs%bcknd%scatter(gs%shared_gs_v(l + 1), l, gs%shared_dof_gs, &
1926 u2, n, gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1927 gs%shared_blk_off, .true., scatter_event)
1928 call gs%bcknd%scatter(gs%shared_gs_v(2*l + 1), l, &
1929 gs%shared_dof_gs, u3, n, gs%shared_gs_dof, gs%nshared_blks, &
1930 gs%shared_blk_len, gs%shared_blk_off, .true., scatter_event)
1931 end if
1932
1933 call profiler_end_region("gather_scatter", 5)
1934 !$omp end parallel
1935
1936 else
1937
1938 call gs_op_r3_device(gs, u1, u2, u3, n, op, nc, lo, so, m, l, tid, &
1939 scatter_event)
1940
1941 end if
1942
1943 end subroutine gs_op_vector3
1944
1954 subroutine gs_op_r3_device(gs, u1, u2, u3, n, op, nc, lo, so, m, l, tid, &
1955 scatter_event)
1956 class(gs_t), intent(inout) :: gs
1957 integer, intent(in) :: n, op, nc, lo, so, m, l, tid
1958 real(kind=rp), dimension(n), intent(inout) :: u1, u2, u3
1959 type(c_ptr), intent(inout) :: scatter_event
1960 type(c_ptr) :: sgs_d, col_d, col_event
1961 integer(c_intptr_t) :: sv_addr, off_bytes
1962 integer(c_size_t) :: colbytes
1963 real(c_rp) :: rp_dummy
1964 logical :: on_host
1965
1966 on_host = .true.
1967 sgs_d = c_null_ptr
1968 select type (b => gs%bcknd)
1969 type is (gs_device_t)
1970 on_host = b%shared_on_host
1971 sgs_d = b%shared_gs_d
1972 end select
1973
1974 sv_addr = transfer(gs%shared_gs_v_d, sv_addr)
1975 colbytes = c_sizeof(rp_dummy) * int(l, c_size_t)
1976 off_bytes = int(l, c_intptr_t) * int(c_sizeof(rp_dummy), c_intptr_t)
1977
1978 ! With a host-mirrored shared buffer, each scatter below issues an
1979 ! asynchronous host-to-device copy of shared_gs; a null event makes the
1980 ! scatter sync so the next column may safely overwrite the host buffer.
1981 ! Device-resident staging is stream-ordered and carries the caller's
1982 ! event.
1983 if (on_host) then
1984 col_event = c_null_ptr
1985 else
1986 col_event = scatter_event
1987 end if
1988
1989 if (pe_size .gt. 1 .and. n .gt. 0) then
1990 call gs%comm%nbrecv_vec(tid, nc)
1991
1992 ! Gather each component into the backend's shared buffer, then stage
1993 ! it into its column of shared_gs_v.
1994 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u1, n, &
1995 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1996 gs%shared_blk_off, op, .true.)
1997 if (on_host) then
1998 ! The gather mirrored the shared buffer to the host (synchronous).
1999 gs%shared_gs_v(1:l) = gs%shared_gs(1:l)
2000 else
2001 ! shared_gs_d is created lazily on the first gather.
2002 if (.not. c_associated(sgs_d)) then
2003 select type (b => gs%bcknd)
2004 type is (gs_device_t)
2005 sgs_d = b%shared_gs_d
2006 end select
2007 end if
2008 col_d = transfer(sv_addr, col_d)
2009 call device_memcpy(col_d, sgs_d, colbytes, device_to_device, &
2010 sync = .false., strm = gs%bcknd%gs_stream)
2011 end if
2012
2013 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u2, n, &
2014 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2015 gs%shared_blk_off, op, .true.)
2016 if (on_host) then
2017 gs%shared_gs_v(l + 1:2*l) = gs%shared_gs(1:l)
2018 else
2019 col_d = transfer(sv_addr + off_bytes, col_d)
2020 call device_memcpy(col_d, sgs_d, colbytes, device_to_device, &
2021 sync = .false., strm = gs%bcknd%gs_stream)
2022 end if
2023
2024 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u3, n, &
2025 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2026 gs%shared_blk_off, op, .true.)
2027 if (on_host) then
2028 gs%shared_gs_v(2*l + 1:3*l) = gs%shared_gs(1:l)
2029 else
2030 col_d = transfer(sv_addr + 2_c_intptr_t*off_bytes, col_d)
2031 call device_memcpy(col_d, sgs_d, colbytes, device_to_device, &
2032 sync = .false., strm = gs%bcknd%gs_stream)
2033 ! Re-record the gather event so it covers the column copies above.
2034 ! Comm backends that order their per-peer packing streams on this
2035 ! event (NCCL, NVSHMEM) would otherwise race with the copies; the
2036 ! device MPI backend packs on gs_stream itself and is unaffected.
2037 call device_event_record(gs%bcknd%gather_event, gs%bcknd%gs_stream)
2038 end if
2039
2040 call gs%comm%nbsend_vec(gs%shared_gs_v, l, nc, tid, &
2041 gs%bcknd%gather_event, gs%bcknd%gs_stream)
2042 end if
2043
2044 ! Local gather-scatter per component.
2045 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u1, n, &
2046 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2047 op, .false.)
2048 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u1, n, &
2049 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2050 .false., c_null_ptr)
2051 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u2, n, &
2052 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2053 op, .false.)
2054 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u2, n, &
2055 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2056 .false., c_null_ptr)
2057 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u3, n, &
2058 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2059 op, .false.)
2060 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u3, n, &
2061 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2062 .false., c_null_ptr)
2063
2064 ! Wait for the fused exchange (reduces into shared_gs_v), then stage each
2065 ! column back into the shared buffer and scatter.
2066 if (pe_size .gt. 1 .and. n .gt. 0) then
2067 call gs%comm%nbwait_vec(gs%shared_gs_v, l, nc, op, gs%bcknd%gs_stream)
2068
2069 if (on_host) then
2070 gs%shared_gs(1:l) = gs%shared_gs_v(1:l)
2071 else
2072 col_d = transfer(sv_addr, col_d)
2073 call device_memcpy(sgs_d, col_d, colbytes, device_to_device, &
2074 sync = .false., strm = gs%bcknd%gs_stream)
2075 end if
2076 call gs%bcknd%scatter(gs%shared_gs, l, gs%shared_dof_gs, u1, n, &
2077 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2078 gs%shared_blk_off, .true., col_event)
2079
2080 if (on_host) then
2081 gs%shared_gs(1:l) = gs%shared_gs_v(l + 1:2*l)
2082 else
2083 col_d = transfer(sv_addr + off_bytes, col_d)
2084 call device_memcpy(sgs_d, col_d, colbytes, device_to_device, &
2085 sync = .false., strm = gs%bcknd%gs_stream)
2086 end if
2087 call gs%bcknd%scatter(gs%shared_gs, l, gs%shared_dof_gs, u2, n, &
2088 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2089 gs%shared_blk_off, .true., col_event)
2090
2091 if (on_host) then
2092 gs%shared_gs(1:l) = gs%shared_gs_v(2*l + 1:3*l)
2093 else
2094 col_d = transfer(sv_addr + 2_c_intptr_t*off_bytes, col_d)
2095 call device_memcpy(sgs_d, col_d, colbytes, device_to_device, &
2096 sync = .false., strm = gs%bcknd%gs_stream)
2097 end if
2098 call gs%bcknd%scatter(gs%shared_gs, l, gs%shared_dof_gs, u3, n, &
2099 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2100 gs%shared_blk_off, .true., col_event)
2101 end if
2102
2103 end subroutine gs_op_r3_device
2104
2105end 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:91
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:326
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