Neko 1.99.6
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_neighbour.f90
Go to the documentation of this file.
1! Copyright (c) 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!
35 use num_types, only : rp
36 use gs_comm, only : gs_comm_t, gs_vec_nc
38 use stack, only : stack_i4_t
39 use mpi_f08, only : mpi_comm, mpi_request, mpi_status_ignore, &
40 mpi_info_null, mpi_dist_graph_create_adjacent, &
41 mpi_ineighbor_alltoallv, mpi_wait, mpi_comm_free
43 use, intrinsic :: iso_c_binding
44 use utils, only : neko_error
45 implicit none
46 private
47
56 type, public, extends(gs_comm_t) :: gs_neighbour_t
58 real(kind=rp), allocatable :: send_buf(:)
60 real(kind=rp), allocatable :: recv_buf(:)
64 integer, allocatable :: sendcounts(:), sdispls(:)
65 integer, allocatable :: recvcounts(:), rdispls(:)
67 type(mpi_comm) :: neigh_comm
69 type(mpi_request) :: request
74 real(kind=rp), allocatable :: send_buf_v(:)
75 real(kind=rp), allocatable :: recv_buf_v(:)
76 integer, allocatable :: sendcounts_v(:), sdispls_v(:)
77 integer, allocatable :: recvcounts_v(:), rdispls_v(:)
78 contains
79 procedure, pass(this) :: init => gs_neighbour_init
80 procedure, pass(this) :: free => gs_neighbour_free
82 procedure, pass(this) :: nbsend => gs_nbsend_neighbour
83 procedure, pass(this) :: nbrecv => gs_nbrecv_neighbour
84 procedure, pass(this) :: nbwait => gs_nbwait_neighbour
85 procedure, pass(this) :: nbsend_vec => gs_nbsend_vec_neighbour
86 procedure, pass(this) :: nbrecv_vec => gs_nbrecv_vec_neighbour
87 procedure, pass(this) :: nbwait_vec => gs_nbwait_vec_neighbour
88 end type gs_neighbour_t
89
90contains
91
94 subroutine gs_neighbour_init(this, send_pe, recv_pe)
95 class(gs_neighbour_t), intent(inout) :: this
96 type(stack_i4_t), intent(inout) :: send_pe
97 type(stack_i4_t), intent(inout) :: recv_pe
98 integer :: i, nsend, nrecv, send_total, recv_total, ierr
102 integer, allocatable :: src_weights(:), dst_weights(:)
103
104 call this%init_order(send_pe, recv_pe)
105
106 nsend = size(this%send_pe)
107 nrecv = size(this%recv_pe)
108
109 ! Allocate the count/displacement arrays with at least one element so
110 ! that a peerless rank (nsend or nrecv == 0) still passes a valid array
111 ! to the collective.
112 allocate(this%sendcounts(max(1, nsend)), this%sdispls(max(1, nsend)))
113 allocate(this%recvcounts(max(1, nrecv)), this%rdispls(max(1, nrecv)))
114 this%sendcounts = 0
115 this%sdispls = 0
116 this%recvcounts = 0
117 this%rdispls = 0
118
119 send_total = 0
120 do i = 1, nsend
121 this%sendcounts(i) = this%send_dof(this%send_pe(i))%size()
122 this%sdispls(i) = send_total
123 send_total = send_total + this%sendcounts(i)
124 end do
125 allocate(this%send_buf(max(1, send_total)))
126
127 recv_total = 0
128 do i = 1, nrecv
129 this%recvcounts(i) = this%recv_dof(this%recv_pe(i))%size()
130 this%rdispls(i) = recv_total
131 recv_total = recv_total + this%recvcounts(i)
132 end do
133 allocate(this%recv_buf(max(1, recv_total)))
134
135 ! Fused vector exchange buffers/descriptors, sized for GS_VEC_NC comps.
136 allocate(this%send_buf_v(max(1, gs_vec_nc*send_total)))
137 allocate(this%recv_buf_v(max(1, gs_vec_nc*recv_total)))
138 allocate(this%sendcounts_v(max(1, nsend)), this%sdispls_v(max(1, nsend)))
139 allocate(this%recvcounts_v(max(1, nrecv)), this%rdispls_v(max(1, nrecv)))
140 this%sendcounts_v = 0
141 this%sdispls_v = 0
142 this%recvcounts_v = 0
143 this%rdispls_v = 0
144 this%vec_supported = .true.
145
146 ! Build a distributed-graph communicator over the halo neighbourhood.
147 ! With MPI_Dist_graph_create_adjacent and reorder = .false. the order of
148 ! sources/destinations is preserved, so the j-th block of the collective
149 ! corresponds to recv_pe(j)/send_pe(j) and the count/displacement arrays
150 ! above can be used directly.
151 allocate(src_weights(max(1, nrecv)), dst_weights(max(1, nsend)))
152 src_weights = 1
153 dst_weights = 1
154 call mpi_dist_graph_create_adjacent(neko_comm, &
155 nrecv, this%recv_pe, src_weights, &
156 nsend, this%send_pe, dst_weights, &
157 mpi_info_null, .false., this%neigh_comm, ierr)
158 deallocate(src_weights, dst_weights)
159
160 end subroutine gs_neighbour_init
161
163 subroutine gs_neighbour_free(this)
164 class(gs_neighbour_t), intent(inout) :: this
165 integer :: ierr
166
167 if (allocated(this%send_buf)) deallocate(this%send_buf)
168 if (allocated(this%recv_buf)) deallocate(this%recv_buf)
169 if (allocated(this%sendcounts)) deallocate(this%sendcounts)
170 if (allocated(this%sdispls)) deallocate(this%sdispls)
171 if (allocated(this%recvcounts)) deallocate(this%recvcounts)
172 if (allocated(this%rdispls)) deallocate(this%rdispls)
173
174 if (allocated(this%send_buf_v)) deallocate(this%send_buf_v)
175 if (allocated(this%recv_buf_v)) deallocate(this%recv_buf_v)
176 if (allocated(this%sendcounts_v)) deallocate(this%sendcounts_v)
177 if (allocated(this%sdispls_v)) deallocate(this%sdispls_v)
178 if (allocated(this%recvcounts_v)) deallocate(this%recvcounts_v)
179 if (allocated(this%rdispls_v)) deallocate(this%rdispls_v)
180
181 call mpi_comm_free(this%neigh_comm, ierr)
182
183 call this%free_order()
184 call this%free_dofs()
185
186 end subroutine gs_neighbour_free
187
191 subroutine gs_nbsend_neighbour(this, u, n, tag, deps, strm)
192 class(gs_neighbour_t), intent(inout) :: this
193 integer, intent(in) :: n
194 real(kind=rp), dimension(n), intent(inout) :: u
195 integer, intent(in) :: tag
196 type(c_ptr), intent(inout) :: deps
197 type(c_ptr), intent(inout) :: strm
198 integer :: i, j, dst, off, ndst, ierr
199
200 ! Gather data from u into the per-peer slab of send_buf according to the
201 ! indices in send_dof. Each thread packs a different peer's slab; the
202 ! implicit barrier at end-do guarantees the buffer is complete before
203 ! the master fires the collective.
204 !$omp do
205 do i = 1, size(this%send_pe)
206 dst = this%send_pe(i)
207 off = this%sdispls(i)
208 ndst = this%sendcounts(i)
209 select type (dofs => this%send_dof(dst)%data)
210 type is (integer)
211 !$omp simd
212 do j = 1, ndst
213 this%send_buf(off + j) = u(dofs(j))
214 end do
215 end select
216 end do
217 !$omp end do
218
219 ! A neighbourhood collective is one call over neigh_comm and must be
220 ! issued by a single thread.
221 !$omp master
222 call mpi_ineighbor_alltoallv(this%send_buf, this%sendcounts, &
223 this%sdispls, mpi_real_precision, this%recv_buf, this%recvcounts, &
224 this%rdispls, mpi_real_precision, this%neigh_comm, this%request, &
225 ierr)
226 !$omp end master
227
228 end subroutine gs_nbsend_neighbour
229
232 subroutine gs_nbrecv_neighbour(this, tag)
233 class(gs_neighbour_t), intent(inout) :: this
234 integer, intent(in) :: tag
235 ! Nothing to do: the collective is launched in nbsend.
236 end subroutine gs_nbrecv_neighbour
237
239 subroutine gs_nbwait_neighbour(this, u, n, op, strm)
240 class(gs_neighbour_t), intent(inout) :: this
241 integer, intent(in) :: n
242 real(kind=rp), dimension(n), intent(inout) :: u
243 type(c_ptr), intent(inout) :: strm
244 integer :: i, j, src, off, nsrc
245 integer :: op
246 integer :: ierr
247
248 ! The collective is master-issued, so wait on it from the master and
249 ! barrier before anyone touches recv_buf.
250 !$omp master
251 call mpi_wait(this%request, mpi_status_ignore, ierr)
252 !$omp end master
253 !$omp barrier
254
255 ! Reduce each received slab into u. The outer loop over peers stays
256 ! serial: a dof shared by 3+ ranks appears in several recv_dof lists, so
257 ! reducing two slabs concurrently would race on that dof. Parallelism is
258 ! taken within each slab instead.
259 do i = 1, size(this%recv_pe)
260 src = this%recv_pe(i)
261 off = this%rdispls(i)
262 nsrc = this%recvcounts(i)
263 select type (dofs => this%recv_dof(src)%data)
264 type is (integer)
265 select case (op)
266 case (gs_op_add)
267 !OCL NORECURRENCE, NOVREC, NOALIAS
268 !DIR$ CONCURRENT
269 !DIR$ IVDEP
270 !GCC$ ivdep
271 !NEC$ IVDEP
272 !$omp do
273 do j = 1, nsrc
274 u(dofs(j)) = u(dofs(j)) + this%recv_buf(off + j)
275 end do
276 !$omp end do
277 case (gs_op_mul)
278 !OCL NORECURRENCE, NOVREC, NOALIAS
279 !DIR$ CONCURRENT
280 !DIR$ IVDEP
281 !GCC$ ivdep
282 !NEC$ IVDEP
283 !$omp do
284 do j = 1, nsrc
285 u(dofs(j)) = u(dofs(j)) * this%recv_buf(off + j)
286 end do
287 !$omp end do
288 case (gs_op_min)
289 !OCL NORECURRENCE, NOVREC, NOALIAS
290 !DIR$ CONCURRENT
291 !DIR$ IVDEP
292 !GCC$ ivdep
293 !NEC$ IVDEP
294 !$omp do
295 do j = 1, nsrc
296 u(dofs(j)) = min(u(dofs(j)), this%recv_buf(off + j))
297 end do
298 !$omp end do
299 case (gs_op_max)
300 !OCL NORECURRENCE, NOVREC, NOALIAS
301 !DIR$ CONCURRENT
302 !DIR$ IVDEP
303 !GCC$ ivdep
304 !NEC$ IVDEP
305 !$omp do
306 do j = 1, nsrc
307 u(dofs(j)) = max(u(dofs(j)), this%recv_buf(off + j))
308 end do
309 !$omp end do
310 case default
311 call neko_error("Unknown operation in gs_nbwait_neighbour")
312 end select
313 end select
314 end do
315
316 end subroutine gs_nbwait_neighbour
317
322 subroutine gs_nbsend_vec_neighbour(this, u, n, nc, tag, deps, strm)
323 class(gs_neighbour_t), intent(inout) :: this
324 integer, intent(in) :: n, nc
325 real(kind=rp), dimension(nc*n), intent(inout) :: u
326 integer, intent(in) :: tag
327 type(c_ptr), intent(inout) :: deps
328 type(c_ptr), intent(inout) :: strm
329 integer :: i, j, c, dst, off, ndst, ierr
330
331 !$omp do
332 do i = 1, size(this%send_pe)
333 dst = this%send_pe(i)
334 off = this%sdispls(i)
335 ndst = this%sendcounts(i)
336 select type (dofs => this%send_dof(dst)%data)
337 type is (integer)
338 do c = 1, nc
339 !$omp simd
340 do j = 1, ndst
341 this%send_buf_v(nc*off + (c-1)*ndst + j) = u((c-1)*n + dofs(j))
342 end do
343 end do
344 end select
345 end do
346 !$omp end do
347
348 ! A neighbourhood collective is one call over neigh_comm and must be
349 ! issued by a single thread. The end-do barrier above guarantees the
350 ! send buffer is fully packed first.
351 !$omp master
352 do i = 1, size(this%send_pe)
353 this%sendcounts_v(i) = nc * this%sendcounts(i)
354 this%sdispls_v(i) = nc * this%sdispls(i)
355 end do
356 do i = 1, size(this%recv_pe)
357 this%recvcounts_v(i) = nc * this%recvcounts(i)
358 this%rdispls_v(i) = nc * this%rdispls(i)
359 end do
360 call mpi_ineighbor_alltoallv(this%send_buf_v, this%sendcounts_v, &
361 this%sdispls_v, mpi_real_precision, this%recv_buf_v, &
362 this%recvcounts_v, this%rdispls_v, mpi_real_precision, &
363 this%neigh_comm, this%request, ierr)
364 !$omp end master
365
366 end subroutine gs_nbsend_vec_neighbour
367
369 subroutine gs_nbrecv_vec_neighbour(this, tag, nc)
370 class(gs_neighbour_t), intent(inout) :: this
371 integer, intent(in) :: tag, nc
372 ! Nothing to do: the collective is launched in nbsend_vec.
373 end subroutine gs_nbrecv_vec_neighbour
374
378 subroutine gs_nbwait_vec_neighbour(this, u, n, nc, op, strm)
379 class(gs_neighbour_t), intent(inout) :: this
380 integer, intent(in) :: n, nc
381 real(kind=rp), dimension(nc*n), intent(inout) :: u
382 type(c_ptr), intent(inout) :: strm
383 integer :: i, j, c, src, off, nsrc, ierr
384 integer :: op
385
386 !$omp master
387 call mpi_wait(this%request, mpi_status_ignore, ierr)
388 !$omp end master
389 !$omp barrier
390
391 ! Serial over peers (a dof shared by 3+ ranks appears in several recv
392 ! lists); parallelism is taken within each slab.
393 do i = 1, size(this%recv_pe)
394 src = this%recv_pe(i)
395 off = this%rdispls(i)
396 nsrc = this%recvcounts(i)
397 select type (dofs => this%recv_dof(src)%data)
398 type is (integer)
399 select case (op)
400 case (gs_op_add)
401 !$omp do
402 do j = 1, nsrc
403 do c = 1, nc
404 u((c-1)*n + dofs(j)) = u((c-1)*n + dofs(j)) + &
405 this%recv_buf_v(nc*off + (c-1)*nsrc + j)
406 end do
407 end do
408 !$omp end do
409 case (gs_op_mul)
410 !$omp do
411 do j = 1, nsrc
412 do c = 1, nc
413 u((c-1)*n + dofs(j)) = u((c-1)*n + dofs(j)) * &
414 this%recv_buf_v(nc*off + (c-1)*nsrc + j)
415 end do
416 end do
417 !$omp end do
418 case (gs_op_min)
419 !$omp do
420 do j = 1, nsrc
421 do c = 1, nc
422 u((c-1)*n + dofs(j)) = min(u((c-1)*n + dofs(j)), &
423 this%recv_buf_v(nc*off + (c-1)*nsrc + j))
424 end do
425 end do
426 !$omp end do
427 case (gs_op_max)
428 !$omp do
429 do j = 1, nsrc
430 do c = 1, nc
431 u((c-1)*n + dofs(j)) = max(u((c-1)*n + dofs(j)), &
432 this%recv_buf_v(nc*off + (c-1)*nsrc + j))
433 end do
434 end do
435 !$omp end do
436 case default
437 call neko_error("Unknown operation in gs_nbwait_vec_neighbour")
438 end select
439 end select
440 end do
441
442 end subroutine gs_nbwait_vec_neighbour
443
444end module gs_neighbour
Definition comm.F90:1
type(mpi_datatype), public mpi_real_precision
MPI type for working precision of REAL types.
Definition comm.F90:54
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
Defines a gather-scatter communication method.
Definition gs_comm.f90:34
integer, parameter, public gs_vec_nc
Maximum number of components handled by the fused vector (multi-component) halo exchange used by gs_o...
Definition gs_comm.f90:49
Defines gather-scatter communication using MPI neighbourhood collectives.
subroutine gs_nbwait_neighbour(this, u, n, op, strm)
Wait for the neighbourhood collective and reduce the received slabs.
subroutine gs_nbsend_vec_neighbour(this, u, n, nc, tag, deps, strm)
Pack the nc components and launch a single neighbourhood collective.
subroutine gs_nbwait_vec_neighbour(this, u, n, nc, op, strm)
Wait for the vector collective and reduce each received slab into u. Peer i's received slab holds nc ...
subroutine gs_nbrecv_neighbour(this, tag)
No-op: the neighbourhood collective issued in nbsend handles both the send and receive directions,...
subroutine gs_neighbour_free(this)
Deallocate the neighbourhood-collective communication method.
subroutine gs_neighbour_init(this, send_pe, recv_pe)
Initialise the neighbourhood-collective communication method See gs_comm.f90 for details.
subroutine gs_nbsend_neighbour(this, u, n, tag, deps, strm)
Pack the send buffer and initiate the neighbourhood collective. The collective covers both directions...
subroutine gs_nbrecv_vec_neighbour(this, tag, nc)
No-op: the collective is launched in nbsend_vec.
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
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Gather-scatter communication method.
Definition gs_comm.f90:52
Gather-scatter communication using an MPI neighbourhood collective. The whole halo exchange is carrie...
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40