Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
gs_crystal.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!
51 use num_types, only : rp
52 use gs_comm, only : gs_comm_t, gs_vec_nc
55 use stack, only : stack_i4_t
57 use mpi_f08, only : mpi_request, mpi_isend, mpi_irecv, mpi_waitall, &
58 mpi_statuses_ignore
59 use utils, only : neko_error
60 use, intrinsic :: iso_c_binding
61 implicit none
62 private
63
65 type, public, extends(gs_comm_t) :: gs_crystal_t
67 type(gs_crystal_plan_t) :: plan
70 real(kind=rp), allocatable :: buf(:)
72 real(kind=rp), allocatable :: sbuf(:)
76 real(kind=rp), allocatable :: buf_v(:)
77 real(kind=rp), allocatable :: sbuf_v(:)
79 type(mpi_request) :: sreq(1), rreq(2)
80 integer :: nsreq = 0, nrreq = 0
83 integer :: tag = 0
84 contains
85 procedure, pass(this) :: init => gs_crystal_init
86 procedure, pass(this) :: free => gs_crystal_free
88 procedure, pass(this) :: nbsend => gs_crystal_nbsend
89 procedure, pass(this) :: nbrecv => gs_crystal_nbrecv
90 procedure, pass(this) :: nbwait => gs_crystal_nbwait
91 procedure, pass(this) :: init_vec => gs_crystal_init_vec
92 procedure, pass(this) :: nbsend_vec => gs_crystal_nbsend_vec
93 procedure, pass(this) :: nbrecv_vec => gs_crystal_nbrecv_vec
94 procedure, pass(this) :: nbwait_vec => gs_crystal_nbwait_vec
95 end type gs_crystal_t
96
97contains
98
101 subroutine gs_crystal_init(this, send_pe, recv_pe)
102 class(gs_crystal_t), intent(inout) :: this
103 type(stack_i4_t), intent(inout) :: send_pe
104 type(stack_i4_t), intent(inout) :: recv_pe
105
106 call this%init_order(send_pe, recv_pe)
107
108 call this%plan%init(this%send_pe, this%recv_pe, this%send_dof, &
109 this%recv_dof)
110
111 allocate(this%buf(2*this%plan%nwrk))
112 allocate(this%sbuf(this%plan%nsmax))
113
114 this%vec_supported = .true.
115 this%vec_ready = .false.
116
117 end subroutine gs_crystal_init
118
123 subroutine gs_crystal_init_vec(this)
124 class(gs_crystal_t), intent(inout) :: this
125
126 allocate(this%buf_v(2*gs_vec_nc*this%plan%nwrk))
127 allocate(this%sbuf_v(gs_vec_nc*this%plan%nsmax))
128
129 end subroutine gs_crystal_init_vec
130
132 subroutine gs_crystal_free(this)
133 class(gs_crystal_t), intent(inout) :: this
134
135 if (allocated(this%buf)) deallocate(this%buf)
136 if (allocated(this%sbuf)) deallocate(this%sbuf)
137 if (allocated(this%buf_v)) deallocate(this%buf_v)
138 if (allocated(this%sbuf_v)) deallocate(this%sbuf_v)
139 this%vec_ready = .false.
140
141 call this%plan%free()
142
143 call this%free_order()
144 call this%free_dofs()
145
146 end subroutine gs_crystal_free
147
149 subroutine gs_crystal_nbrecv(this, tag)
150 class(gs_crystal_t), intent(inout) :: this
151 integer, intent(in) :: tag
152 integer :: co, ierr
153
154 !$omp master
155 this%tag = tag
156 this%nrreq = 0
157 this%nsreq = 0
158 if (this%plan%nstage .gt. 0) then
159 associate(st => this%plan%stage(1))
160 ! The words that stay are packed into [1, nkw] of the same column
161 ! by nbsend, so the arriving ones can land straight behind them
162 co = (st%dst_sel - 1) * this%plan%nwrk
163 if (st%src .ge. 0) then
164 this%nrreq = this%nrreq + 1
165 call mpi_irecv(this%buf(co + st%nkw + 1), st%nrw, &
166 mpi_real_precision, st%src, tag, neko_comm, &
167 this%rreq(this%nrreq), ierr)
168 end if
169 if (st%src2 .ge. 0) then
170 this%nrreq = this%nrreq + 1
171 call mpi_irecv(this%buf(co + st%nkw + st%nrw + 1), st%nr2w, &
172 mpi_real_precision, st%src2, tag, neko_comm, &
173 this%rreq(this%nrreq), ierr)
174 end if
175 end associate
176 end if
177 !$omp end master
178 !$omp barrier
179
180 end subroutine gs_crystal_nbrecv
181
183 subroutine gs_crystal_nbsend(this, u, n, tag, deps, strm)
184 class(gs_crystal_t), intent(inout) :: this
185 integer, intent(in) :: n
186 real(kind=rp), dimension(n), intent(inout) :: u
187 integer, intent(in) :: tag
188 type(c_ptr), intent(inout) :: deps
189 type(c_ptr), intent(inout) :: strm
190 integer :: j, co, ierr
191
192 if (this%plan%nstage .eq. 0) return
193
194 associate(st => this%plan%stage(1))
195 co = (st%dst_sel - 1) * this%plan%nwrk
196
197 ! Gather the words leaving on the first stage straight out of the
198 ! shared vector, so the routing never sees a separate packed copy
199 !$omp do
200 do j = 1, st%nsw
201 this%sbuf(j) = u(this%plan%pack_send_dof(j))
202 end do
203 !$omp end do
204
205 !$omp master
206 if (st%dst .ge. 0) then
207 this%nsreq = 1
208 call mpi_isend(this%sbuf(1), st%nsw, mpi_real_precision, &
209 st%dst, tag, neko_comm, this%sreq(1), ierr)
210 end if
211 !$omp end master
212
213 ! The words that stay put on this stage, into the same column the
214 ! arriving ones are being received behind
215 !$omp do
216 do j = 1, st%nkw
217 this%buf(co + j) = u(this%plan%pack_keep_dof(j))
218 end do
219 !$omp end do
220 end associate
221
222 end subroutine gs_crystal_nbsend
223
226 subroutine gs_crystal_nbwait(this, u, n, op, strm)
227 class(gs_crystal_t), intent(inout) :: this
228 integer, intent(in) :: n
229 real(kind=rp), dimension(n), intent(inout) :: u
230 type(c_ptr), intent(inout) :: strm
231 integer :: op
232 integer :: s, i, j, co, so, fo, ierr
233
234 if (this%plan%nstage .eq. 0) return
235
236 !$omp master
237 call mpi_waitall(this%nrreq, this%rreq, mpi_statuses_ignore, ierr)
238 call mpi_waitall(this%nsreq, this%sreq, mpi_statuses_ignore, ierr)
239 !$omp end master
240 !$omp barrier
241
242 do s = 2, this%plan%nstage
243 associate(st => this%plan%stage(s))
244 so = (st%src_sel - 1) * this%plan%nwrk
245 co = (st%dst_sel - 1) * this%plan%nwrk
246
247 !$omp master
248 this%nrreq = 0
249 this%nsreq = 0
250 if (st%src .ge. 0) then
251 this%nrreq = this%nrreq + 1
252 call mpi_irecv(this%buf(co + st%nkw + 1), st%nrw, &
253 mpi_real_precision, st%src, this%tag, neko_comm, &
254 this%rreq(this%nrreq), ierr)
255 end if
256 if (st%src2 .ge. 0) then
257 this%nrreq = this%nrreq + 1
258 call mpi_irecv(this%buf(co + st%nkw + st%nrw + 1), st%nr2w, &
259 mpi_real_precision, st%src2, this%tag, neko_comm, &
260 this%rreq(this%nrreq), ierr)
261 end if
262 !$omp end master
263
264 if (st%dst .ge. 0) then
265 !$omp do
266 do j = 1, st%nsw
267 this%sbuf(j) = this%buf(so + st%send_idx(j))
268 end do
269 !$omp end do
270 !$omp master
271 this%nsreq = 1
272 call mpi_isend(this%sbuf(1), st%nsw, mpi_real_precision, &
273 st%dst, this%tag, neko_comm, this%sreq(1), ierr)
274 !$omp end master
275 end if
276
277 ! A stage that sends nothing keeps every word where it is, and is
278 ! receiving into the column it already occupies
279 if (.not. st%inplace) then
280 !$omp do
281 do j = 1, st%nkw
282 this%buf(co + j) = this%buf(so + st%keep_idx(j))
283 end do
284 !$omp end do
285 end if
286
287 !$omp master
288 call mpi_waitall(this%nrreq, this%rreq, mpi_statuses_ignore, ierr)
289 call mpi_waitall(this%nsreq, this%sreq, mpi_statuses_ignore, ierr)
290 !$omp end master
291 !$omp barrier
292 end associate
293 end do
294
295 ! Reduce record by record: a shared dof may be delivered by several
296 ! peers, so its contributions are separated by the record barriers
297 fo = (this%plan%final_sel - 1) * this%plan%nwrk
298 do i = 1, this%plan%nfinal_rec
299 associate(ro => this%plan%final_off(i), rl => this%plan%final_len(i))
300 select case (op)
301 case (gs_op_add)
302 !OCL NORECURRENCE, NOVREC, NOALIAS
303 !DIR$ CONCURRENT
304 !DIR$ IVDEP
305 !GCC$ ivdep
306 !NEC$ IVDEP
307 !$omp do
308 do j = 1, rl
309 u(this%plan%unpack_dof(ro + j)) = &
310 u(this%plan%unpack_dof(ro + j)) + this%buf(fo + ro + j)
311 end do
312 !$omp end do
313 case (gs_op_mul)
314 !OCL NORECURRENCE, NOVREC, NOALIAS
315 !DIR$ CONCURRENT
316 !DIR$ IVDEP
317 !GCC$ ivdep
318 !NEC$ IVDEP
319 !$omp do
320 do j = 1, rl
321 u(this%plan%unpack_dof(ro + j)) = &
322 u(this%plan%unpack_dof(ro + j)) * this%buf(fo + ro + j)
323 end do
324 !$omp end do
325 case (gs_op_min)
326 !OCL NORECURRENCE, NOVREC, NOALIAS
327 !DIR$ CONCURRENT
328 !DIR$ IVDEP
329 !GCC$ ivdep
330 !NEC$ IVDEP
331 !$omp do
332 do j = 1, rl
333 u(this%plan%unpack_dof(ro + j)) = &
334 min(u(this%plan%unpack_dof(ro + j)), &
335 this%buf(fo + ro + j))
336 end do
337 !$omp end do
338 case (gs_op_max)
339 !OCL NORECURRENCE, NOVREC, NOALIAS
340 !DIR$ CONCURRENT
341 !DIR$ IVDEP
342 !GCC$ ivdep
343 !NEC$ IVDEP
344 !$omp do
345 do j = 1, rl
346 u(this%plan%unpack_dof(ro + j)) = &
347 max(u(this%plan%unpack_dof(ro + j)), &
348 this%buf(fo + ro + j))
349 end do
350 !$omp end do
351 case default
352 call neko_error("Unknown operation in gs_crystal_nbwait")
353 end select
354 end associate
355 end do
356
357 end subroutine gs_crystal_nbwait
358
360 subroutine gs_crystal_nbrecv_vec(this, tag, nc)
361 class(gs_crystal_t), intent(inout) :: this
362 integer, intent(in) :: tag, nc
363 integer :: co, ierr
364
365 if (nc .gt. gs_vec_nc) then
366 call neko_error('gs_crystal: too many components in vector exchange')
367 end if
368
369 !$omp master
370 this%tag = tag
371 this%nrreq = 0
372 this%nsreq = 0
373 if (this%plan%nstage .gt. 0) then
374 associate(st => this%plan%stage(1))
375 co = (st%dst_sel - 1) * nc * this%plan%nwrk
376 if (st%src .ge. 0) then
377 this%nrreq = this%nrreq + 1
378 call mpi_irecv(this%buf_v(co + nc*st%nkw + 1), nc*st%nrw, &
379 mpi_real_precision, st%src, tag, neko_comm, &
380 this%rreq(this%nrreq), ierr)
381 end if
382 if (st%src2 .ge. 0) then
383 this%nrreq = this%nrreq + 1
384 call mpi_irecv(this%buf_v(co + nc*(st%nkw + st%nrw) + 1), &
385 nc*st%nr2w, mpi_real_precision, st%src2, tag, neko_comm, &
386 this%rreq(this%nrreq), ierr)
387 end if
388 end associate
389 end if
390 !$omp end master
391 !$omp barrier
392
393 end subroutine gs_crystal_nbrecv_vec
394
398 subroutine gs_crystal_nbsend_vec(this, u, n, nc, tag, deps, strm)
399 class(gs_crystal_t), intent(inout) :: this
400 integer, intent(in) :: n, nc
401 real(kind=rp), dimension(nc*n), intent(inout) :: u
402 integer, intent(in) :: tag
403 type(c_ptr), intent(inout) :: deps
404 type(c_ptr), intent(inout) :: strm
405 integer :: j, c, co, ierr
406
407 if (this%plan%nstage .eq. 0) return
408
409 associate(st => this%plan%stage(1))
410 co = (st%dst_sel - 1) * nc * this%plan%nwrk
411
412 !$omp do
413 do j = 1, st%nsw
414 do c = 1, nc
415 this%sbuf_v(nc*(j-1) + c) = &
416 u((c-1)*n + this%plan%pack_send_dof(j))
417 end do
418 end do
419 !$omp end do
420
421 !$omp master
422 if (st%dst .ge. 0) then
423 this%nsreq = 1
424 call mpi_isend(this%sbuf_v(1), nc*st%nsw, mpi_real_precision, &
425 st%dst, tag, neko_comm, this%sreq(1), ierr)
426 end if
427 !$omp end master
428
429 !$omp do
430 do j = 1, st%nkw
431 do c = 1, nc
432 this%buf_v(co + nc*(j-1) + c) = &
433 u((c-1)*n + this%plan%pack_keep_dof(j))
434 end do
435 end do
436 !$omp end do
437 end associate
438
439 end subroutine gs_crystal_nbsend_vec
440
443 subroutine gs_crystal_nbwait_vec(this, u, n, nc, op, strm)
444 class(gs_crystal_t), intent(inout) :: this
445 integer, intent(in) :: n, nc
446 real(kind=rp), dimension(nc*n), intent(inout) :: u
447 type(c_ptr), intent(inout) :: strm
448 integer :: op
449 integer :: s, i, j, c, co, so, fo, ierr
450
451 if (this%plan%nstage .eq. 0) return
452
453 !$omp master
454 call mpi_waitall(this%nrreq, this%rreq, mpi_statuses_ignore, ierr)
455 call mpi_waitall(this%nsreq, this%sreq, mpi_statuses_ignore, ierr)
456 !$omp end master
457 !$omp barrier
458
459 do s = 2, this%plan%nstage
460 associate(st => this%plan%stage(s))
461 so = (st%src_sel - 1) * nc * this%plan%nwrk
462 co = (st%dst_sel - 1) * nc * this%plan%nwrk
463
464 !$omp master
465 this%nrreq = 0
466 this%nsreq = 0
467 if (st%src .ge. 0) then
468 this%nrreq = this%nrreq + 1
469 call mpi_irecv(this%buf_v(co + nc*st%nkw + 1), nc*st%nrw, &
470 mpi_real_precision, st%src, this%tag, neko_comm, &
471 this%rreq(this%nrreq), ierr)
472 end if
473 if (st%src2 .ge. 0) then
474 this%nrreq = this%nrreq + 1
475 call mpi_irecv(this%buf_v(co + nc*(st%nkw + st%nrw) + 1), &
476 nc*st%nr2w, mpi_real_precision, st%src2, this%tag, &
477 neko_comm, this%rreq(this%nrreq), ierr)
478 end if
479 !$omp end master
480
481 if (st%dst .ge. 0) then
482 !$omp do
483 do j = 1, st%nsw
484 do c = 1, nc
485 this%sbuf_v(nc*(j-1) + c) = &
486 this%buf_v(so + nc*(st%send_idx(j) - 1) + c)
487 end do
488 end do
489 !$omp end do
490 !$omp master
491 this%nsreq = 1
492 call mpi_isend(this%sbuf_v(1), nc*st%nsw, mpi_real_precision, &
493 st%dst, this%tag, neko_comm, this%sreq(1), ierr)
494 !$omp end master
495 end if
496
497 if (.not. st%inplace) then
498 !$omp do
499 do j = 1, st%nkw
500 do c = 1, nc
501 this%buf_v(co + nc*(j-1) + c) = &
502 this%buf_v(so + nc*(st%keep_idx(j) - 1) + c)
503 end do
504 end do
505 !$omp end do
506 end if
507
508 !$omp master
509 call mpi_waitall(this%nrreq, this%rreq, mpi_statuses_ignore, ierr)
510 call mpi_waitall(this%nsreq, this%sreq, mpi_statuses_ignore, ierr)
511 !$omp end master
512 !$omp barrier
513 end associate
514 end do
515
516 fo = (this%plan%final_sel - 1) * nc * this%plan%nwrk
517 do i = 1, this%plan%nfinal_rec
518 associate(ro => this%plan%final_off(i), rl => this%plan%final_len(i))
519 select case (op)
520 case (gs_op_add)
521 !$omp do
522 do j = 1, rl
523 do c = 1, nc
524 u((c-1)*n + this%plan%unpack_dof(ro + j)) = &
525 u((c-1)*n + this%plan%unpack_dof(ro + j)) + &
526 this%buf_v(fo + nc*(ro + j - 1) + c)
527 end do
528 end do
529 !$omp end do
530 case (gs_op_mul)
531 !$omp do
532 do j = 1, rl
533 do c = 1, nc
534 u((c-1)*n + this%plan%unpack_dof(ro + j)) = &
535 u((c-1)*n + this%plan%unpack_dof(ro + j)) * &
536 this%buf_v(fo + nc*(ro + j - 1) + c)
537 end do
538 end do
539 !$omp end do
540 case (gs_op_min)
541 !$omp do
542 do j = 1, rl
543 do c = 1, nc
544 u((c-1)*n + this%plan%unpack_dof(ro + j)) = &
545 min(u((c-1)*n + this%plan%unpack_dof(ro + j)), &
546 this%buf_v(fo + nc*(ro + j - 1) + c))
547 end do
548 end do
549 !$omp end do
550 case (gs_op_max)
551 !$omp do
552 do j = 1, rl
553 do c = 1, nc
554 u((c-1)*n + this%plan%unpack_dof(ro + j)) = &
555 max(u((c-1)*n + this%plan%unpack_dof(ro + j)), &
556 this%buf_v(fo + nc*(ro + j - 1) + c))
557 end do
558 end do
559 !$omp end do
560 case default
561 call neko_error("Unknown operation in gs_crystal_nbwait_vec")
562 end select
563 end associate
564 end do
565
566 end subroutine gs_crystal_nbwait_vec
567
568end module gs_crystal
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:50
Routing plan for the crystal router gather-scatter comm. backends.
Defines crystal router gather-scatter communication.
subroutine gs_crystal_nbsend_vec(this, u, n, nc, tag, deps, strm)
Pack the shared vector and post the send of the first routing stage, fused nc-component.
subroutine gs_crystal_nbwait(this, u, n, op, strm)
Drive the remaining routing stages and reduce what is delivered into the shared vector.
subroutine gs_crystal_init(this, send_pe, recv_pe)
Initialise crystal router based communication method See gs_comm.f90 for details.
subroutine gs_crystal_init_vec(this)
Allocate the fused vector working and send buffers, sized for GS_VEC_NC components....
subroutine gs_crystal_nbwait_vec(this, u, n, nc, op, strm)
Drive the remaining routing stages and reduce what is delivered into the shared vector,...
subroutine gs_crystal_nbrecv_vec(this, tag, nc)
Post the receives of the first routing stage, fused nc-component.
subroutine gs_crystal_nbsend(this, u, n, tag, deps, strm)
Pack the shared vector and post the send of the first routing stage.
subroutine gs_crystal_free(this)
Deallocate crystal router based communication method.
subroutine gs_crystal_nbrecv(this, tag)
Post the receives of the first routing stage.
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:14
Implements a dynamic stack ADT.
Definition stack.f90:49
Utilities.
Definition utils.f90:35
Gather-scatter communication method.
Definition gs_comm.f90:53
Gather-scatter communication using a crystal router.
The full routing plan for one gather-scatter schedule.
Integer based stack.
Definition stack.f90:77
#define max(a, b)
Definition tensor.cu:40