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 ! Separate tests, since .and. need not short-circuit: a combined
1474 ! condition would read dg(n + 1) once j reaches n
1475 do while (j + 1 .le. n)
1476 if (dg(j + 1) .ne. id) exit
1477 j = j + 1
1478 count = count + 1
1479 end do
1480 call blks%push(count)
1481 i = j + 1
1482 end do
1483
1484 select type (blk_array => blks%data)
1485 type is (integer)
1486 nblks = blks%size()
1487 allocate(blk_len(nblks))
1488 do i = 1, nblks
1489 blk_len(i) = blk_array(i)
1490 end do
1491 allocate(blk_off(nblks))
1492 blk_off(1) = 0
1493 do i = 2, nblks
1494 blk_off(i) = blk_off(i - 1) + blk_len(i - 1)
1495 end do
1496 end select
1497 call blks%free()
1498
1499 end subroutine gs_find_blks
1500
1501 end subroutine gs_init_mapping
1502
1514 subroutine gs_schedule(gs)
1515 type(gs_t), target, intent(inout) :: gs
1516 type(htable_iter_i8_t) :: it
1517 type(stack_i4_t) :: send_pe, recv_pe
1518 type(stack_i8_t) :: cr_buf
1519 integer(i8), allocatable :: buf(:)
1520 integer(i8), pointer :: cr_data(:)
1521 integer(i8), allocatable :: rgid(:), gtmp(:)
1522 integer, allocatable :: rpeer(:), rgsid(:), rperm(:), gperm(:)
1523 integer(i8) :: gid
1524 integer :: i, j, n, owner, nrec, peer, shared_gs_id, tmp
1525 integer :: a, b, cnt, t
1526
1527 call send_pe%init()
1528 call recv_pe%init()
1529
1530 !
1531 ! Phase 1: route every local shared dof to its canonical owner.
1532 ! record = [dest=owner, len=2, gid, origin]
1533 !
1534 call cr_buf%init(max(gs%shared_dofs%num_entries(), 1) * 4)
1535 call it%init(gs%shared_dofs)
1536 do while (it%next())
1537 gid = it%key()
1538 owner = int(modulo(gid, int(pe_size, i8)))
1539 call crystal_router_pack(cr_buf, owner, [gid, int(pe_rank, i8)])
1540 end do
1541
1542 n = cr_buf%size()
1543 allocate(buf(max(n, 1)))
1544 if (n .gt. 0) then
1545 cr_data => cr_buf%array()
1546 buf(1:n) = cr_data(1:n)
1547 end if
1548 call cr_buf%free()
1549
1550 call crystal_router_transfer(buf, n)
1551
1552 !
1553 ! Phase 2: at the owner, group holders by gid and reflect, to each holder,
1554 ! every other holder of the same dof.
1555 ! reply = [dest=holder, len=2, gid, peer]
1556 !
1557 nrec = n / 4 ! every record here has the fixed form [me, 2, gid, origin]
1558 allocate(rgid(max(nrec, 1)), rgsid(max(nrec, 1)), gperm(max(nrec, 1)))
1559 do i = 1, nrec
1560 rgid(i) = buf((i - 1) * 4 + 3) ! gid
1561 rgsid(i) = int(buf((i - 1) * 4 + 4)) ! origin rank (reuse array)
1562 end do
1563 if (nrec .gt. 0) call gs_sort_i8(rgid, gperm, nrec)
1564
1565 call cr_buf%init(max(n, 1))
1566 i = 1
1567 do while (i .le. nrec)
1568 j = i
1569 do while (j .le. nrec)
1570 if (rgid(j) .ne. rgid(i)) exit
1571 j = j + 1
1572 end do
1573 ! Reflect, to each holder, every other holder of this dof.
1574 if (j - i .gt. 1) then
1575 do a = i, j - 1 ! recipient holder
1576 do b = i, j - 1 ! the other holder
1577 if (a .eq. b) cycle
1578 call crystal_router_pack(cr_buf, rgsid(gperm(a)), &
1579 [rgid(i), int(rgsid(gperm(b)), i8)])
1580 end do
1581 end do
1582 end if
1583 i = j
1584 end do
1585 deallocate(rgid, rgsid, gperm)
1586
1587 n = cr_buf%size()
1588 if (allocated(buf)) deallocate(buf)
1589 allocate(buf(max(n, 1)))
1590 if (n .gt. 0) then
1591 cr_data => cr_buf%array()
1592 buf(1:n) = cr_data(1:n)
1593 end if
1594 call cr_buf%free()
1595
1596 call crystal_router_transfer(buf, n)
1597
1598 !
1599 ! Phase 3: register each (dof, peer) for both send and receive. Order each
1600 ! peer's dof list by gid so both ranks of a pair agree on the order.
1601 !
1602 nrec = n / 4 ! replies are [me, 2, gid, peer]
1603 allocate(rgid(max(nrec, 1)), rpeer(max(nrec, 1)), rgsid(max(nrec, 1)), &
1604 rperm(max(nrec, 1)))
1605 do i = 1, nrec
1606 gid = buf((i - 1) * 4 + 3)
1607 rgid(i) = gid
1608 rpeer(i) = int(buf((i - 1) * 4 + 4))
1609 tmp = gs%shared_dofs%get(gid, shared_gs_id)
1610 rgsid(i) = shared_gs_id
1611 end do
1612
1613 ! Sort by peer; within each peer run, sort by gid and register in that order.
1614 if (nrec .gt. 0) call sort(rpeer, rperm, nrec)
1615 a = 1
1616 do while (a .le. nrec)
1617 b = a
1618 do while (b .le. nrec)
1619 if (rpeer(b) .ne. rpeer(a)) exit
1620 b = b + 1
1621 end do
1622 peer = rpeer(a)
1623 cnt = b - a
1624 allocate(gtmp(cnt), gperm(cnt))
1625 do t = 1, cnt
1626 gtmp(t) = rgid(rperm(a + t - 1))
1627 end do
1628 call gs_sort_i8(gtmp, gperm, cnt)
1629 do t = 1, cnt
1630 shared_gs_id = rgsid(rperm(a + gperm(t) - 1))
1631 call gs%comm%send_dof(peer)%push(shared_gs_id)
1632 call gs%comm%recv_dof(peer)%push(shared_gs_id)
1633 end do
1634 deallocate(gtmp, gperm)
1635 call send_pe%push(peer)
1636 call recv_pe%push(peer)
1637 a = b
1638 end do
1639 deallocate(rgid, rpeer, rgsid, rperm)
1640 if (allocated(buf)) deallocate(buf)
1641
1642 call gs%comm%init(send_pe, recv_pe)
1643
1644 call send_pe%free()
1645 call recv_pe%free()
1646
1647 !This arrays seems to take massive amounts of memory...
1648 call gs%shared_dofs%free()
1649
1650 end subroutine gs_schedule
1651
1654 subroutine gs_sort_i8(a, ind, n)
1655 integer, intent(in) :: n
1656 integer(i8), intent(inout) :: a(n)
1657 integer, intent(out) :: ind(n)
1658 integer(i8) :: aa
1659 integer :: j, ir, i, ii, l
1660
1661 do j = 1, n
1662 ind(j) = j
1663 end do
1664
1665 if (n .le. 1) return
1666
1667 l = n/2 + 1
1668 ir = n
1669 do while (.true.)
1670 if (l .gt. 1) then
1671 l = l - 1
1672 aa = a(l)
1673 ii = ind(l)
1674 else
1675 aa = a(ir)
1676 ii = ind(ir)
1677 a(ir) = a(1)
1678 ind(ir) = ind(1)
1679 ir = ir - 1
1680 if (ir .eq. 1) then
1681 a(1) = aa
1682 ind(1) = ii
1683 return
1684 end if
1685 end if
1686 i = l
1687 j = l + l
1688 do while (j .le. ir)
1689 if (j .lt. ir) then
1690 if (a(j) .lt. a(j + 1)) j = j + 1
1691 end if
1692 if (aa .lt. a(j)) then
1693 a(i) = a(j)
1694 ind(i) = ind(j)
1695 i = j
1696 j = j + j
1697 else
1698 j = ir + 1
1699 end if
1700 end do
1701 a(i) = aa
1702 ind(i) = ii
1703 end do
1704 end subroutine gs_sort_i8
1705
1707 subroutine gs_op_fld(gs, u, op, event)
1708 class(gs_t), intent(inout) :: gs
1709 type(field_t), intent(inout) :: u
1710 type(c_ptr), optional, intent(inout) :: event
1711 integer :: n, op
1712
1713 n = u%msh%nelv * u%Xh%lx * u%Xh%ly * u%Xh%lz
1714 if (present(event)) then
1715 call gs_op_vector(gs, u%x, n, op, event)
1716 else
1717 call gs_op_vector(gs, u%x, n, op)
1718 end if
1719
1720 end subroutine gs_op_fld
1721
1723 subroutine gs_op_r4(gs, u, n, op, event)
1724 class(gs_t), intent(inout) :: gs
1725 integer, intent(in) :: n
1726 real(kind=rp), contiguous, dimension(:,:,:,:), intent(inout) :: u
1727 type(c_ptr), optional, intent(inout) :: event
1728 integer :: op
1729
1730 if (present(event)) then
1731 call gs_op_vector(gs, u, n, op, event)
1732 else
1733 call gs_op_vector(gs, u, n, op)
1734 end if
1735
1736 end subroutine gs_op_r4
1737
1739 subroutine gs_op_vector(gs, u, n, op, event)
1740 class(gs_t), intent(inout) :: gs
1741 integer, intent(in) :: n
1742 real(kind=rp), dimension(n), intent(inout) :: u
1743 type(c_ptr), optional, intent(inout) :: event
1744 integer :: m, l, op, lo, so, tid
1745 type(c_ptr) :: scatter_event
1746
1747 lo = gs%local_facet_offset
1748 so = -gs%shared_facet_offset
1749 m = gs%nlocal
1750 l = gs%nshared
1751
1752 ! Capture the calling thread id before opening any parallel region; it
1753 ! is used as the MPI tag so concurrent gs ops driven from different
1754 ! threads (device path) don't collide.
1755 tid = 0
1756 !$ tid = omp_get_thread_num()
1757
1758 ! Resolve the optional event into a non-optional local before opening
1759 ! the parallel region. An absent optional dummy must not be captured by
1760 ! the region's data-sharing, otherwise the outlined region prologue
1761 ! dereferences a null descriptor (segfaults on CCE).
1762 scatter_event = c_null_ptr
1763 if (present(event)) scatter_event = event
1764
1765 !$omp parallel if (NEKO_BCKND_DEVICE .eq. 0)
1766 call profiler_start_region("gather_scatter", 5)
1767 ! Gather shared dofs
1768 if (pe_size .gt. 1 .and. n .gt. 0) then
1769 call profiler_start_region("gs_nbrecv", 13)
1770 call gs%comm%nbrecv(tid)
1771 call profiler_end_region("gs_nbrecv", 13)
1772 call profiler_start_region("gs_gather_shared", 14)
1773 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u, n, &
1774 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1775 gs%shared_blk_off, op, .true.)
1776 call profiler_end_region("gs_gather_shared", 14)
1777 call profiler_start_region("gs_nbsend", 6)
1778 call gs%comm%nbsend(gs%shared_gs, l, tid, &
1779 gs%bcknd%gather_event, gs%bcknd%gs_stream)
1780 call profiler_end_region("gs_nbsend", 6)
1781
1782 end if
1783
1784 ! Gather-scatter local dofs
1785 call profiler_start_region("gs_local", 12)
1786 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u, n, &
1787 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1788 op, .false.)
1789 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u, n, &
1790 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
1791 .false., c_null_ptr)
1792 call profiler_end_region("gs_local", 12)
1793 ! Scatter shared dofs
1794 if (pe_size .gt. 1 .and. n .gt. 0) then
1795 call profiler_start_region("gs_nbwait", 7)
1796 call gs%comm%nbwait(gs%shared_gs, l, op, gs%bcknd%gs_stream)
1797 call profiler_end_region("gs_nbwait", 7)
1798 call profiler_start_region("gs_scatter_shared", 15)
1799 call gs%bcknd%scatter(gs%shared_gs, l,&
1800 gs%shared_dof_gs, u, n, &
1801 gs%shared_gs_dof, gs%nshared_blks, &
1802 gs%shared_blk_len, gs%shared_blk_off, .true., scatter_event)
1803 call profiler_end_region("gs_scatter_shared", 15)
1804 end if
1805
1806 call profiler_end_region("gather_scatter", 5)
1807 !$omp end parallel
1808 end subroutine gs_op_vector
1809
1812 subroutine gs_op_r3(gs, u1, u2, u3, n, op, event)
1813 class(gs_t), intent(inout) :: gs
1814 integer, intent(in) :: n
1815 real(kind=rp), contiguous, dimension(:,:,:,:), intent(inout) :: u1, u2, u3
1816 type(c_ptr), optional, intent(inout) :: event
1817 integer :: op
1818
1819 if (present(event)) then
1820 call gs_op_vector3(gs, u1, u2, u3, n, op, event)
1821 else
1822 call gs_op_vector3(gs, u1, u2, u3, n, op)
1823 end if
1824
1825 end subroutine gs_op_r3
1826
1836 subroutine gs_op_vector3(gs, u1, u2, u3, n, op, event)
1837 class(gs_t), intent(inout) :: gs
1838 integer, intent(in) :: n
1839 real(kind=rp), dimension(n), intent(inout) :: u1, u2, u3
1840 type(c_ptr), optional, intent(inout) :: event
1841 integer :: m, l, op, lo, so, tid
1842 integer, parameter :: nc = 3
1843 type(c_ptr) :: scatter_event
1844
1845 ! Fall back to nc independent scalar exchanges when the comm backend has
1846 ! no fused vector path.
1847 if (.not. gs%comm%vec_supported) then
1848 if (present(event)) then
1849 call gs_op_vector(gs, u1, n, op, event)
1850 call gs_op_vector(gs, u2, n, op, event)
1851 call gs_op_vector(gs, u3, n, op, event)
1852 else
1853 call gs_op_vector(gs, u1, n, op)
1854 call gs_op_vector(gs, u2, n, op)
1855 call gs_op_vector(gs, u3, n, op)
1856 end if
1857 return
1858 end if
1859
1860 ! The fused path is the only user of the vector buffers, so they are
1861 ! allocated here rather than in the setup. A rank that skips the
1862 ! exchange below (no dofs, or a single rank run) needs none of them,
1863 ! and gs_vec_alloc communicates nothing, so ranks may disagree.
1864 if (pe_size .gt. 1 .and. n .gt. 0) call gs_vec_alloc(gs)
1865
1866 lo = gs%local_facet_offset
1867 so = -gs%shared_facet_offset
1868 m = gs%nlocal
1869 l = gs%nshared
1870
1871 tid = 0
1872 !$ tid = omp_get_thread_num()
1873
1874 scatter_event = c_null_ptr
1875 if (present(event)) scatter_event = event
1876
1877 if (neko_bcknd_device .eq. 0) then
1878
1879 !$omp parallel
1880 call profiler_start_region("gather_scatter", 5)
1881
1882 ! Gather each component's shared dofs directly into its column of
1883 ! shared_gs_v (the host backends write the actual argument), then
1884 ! launch ONE fused exchange covering all nc components.
1885 if (pe_size .gt. 1 .and. n .gt. 0) then
1886 call gs%comm%nbrecv_vec(tid, nc)
1887 call gs%bcknd%gather(gs%shared_gs_v(1), l, so, gs%shared_dof_gs, &
1888 u1, n, gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1889 gs%shared_blk_off, op, .true.)
1890 call gs%bcknd%gather(gs%shared_gs_v(l + 1), l, so, &
1891 gs%shared_dof_gs, u2, n, gs%shared_gs_dof, gs%nshared_blks, &
1892 gs%shared_blk_len, gs%shared_blk_off, op, .true.)
1893 call gs%bcknd%gather(gs%shared_gs_v(2*l + 1), l, so, &
1894 gs%shared_dof_gs, u3, n, gs%shared_gs_dof, gs%nshared_blks, &
1895 gs%shared_blk_len, gs%shared_blk_off, op, .true.)
1896 call gs%comm%nbsend_vec(gs%shared_gs_v, l, nc, tid, &
1897 gs%bcknd%gather_event, gs%bcknd%gs_stream)
1898 end if
1899
1900 ! Local gather-scatter, one scalar pass per component (reuses local_gs;
1901 ! the internal barriers make the sequential reuse safe).
1902 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u1, n, &
1903 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1904 gs%local_blk_off, op, .false.)
1905 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u1, n, &
1906 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1907 gs%local_blk_off, .false., c_null_ptr)
1908 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u2, n, &
1909 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1910 gs%local_blk_off, op, .false.)
1911 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u2, n, &
1912 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1913 gs%local_blk_off, .false., c_null_ptr)
1914 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u3, n, &
1915 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1916 gs%local_blk_off, op, .false.)
1917 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u3, n, &
1918 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, &
1919 gs%local_blk_off, .false., c_null_ptr)
1920
1921 ! Wait for the fused exchange and scatter each component back.
1922 if (pe_size .gt. 1 .and. n .gt. 0) then
1923 call gs%comm%nbwait_vec(gs%shared_gs_v, l, nc, op, &
1924 gs%bcknd%gs_stream)
1925 call gs%bcknd%scatter(gs%shared_gs_v(1), l, gs%shared_dof_gs, u1, &
1926 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(l + 1), l, gs%shared_dof_gs, &
1929 u2, n, gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1930 gs%shared_blk_off, .true., scatter_event)
1931 call gs%bcknd%scatter(gs%shared_gs_v(2*l + 1), l, &
1932 gs%shared_dof_gs, u3, n, gs%shared_gs_dof, gs%nshared_blks, &
1933 gs%shared_blk_len, gs%shared_blk_off, .true., scatter_event)
1934 end if
1935
1936 call profiler_end_region("gather_scatter", 5)
1937 !$omp end parallel
1938
1939 else
1940
1941 call gs_op_r3_device(gs, u1, u2, u3, n, op, nc, lo, so, m, l, tid, &
1942 scatter_event)
1943
1944 end if
1945
1946 end subroutine gs_op_vector3
1947
1957 subroutine gs_op_r3_device(gs, u1, u2, u3, n, op, nc, lo, so, m, l, tid, &
1958 scatter_event)
1959 class(gs_t), intent(inout) :: gs
1960 integer, intent(in) :: n, op, nc, lo, so, m, l, tid
1961 real(kind=rp), dimension(n), intent(inout) :: u1, u2, u3
1962 type(c_ptr), intent(inout) :: scatter_event
1963 type(c_ptr) :: sgs_d, col_d, col_event
1964 integer(c_intptr_t) :: sv_addr, off_bytes
1965 integer(c_size_t) :: colbytes
1966 real(c_rp) :: rp_dummy
1967 logical :: on_host
1968
1969 on_host = .true.
1970 sgs_d = c_null_ptr
1971 select type (b => gs%bcknd)
1972 type is (gs_device_t)
1973 on_host = b%shared_on_host
1974 sgs_d = b%shared_gs_d
1975 end select
1976
1977 sv_addr = transfer(gs%shared_gs_v_d, sv_addr)
1978 colbytes = c_sizeof(rp_dummy) * int(l, c_size_t)
1979 off_bytes = int(l, c_intptr_t) * int(c_sizeof(rp_dummy), c_intptr_t)
1980
1981 ! With a host-mirrored shared buffer, each scatter below issues an
1982 ! asynchronous host-to-device copy of shared_gs; a null event makes the
1983 ! scatter sync so the next column may safely overwrite the host buffer.
1984 ! Device-resident staging is stream-ordered and carries the caller's
1985 ! event.
1986 if (on_host) then
1987 col_event = c_null_ptr
1988 else
1989 col_event = scatter_event
1990 end if
1991
1992 if (pe_size .gt. 1 .and. n .gt. 0) then
1993 call gs%comm%nbrecv_vec(tid, nc)
1994
1995 ! Gather each component into the backend's shared buffer, then stage
1996 ! it into its column of shared_gs_v.
1997 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u1, n, &
1998 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
1999 gs%shared_blk_off, op, .true.)
2000 if (on_host) then
2001 ! The gather mirrored the shared buffer to the host (synchronous).
2002 gs%shared_gs_v(1:l) = gs%shared_gs(1:l)
2003 else
2004 ! shared_gs_d is created lazily on the first gather.
2005 if (.not. c_associated(sgs_d)) then
2006 select type (b => gs%bcknd)
2007 type is (gs_device_t)
2008 sgs_d = b%shared_gs_d
2009 end select
2010 end if
2011 col_d = transfer(sv_addr, col_d)
2012 call device_memcpy(col_d, sgs_d, colbytes, device_to_device, &
2013 sync = .false., strm = gs%bcknd%gs_stream)
2014 end if
2015
2016 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u2, n, &
2017 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2018 gs%shared_blk_off, op, .true.)
2019 if (on_host) then
2020 gs%shared_gs_v(l + 1:2*l) = gs%shared_gs(1:l)
2021 else
2022 col_d = transfer(sv_addr + off_bytes, col_d)
2023 call device_memcpy(col_d, sgs_d, colbytes, device_to_device, &
2024 sync = .false., strm = gs%bcknd%gs_stream)
2025 end if
2026
2027 call gs%bcknd%gather(gs%shared_gs, l, so, gs%shared_dof_gs, u3, n, &
2028 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2029 gs%shared_blk_off, op, .true.)
2030 if (on_host) then
2031 gs%shared_gs_v(2*l + 1:3*l) = gs%shared_gs(1:l)
2032 else
2033 col_d = transfer(sv_addr + 2_c_intptr_t*off_bytes, col_d)
2034 call device_memcpy(col_d, sgs_d, colbytes, device_to_device, &
2035 sync = .false., strm = gs%bcknd%gs_stream)
2036 ! Re-record the gather event so it covers the column copies above.
2037 ! Comm backends that order their per-peer packing streams on this
2038 ! event (NCCL, NVSHMEM) would otherwise race with the copies; the
2039 ! device MPI backend packs on gs_stream itself and is unaffected.
2040 call device_event_record(gs%bcknd%gather_event, gs%bcknd%gs_stream)
2041 end if
2042
2043 call gs%comm%nbsend_vec(gs%shared_gs_v, l, nc, tid, &
2044 gs%bcknd%gather_event, gs%bcknd%gs_stream)
2045 end if
2046
2047 ! Local gather-scatter per component.
2048 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u1, n, &
2049 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2050 op, .false.)
2051 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u1, n, &
2052 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2053 .false., c_null_ptr)
2054 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u2, n, &
2055 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2056 op, .false.)
2057 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u2, n, &
2058 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2059 .false., c_null_ptr)
2060 call gs%bcknd%gather(gs%local_gs, m, lo, gs%local_dof_gs, u3, n, &
2061 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2062 op, .false.)
2063 call gs%bcknd%scatter(gs%local_gs, m, gs%local_dof_gs, u3, n, &
2064 gs%local_gs_dof, gs%nlocal_blks, gs%local_blk_len, gs%local_blk_off, &
2065 .false., c_null_ptr)
2066
2067 ! Wait for the fused exchange (reduces into shared_gs_v), then stage each
2068 ! column back into the shared buffer and scatter.
2069 if (pe_size .gt. 1 .and. n .gt. 0) then
2070 call gs%comm%nbwait_vec(gs%shared_gs_v, l, nc, op, gs%bcknd%gs_stream)
2071
2072 if (on_host) then
2073 gs%shared_gs(1:l) = gs%shared_gs_v(1:l)
2074 else
2075 col_d = transfer(sv_addr, col_d)
2076 call device_memcpy(sgs_d, col_d, colbytes, device_to_device, &
2077 sync = .false., strm = gs%bcknd%gs_stream)
2078 end if
2079 call gs%bcknd%scatter(gs%shared_gs, l, gs%shared_dof_gs, u1, n, &
2080 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2081 gs%shared_blk_off, .true., col_event)
2082
2083 if (on_host) then
2084 gs%shared_gs(1:l) = gs%shared_gs_v(l + 1:2*l)
2085 else
2086 col_d = transfer(sv_addr + off_bytes, col_d)
2087 call device_memcpy(sgs_d, col_d, colbytes, device_to_device, &
2088 sync = .false., strm = gs%bcknd%gs_stream)
2089 end if
2090 call gs%bcknd%scatter(gs%shared_gs, l, gs%shared_dof_gs, u2, n, &
2091 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2092 gs%shared_blk_off, .true., col_event)
2093
2094 if (on_host) then
2095 gs%shared_gs(1:l) = gs%shared_gs_v(2*l + 1:3*l)
2096 else
2097 col_d = transfer(sv_addr + 2_c_intptr_t*off_bytes, col_d)
2098 call device_memcpy(sgs_d, col_d, colbytes, device_to_device, &
2099 sync = .false., strm = gs%bcknd%gs_stream)
2100 end if
2101 call gs%bcknd%scatter(gs%shared_gs, l, gs%shared_dof_gs, u3, n, &
2102 gs%shared_gs_dof, gs%nshared_blks, gs%shared_blk_len, &
2103 gs%shared_blk_off, .true., col_event)
2104 end if
2105
2106 end subroutine gs_op_r3_device
2107
2108end 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:1682
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:92
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:328
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