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) :: nbsend_vec => gs_crystal_nbsend_vec
92 procedure, pass(this) :: nbrecv_vec => gs_crystal_nbrecv_vec
93 procedure, pass(this) :: nbwait_vec => gs_crystal_nbwait_vec
94 end type gs_crystal_t
95
96contains
97
100 subroutine gs_crystal_init(this, send_pe, recv_pe)
101 class(gs_crystal_t), intent(inout) :: this
102 type(stack_i4_t), intent(inout) :: send_pe
103 type(stack_i4_t), intent(inout) :: recv_pe
104
105 call this%init_order(send_pe, recv_pe)
106
107 call this%plan%init(this%send_pe, this%recv_pe, this%send_dof, &
108 this%recv_dof)
109
110 allocate(this%buf(2*this%plan%nwrk))
111 allocate(this%sbuf(this%plan%nsmax))
112 allocate(this%buf_v(2*gs_vec_nc*this%plan%nwrk))
113 allocate(this%sbuf_v(gs_vec_nc*this%plan%nsmax))
114
115 this%vec_supported = .true.
116
117 end subroutine gs_crystal_init
118
120 subroutine gs_crystal_free(this)
121 class(gs_crystal_t), intent(inout) :: this
122
123 if (allocated(this%buf)) deallocate(this%buf)
124 if (allocated(this%sbuf)) deallocate(this%sbuf)
125 if (allocated(this%buf_v)) deallocate(this%buf_v)
126 if (allocated(this%sbuf_v)) deallocate(this%sbuf_v)
127
128 call this%plan%free()
129
130 call this%free_order()
131 call this%free_dofs()
132
133 end subroutine gs_crystal_free
134
136 subroutine gs_crystal_nbrecv(this, tag)
137 class(gs_crystal_t), intent(inout) :: this
138 integer, intent(in) :: tag
139 integer :: co, ierr
140
141 !$omp master
142 this%tag = tag
143 this%nrreq = 0
144 this%nsreq = 0
145 if (this%plan%nstage .gt. 0) then
146 associate(st => this%plan%stage(1))
147 ! The words that stay are packed into [1, nkw] of the same column
148 ! by nbsend, so the arriving ones can land straight behind them
149 co = (st%dst_sel - 1) * this%plan%nwrk
150 if (st%src .ge. 0) then
151 this%nrreq = this%nrreq + 1
152 call mpi_irecv(this%buf(co + st%nkw + 1), st%nrw, &
153 mpi_real_precision, st%src, tag, neko_comm, &
154 this%rreq(this%nrreq), ierr)
155 end if
156 if (st%src2 .ge. 0) then
157 this%nrreq = this%nrreq + 1
158 call mpi_irecv(this%buf(co + st%nkw + st%nrw + 1), st%nr2w, &
159 mpi_real_precision, st%src2, tag, neko_comm, &
160 this%rreq(this%nrreq), ierr)
161 end if
162 end associate
163 end if
164 !$omp end master
165 !$omp barrier
166
167 end subroutine gs_crystal_nbrecv
168
170 subroutine gs_crystal_nbsend(this, u, n, tag, deps, strm)
171 class(gs_crystal_t), intent(inout) :: this
172 integer, intent(in) :: n
173 real(kind=rp), dimension(n), intent(inout) :: u
174 integer, intent(in) :: tag
175 type(c_ptr), intent(inout) :: deps
176 type(c_ptr), intent(inout) :: strm
177 integer :: j, co, ierr
178
179 if (this%plan%nstage .eq. 0) return
180
181 associate(st => this%plan%stage(1))
182 co = (st%dst_sel - 1) * this%plan%nwrk
183
184 ! Gather the words leaving on the first stage straight out of the
185 ! shared vector, so the routing never sees a separate packed copy
186 !$omp do
187 do j = 1, st%nsw
188 this%sbuf(j) = u(this%plan%pack_send_dof(j))
189 end do
190 !$omp end do
191
192 !$omp master
193 if (st%dst .ge. 0) then
194 this%nsreq = 1
195 call mpi_isend(this%sbuf(1), st%nsw, mpi_real_precision, &
196 st%dst, tag, neko_comm, this%sreq(1), ierr)
197 end if
198 !$omp end master
199
200 ! The words that stay put on this stage, into the same column the
201 ! arriving ones are being received behind
202 !$omp do
203 do j = 1, st%nkw
204 this%buf(co + j) = u(this%plan%pack_keep_dof(j))
205 end do
206 !$omp end do
207 end associate
208
209 end subroutine gs_crystal_nbsend
210
213 subroutine gs_crystal_nbwait(this, u, n, op, strm)
214 class(gs_crystal_t), intent(inout) :: this
215 integer, intent(in) :: n
216 real(kind=rp), dimension(n), intent(inout) :: u
217 type(c_ptr), intent(inout) :: strm
218 integer :: op
219 integer :: s, i, j, co, so, fo, ierr
220
221 if (this%plan%nstage .eq. 0) return
222
223 !$omp master
224 call mpi_waitall(this%nrreq, this%rreq, mpi_statuses_ignore, ierr)
225 call mpi_waitall(this%nsreq, this%sreq, mpi_statuses_ignore, ierr)
226 !$omp end master
227 !$omp barrier
228
229 do s = 2, this%plan%nstage
230 associate(st => this%plan%stage(s))
231 so = (st%src_sel - 1) * this%plan%nwrk
232 co = (st%dst_sel - 1) * this%plan%nwrk
233
234 !$omp master
235 this%nrreq = 0
236 this%nsreq = 0
237 if (st%src .ge. 0) then
238 this%nrreq = this%nrreq + 1
239 call mpi_irecv(this%buf(co + st%nkw + 1), st%nrw, &
240 mpi_real_precision, st%src, this%tag, neko_comm, &
241 this%rreq(this%nrreq), ierr)
242 end if
243 if (st%src2 .ge. 0) then
244 this%nrreq = this%nrreq + 1
245 call mpi_irecv(this%buf(co + st%nkw + st%nrw + 1), st%nr2w, &
246 mpi_real_precision, st%src2, this%tag, neko_comm, &
247 this%rreq(this%nrreq), ierr)
248 end if
249 !$omp end master
250
251 if (st%dst .ge. 0) then
252 !$omp do
253 do j = 1, st%nsw
254 this%sbuf(j) = this%buf(so + st%send_idx(j))
255 end do
256 !$omp end do
257 !$omp master
258 this%nsreq = 1
259 call mpi_isend(this%sbuf(1), st%nsw, mpi_real_precision, &
260 st%dst, this%tag, neko_comm, this%sreq(1), ierr)
261 !$omp end master
262 end if
263
264 ! A stage that sends nothing keeps every word where it is, and is
265 ! receiving into the column it already occupies
266 if (.not. st%inplace) then
267 !$omp do
268 do j = 1, st%nkw
269 this%buf(co + j) = this%buf(so + st%keep_idx(j))
270 end do
271 !$omp end do
272 end if
273
274 !$omp master
275 call mpi_waitall(this%nrreq, this%rreq, mpi_statuses_ignore, ierr)
276 call mpi_waitall(this%nsreq, this%sreq, mpi_statuses_ignore, ierr)
277 !$omp end master
278 !$omp barrier
279 end associate
280 end do
281
282 ! Reduce record by record: a shared dof may be delivered by several
283 ! peers, so its contributions are separated by the record barriers
284 fo = (this%plan%final_sel - 1) * this%plan%nwrk
285 do i = 1, this%plan%nfinal_rec
286 associate(ro => this%plan%final_off(i), rl => this%plan%final_len(i))
287 select case (op)
288 case (gs_op_add)
289 !OCL NORECURRENCE, NOVREC, NOALIAS
290 !DIR$ CONCURRENT
291 !DIR$ IVDEP
292 !GCC$ ivdep
293 !NEC$ IVDEP
294 !$omp do
295 do j = 1, rl
296 u(this%plan%unpack_dof(ro + j)) = &
297 u(this%plan%unpack_dof(ro + j)) + this%buf(fo + ro + j)
298 end do
299 !$omp end do
300 case (gs_op_mul)
301 !OCL NORECURRENCE, NOVREC, NOALIAS
302 !DIR$ CONCURRENT
303 !DIR$ IVDEP
304 !GCC$ ivdep
305 !NEC$ IVDEP
306 !$omp do
307 do j = 1, rl
308 u(this%plan%unpack_dof(ro + j)) = &
309 u(this%plan%unpack_dof(ro + j)) * this%buf(fo + ro + j)
310 end do
311 !$omp end do
312 case (gs_op_min)
313 !OCL NORECURRENCE, NOVREC, NOALIAS
314 !DIR$ CONCURRENT
315 !DIR$ IVDEP
316 !GCC$ ivdep
317 !NEC$ IVDEP
318 !$omp do
319 do j = 1, rl
320 u(this%plan%unpack_dof(ro + j)) = &
321 min(u(this%plan%unpack_dof(ro + j)), &
322 this%buf(fo + ro + j))
323 end do
324 !$omp end do
325 case (gs_op_max)
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 max(u(this%plan%unpack_dof(ro + j)), &
335 this%buf(fo + ro + j))
336 end do
337 !$omp end do
338 case default
339 call neko_error("Unknown operation in gs_crystal_nbwait")
340 end select
341 end associate
342 end do
343
344 end subroutine gs_crystal_nbwait
345
347 subroutine gs_crystal_nbrecv_vec(this, tag, nc)
348 class(gs_crystal_t), intent(inout) :: this
349 integer, intent(in) :: tag, nc
350 integer :: co, ierr
351
352 if (nc .gt. gs_vec_nc) then
353 call neko_error('gs_crystal: too many components in vector exchange')
354 end if
355
356 !$omp master
357 this%tag = tag
358 this%nrreq = 0
359 this%nsreq = 0
360 if (this%plan%nstage .gt. 0) then
361 associate(st => this%plan%stage(1))
362 co = (st%dst_sel - 1) * nc * this%plan%nwrk
363 if (st%src .ge. 0) then
364 this%nrreq = this%nrreq + 1
365 call mpi_irecv(this%buf_v(co + nc*st%nkw + 1), nc*st%nrw, &
366 mpi_real_precision, st%src, tag, neko_comm, &
367 this%rreq(this%nrreq), ierr)
368 end if
369 if (st%src2 .ge. 0) then
370 this%nrreq = this%nrreq + 1
371 call mpi_irecv(this%buf_v(co + nc*(st%nkw + st%nrw) + 1), &
372 nc*st%nr2w, mpi_real_precision, st%src2, tag, neko_comm, &
373 this%rreq(this%nrreq), ierr)
374 end if
375 end associate
376 end if
377 !$omp end master
378 !$omp barrier
379
380 end subroutine gs_crystal_nbrecv_vec
381
385 subroutine gs_crystal_nbsend_vec(this, u, n, nc, tag, deps, strm)
386 class(gs_crystal_t), intent(inout) :: this
387 integer, intent(in) :: n, nc
388 real(kind=rp), dimension(nc*n), intent(inout) :: u
389 integer, intent(in) :: tag
390 type(c_ptr), intent(inout) :: deps
391 type(c_ptr), intent(inout) :: strm
392 integer :: j, c, co, ierr
393
394 if (this%plan%nstage .eq. 0) return
395
396 associate(st => this%plan%stage(1))
397 co = (st%dst_sel - 1) * nc * this%plan%nwrk
398
399 !$omp do
400 do j = 1, st%nsw
401 do c = 1, nc
402 this%sbuf_v(nc*(j-1) + c) = &
403 u((c-1)*n + this%plan%pack_send_dof(j))
404 end do
405 end do
406 !$omp end do
407
408 !$omp master
409 if (st%dst .ge. 0) then
410 this%nsreq = 1
411 call mpi_isend(this%sbuf_v(1), nc*st%nsw, mpi_real_precision, &
412 st%dst, tag, neko_comm, this%sreq(1), ierr)
413 end if
414 !$omp end master
415
416 !$omp do
417 do j = 1, st%nkw
418 do c = 1, nc
419 this%buf_v(co + nc*(j-1) + c) = &
420 u((c-1)*n + this%plan%pack_keep_dof(j))
421 end do
422 end do
423 !$omp end do
424 end associate
425
426 end subroutine gs_crystal_nbsend_vec
427
430 subroutine gs_crystal_nbwait_vec(this, u, n, nc, op, strm)
431 class(gs_crystal_t), intent(inout) :: this
432 integer, intent(in) :: n, nc
433 real(kind=rp), dimension(nc*n), intent(inout) :: u
434 type(c_ptr), intent(inout) :: strm
435 integer :: op
436 integer :: s, i, j, c, co, so, fo, ierr
437
438 if (this%plan%nstage .eq. 0) return
439
440 !$omp master
441 call mpi_waitall(this%nrreq, this%rreq, mpi_statuses_ignore, ierr)
442 call mpi_waitall(this%nsreq, this%sreq, mpi_statuses_ignore, ierr)
443 !$omp end master
444 !$omp barrier
445
446 do s = 2, this%plan%nstage
447 associate(st => this%plan%stage(s))
448 so = (st%src_sel - 1) * nc * this%plan%nwrk
449 co = (st%dst_sel - 1) * nc * this%plan%nwrk
450
451 !$omp master
452 this%nrreq = 0
453 this%nsreq = 0
454 if (st%src .ge. 0) then
455 this%nrreq = this%nrreq + 1
456 call mpi_irecv(this%buf_v(co + nc*st%nkw + 1), nc*st%nrw, &
457 mpi_real_precision, st%src, this%tag, neko_comm, &
458 this%rreq(this%nrreq), ierr)
459 end if
460 if (st%src2 .ge. 0) then
461 this%nrreq = this%nrreq + 1
462 call mpi_irecv(this%buf_v(co + nc*(st%nkw + st%nrw) + 1), &
463 nc*st%nr2w, mpi_real_precision, st%src2, this%tag, &
464 neko_comm, this%rreq(this%nrreq), ierr)
465 end if
466 !$omp end master
467
468 if (st%dst .ge. 0) then
469 !$omp do
470 do j = 1, st%nsw
471 do c = 1, nc
472 this%sbuf_v(nc*(j-1) + c) = &
473 this%buf_v(so + nc*(st%send_idx(j) - 1) + c)
474 end do
475 end do
476 !$omp end do
477 !$omp master
478 this%nsreq = 1
479 call mpi_isend(this%sbuf_v(1), nc*st%nsw, mpi_real_precision, &
480 st%dst, this%tag, neko_comm, this%sreq(1), ierr)
481 !$omp end master
482 end if
483
484 if (.not. st%inplace) then
485 !$omp do
486 do j = 1, st%nkw
487 do c = 1, nc
488 this%buf_v(co + nc*(j-1) + c) = &
489 this%buf_v(so + nc*(st%keep_idx(j) - 1) + c)
490 end do
491 end do
492 !$omp end do
493 end if
494
495 !$omp master
496 call mpi_waitall(this%nrreq, this%rreq, mpi_statuses_ignore, ierr)
497 call mpi_waitall(this%nsreq, this%sreq, mpi_statuses_ignore, ierr)
498 !$omp end master
499 !$omp barrier
500 end associate
501 end do
502
503 fo = (this%plan%final_sel - 1) * nc * this%plan%nwrk
504 do i = 1, this%plan%nfinal_rec
505 associate(ro => this%plan%final_off(i), rl => this%plan%final_len(i))
506 select case (op)
507 case (gs_op_add)
508 !$omp do
509 do j = 1, rl
510 do c = 1, nc
511 u((c-1)*n + this%plan%unpack_dof(ro + j)) = &
512 u((c-1)*n + this%plan%unpack_dof(ro + j)) + &
513 this%buf_v(fo + nc*(ro + j - 1) + c)
514 end do
515 end do
516 !$omp end do
517 case (gs_op_mul)
518 !$omp do
519 do j = 1, rl
520 do c = 1, nc
521 u((c-1)*n + this%plan%unpack_dof(ro + j)) = &
522 u((c-1)*n + this%plan%unpack_dof(ro + j)) * &
523 this%buf_v(fo + nc*(ro + j - 1) + c)
524 end do
525 end do
526 !$omp end do
527 case (gs_op_min)
528 !$omp do
529 do j = 1, rl
530 do c = 1, nc
531 u((c-1)*n + this%plan%unpack_dof(ro + j)) = &
532 min(u((c-1)*n + this%plan%unpack_dof(ro + j)), &
533 this%buf_v(fo + nc*(ro + j - 1) + c))
534 end do
535 end do
536 !$omp end do
537 case (gs_op_max)
538 !$omp do
539 do j = 1, rl
540 do c = 1, nc
541 u((c-1)*n + this%plan%unpack_dof(ro + j)) = &
542 max(u((c-1)*n + this%plan%unpack_dof(ro + j)), &
543 this%buf_v(fo + nc*(ro + j - 1) + c))
544 end do
545 end do
546 !$omp end do
547 case default
548 call neko_error("Unknown operation in gs_crystal_nbwait_vec")
549 end select
550 end associate
551 end do
552
553 end subroutine gs_crystal_nbwait_vec
554
555end 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_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