51 use mpi_f08,
only : mpi_request, mpi_isend, mpi_irecv, mpi_waitall, &
52 mpi_integer8, mpi_statuses_ignore
57 use,
intrinsic :: iso_c_binding
80 real(kind=
rp),
pointer :: send_buf(:) => null()
84 real(kind=
rp),
pointer :: recv_buf(:) => null()
86 integer,
allocatable :: send_len(:), recv_len(:)
87 integer,
allocatable :: send_offset(:), recv_offset(:)
89 integer(c_int64_t),
allocatable :: send_off64(:), send_len64(:)
93 integer(c_int64_t),
allocatable :: rmt_vcq_id(:), rmt_stadd(:)
94 integer(c_int64_t),
allocatable :: dst_off0(:), dst_off1(:)
95 integer(c_int),
allocatable :: dst_tag(:)
98 integer(c_int),
allocatable :: recv_indices(:)
99 integer(c_int) :: ncompleted = 0
101 integer :: buf_size = 0
105 integer :: send_size = 0
107 integer :: elem_size = 0
109 integer :: parity = 0
111 type(c_ptr) :: ctx = c_null_ptr
119 real(kind=
rp),
pointer :: send_buf_v(:) => null()
120 real(kind=
rp),
pointer :: recv_buf_v(:) => null()
125 integer(c_int64_t),
allocatable :: rmt_vcq_id_v(:), rmt_stadd_v(:)
126 integer(c_int64_t),
allocatable :: rmt_off_v(:), rmt_bufsz_v(:)
130 integer(c_int64_t),
allocatable :: send_off64_v(:), send_len64_v(:)
131 integer(c_int64_t),
allocatable :: dst_off_v(:)
133 integer :: parity_v = 0
135 type(c_ptr) :: ctx_v = c_null_ptr
149 integer(c_int64_t) :: gs_utofu_edata_max = 255
152 integer(c_int) :: gs_utofu_nrvcq = 1
154 logical,
save :: gs_utofu_logged = .false.
157 subroutine gs_utofu_init_ep(ntni_req, nthreads, ntni_out, nvcq_out, &
158 nrvcq_out, edata_max, ierr) bind(c, name = 'gs_utofu_init')
159 import :: c_int, c_int64_t
160 integer(c_int),
value :: ntni_req, nthreads
161 integer(c_int) :: ntni_out, nvcq_out, nrvcq_out
162 integer(c_int64_t) :: edata_max
163 integer(c_int) :: ierr
164 end subroutine gs_utofu_init_ep
167 recv_bytes, ctx, nrvcq, recv_vcq_id, recv_stadd, ierr) &
168 bind(c, name =
'gs_utofu_ctx_create')
169 import :: c_ptr, c_size_t, c_int64_t, c_int
170 type(c_ptr),
value :: send_buf, recv_buf
171 integer(c_size_t),
value :: send_bytes, recv_bytes
173 integer(c_int) :: nrvcq
174 integer(c_int64_t) :: recv_vcq_id(*)
175 integer(c_int64_t) :: recv_stadd(*)
176 integer(c_int) :: ierr
180 import :: c_ptr, c_int
181 type(c_ptr),
value :: ctx
182 integer(c_int) :: ierr
186 send_len, rmt_vcq_id, rmt_stadd, dst_off0, dst_off1, dst_tag, &
187 parity, send_base, elem_size, ierr) &
188 bind(c, name =
'gs_utofu_post_puts')
189 import :: c_ptr, c_int, c_int64_t
190 type(c_ptr),
value :: ctx
191 integer(c_int),
value :: tid, nteam, nsend, parity, elem_size
192 integer(c_int64_t) :: send_off(*), send_len(*)
193 integer(c_int64_t) :: rmt_vcq_id(*), rmt_stadd(*)
194 integer(c_int64_t) :: dst_off0(*), dst_off1(*)
195 integer(c_int) :: dst_tag(*)
196 integer(c_int64_t),
value :: send_base
197 integer(c_int) :: ierr
201 ierr) bind(c, name = 'gs_utofu_poll_recv')
202 import :: c_ptr, c_int
203 type(c_ptr),
value :: ctx
204 integer(c_int),
value :: parity, cap
205 integer(c_int) :: ncompleted
206 integer(c_int) :: out_idx(*)
207 integer(c_int) :: ierr
211 bind(c, name =
'gs_utofu_drain_half')
212 import :: c_ptr, c_int
213 type(c_ptr),
value :: ctx
214 integer(c_int),
value :: half, tid, nteam
215 integer(c_int) :: ierr
228 integer :: i, nsend, nrecv, send_total, recv_total
229 integer :: ntni_req, env_len, max_tag, nthreads
230 integer(c_int) :: ntni, nvcq, nrv, ierr
231 integer(c_int64_t),
allocatable :: recv_vcq_id(:), recv_stadd(:)
232 integer(c_size_t) :: send_bytes, recv_bytes
233 character(len=32) :: env_val
234 character(len=LOG_SIZE) :: log_buf
235 integer(c_int64_t),
allocatable :: msg_out(:,:), msg_in(:,:)
236 type(mpi_request),
allocatable :: sreq(:), rreq(:)
238 call this%init_order(send_pe, recv_pe)
240 nsend =
size(this%send_pe)
241 nrecv =
size(this%recv_pe)
242 this%elem_size = int(storage_size(1.0_rp) / 8)
244 allocate(this%send_len(nsend), this%send_offset(nsend))
245 allocate(this%send_off64(nsend), this%send_len64(nsend))
246 allocate(this%rmt_vcq_id(nsend), this%rmt_stadd(nsend))
247 allocate(this%dst_off0(nsend), this%dst_off1(nsend), this%dst_tag(nsend))
248 allocate(this%recv_len(nrecv), this%recv_offset(nrecv))
249 allocate(this%recv_indices(nrecv))
254 this%recv_len(i) = this%recv_dof(this%recv_pe(i))%size()
255 this%recv_offset(i) = recv_total
256 recv_total = recv_total + this%recv_len(i)
258 this%buf_size = recv_total
263 this%send_len(i) = this%send_dof(this%send_pe(i))%size()
264 this%send_offset(i) = send_total
265 this%send_off64(i) = int(send_total, c_int64_t)
266 this%send_len64(i) = int(this%send_len(i), c_int64_t)
267 send_total = send_total + this%send_len(i)
269 this%send_size = send_total
271 allocate(this%send_buf(
max(1, 2 * send_total)))
272 allocate(this%recv_buf(
max(1, 2 * recv_total)))
276 call get_environment_variable(
"NEKO_GS_UTOFU_NTNI", env_val, env_len)
277 if (env_len .gt. 0)
read(env_val(1:env_len), *) ntni_req
283 call gs_utofu_init_ep(int(ntni_req, c_int), int(nthreads, c_int), &
284 ntni, nvcq, gs_utofu_nrvcq, gs_utofu_edata_max, ierr)
285 if (ierr .ne. 0)
call neko_error(
"gs_utofu: endpoint init failed")
287 send_bytes = int(
size(this%send_buf), c_size_t) * this%elem_size
288 recv_bytes = int(
size(this%recv_buf), c_size_t) * this%elem_size
293 allocate(recv_vcq_id(gs_utofu_nrvcq), recv_stadd(gs_utofu_nrvcq))
294 recv_vcq_id = 0_c_int64_t
295 recv_stadd = 0_c_int64_t
298 c_loc(this%recv_buf), recv_bytes, this%ctx, nrv, recv_vcq_id, &
300 if (ierr .eq. 1)
then
301 call neko_error(
"gs_utofu: out of VCQs for the receive queues " // &
302 "(lower OMP_NUM_THREADS or NEKO_GS_UTOFU_NRVCQ, or set " // &
303 "NEKO_GS_UTOFU_NVCQ)")
304 else if (ierr .ne. 0)
then
305 call neko_error(
"gs_utofu: buffer registration failed")
311 if (.not. gs_utofu_logged .and.
pe_rank .eq. 0)
then
312 write(log_buf,
'(A,I0,A,I0,A,I0,A,I0,A)')
'uTofu inj. : ', &
313 int(nvcq),
' VCQs (', nthreads,
' threads) over ', int(ntni), &
314 ' TNIs (', ntni_req,
' requested)'
316 write(log_buf,
'(A,I0,A,I0,A)')
'uTofu recv : ', int(nrv), &
317 ' of ', int(gs_utofu_nrvcq),
' VCQs (first instance)'
319 gs_utofu_logged = .true.
325 allocate(msg_out(5,
max(1, nrecv)), msg_in(5,
max(1, nsend)))
326 allocate(sreq(
max(1, nrecv)), rreq(
max(1, nsend)))
331 msg_out(1, i) = recv_stadd(mod(i - 1, int(nrv)) + 1)
332 msg_out(2, i) = recv_vcq_id(mod(i - 1, int(nrv)) + 1)
333 msg_out(3, i) = int(this%recv_offset(i), c_int64_t)
334 msg_out(4, i) = int(this%buf_size + this%recv_offset(i), c_int64_t)
335 msg_out(5, i) = int(i - 1, c_int64_t)
339 call mpi_irecv(msg_in(:, i), 5, mpi_integer8, this%send_pe(i), &
343 call mpi_isend(msg_out(:, i), 5, mpi_integer8, this%recv_pe(i), &
346 if (nsend .gt. 0)
call mpi_waitall(nsend, rreq, mpi_statuses_ignore)
347 if (nrecv .gt. 0)
call mpi_waitall(nrecv, sreq, mpi_statuses_ignore)
351 this%rmt_stadd(i) = msg_in(1, i)
352 this%rmt_vcq_id(i) = msg_in(2, i)
353 this%dst_off0(i) = msg_in(3, i)
354 this%dst_off1(i) = msg_in(4, i)
355 this%dst_tag(i) = int(msg_in(5, i), c_int)
356 max_tag =
max(max_tag, this%dst_tag(i))
359 deallocate(msg_out, msg_in, sreq, rreq)
364 if (int(2 * max_tag + 1,
i8) .gt. int(gs_utofu_edata_max,
i8)) &
365 call neko_error(
"gs_utofu: neighbour count exceeds edata capacity")
369 allocate(this%send_buf_v(
max(1, 2 *
gs_vec_nc * send_total)))
370 allocate(this%recv_buf_v(
max(1, 2 *
gs_vec_nc * recv_total)))
371 allocate(this%rmt_vcq_id_v(nsend), this%rmt_stadd_v(nsend))
372 allocate(this%rmt_off_v(nsend), this%rmt_bufsz_v(nsend))
373 allocate(this%send_off64_v(nsend), this%send_len64_v(nsend))
374 allocate(this%dst_off_v(nsend))
376 send_bytes = int(
size(this%send_buf_v), c_size_t) * this%elem_size
377 recv_bytes = int(
size(this%recv_buf_v), c_size_t) * this%elem_size
378 recv_vcq_id = 0_c_int64_t
379 recv_stadd = 0_c_int64_t
382 c_loc(this%recv_buf_v), recv_bytes, this%ctx_v, nrv, recv_vcq_id, &
384 if (ierr .eq. 1)
then
385 call neko_error(
"gs_utofu: out of VCQs for the vector receive " // &
386 "queues (lower OMP_NUM_THREADS or NEKO_GS_UTOFU_NRVCQ, or " // &
387 "set NEKO_GS_UTOFU_NVCQ)")
388 else if (ierr .ne. 0)
then
389 call neko_error(
"gs_utofu: vector buffer registration failed")
397 allocate(msg_out(4,
max(1, nrecv)), msg_in(4,
max(1, nsend)))
398 allocate(sreq(
max(1, nrecv)), rreq(
max(1, nsend)))
402 msg_out(1, i) = recv_stadd(mod(i - 1, int(nrv)) + 1)
403 msg_out(2, i) = recv_vcq_id(mod(i - 1, int(nrv)) + 1)
404 msg_out(3, i) = int(this%recv_offset(i), c_int64_t)
405 msg_out(4, i) = int(this%buf_size, c_int64_t)
409 call mpi_irecv(msg_in(:, i), 4, mpi_integer8, this%send_pe(i), &
413 call mpi_isend(msg_out(:, i), 4, mpi_integer8, this%recv_pe(i), &
416 if (nsend .gt. 0)
call mpi_waitall(nsend, rreq, mpi_statuses_ignore)
417 if (nrecv .gt. 0)
call mpi_waitall(nrecv, sreq, mpi_statuses_ignore)
420 this%rmt_stadd_v(i) = msg_in(1, i)
421 this%rmt_vcq_id_v(i) = msg_in(2, i)
422 this%rmt_off_v(i) = msg_in(3, i)
423 this%rmt_bufsz_v(i) = msg_in(4, i)
426 deallocate(msg_out, msg_in, sreq, rreq)
431 call get_environment_variable(
"NEKO_GS_UTOFU_VEC", env_val, env_len)
432 this%vec_supported = .not. (env_len .gt. 0 .and. env_val(1:1) .eq.
'0')
437 this%vec_ready = .true.
439 call neko_error(
"uTofu support not built; reconfigure with --with-utofu")
447 integer(c_int) :: ierr
449 if (c_associated(this%ctx))
then
451 this%ctx = c_null_ptr
454 if (c_associated(this%ctx_v))
then
456 this%ctx_v = c_null_ptr
459 if (
associated(this%send_buf))
then
460 deallocate(this%send_buf)
463 if (
associated(this%recv_buf))
then
464 deallocate(this%recv_buf)
467 if (
associated(this%send_buf_v))
then
468 deallocate(this%send_buf_v)
471 if (
associated(this%recv_buf_v))
then
472 deallocate(this%recv_buf_v)
475 if (
allocated(this%send_len))
then
476 deallocate(this%send_len)
479 if (
allocated(this%recv_len))
then
480 deallocate(this%recv_len)
483 if (
allocated(this%send_offset))
then
484 deallocate(this%send_offset)
487 if (
allocated(this%recv_offset))
then
488 deallocate(this%recv_offset)
491 if (
allocated(this%send_off64))
then
492 deallocate(this%send_off64)
495 if (
allocated(this%send_len64))
then
496 deallocate(this%send_len64)
499 if (
allocated(this%rmt_vcq_id))
then
500 deallocate(this%rmt_vcq_id)
503 if (
allocated(this%rmt_stadd))
then
504 deallocate(this%rmt_stadd)
507 if (
allocated(this%dst_off0))
then
508 deallocate(this%dst_off0)
511 if (
allocated(this%dst_off1))
then
512 deallocate(this%dst_off1)
515 if (
allocated(this%dst_tag))
then
516 deallocate(this%dst_tag)
519 if (
allocated(this%recv_indices))
then
520 deallocate(this%recv_indices)
523 if (
allocated(this%rmt_vcq_id_v))
then
524 deallocate(this%rmt_vcq_id_v)
527 if (
allocated(this%rmt_stadd_v))
then
528 deallocate(this%rmt_stadd_v)
531 if (
allocated(this%rmt_off_v))
then
532 deallocate(this%rmt_off_v)
535 if (
allocated(this%rmt_bufsz_v))
then
536 deallocate(this%rmt_bufsz_v)
539 if (
allocated(this%send_off64_v))
then
540 deallocate(this%send_off64_v)
543 if (
allocated(this%send_len64_v))
then
544 deallocate(this%send_len64_v)
547 if (
allocated(this%dst_off_v))
then
548 deallocate(this%dst_off_v)
551 call this%free_order()
552 call this%free_dofs()
559 integer,
intent(in) :: n
560 real(kind=
rp),
dimension(n),
intent(inout) :: u
561 integer,
intent(in) :: tag
562 type(c_ptr),
intent(inout) :: deps
563 type(c_ptr),
intent(inout) :: strm
565 integer :: i, j, dst, off, ndst, send_base, tid, nteam
566 integer(c_int) :: ierr
567 integer,
pointer :: sp(:)
586 int(tid, c_int), int(nteam, c_int), ierr)
587 if (ierr .ne. 0)
call neko_error(
"gs_utofu: send drain failed")
595 send_base = this%parity * this%send_size
597 do i = 1,
size(this%send_pe)
598 dst = this%send_pe(i)
599 off = this%send_offset(i)
600 ndst = this%send_len(i)
601 sp => this%send_dof(dst)%array()
609 this%send_buf(send_base + off + j) = u(sp(j))
619 int(
size(this%send_pe), c_int), &
620 this%send_off64, this%send_len64, this%rmt_vcq_id, this%rmt_stadd, &
621 this%dst_off0, this%dst_off1, this%dst_tag, &
622 int(this%parity, c_int), int(send_base, c_int64_t), &
623 int(this%elem_size, c_int), ierr)
624 if (ierr .ne. 0)
call neko_error(
"gs_utofu: put failed")
635 integer,
intent(in) :: tag
642 integer,
intent(in) :: n
643 real(kind=
rp),
dimension(n),
intent(inout) :: u
644 type(c_ptr),
intent(inout) :: strm
647 integer :: i, j, k, src, off, nsrc, nreqs, base
648 integer(c_int) :: ierr
649 integer,
pointer ::
sp(:)
652 real(kind=
rp),
pointer :: rbuf(:)
654 base = this%parity * this%buf_size
655 nreqs =
size(this%recv_pe)
656 rbuf => this%recv_buf
658 do while (nreqs .gt. 0)
665 do while (this%ncompleted .eq. 0)
667 int(
size(this%recv_pe), c_int), this%ncompleted, &
668 this%recv_indices, ierr)
669 if (ierr .ne. 0)
call neko_error(
"gs_utofu: mrq poll failed")
674 do k = 1, this%ncompleted
675 i = this%recv_indices(k)
676 src = this%recv_pe(i)
677 off = this%recv_offset(i)
678 nsrc = this%recv_len(i)
679 sp => this%recv_dof(src)%array()
689 u(
sp(j)) = u(
sp(j)) + rbuf(base + off + j)
700 u(
sp(j)) = u(
sp(j)) * rbuf(base + off + j)
711 u(
sp(j)) = min(u(
sp(j)), rbuf(base + off + j))
722 u(
sp(j)) =
max(u(
sp(j)), rbuf(base + off + j))
726 call neko_error(
"Unknown operation in gs_nbwait_utofu")
730 nreqs = nreqs - this%ncompleted
740 this%parity = 1 - this%parity
755 integer,
intent(in) :: n, nc
756 real(kind=
rp),
dimension(nc*n),
intent(inout) :: u
757 integer,
intent(in) :: tag
758 type(c_ptr),
intent(inout) :: deps
759 type(c_ptr),
intent(inout) :: strm
761 integer :: i, j, c, dst, off, ndst, send_base, tid, nteam
762 integer(c_int) :: ierr
763 integer,
pointer :: sp(:)
773 int(tid, c_int), int(nteam, c_int), ierr)
774 if (ierr .ne. 0)
call neko_error(
"gs_utofu: vector send drain failed")
781 send_base = this%parity_v * (
gs_vec_nc * this%send_size)
783 do i = 1,
size(this%send_pe)
784 this%send_off64_v(i) = int(nc, c_int64_t) * this%send_off64(i)
785 this%send_len64_v(i) = int(nc, c_int64_t) * this%send_len64(i)
786 this%dst_off_v(i) = int(this%parity_v, c_int64_t) &
788 + int(nc, c_int64_t) * this%rmt_off_v(i)
794 do i = 1,
size(this%send_pe)
795 dst = this%send_pe(i)
796 off = this%send_offset(i)
797 ndst = this%send_len(i)
798 sp => this%send_dof(dst)%array()
807 this%send_buf_v(send_base + nc*off + (c-1)*ndst + j) = &
818 int(nteam, c_int), int(
size(this%send_pe), c_int), &
819 this%send_off64_v, this%send_len64_v, this%rmt_vcq_id_v, &
820 this%rmt_stadd_v, this%dst_off_v, this%dst_off_v, this%dst_tag, &
821 int(this%parity_v, c_int), int(send_base, c_int64_t), &
822 int(this%elem_size, c_int), ierr)
823 if (ierr .ne. 0)
call neko_error(
"gs_utofu: vector put failed")
834 integer,
intent(in) :: tag, nc
842 integer,
intent(in) :: n, nc
843 real(kind=
rp),
dimension(nc*n),
intent(inout) :: u
844 type(c_ptr),
intent(inout) :: strm
847 integer :: i, j, c, k, src, off, nsrc, nreqs, base
848 integer(c_int) :: ierr
849 integer,
pointer ::
sp(:)
850 real(kind=
rp),
pointer :: rbuf(:)
852 base = this%parity_v * (
gs_vec_nc * this%buf_size)
853 nreqs =
size(this%recv_pe)
854 rbuf => this%recv_buf_v
856 do while (nreqs .gt. 0)
860 do while (this%ncompleted .eq. 0)
862 int(
size(this%recv_pe), c_int), this%ncompleted, &
863 this%recv_indices, ierr)
865 call neko_error(
"gs_utofu: vector mrq poll failed")
870 do k = 1, this%ncompleted
871 i = this%recv_indices(k)
872 src = this%recv_pe(i)
873 off = this%recv_offset(i)
874 nsrc = this%recv_len(i)
875 sp => this%recv_dof(src)%array()
881 u((c-1)*n +
sp(j)) = u((c-1)*n +
sp(j)) + &
882 rbuf(base + nc*off + (c-1)*nsrc + j)
890 u((c-1)*n +
sp(j)) = u((c-1)*n +
sp(j)) * &
891 rbuf(base + nc*off + (c-1)*nsrc + j)
899 u((c-1)*n +
sp(j)) = min(u((c-1)*n +
sp(j)), &
900 rbuf(base + nc*off + (c-1)*nsrc + j))
908 u((c-1)*n +
sp(j)) =
max(u((c-1)*n +
sp(j)), &
909 rbuf(base + nc*off + (c-1)*nsrc + j))
914 call neko_error(
"Unknown operation in gs_nbwait_vec_utofu")
918 nreqs = nreqs - this%ncompleted
927 this%parity_v = 1 - this%parity_v
void gs_utofu_post_puts(void *vctx, int tid, int nteam, int nsend, const int64_t *send_off, const int64_t *send_len, const uint64_t *rmt_vcq_id, const uint64_t *rmt_stadd, const int64_t *dst_off0, const int64_t *dst_off1, const int *dst_tag, int parity, int64_t send_base, int elem_size, int *ierr)
void gs_utofu_ctx_free(void *vctx, int *ierr)
void gs_utofu_poll_recv(void *vctx, int parity, int cap, int *ncompleted, int *out_idx, int *ierr)
void gs_utofu_ctx_create(void *send_buf, size_t send_bytes, void *recv_buf, size_t recv_bytes, void **ctx_out, int *nrvcq_out, uint64_t *recv_vcq_id, uint64_t *recv_stadd, int *ierr)
void gs_utofu_drain_half(void *vctx, int half, int tid, int nteam, int *ierr)
integer, public pe_rank
MPI rank.
type(mpi_comm), public neko_comm
MPI communicator.
Defines a gather-scatter communication method.
integer, parameter, public gs_vec_nc
Maximum number of components handled by the fused vector (multi-component) halo exchange used by gs_o...
integer, parameter, public gs_comm_utofu
Defines Gather-scatter operations.
integer, parameter, public gs_op_add
integer, parameter, public gs_op_max
integer, parameter, public gs_op_min
integer, parameter, public gs_op_mul
Defines a gather-scatter backend using the native Tofu interconnect (uTofu). Each rank registers its ...
integer, parameter gs_utofu_xchg_tag
MPI tag used for the one-off neighbour metadata exchange at init.
subroutine gs_utofu_init(this, send_pe, recv_pe)
Initialise the uTofu communication method. See gs_comm.f90 for details.
subroutine gs_nbsend_vec_utofu(this, u, n, nc, tag, deps, strm)
Pack the fused nc-component send slabs and fire the puts (non-blocking).
integer, parameter gs_utofu_xchg_tag_v
Ditto for the fused vector path's metadata exchange.
subroutine gs_nbwait_vec_utofu(this, u, n, nc, op, strm)
Poll for incoming vector puts, reducing each nc-component slab into u as it lands,...
subroutine gs_nbwait_utofu(this, u, n, op, strm)
Poll for incoming puts, reducing each slab into u as it lands, then wait for our own sends to drain a...
logical, parameter, public gs_utofu_avail
Whether uTofu support was built into this Neko (–with-utofu). Lets callers (e.g. the gs comm....
subroutine gs_nbsend_utofu(this, u, n, tag, deps, strm)
Pack the send slabs and fire all one-sided puts (non-blocking).
subroutine gs_nbrecv_vec_utofu(this, tag, nc)
No-op: the vector receive buffer is registered once at init, so there is nothing to post per round.
subroutine gs_nbrecv_utofu(this, tag)
No-op: the one-sided receive buffer is registered once at init, so there is nothing to post per round...
subroutine gs_utofu_free(this)
Deallocate the uTofu communication method.
type(log_t), public neko_log
Global log stream.
integer, parameter, public log_size
integer, parameter, public i8
integer, parameter, public sp
integer, parameter, public rp
Global precision used in computations.
Implements a dynamic stack ADT.
Gather-scatter communication method.
Gather-scatter communication using one-sided uTofu puts.