Neko 1.99.5
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
crystal_router.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!
73 use comm, only : pe_rank, pe_size, neko_comm
74 use num_types, only : i8
75 use stack, only : stack_i8_t
76 use utils, only : neko_error
77 use mpi_f08, only : mpi_sendrecv, mpi_status, mpi_integer, mpi_integer8, &
78 mpi_proc_null
79 implicit none
80 private
81
84 integer, parameter :: cr_tag = 0
85
87
88contains
89
98 subroutine crystal_router_pack(out, dest, body)
99 type(stack_i8_t), intent(inout) :: out
100 integer, intent(in) :: dest
101 integer(i8), intent(in) :: body(:)
102 integer(i8) :: w
103 integer :: i
104
105 w = int(dest, i8)
106 call out%push(w)
107 w = int(size(body), i8)
108 call out%push(w)
109 do i = 1, size(body)
110 w = body(i)
111 call out%push(w)
112 end do
113 end subroutine crystal_router_pack
114
121 subroutine crystal_router_transfer(buf, n)
122 integer(i8), allocatable, intent(inout) :: buf(:)
123 integer, intent(inout) :: n
124 integer(i8), allocatable :: keep(:), snd(:), rcv(:), extra(:)
125 integer :: nkeep, nsnd, nrcv, nextra
126 integer :: lo, hi, m, half, mid, r, partner
127 logical :: lower
128
129 ! Validate destinations up front so a packing bug surfaces here rather
130 ! than as a silently misrouted (lost) record at scale.
131 call cr_check_dest(buf, n)
132
133 lo = 0
134 hi = pe_size
135
136 do while (hi - lo > 1)
137 m = hi - lo
138 half = m / 2
139 mid = lo + half
140 lower = (pe_rank < mid)
141
142 ! Split the local buffer: records staying in our half vs. those bound
143 ! for the opposite half.
144 call cr_partition(buf, n, mid, lower, keep, nkeep, snd, nsnd)
145
146 ! Pick the partner in the opposite half. Lower ranks always have one;
147 ! in an odd-sized range the last upper rank (r == half) is unpaired.
148 if (lower) then
149 partner = mid + (pe_rank - lo)
150 else
151 r = pe_rank - mid
152 if (r .lt. half) then
153 partner = lo + r
154 else
155 partner = mpi_proc_null
156 end if
157 end if
158
159 ! Bidirectional exchange with the partner, then rebuild the local
160 ! buffer as (kept records) ++ (received records).
161 call cr_exchange(snd, nsnd, partner, rcv, nrcv, partner)
162 call cr_concat(buf, n, keep, nkeep, rcv, nrcv)
163
164 ! Odd-sized range: the unpaired upper rank (hi-1) hands its lower-half
165 ! records to rank lo, which absorbs them with one extra receive.
166 if (iand(m, 1) .eq. 1) then
167 if ((.not. lower) .and. ((pe_rank - mid) .eq. half)) then
168 call cr_exchange(snd, nsnd, lo, extra, nextra, mpi_proc_null)
169 else if (pe_rank .eq. lo) then
170 call cr_exchange(snd, 0, mpi_proc_null, extra, nextra, hi - 1)
171 call cr_append(buf, n, extra, nextra)
172 end if
173 end if
174
175 ! Narrow the active range to the half containing pe_rank.
176 if (lower) then
177 hi = mid
178 else
179 lo = mid
180 end if
181 end do
182
183 ! Release the per-stage scratch buffers (buf is returned to the caller).
184 if (allocated(keep)) deallocate(keep)
185 if (allocated(snd)) deallocate(snd)
186 if (allocated(rcv)) deallocate(rcv)
187 if (allocated(extra)) deallocate(extra)
188
189 end subroutine crystal_router_transfer
190
192 subroutine cr_check_dest(buf, n)
193 integer(i8), allocatable, intent(in) :: buf(:)
194 integer, intent(in) :: n
195 integer :: p, dest, rlen
196
197 p = 1
198 do while (p .le. n)
199 dest = int(buf(p))
200 rlen = int(buf(p + 1))
201 if (dest .lt. 0 .or. dest .ge. pe_size) then
202 call neko_error('crystal_router: record destination out of range')
203 end if
204 p = p + 2 + rlen
205 end do
206 end subroutine cr_check_dest
207
211 subroutine cr_partition(buf, n, mid, lower, keep, nkeep, snd, nsnd)
212 integer(i8), allocatable, intent(in) :: buf(:)
213 integer, intent(in) :: n, mid
214 logical, intent(in) :: lower
215 integer(i8), allocatable, intent(out) :: keep(:), snd(:)
216 integer, intent(out) :: nkeep, nsnd
217 integer :: p, dest, rlen, reclen
218 logical :: in_lower
219
220 allocate(keep(max(n, 1)))
221 allocate(snd(max(n, 1)))
222 nkeep = 0
223 nsnd = 0
224
225 p = 1
226 do while (p .le. n)
227 dest = int(buf(p))
228 rlen = int(buf(p + 1))
229 reclen = 2 + rlen
230 in_lower = dest .lt. mid
231 if (in_lower .eqv. lower) then
232 keep(nkeep + 1:nkeep + reclen) = buf(p:p + reclen - 1)
233 nkeep = nkeep + reclen
234 else
235 snd(nsnd + 1:nsnd + reclen) = buf(p:p + reclen - 1)
236 nsnd = nsnd + reclen
237 end if
238 p = p + reclen
239 end do
240 end subroutine cr_partition
241
244 subroutine cr_exchange(sbuf, sn, dst, rbuf, rn, src)
245 integer(i8), intent(in) :: sbuf(:)
246 integer, intent(in) :: sn, dst, src
247 integer(i8), allocatable, intent(out) :: rbuf(:)
248 integer, intent(out) :: rn
249 type(mpi_status) :: status
250 integer :: ierr
251
252 rn = 0
253 call mpi_sendrecv(sn, 1, mpi_integer, dst, cr_tag, &
254 rn, 1, mpi_integer, src, cr_tag, neko_comm, status, ierr)
255
256 allocate(rbuf(max(rn, 1)))
257 call mpi_sendrecv(sbuf, sn, mpi_integer8, dst, cr_tag, &
258 rbuf, rn, mpi_integer8, src, cr_tag, neko_comm, status, ierr)
259 end subroutine cr_exchange
260
262 subroutine cr_concat(buf, n, a, na, b, nb)
263 integer(i8), allocatable, intent(inout) :: buf(:)
264 integer, intent(out) :: n
265 integer(i8), allocatable, intent(in) :: a(:), b(:)
266 integer, intent(in) :: na, nb
267
268 if (allocated(buf)) deallocate(buf)
269 allocate(buf(max(na + nb, 1)))
270 if (na .gt. 0) buf(1:na) = a(1:na)
271 if (nb .gt. 0) buf(na + 1:na + nb) = b(1:nb)
272 n = na + nb
273 end subroutine cr_concat
274
276 subroutine cr_append(buf, n, e, ne)
277 integer(i8), allocatable, intent(inout) :: buf(:)
278 integer, intent(inout) :: n
279 integer(i8), allocatable, intent(in) :: e(:)
280 integer, intent(in) :: ne
281 integer(i8), allocatable :: tmp(:)
282
283 if (ne .le. 0) return
284 allocate(tmp(n + ne))
285 if (n .gt. 0) tmp(1:n) = buf(1:n)
286 tmp(n + 1:n + ne) = e(1:ne)
287 call move_alloc(tmp, buf)
288 n = n + ne
289 end subroutine cr_append
290
291end module crystal_router
Definition comm.F90:1
integer, public pe_size
MPI size of communicator.
Definition comm.F90:61
integer, public pe_rank
MPI rank.
Definition comm.F90:58
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:45
Crystal router: scalable all-to-some personalized exchange.
subroutine, public crystal_router_transfer(buf, n)
Route packed records to their destination ranks.
subroutine cr_append(buf, n, e, ne)
Append e(1:ne) to buf(1:n) in place.
subroutine cr_concat(buf, n, a, na, b, nb)
Rebuild buf as the concatenation a(1:na) ++ b(1:nb).
subroutine cr_check_dest(buf, n)
Abort if any record destination falls outside .
integer, parameter cr_tag
Message tag used for all crystal-router exchanges. Stages are fully synchronised (blocking MPI_Sendre...
subroutine cr_partition(buf, n, mid, lower, keep, nkeep, snd, nsnd)
Partition buf into records kept locally and records to be sent. A record is kept when the side of its...
subroutine, public crystal_router_pack(out, dest, body)
Append one record to a packed crystal-router buffer.
subroutine cr_exchange(sbuf, sn, dst, rbuf, rn, src)
Size-negotiated bidirectional exchange with a partner. Either dst or src may be MPI_PROC_NULL to make...
integer, parameter, public i8
Definition num_types.f90:7
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Integer*8 based stack.
Definition stack.f90:84
#define max(a, b)
Definition tensor.cu:40