Neko 1.99.9
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!
76 use comm, only : pe_rank, pe_size, neko_comm
77 use num_types, only : i8
78 use stack, only : stack_i8_t
79 use utils, only : neko_error
80 use mpi_f08, only : mpi_sendrecv, mpi_status, mpi_integer, mpi_integer8, &
81 mpi_proc_null
82 implicit none
83 private
84
87 integer, parameter :: cr_tag = 0
88
90
91contains
92
101 subroutine crystal_router_pack(out, dest, body)
102 type(stack_i8_t), intent(inout) :: out
103 integer, intent(in) :: dest
104 integer(i8), intent(in) :: body(:)
105 integer(i8) :: w
106 integer :: i
107
108 w = int(dest, i8)
109 call out%push(w)
110 w = int(size(body), i8)
111 call out%push(w)
112 do i = 1, size(body)
113 w = body(i)
114 call out%push(w)
115 end do
116 end subroutine crystal_router_pack
117
124 subroutine crystal_router_transfer(buf, n)
125 integer(i8), allocatable, intent(inout) :: buf(:)
126 integer, intent(inout) :: n
127 integer(i8), allocatable :: keep(:), snd(:), rcv(:), extra(:)
128 integer :: nkeep, nsnd, nrcv, nextra
129 integer :: lo, hi, m, half, mid, r, partner
130 logical :: lower
131
132 ! Validate destinations up front so a packing bug surfaces here rather
133 ! than as a silently misrouted (lost) record at scale.
134 call cr_check_dest(buf, n)
135
136 lo = 0
137 hi = pe_size
138
139 do while (hi - lo > 1)
140 m = hi - lo
141 half = m / 2
142 mid = lo + half
143 lower = (pe_rank < mid)
144
145 ! Split the local buffer: records staying in our half vs. those bound
146 ! for the opposite half.
147 call cr_partition(buf, n, mid, lower, keep, nkeep, snd, nsnd)
148
149 ! Pick the partner in the opposite half. Lower ranks always have one;
150 ! in an odd-sized range the last upper rank (r == half) is unpaired.
151 if (lower) then
152 partner = mid + (pe_rank - lo)
153 else
154 r = pe_rank - mid
155 if (r .lt. half) then
156 partner = lo + r
157 else
158 partner = mpi_proc_null
159 end if
160 end if
161
162 ! Bidirectional exchange with the partner, then rebuild the local
163 ! buffer as (kept records) ++ (received records).
164 call cr_exchange(snd, nsnd, partner, rcv, nrcv, partner)
165 call cr_concat(buf, n, keep, nkeep, rcv, nrcv)
166
167 ! Odd-sized range: the unpaired upper rank (hi-1) hands its lower-half
168 ! records to rank lo, which absorbs them with one extra receive.
169 if (iand(m, 1) .eq. 1) then
170 if ((.not. lower) .and. ((pe_rank - mid) .eq. half)) then
171 call cr_exchange(snd, nsnd, lo, extra, nextra, mpi_proc_null)
172 else if (pe_rank .eq. lo) then
173 call cr_exchange(snd, 0, mpi_proc_null, extra, nextra, hi - 1)
174 call cr_append(buf, n, extra, nextra)
175 end if
176 end if
177
178 ! Narrow the active range to the half containing pe_rank.
179 if (lower) then
180 hi = mid
181 else
182 lo = mid
183 end if
184 end do
185
186 ! Release the per-stage scratch buffers (buf is returned to the caller).
187 if (allocated(keep)) deallocate(keep)
188 if (allocated(snd)) deallocate(snd)
189 if (allocated(rcv)) deallocate(rcv)
190 if (allocated(extra)) deallocate(extra)
191
192 end subroutine crystal_router_transfer
193
195 subroutine cr_check_dest(buf, n)
196 integer(i8), allocatable, intent(in) :: buf(:)
197 integer, intent(in) :: n
198 integer :: p, dest, rlen
199
200 p = 1
201 do while (p .le. n)
202 dest = int(buf(p))
203 rlen = int(buf(p + 1))
204 if (dest .lt. 0 .or. dest .ge. pe_size) then
205 call neko_error('crystal_router: record destination out of range')
206 end if
207 p = p + 2 + rlen
208 end do
209 end subroutine cr_check_dest
210
214 subroutine cr_partition(buf, n, mid, lower, keep, nkeep, snd, nsnd)
215 integer(i8), allocatable, intent(in) :: buf(:)
216 integer, intent(in) :: n, mid
217 logical, intent(in) :: lower
218 integer(i8), allocatable, intent(out) :: keep(:), snd(:)
219 integer, intent(out) :: nkeep, nsnd
220 integer :: p, dest, rlen, reclen
221 logical :: in_lower
222
223 allocate(keep(max(n, 1)))
224 allocate(snd(max(n, 1)))
225 nkeep = 0
226 nsnd = 0
227
228 p = 1
229 do while (p .le. n)
230 dest = int(buf(p))
231 rlen = int(buf(p + 1))
232 reclen = 2 + rlen
233 in_lower = dest .lt. mid
234 if (in_lower .eqv. lower) then
235 keep(nkeep + 1:nkeep + reclen) = buf(p:p + reclen - 1)
236 nkeep = nkeep + reclen
237 else
238 snd(nsnd + 1:nsnd + reclen) = buf(p:p + reclen - 1)
239 nsnd = nsnd + reclen
240 end if
241 p = p + reclen
242 end do
243 end subroutine cr_partition
244
247 subroutine cr_exchange(sbuf, sn, dst, rbuf, rn, src)
248 integer(i8), intent(in) :: sbuf(:)
249 integer, intent(in) :: sn, dst, src
250 integer(i8), allocatable, intent(out) :: rbuf(:)
251 integer, intent(out) :: rn
252 type(mpi_status) :: status
253 integer :: ierr
254
255 rn = 0
256 call mpi_sendrecv(sn, 1, mpi_integer, dst, cr_tag, &
257 rn, 1, mpi_integer, src, cr_tag, neko_comm, status, ierr)
258
259 allocate(rbuf(max(rn, 1)))
260 call mpi_sendrecv(sbuf, sn, mpi_integer8, dst, cr_tag, &
261 rbuf, rn, mpi_integer8, src, cr_tag, neko_comm, status, ierr)
262 end subroutine cr_exchange
263
265 subroutine cr_concat(buf, n, a, na, b, nb)
266 integer(i8), allocatable, intent(inout) :: buf(:)
267 integer, intent(out) :: n
268 integer(i8), allocatable, intent(in) :: a(:), b(:)
269 integer, intent(in) :: na, nb
270
271 if (allocated(buf)) deallocate(buf)
272 allocate(buf(max(na + nb, 1)))
273 if (na .gt. 0) buf(1:na) = a(1:na)
274 if (nb .gt. 0) buf(na + 1:na + nb) = b(1:nb)
275 n = na + nb
276 end subroutine cr_concat
277
279 subroutine cr_append(buf, n, e, ne)
280 integer(i8), allocatable, intent(inout) :: buf(:)
281 integer, intent(inout) :: n
282 integer(i8), allocatable, intent(in) :: e(:)
283 integer, intent(in) :: ne
284 integer(i8), allocatable :: tmp(:)
285
286 if (ne .le. 0) return
287 allocate(tmp(n + ne))
288 if (n .gt. 0) tmp(1:n) = buf(1:n)
289 tmp(n + 1:n + ne) = e(1:ne)
290 call move_alloc(tmp, buf)
291 n = n + ne
292 end subroutine cr_append
293
294end module crystal_router
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
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 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