Cabana 0.8.0-dev
 
Loading...
Searching...
No Matches
Cabana_Grid_Halo.hpp
Go to the documentation of this file.
1/****************************************************************************
2 * Copyright (c) 2018-2023 by the Cabana authors *
3 * All rights reserved. *
4 * *
5 * This file is part of the Cabana library. Cabana is distributed under a *
6 * BSD 3-clause license. For the licensing terms see the LICENSE file in *
7 * the top-level directory. *
8 * *
9 * SPDX-License-Identifier: BSD-3-Clause *
10 ****************************************************************************/
11
16#ifndef CABANA_GRID_HALO_HPP
17#define CABANA_GRID_HALO_HPP
18
19#include <Cabana_Grid_Array.hpp>
21
23
24#include <Kokkos_Core.hpp>
25#include <Kokkos_Profiling_ScopedRegion.hpp>
26
27#include <mpi.h>
28
29#include <algorithm>
30#include <array>
31#include <cmath>
32#include <type_traits>
33#include <vector>
34
35namespace Cabana
36{
37namespace Grid
38{
39//---------------------------------------------------------------------------//
40// Halo exchange patterns.
41//---------------------------------------------------------------------------//
43template <std::size_t NumSpaceDim>
44class HaloPattern
45{
46 public:
48 static constexpr std::size_t num_space_dim = NumSpaceDim;
49
50 // Default constructor.
51 HaloPattern() {}
52
53 // Destructor
54 virtual ~HaloPattern() = default;
55
57 void
58 setNeighbors( const std::vector<std::array<int, num_space_dim>>& neighbors )
59 {
60 _neighbors = neighbors;
61 }
62
64 std::vector<std::array<int, num_space_dim>> getNeighbors() const
65 {
66 return _neighbors;
67 }
68
69 private:
70 std::vector<std::array<int, num_space_dim>> _neighbors;
71};
72
75template <std::size_t NumSpaceDim>
77
80template <>
81class NodeHaloPattern<3> : public HaloPattern<3>
82{
83 public:
84 NodeHaloPattern()
85 : HaloPattern<3>()
86 {
87 std::vector<std::array<int, 3>> neighbors;
88 neighbors.reserve( 26 );
89 for ( int i = -1; i < 2; ++i )
90 for ( int j = -1; j < 2; ++j )
91 for ( int k = -1; k < 2; ++k )
92 if ( !( i == 0 && j == 0 && k == 0 ) )
93 neighbors.push_back( { i, j, k } );
94 this->setNeighbors( neighbors );
95 }
96};
97
100template <>
101class NodeHaloPattern<2> : public HaloPattern<2>
102{
103 public:
104 NodeHaloPattern()
105 : HaloPattern<2>()
106 {
107 std::vector<std::array<int, 2>> neighbors;
108 neighbors.reserve( 8 );
109 for ( int i = -1; i < 2; ++i )
110 for ( int j = -1; j < 2; ++j )
111 if ( !( i == 0 && j == 0 ) )
112 neighbors.push_back( { i, j } );
113 this->setNeighbors( neighbors );
114 }
115};
116
119template <std::size_t NumSpaceDim>
121
124template <>
125class FaceHaloPattern<3> : public HaloPattern<3>
126{
127 public:
128 FaceHaloPattern()
129 : HaloPattern<3>()
130 {
131 std::vector<std::array<int, 3>> neighbors = {
132 { -1, 0, 0 }, { 1, 0, 0 }, { 0, -1, 0 },
133 { 0, 1, 0 }, { 0, 0, -1 }, { 0, 0, 1 } };
134 this->setNeighbors( neighbors );
135 }
136};
137
140template <>
141class FaceHaloPattern<2> : public HaloPattern<2>
142{
143 public:
144 FaceHaloPattern()
145 : HaloPattern<2>()
146 {
147 std::vector<std::array<int, 2>> neighbors = {
148 { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
149 this->setNeighbors( neighbors );
150 }
151};
152
153//---------------------------------------------------------------------------//
154// Scatter reduction.
155//---------------------------------------------------------------------------//
156namespace ScatterReduce
157{
158
160struct Sum
161{
162};
163
166struct Min
167{
168};
169
172struct Max
173{
174};
175
181{
182};
183
184} // end namespace ScatterReduce
185
186//---------------------------------------------------------------------------//
187// Halo
188// ---------------------------------------------------------------------------//
199template <class MemorySpace>
200class Halo
201{
202 public:
204 using memory_space = MemorySpace;
205
215 template <class Pattern, class... ArrayTypes>
216 Halo( const Pattern& pattern, const int width, const ArrayTypes&... arrays )
217 {
218 // Spatial dimension.
219 const std::size_t num_space_dim = Pattern::num_space_dim;
220
221 // Get the local grid.
222 auto local_grid = getLocalGrid( arrays... );
223
224 // Function to get the local id of the neighbor.
225 auto neighbor_id = []( const std::array<int, num_space_dim>& ijk )
226 {
227 int id = ijk[0];
228 for ( std::size_t d = 1; d < num_space_dim; ++d )
229 id += num_space_dim * id + ijk[d];
230 return id;
231 };
232
233 // Neighbor id flip function. This lets us compute what neighbor we
234 // are relative to a given neighbor.
235 auto flip_id = [=]( const std::array<int, num_space_dim>& ijk )
236 {
237 std::array<int, num_space_dim> flip_ijk;
238 for ( std::size_t d = 0; d < num_space_dim; ++d )
239 flip_ijk[d] = -ijk[d];
240 return flip_ijk;
241 };
242
243 // Get the neighbor ranks we will exchange with in the halo and
244 // allocate buffers. If any of the exchanges are self sends mark these
245 // so we know which send buffers correspond to which receive buffers.
246 auto neighbors = pattern.getNeighbors();
247 for ( const auto& n : neighbors )
248 {
249 // Get the rank of the neighbor.
250 int rank = local_grid->neighborRank( n );
251
252 // If this is a valid rank add it as a neighbor.
253 if ( rank >= 0 )
254 {
255 // Add the rank.
256 _neighbor_ranks.push_back( rank );
257
258 // Set the tag we will use to send data to this neighbor. The
259 // receiving rank should have a matching tag.
260 _send_tags.push_back( neighbor_id( n ) );
261
262 // Set the tag we will use to receive data from this
263 // neighbor. The sending rank should have a matching tag.
264 _receive_tags.push_back( neighbor_id( flip_id( n ) ) );
265
266 // Create communication data for owned entities.
267 buildCommData( Own(), width, n, _owned_buffers, _owned_steering,
268 arrays... );
269
270 // Create communication data for ghosted entities.
271 buildCommData( Ghost(), width, n, _ghosted_buffers,
272 _ghosted_steering, arrays... );
273 }
274 }
275 }
276
287 template <class ExecutionSpace, class... ArrayTypes>
288 void gather( const ExecutionSpace& exec_space,
289 const ArrayTypes&... arrays ) const
290 {
291 Kokkos::Profiling::ScopedRegion region( "Cabana::Grid::gather" );
292
293 // Get the number of neighbors. Return if we have none.
294 int num_n = _neighbor_ranks.size();
295 if ( 0 == num_n )
296 return;
297
298 // Get the MPI communicator.
299 auto comm = getComm( arrays... );
300
301 // Allocate requests.
302 std::vector<MPI_Request> requests( 2 * num_n, MPI_REQUEST_NULL );
303
304 // Pick a tag to use for communication. This object has its own
305 // communication space so any tag will do.
306 const int mpi_tag = 1234;
307
308 // Post receives.
309 for ( int n = 0; n < num_n; ++n )
310 {
311 // Only process this neighbor if there is work to do.
312 if ( 0 < _ghosted_buffers[n].size() )
313 {
314 MPI_Irecv( _ghosted_buffers[n].data(),
315 _ghosted_buffers[n].size(), MPI_BYTE,
316 _neighbor_ranks[n], mpi_tag + _receive_tags[n], comm,
317 &requests[n] );
318 }
319 }
320
321 // Pack send buffers and post sends.
322 for ( int n = 0; n < num_n; ++n )
323 {
324 // Only process this neighbor if there is work to do.
325 if ( 0 < _owned_buffers[n].size() )
326 {
327 // Pack the send buffer.
328 packBuffer( exec_space, _owned_buffers[n], _owned_steering[n],
329 arrays.view()... );
330
331 // Post a send.
332 MPI_Isend( _owned_buffers[n].data(), _owned_buffers[n].size(),
333 MPI_BYTE, _neighbor_ranks[n],
334 mpi_tag + _send_tags[n], comm,
335 &requests[num_n + n] );
336 }
337 }
338
339 // Unpack receive buffers.
340 bool unpack_complete = false;
341 while ( !unpack_complete )
342 {
343 // Get the next buffer to unpack.
344 int unpack_index = MPI_UNDEFINED;
345 MPI_Waitany( num_n, requests.data(), &unpack_index,
346 MPI_STATUS_IGNORE );
347
348 // If there are no more buffers to unpack we are done.
349 if ( MPI_UNDEFINED == unpack_index )
350 {
351 unpack_complete = true;
352 }
353
354 // Otherwise unpack the next buffer.
355 else
356 {
358 _ghosted_buffers[unpack_index],
359 _ghosted_steering[unpack_index],
360 arrays.view()... );
361 }
362 }
363
364 // Wait on send requests.
365 MPI_Waitall( num_n, requests.data() + num_n, MPI_STATUSES_IGNORE );
366 }
367
375 template <class ExecutionSpace, class ReduceOp, class... ArrayTypes>
376 void scatter( const ExecutionSpace& exec_space, const ReduceOp& reduce_op,
377 const ArrayTypes&... arrays ) const
378 {
379 Kokkos::Profiling::ScopedRegion region( "Cabana::Grid::scatter" );
380
381 // Get the number of neighbors. Return if we have none.
382 int num_n = _neighbor_ranks.size();
383 if ( 0 == num_n )
384 return;
385
386 // Get the MPI communicator.
387 auto comm = getComm( arrays... );
388
389 // Requests.
390 std::vector<MPI_Request> requests( 2 * num_n, MPI_REQUEST_NULL );
391
392 // Pick a tag to use for communication. This object has its own
393 // communication space so any tag will do.
394 const int mpi_tag = 2345;
395
396 // Post receives for all neighbors that are not self sends.
397 for ( int n = 0; n < num_n; ++n )
398 {
399 // Only process this neighbor if there is work to do.
400 if ( 0 < _owned_buffers[n].size() )
401 {
402 MPI_Irecv( _owned_buffers[n].data(), _owned_buffers[n].size(),
403 MPI_BYTE, _neighbor_ranks[n],
404 mpi_tag + _receive_tags[n], comm, &requests[n] );
405 }
406 }
407
408 // Pack send buffers and post sends.
409 for ( int n = 0; n < num_n; ++n )
410 {
411 // Only process this neighbor if there is work to do.
412 if ( 0 < _ghosted_buffers[n].size() )
413 {
414 // Pack the send buffer.
415 packBuffer( exec_space, _ghosted_buffers[n],
416 _ghosted_steering[n], arrays.view()... );
417
418 // Post a send.
419 MPI_Isend( _ghosted_buffers[n].data(),
420 _ghosted_buffers[n].size(), MPI_BYTE,
421 _neighbor_ranks[n], mpi_tag + _send_tags[n], comm,
422 &requests[num_n + n] );
423 }
424 }
425
426 // Unpack receive buffers.
427 bool unpack_complete = false;
428 while ( !unpack_complete )
429 {
430 // Get the next buffer to unpack.
431 int unpack_index = MPI_UNDEFINED;
432 MPI_Waitany( num_n, requests.data(), &unpack_index,
433 MPI_STATUS_IGNORE );
434
435 // If there are no more buffers to unpack we are done.
436 if ( MPI_UNDEFINED == unpack_index )
437 {
438 unpack_complete = true;
439 }
440
441 // Otherwise unpack the next buffer and apply the reduce operation.
442 else
443 {
444 unpackBuffer( reduce_op, exec_space,
445 _owned_buffers[unpack_index],
446 _owned_steering[unpack_index], arrays.view()... );
447 }
448
449 // Wait on send requests.
450 MPI_Waitall( num_n, requests.data() + num_n, MPI_STATUSES_IGNORE );
451 }
452 }
453
454 public:
456 template <class Array_t>
457 MPI_Comm getComm( const Array_t& array ) const
458 {
459 return array.layout()->localGrid()->globalGrid().comm();
460 }
461
463 template <class Array_t, class... ArrayTypes>
464 MPI_Comm getComm( const Array_t& array, const ArrayTypes&... arrays ) const
465 {
466 auto comm = getComm( array );
467
468 // Check that the communicator of this array is the same as the other
469 // arrays.
470 int result;
471 MPI_Comm_compare( comm, getComm( arrays... ), &result );
472 if ( result != MPI_IDENT && result != MPI_CONGRUENT )
473 throw std::runtime_error( "Arrays have different communicators" );
474
475 return comm;
476 }
477
480 template <class Array_t>
481 auto getLocalGrid( const Array_t& array )
482 {
483 return array.layout()->localGrid();
484 }
485
488 template <class Array_t, class... ArrayTypes>
489 auto getLocalGrid( const Array_t& array, const ArrayTypes&... arrays )
490 {
491 // Recurse.
492 auto local_grid = getLocalGrid( arrays... );
493
494 // Check that the halo sizes same.
495 if ( local_grid->haloCellWidth() !=
496 array.layout()->localGrid()->haloCellWidth() )
497 {
498 throw std::runtime_error( "Arrays have different halo widths" );
499 }
500
501 return local_grid;
502 }
503
505 template <class DecompositionTag, std::size_t NumSpaceDim,
506 class... ArrayTypes>
507 void
508 buildCommData( DecompositionTag decomposition_tag, const int width,
509 const std::array<int, NumSpaceDim>& nid,
510 std::vector<Kokkos::View<char*, memory_space>>& buffers,
511 std::vector<Kokkos::View<int**, memory_space>>& steering,
512 const ArrayTypes&... arrays )
513 {
514 // Number of arrays.
515 const std::size_t num_array = sizeof...( ArrayTypes );
516
517 // Get the byte sizes of array value types.
518 std::array<std::size_t, num_array> value_byte_sizes = {
519 sizeof( typename ArrayTypes::value_type )... };
520
521 // Get the index spaces we share with this neighbor. We
522 // get a shared index space for each array.
523 std::array<IndexSpace<NumSpaceDim + 1>, num_array> spaces = {
524 ( arrays.layout()->sharedIndexSpace( decomposition_tag, nid,
525 width ) )... };
526
527 // Compute the buffer size of this neighbor and the
528 // number of elements in the buffer.
529 int buffer_bytes = 0;
530 int buffer_num_element = 0;
531 for ( std::size_t a = 0; a < num_array; ++a )
532 {
533 buffer_bytes += value_byte_sizes[a] * spaces[a].size();
534 buffer_num_element += spaces[a].size();
535 }
536
537 // Allocate the buffer of data that we share with this neighbor. All
538 // arrays will be packed into a single buffer.
539 buffers.push_back(
540 Kokkos::View<char*, memory_space>( "halo_buffer", buffer_bytes ) );
541
542 // Allocate the steering vector for building the buffer.
543 steering.push_back( Kokkos::View<int**, memory_space>(
544 "steering", buffer_num_element, 3 + NumSpaceDim ) );
545
546 // Build steering vector.
547 buildSteeringVector( spaces, value_byte_sizes, buffer_bytes,
548 buffer_num_element, steering );
549 }
550
552 template <std::size_t NumArray>
554 const std::array<IndexSpace<4>, NumArray>& spaces,
555 const std::array<std::size_t, NumArray>& value_byte_sizes,
556 const int buffer_bytes, const int buffer_num_element,
557 std::vector<Kokkos::View<int**, memory_space>>& steering )
558 {
559 // Create the steering vector. For each element in the buffer it gives
560 // the starting byte location of the element, the array the element is
561 // in, and the ijkl structured index in the array of the element.
562 auto host_steering =
563 Kokkos::create_mirror_view( Kokkos::HostSpace(), steering.back() );
564 int elem_counter = 0;
565 int byte_counter = 0;
566 for ( std::size_t a = 0; a < NumArray; ++a )
567 {
568 for ( int i = spaces[a].min( 0 ); i < spaces[a].max( 0 ); ++i )
569 {
570 for ( int j = spaces[a].min( 1 ); j < spaces[a].max( 1 ); ++j )
571 {
572 for ( int k = spaces[a].min( 2 ); k < spaces[a].max( 2 );
573 ++k )
574 {
575 for ( int l = spaces[a].min( 3 );
576 l < spaces[a].max( 3 ); ++l )
577 {
578 // Byte starting location in buffer.
579 host_steering( elem_counter, 0 ) = byte_counter;
580
581 // Array location of element.
582 host_steering( elem_counter, 1 ) = a;
583
584 // Structured index in array of element.
585 host_steering( elem_counter, 2 ) = i;
586 host_steering( elem_counter, 3 ) = j;
587 host_steering( elem_counter, 4 ) = k;
588 host_steering( elem_counter, 5 ) = l;
589
590 // Update element id.
591 ++elem_counter;
592
593 // Update buffer position.
594 byte_counter += value_byte_sizes[a];
595 }
596 }
597 }
598 }
599 }
600
601 // Check that all elements and bytes are accounted for.
602 if ( byte_counter != buffer_bytes )
603 throw std::logic_error( "Steering vector contains different number "
604 "of bytes than buffer" );
605 if ( elem_counter != buffer_num_element )
606 throw std::logic_error( "Steering vector contains different number "
607 "of elements than buffer" );
608
609 // Copy steering vector to device.
610 Kokkos::deep_copy( steering.back(), host_steering );
611 }
612
614 template <std::size_t NumArray>
616 const std::array<IndexSpace<3>, NumArray>& spaces,
617 const std::array<std::size_t, NumArray>& value_byte_sizes,
618 const int buffer_bytes, const int buffer_num_element,
619 std::vector<Kokkos::View<int**, memory_space>>& steering )
620 {
621 // Create the steering vector. For each element in the buffer it gives
622 // the starting byte location of the element, the array the element is
623 // in, and the ijkl structured index in the array of the element.
624 auto host_steering =
625 Kokkos::create_mirror_view( Kokkos::HostSpace(), steering.back() );
626 int elem_counter = 0;
627 int byte_counter = 0;
628 for ( std::size_t a = 0; a < NumArray; ++a )
629 {
630 for ( int i = spaces[a].min( 0 ); i < spaces[a].max( 0 ); ++i )
631 {
632 for ( int j = spaces[a].min( 1 ); j < spaces[a].max( 1 ); ++j )
633 {
634 for ( int l = spaces[a].min( 2 ); l < spaces[a].max( 2 );
635 ++l )
636 {
637 // Byte starting location in buffer.
638 host_steering( elem_counter, 0 ) = byte_counter;
639
640 // Array location of element.
641 host_steering( elem_counter, 1 ) = a;
642
643 // Structured index in array of element.
644 host_steering( elem_counter, 2 ) = i;
645 host_steering( elem_counter, 3 ) = j;
646 host_steering( elem_counter, 4 ) = l;
647
648 // Update element id.
649 ++elem_counter;
650
651 // Update buffer position.
652 byte_counter += value_byte_sizes[a];
653 }
654 }
655 }
656 }
657
658 // Check that all elements and bytes are accounted for.
659 if ( byte_counter != buffer_bytes )
660 throw std::logic_error( "Steering vector contains different number "
661 "of bytes than buffer" );
662 if ( elem_counter != buffer_num_element )
663 throw std::logic_error( "Steering vector contains different number "
664 "of elements than buffer" );
665
666 // Copy steering vector to device.
667 Kokkos::deep_copy( steering.back(), host_steering );
668 }
669
672 template <class ArrayView>
673 KOKKOS_INLINE_FUNCTION static std::enable_if_t<4 == ArrayView::rank, void>
674 packElement( const Kokkos::View<char*, memory_space>& buffer,
675 const Kokkos::View<int**, memory_space>& steering,
676 const int element_idx, const ArrayView& array_view )
677 {
678 const char* elem_ptr = reinterpret_cast<const char*>( &array_view(
679 steering( element_idx, 2 ), steering( element_idx, 3 ),
680 steering( element_idx, 4 ), steering( element_idx, 5 ) ) );
681 for ( std::size_t b = 0; b < sizeof( typename ArrayView::value_type );
682 ++b )
683 {
684 buffer( steering( element_idx, 0 ) + b ) = *( elem_ptr + b );
685 }
686 }
687
690 template <class ArrayView>
691 KOKKOS_INLINE_FUNCTION static std::enable_if_t<3 == ArrayView::rank, void>
692 packElement( const Kokkos::View<char*, memory_space>& buffer,
693 const Kokkos::View<int**, memory_space>& steering,
694 const int element_idx, const ArrayView& array_view )
695 {
696 const char* elem_ptr = reinterpret_cast<const char*>(
697 &array_view( steering( element_idx, 2 ), steering( element_idx, 3 ),
698 steering( element_idx, 4 ) ) );
699 for ( std::size_t b = 0; b < sizeof( typename ArrayView::value_type );
700 ++b )
701 {
702 buffer( steering( element_idx, 0 ) + b ) = *( elem_ptr + b );
703 }
704 }
705
707 template <class... ArrayViews>
708 KOKKOS_INLINE_FUNCTION static void
709 packArray( const Kokkos::View<char*, memory_space>& buffer,
710 const Kokkos::View<int**, memory_space>& steering,
711 const int element_idx,
712 const std::integral_constant<std::size_t, 0>,
713 const Cabana::ParameterPack<ArrayViews...>& array_views )
714 {
715 // If the pack element_idx is in the current array, pack it.
716 if ( 0 == steering( element_idx, 1 ) )
717 packElement( buffer, steering, element_idx,
718 Cabana::get<0>( array_views ) );
719 }
720
722 template <std::size_t N, class... ArrayViews>
723 KOKKOS_INLINE_FUNCTION static void
724 packArray( const Kokkos::View<char*, memory_space>& buffer,
725 const Kokkos::View<int**, memory_space>& steering,
726 const int element_idx,
727 const std::integral_constant<std::size_t, N>,
728 const Cabana::ParameterPack<ArrayViews...>& array_views )
729 {
730 // If the pack element_idx is in the current array, pack it.
731 if ( N == steering( element_idx, 1 ) )
732 {
733 packElement( buffer, steering, element_idx,
734 Cabana::get<N>( array_views ) );
735 return;
736 }
737
738 // Recurse.
739 packArray( buffer, steering, element_idx,
740 std::integral_constant<std::size_t, N - 1>(), array_views );
741 }
742
744 template <class ExecutionSpace, class... ArrayViews>
745 void packBuffer( const ExecutionSpace& exec_space,
746 const Kokkos::View<char*, memory_space>& buffer,
747 const Kokkos::View<int**, memory_space>& steering,
748 ArrayViews... array_views ) const
749 {
750 auto pp = Cabana::makeParameterPack( array_views... );
751 Kokkos::parallel_for(
752 "Cabana::Grid::Halo::pack_buffer",
753 Kokkos::RangePolicy<ExecutionSpace>( exec_space, 0,
754 steering.extent( 0 ) ),
755 KOKKOS_LAMBDA( const int i ) {
756 packArray(
757 buffer, steering, i,
758 std::integral_constant<std::size_t,
759 sizeof...( ArrayViews ) - 1>(),
760 pp );
761 } );
762 exec_space.fence();
763 }
764
766 template <class T>
767 KOKKOS_INLINE_FUNCTION static void
768 unpackOp( ScatterReduce::Sum, const T& buffer_val, T& array_val )
769 {
770 array_val += buffer_val;
771 }
772
774 template <class T>
775 KOKKOS_INLINE_FUNCTION static void
776 unpackOp( ScatterReduce::Min, const T& buffer_val, T& array_val )
777 {
778 if ( buffer_val < array_val )
779 array_val = buffer_val;
780 }
781
783 template <class T>
784 KOKKOS_INLINE_FUNCTION static void
785 unpackOp( ScatterReduce::Max, const T& buffer_val, T& array_val )
786 {
787 if ( buffer_val > array_val )
788 array_val = buffer_val;
789 }
790
792 template <class T>
793 KOKKOS_INLINE_FUNCTION static void
794 unpackOp( ScatterReduce::Replace, const T& buffer_val, T& array_val )
795 {
796 array_val = buffer_val;
797 }
798
801 template <class ReduceOp, class ArrayView>
802 KOKKOS_INLINE_FUNCTION static std::enable_if_t<4 == ArrayView::rank, void>
803 unpackElement( const ReduceOp& reduce_op,
804 const Kokkos::View<char*, memory_space>& buffer,
805 const Kokkos::View<int**, memory_space>& steering,
806 const int element_idx, const ArrayView& array_view )
807 {
808 typename ArrayView::value_type elem;
809 char* elem_ptr = reinterpret_cast<char*>( &elem );
810 for ( std::size_t b = 0; b < sizeof( typename ArrayView::value_type );
811 ++b )
812 {
813 *( elem_ptr + b ) = buffer( steering( element_idx, 0 ) + b );
814 }
815 unpackOp( reduce_op, elem,
816 array_view( steering( element_idx, 2 ),
817 steering( element_idx, 3 ),
818 steering( element_idx, 4 ),
819 steering( element_idx, 5 ) ) );
820 }
821
824 template <class ReduceOp, class ArrayView>
825 KOKKOS_INLINE_FUNCTION static std::enable_if_t<3 == ArrayView::rank, void>
826 unpackElement( const ReduceOp& reduce_op,
827 const Kokkos::View<char*, memory_space>& buffer,
828 const Kokkos::View<int**, memory_space>& steering,
829 const int element_idx, const ArrayView& array_view )
830 {
831 typename ArrayView::value_type elem;
832 char* elem_ptr = reinterpret_cast<char*>( &elem );
833 for ( std::size_t b = 0; b < sizeof( typename ArrayView::value_type );
834 ++b )
835 {
836 *( elem_ptr + b ) = buffer( steering( element_idx, 0 ) + b );
837 }
838 unpackOp( reduce_op, elem,
839 array_view( steering( element_idx, 2 ),
840 steering( element_idx, 3 ),
841 steering( element_idx, 4 ) ) );
842 }
843
845 template <class ReduceOp, class... ArrayViews>
846 KOKKOS_INLINE_FUNCTION static void
847 unpackArray( const ReduceOp& reduce_op,
848 const Kokkos::View<char*, memory_space>& buffer,
849 const Kokkos::View<int**, memory_space>& steering,
850 const int element_idx,
851 const std::integral_constant<std::size_t, 0>,
852 const Cabana::ParameterPack<ArrayViews...>& array_views )
853 {
854 // If the unpack element_idx is in the current array, unpack it.
855 if ( 0 == steering( element_idx, 1 ) )
856 unpackElement( reduce_op, buffer, steering, element_idx,
857 Cabana::get<0>( array_views ) );
858 }
859
861 template <class ReduceOp, std::size_t N, class... ArrayViews>
862 KOKKOS_INLINE_FUNCTION static void
863 unpackArray( const ReduceOp reduce_op,
864 const Kokkos::View<char*, memory_space>& buffer,
865 const Kokkos::View<int**, memory_space>& steering,
866 const int element_idx,
867 const std::integral_constant<std::size_t, N>,
868 const Cabana::ParameterPack<ArrayViews...>& array_views )
869 {
870 // If the unpack element_idx is in the current array, unpack it.
871 if ( N == steering( element_idx, 1 ) )
872 {
873 unpackElement( reduce_op, buffer, steering, element_idx,
874 Cabana::get<N>( array_views ) );
875 return;
876 }
877
878 // Recurse.
879 unpackArray( reduce_op, buffer, steering, element_idx,
880 std::integral_constant<std::size_t, N - 1>(),
881 array_views );
882 }
883
885 template <class ExecutionSpace, class ReduceOp, class... ArrayViews>
886 void unpackBuffer( const ReduceOp& reduce_op,
887 const ExecutionSpace& exec_space,
888 const Kokkos::View<char*, memory_space>& buffer,
889 const Kokkos::View<int**, memory_space>& steering,
890 ArrayViews... array_views ) const
891 {
892 auto pp = Cabana::makeParameterPack( array_views... );
893 Kokkos::parallel_for(
894 "Cabana::Grid::Halo::unpack_buffer",
895 Kokkos::RangePolicy<ExecutionSpace>( exec_space, 0,
896 steering.extent( 0 ) ),
897 KOKKOS_LAMBDA( const int i ) {
899 reduce_op, buffer, steering, i,
900 std::integral_constant<std::size_t,
901 sizeof...( ArrayViews ) - 1>(),
902 pp );
903 } );
904 }
905
906 private:
907 // The ranks we will send/receive from.
908 std::vector<int> _neighbor_ranks;
909
910 // The tag we use for sending to each neighbor.
911 std::vector<int> _send_tags;
912
913 // The tag we use for receiving from each neighbor.
914 std::vector<int> _receive_tags;
915
916 // For each neighbor, send/receive buffers for data we own.
917 std::vector<Kokkos::View<char*, memory_space>> _owned_buffers;
918
919 // For each neighbor, send/receive buffers for data we ghost.
920 std::vector<Kokkos::View<char*, memory_space>> _ghosted_buffers;
921
922 // For each neighbor, steering vector for the owned buffer.
923 std::vector<Kokkos::View<int**, memory_space>> _owned_steering;
924
925 // For each neighbor, steering vector for the ghosted buffer.
926 std::vector<Kokkos::View<int**, memory_space>> _ghosted_steering;
927};
928
929//---------------------------------------------------------------------------//
930// Creation function.
931//---------------------------------------------------------------------------//
933template <class ArrayT, class... Types>
935{
937 using type = typename ArrayT::memory_space;
938};
939
940//---------------------------------------------------------------------------//
948template <class Pattern, class... ArrayTypes>
949auto createHalo( const Pattern& pattern, const int width,
950 const ArrayTypes&... arrays )
951{
952 using memory_space = typename ArrayPackMemorySpace<ArrayTypes...>::type;
953 return std::make_shared<Halo<memory_space>>( pattern, width, arrays... );
954}
955
956//---------------------------------------------------------------------------//
957
958} // namespace Grid
959} // namespace Cabana
960
961#endif // end CABANA_GRID_HALO_HPP
Grid field arrays.
auto createHalo(const Pattern &pattern, const int width, const ArrayTypes &... arrays)
Halo creation function.
Definition Cabana_Grid_Halo.hpp:949
Logical grid indexing.
Pack variadic template parameters for device capture.
Definition Cabana_Grid_Halo.hpp:120
Base halo exchange pattern class.
Definition Cabana_Grid_Halo.hpp:45
std::vector< std::array< int, num_space_dim > > getNeighbors() const
Get the neighbors that are in the halo pattern.
Definition Cabana_Grid_Halo.hpp:64
void setNeighbors(const std::vector< std::array< int, num_space_dim > > &neighbors)
Assign the neighbors that are in the halo pattern.
Definition Cabana_Grid_Halo.hpp:58
static constexpr std::size_t num_space_dim
Spatial dimension.
Definition Cabana_Grid_Halo.hpp:48
static KOKKOS_INLINE_FUNCTION void unpackOp(ScatterReduce::Sum, const T &buffer_val, T &array_val)
Reduce an element into the buffer. Sum reduction.
Definition Cabana_Grid_Halo.hpp:768
MemorySpace memory_space
Memory space.
Definition Cabana_Grid_Halo.hpp:204
void scatter(const ExecutionSpace &exec_space, const ReduceOp &reduce_op, const ArrayTypes &... arrays) const
Scatter data from our ghosts to their owners using the given type of reduce operation.
Definition Cabana_Grid_Halo.hpp:376
static KOKKOS_INLINE_FUNCTION void packArray(const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, const int element_idx, const std::integral_constant< std::size_t, 0 >, const Cabana::ParameterPack< ArrayViews... > &array_views)
Pack an array into a buffer.
Definition Cabana_Grid_Halo.hpp:709
static KOKKOS_INLINE_FUNCTION std::enable_if_t< 4==ArrayView::rank, void > unpackElement(const ReduceOp &reduce_op, const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, const int element_idx, const ArrayView &array_view)
Definition Cabana_Grid_Halo.hpp:803
Halo(const Pattern &pattern, const int width, const ArrayTypes &... arrays)
Constructor.
Definition Cabana_Grid_Halo.hpp:216
void gather(const ExecutionSpace &exec_space, const ArrayTypes &... arrays) const
Gather data into our ghosts from their owners.
Definition Cabana_Grid_Halo.hpp:288
static KOKKOS_INLINE_FUNCTION void unpackOp(ScatterReduce::Replace, const T &buffer_val, T &array_val)
Reduce an element into the buffer. Replace reduction.
Definition Cabana_Grid_Halo.hpp:794
static KOKKOS_INLINE_FUNCTION std::enable_if_t< 4==ArrayView::rank, void > packElement(const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, const int element_idx, const ArrayView &array_view)
Definition Cabana_Grid_Halo.hpp:674
static KOKKOS_INLINE_FUNCTION void unpackArray(const ReduceOp &reduce_op, const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, const int element_idx, const std::integral_constant< std::size_t, 0 >, const Cabana::ParameterPack< ArrayViews... > &array_views)
Unpack an array from a buffer.
Definition Cabana_Grid_Halo.hpp:847
static KOKKOS_INLINE_FUNCTION void unpackOp(ScatterReduce::Min, const T &buffer_val, T &array_val)
Reduce an element into the buffer. Min reduction.
Definition Cabana_Grid_Halo.hpp:776
MPI_Comm getComm(const Array_t &array, const ArrayTypes &... arrays) const
Get the communicator and check to make sure all are the same.
Definition Cabana_Grid_Halo.hpp:464
static KOKKOS_INLINE_FUNCTION void unpackOp(ScatterReduce::Max, const T &buffer_val, T &array_val)
Reduce an element into the buffer. Max reduction.
Definition Cabana_Grid_Halo.hpp:785
static KOKKOS_INLINE_FUNCTION std::enable_if_t< 3==ArrayView::rank, void > packElement(const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, const int element_idx, const ArrayView &array_view)
Definition Cabana_Grid_Halo.hpp:692
void buildCommData(DecompositionTag decomposition_tag, const int width, const std::array< int, NumSpaceDim > &nid, std::vector< Kokkos::View< char *, memory_space > > &buffers, std::vector< Kokkos::View< int **, memory_space > > &steering, const ArrayTypes &... arrays)
Build communication data.
Definition Cabana_Grid_Halo.hpp:508
static KOKKOS_INLINE_FUNCTION void packArray(const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, const int element_idx, const std::integral_constant< std::size_t, N >, const Cabana::ParameterPack< ArrayViews... > &array_views)
Pack an array into a buffer.
Definition Cabana_Grid_Halo.hpp:724
MPI_Comm getComm(const Array_t &array) const
Get the communicator.
Definition Cabana_Grid_Halo.hpp:457
void unpackBuffer(const ReduceOp &reduce_op, const ExecutionSpace &exec_space, const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, ArrayViews... array_views) const
Unpack arrays from a buffer.
Definition Cabana_Grid_Halo.hpp:886
void packBuffer(const ExecutionSpace &exec_space, const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, ArrayViews... array_views) const
Pack arrays into a buffer.
Definition Cabana_Grid_Halo.hpp:745
auto getLocalGrid(const Array_t &array, const ArrayTypes &... arrays)
Definition Cabana_Grid_Halo.hpp:489
static KOKKOS_INLINE_FUNCTION std::enable_if_t< 3==ArrayView::rank, void > unpackElement(const ReduceOp &reduce_op, const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, const int element_idx, const ArrayView &array_view)
Definition Cabana_Grid_Halo.hpp:826
void buildSteeringVector(const std::array< IndexSpace< 4 >, NumArray > &spaces, const std::array< std::size_t, NumArray > &value_byte_sizes, const int buffer_bytes, const int buffer_num_element, std::vector< Kokkos::View< int **, memory_space > > &steering)
Build 3d steering vector.
Definition Cabana_Grid_Halo.hpp:553
auto getLocalGrid(const Array_t &array)
Definition Cabana_Grid_Halo.hpp:481
static KOKKOS_INLINE_FUNCTION void unpackArray(const ReduceOp reduce_op, const Kokkos::View< char *, memory_space > &buffer, const Kokkos::View< int **, memory_space > &steering, const int element_idx, const std::integral_constant< std::size_t, N >, const Cabana::ParameterPack< ArrayViews... > &array_views)
Unpack an array from a buffer.
Definition Cabana_Grid_Halo.hpp:863
void buildSteeringVector(const std::array< IndexSpace< 3 >, NumArray > &spaces, const std::array< std::size_t, NumArray > &value_byte_sizes, const int buffer_bytes, const int buffer_num_element, std::vector< Kokkos::View< int **, memory_space > > &steering)
Build 2d steering vector.
Definition Cabana_Grid_Halo.hpp:615
Structured index space.
Definition Cabana_Grid_IndexSpace.hpp:37
Definition Cabana_Grid_Halo.hpp:76
Core: particle data structures and algorithms.
Definition Cabana_AoSoA.hpp:36
auto size(SliceType slice, typename std::enable_if< is_slice< SliceType >::value, int >::type *=0)
Check slice size (differs from Kokkos View).
Definition Cabana_Slice.hpp:1012
ParameterPack< Types... > makeParameterPack(const Types &... ts)
Create a parameter pack.
Definition Cabana_ParameterPack.hpp:189
KOKKOS_FORCEINLINE_FUNCTION std::enable_if< is_parameter_pack< ParameterPack_t >::value, typenameParameterPack_t::templatevalue_type< N > & >::type get(ParameterPack_t &pp)
Get an element from a parameter pack.
Definition Cabana_ParameterPack.hpp:129
Infer array memory space.
Definition Cabana_Grid_Halo.hpp:935
typename ArrayT::memory_space type
Memory space.
Definition Cabana_Grid_Halo.hpp:937
Ghosted decomposition tag.
Definition Cabana_Grid_Types.hpp:197
Owned decomposition tag.
Definition Cabana_Grid_Types.hpp:190
Definition Cabana_Grid_Halo.hpp:173
Definition Cabana_Grid_Halo.hpp:167
Definition Cabana_Grid_Halo.hpp:181
Sum values from neighboring ranks into this rank's data.
Definition Cabana_Grid_Halo.hpp:161
Definition Cabana_ParameterPack.hpp:86