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( "Cabana::Grid::Halo::getComm: Arrays "
474 "have different communicators" );
475
476 return comm;
477 }
478
481 template <class Array_t>
482 auto getLocalGrid( const Array_t& array )
483 {
484 return array.layout()->localGrid();
485 }
486
489 template <class Array_t, class... ArrayTypes>
490 auto getLocalGrid( const Array_t& array, const ArrayTypes&... arrays )
491 {
492 // Recurse.
493 auto local_grid = getLocalGrid( arrays... );
494
495 // Check that the halo sizes same.
496 if ( local_grid->haloCellWidth() !=
497 array.layout()->localGrid()->haloCellWidth() )
498 {
499 throw std::runtime_error( "Cabana::Grid::Halo::getlocalGrid: "
500 "Arrays have different halo widths" );
501 }
502
503 return local_grid;
504 }
505
507 template <class DecompositionTag, std::size_t NumSpaceDim,
508 class... ArrayTypes>
509 void
510 buildCommData( DecompositionTag decomposition_tag, const int width,
511 const std::array<int, NumSpaceDim>& nid,
512 std::vector<Kokkos::View<char*, memory_space>>& buffers,
513 std::vector<Kokkos::View<int**, memory_space>>& steering,
514 const ArrayTypes&... arrays )
515 {
516 // Number of arrays.
517 const std::size_t num_array = sizeof...( ArrayTypes );
518
519 // Get the byte sizes of array value types.
520 std::array<std::size_t, num_array> value_byte_sizes = {
521 sizeof( typename ArrayTypes::value_type )... };
522
523 // Get the index spaces we share with this neighbor. We
524 // get a shared index space for each array.
525 std::array<IndexSpace<NumSpaceDim + 1>, num_array> spaces = {
526 ( arrays.layout()->sharedIndexSpace( decomposition_tag, nid,
527 width ) )... };
528
529 // Compute the buffer size of this neighbor and the
530 // number of elements in the buffer.
531 int buffer_bytes = 0;
532 int buffer_num_element = 0;
533 for ( std::size_t a = 0; a < num_array; ++a )
534 {
535 buffer_bytes += value_byte_sizes[a] * spaces[a].size();
536 buffer_num_element += spaces[a].size();
537 }
538
539 // Allocate the buffer of data that we share with this neighbor. All
540 // arrays will be packed into a single buffer.
541 buffers.push_back(
542 Kokkos::View<char*, memory_space>( "halo_buffer", buffer_bytes ) );
543
544 // Allocate the steering vector for building the buffer.
545 steering.push_back( Kokkos::View<int**, memory_space>(
546 "steering", buffer_num_element, 3 + NumSpaceDim ) );
547
548 // Build steering vector.
549 buildSteeringVector( spaces, value_byte_sizes, buffer_bytes,
550 buffer_num_element, steering );
551 }
552
554 template <std::size_t NumArray>
556 const std::array<IndexSpace<4>, NumArray>& spaces,
557 const std::array<std::size_t, NumArray>& value_byte_sizes,
558 const int buffer_bytes, const int buffer_num_element,
559 std::vector<Kokkos::View<int**, memory_space>>& steering )
560 {
561 // Create the steering vector. For each element in the buffer it gives
562 // the starting byte location of the element, the array the element is
563 // in, and the ijkl structured index in the array of the element.
564 auto host_steering =
565 Kokkos::create_mirror_view( Kokkos::HostSpace(), steering.back() );
566 int elem_counter = 0;
567 int byte_counter = 0;
568 for ( std::size_t a = 0; a < NumArray; ++a )
569 {
570 for ( int i = spaces[a].min( 0 ); i < spaces[a].max( 0 ); ++i )
571 {
572 for ( int j = spaces[a].min( 1 ); j < spaces[a].max( 1 ); ++j )
573 {
574 for ( int k = spaces[a].min( 2 ); k < spaces[a].max( 2 );
575 ++k )
576 {
577 for ( int l = spaces[a].min( 3 );
578 l < spaces[a].max( 3 ); ++l )
579 {
580 // Byte starting location in buffer.
581 host_steering( elem_counter, 0 ) = byte_counter;
582
583 // Array location of element.
584 host_steering( elem_counter, 1 ) = a;
585
586 // Structured index in array of element.
587 host_steering( elem_counter, 2 ) = i;
588 host_steering( elem_counter, 3 ) = j;
589 host_steering( elem_counter, 4 ) = k;
590 host_steering( elem_counter, 5 ) = l;
591
592 // Update element id.
593 ++elem_counter;
594
595 // Update buffer position.
596 byte_counter += value_byte_sizes[a];
597 }
598 }
599 }
600 }
601 }
602
603 // Check that all elements and bytes are accounted for.
604 if ( byte_counter != buffer_bytes )
605 throw std::logic_error( "Cabana::Grid::Halo::buildSteeringVector: "
606 "Steering vector contains different number "
607 "of bytes than buffer" );
608 if ( elem_counter != buffer_num_element )
609 throw std::logic_error( "Cabana::Grid::Halo::buildSteeringVector: "
610 "Steering vector contains different number "
611 "of elements than buffer" );
612
613 // Copy steering vector to device.
614 Kokkos::deep_copy( steering.back(), host_steering );
615 }
616
618 template <std::size_t NumArray>
620 const std::array<IndexSpace<3>, NumArray>& spaces,
621 const std::array<std::size_t, NumArray>& value_byte_sizes,
622 const int buffer_bytes, const int buffer_num_element,
623 std::vector<Kokkos::View<int**, memory_space>>& steering )
624 {
625 // Create the steering vector. For each element in the buffer it gives
626 // the starting byte location of the element, the array the element is
627 // in, and the ijkl structured index in the array of the element.
628 auto host_steering =
629 Kokkos::create_mirror_view( Kokkos::HostSpace(), steering.back() );
630 int elem_counter = 0;
631 int byte_counter = 0;
632 for ( std::size_t a = 0; a < NumArray; ++a )
633 {
634 for ( int i = spaces[a].min( 0 ); i < spaces[a].max( 0 ); ++i )
635 {
636 for ( int j = spaces[a].min( 1 ); j < spaces[a].max( 1 ); ++j )
637 {
638 for ( int l = spaces[a].min( 2 ); l < spaces[a].max( 2 );
639 ++l )
640 {
641 // Byte starting location in buffer.
642 host_steering( elem_counter, 0 ) = byte_counter;
643
644 // Array location of element.
645 host_steering( elem_counter, 1 ) = a;
646
647 // Structured index in array of element.
648 host_steering( elem_counter, 2 ) = i;
649 host_steering( elem_counter, 3 ) = j;
650 host_steering( elem_counter, 4 ) = l;
651
652 // Update element id.
653 ++elem_counter;
654
655 // Update buffer position.
656 byte_counter += value_byte_sizes[a];
657 }
658 }
659 }
660 }
661
662 // Check that all elements and bytes are accounted for.
663 if ( byte_counter != buffer_bytes )
664 throw std::logic_error( "Cabana::Grid::Halo::buildSteeringVector: "
665 "Steering vector contains different number "
666 "of bytes than buffer" );
667 if ( elem_counter != buffer_num_element )
668 throw std::logic_error( "Cabana::Grid::Halo::buildSteeringVector: "
669 "Steering vector contains different number "
670 "of elements than buffer" );
671
672 // Copy steering vector to device.
673 Kokkos::deep_copy( steering.back(), host_steering );
674 }
675
678 template <class ArrayView>
679 KOKKOS_INLINE_FUNCTION static std::enable_if_t<4 == ArrayView::rank, void>
680 packElement( const Kokkos::View<char*, memory_space>& buffer,
681 const Kokkos::View<int**, memory_space>& steering,
682 const int element_idx, const ArrayView& array_view )
683 {
684 const char* elem_ptr = reinterpret_cast<const char*>( &array_view(
685 steering( element_idx, 2 ), steering( element_idx, 3 ),
686 steering( element_idx, 4 ), steering( element_idx, 5 ) ) );
687 for ( std::size_t b = 0; b < sizeof( typename ArrayView::value_type );
688 ++b )
689 {
690 buffer( steering( element_idx, 0 ) + b ) = *( elem_ptr + b );
691 }
692 }
693
696 template <class ArrayView>
697 KOKKOS_INLINE_FUNCTION static std::enable_if_t<3 == ArrayView::rank, void>
698 packElement( const Kokkos::View<char*, memory_space>& buffer,
699 const Kokkos::View<int**, memory_space>& steering,
700 const int element_idx, const ArrayView& array_view )
701 {
702 const char* elem_ptr = reinterpret_cast<const char*>(
703 &array_view( steering( element_idx, 2 ), steering( element_idx, 3 ),
704 steering( element_idx, 4 ) ) );
705 for ( std::size_t b = 0; b < sizeof( typename ArrayView::value_type );
706 ++b )
707 {
708 buffer( steering( element_idx, 0 ) + b ) = *( elem_ptr + b );
709 }
710 }
711
713 template <class... ArrayViews>
714 KOKKOS_INLINE_FUNCTION static void
715 packArray( const Kokkos::View<char*, memory_space>& buffer,
716 const Kokkos::View<int**, memory_space>& steering,
717 const int element_idx,
718 const std::integral_constant<std::size_t, 0>,
719 const Cabana::ParameterPack<ArrayViews...>& array_views )
720 {
721 // If the pack element_idx is in the current array, pack it.
722 if ( 0 == steering( element_idx, 1 ) )
723 packElement( buffer, steering, element_idx,
724 Cabana::get<0>( array_views ) );
725 }
726
728 template <std::size_t N, class... ArrayViews>
729 KOKKOS_INLINE_FUNCTION static void
730 packArray( const Kokkos::View<char*, memory_space>& buffer,
731 const Kokkos::View<int**, memory_space>& steering,
732 const int element_idx,
733 const std::integral_constant<std::size_t, N>,
734 const Cabana::ParameterPack<ArrayViews...>& array_views )
735 {
736 // If the pack element_idx is in the current array, pack it.
737 if ( N == steering( element_idx, 1 ) )
738 {
739 packElement( buffer, steering, element_idx,
740 Cabana::get<N>( array_views ) );
741 return;
742 }
743
744 // Recurse.
745 packArray( buffer, steering, element_idx,
746 std::integral_constant<std::size_t, N - 1>(), array_views );
747 }
748
750 template <class ExecutionSpace, class... ArrayViews>
751 void packBuffer( const ExecutionSpace& exec_space,
752 const Kokkos::View<char*, memory_space>& buffer,
753 const Kokkos::View<int**, memory_space>& steering,
754 ArrayViews... array_views ) const
755 {
756 auto pp = Cabana::makeParameterPack( array_views... );
757 Kokkos::parallel_for(
758 "Cabana::Grid::Halo::pack_buffer",
759 Kokkos::RangePolicy<ExecutionSpace>( exec_space, 0,
760 steering.extent( 0 ) ),
761 KOKKOS_LAMBDA( const int i ) {
762 packArray(
763 buffer, steering, i,
764 std::integral_constant<std::size_t,
765 sizeof...( ArrayViews ) - 1>(),
766 pp );
767 } );
768 exec_space.fence();
769 }
770
772 template <class T>
773 KOKKOS_INLINE_FUNCTION static void
774 unpackOp( ScatterReduce::Sum, const T& buffer_val, T& array_val )
775 {
776 array_val += buffer_val;
777 }
778
780 template <class T>
781 KOKKOS_INLINE_FUNCTION static void
782 unpackOp( ScatterReduce::Min, const T& buffer_val, T& array_val )
783 {
784 if ( buffer_val < array_val )
785 array_val = buffer_val;
786 }
787
789 template <class T>
790 KOKKOS_INLINE_FUNCTION static void
791 unpackOp( ScatterReduce::Max, const T& buffer_val, T& array_val )
792 {
793 if ( buffer_val > array_val )
794 array_val = buffer_val;
795 }
796
798 template <class T>
799 KOKKOS_INLINE_FUNCTION static void
800 unpackOp( ScatterReduce::Replace, const T& buffer_val, T& array_val )
801 {
802 array_val = buffer_val;
803 }
804
807 template <class ReduceOp, class ArrayView>
808 KOKKOS_INLINE_FUNCTION static std::enable_if_t<4 == ArrayView::rank, void>
809 unpackElement( const ReduceOp& reduce_op,
810 const Kokkos::View<char*, memory_space>& buffer,
811 const Kokkos::View<int**, memory_space>& steering,
812 const int element_idx, const ArrayView& array_view )
813 {
814 typename ArrayView::value_type elem;
815 char* elem_ptr = reinterpret_cast<char*>( &elem );
816 for ( std::size_t b = 0; b < sizeof( typename ArrayView::value_type );
817 ++b )
818 {
819 *( elem_ptr + b ) = buffer( steering( element_idx, 0 ) + b );
820 }
821 unpackOp( reduce_op, elem,
822 array_view( steering( element_idx, 2 ),
823 steering( element_idx, 3 ),
824 steering( element_idx, 4 ),
825 steering( element_idx, 5 ) ) );
826 }
827
830 template <class ReduceOp, class ArrayView>
831 KOKKOS_INLINE_FUNCTION static std::enable_if_t<3 == ArrayView::rank, void>
832 unpackElement( const ReduceOp& reduce_op,
833 const Kokkos::View<char*, memory_space>& buffer,
834 const Kokkos::View<int**, memory_space>& steering,
835 const int element_idx, const ArrayView& array_view )
836 {
837 typename ArrayView::value_type elem;
838 char* elem_ptr = reinterpret_cast<char*>( &elem );
839 for ( std::size_t b = 0; b < sizeof( typename ArrayView::value_type );
840 ++b )
841 {
842 *( elem_ptr + b ) = buffer( steering( element_idx, 0 ) + b );
843 }
844 unpackOp( reduce_op, elem,
845 array_view( steering( element_idx, 2 ),
846 steering( element_idx, 3 ),
847 steering( element_idx, 4 ) ) );
848 }
849
851 template <class ReduceOp, class... ArrayViews>
852 KOKKOS_INLINE_FUNCTION static void
853 unpackArray( const ReduceOp& reduce_op,
854 const Kokkos::View<char*, memory_space>& buffer,
855 const Kokkos::View<int**, memory_space>& steering,
856 const int element_idx,
857 const std::integral_constant<std::size_t, 0>,
858 const Cabana::ParameterPack<ArrayViews...>& array_views )
859 {
860 // If the unpack element_idx is in the current array, unpack it.
861 if ( 0 == steering( element_idx, 1 ) )
862 unpackElement( reduce_op, buffer, steering, element_idx,
863 Cabana::get<0>( array_views ) );
864 }
865
867 template <class ReduceOp, std::size_t N, class... ArrayViews>
868 KOKKOS_INLINE_FUNCTION static void
869 unpackArray( const ReduceOp reduce_op,
870 const Kokkos::View<char*, memory_space>& buffer,
871 const Kokkos::View<int**, memory_space>& steering,
872 const int element_idx,
873 const std::integral_constant<std::size_t, N>,
874 const Cabana::ParameterPack<ArrayViews...>& array_views )
875 {
876 // If the unpack element_idx is in the current array, unpack it.
877 if ( N == steering( element_idx, 1 ) )
878 {
879 unpackElement( reduce_op, buffer, steering, element_idx,
880 Cabana::get<N>( array_views ) );
881 return;
882 }
883
884 // Recurse.
885 unpackArray( reduce_op, buffer, steering, element_idx,
886 std::integral_constant<std::size_t, N - 1>(),
887 array_views );
888 }
889
891 template <class ExecutionSpace, class ReduceOp, class... ArrayViews>
892 void unpackBuffer( const ReduceOp& reduce_op,
893 const ExecutionSpace& exec_space,
894 const Kokkos::View<char*, memory_space>& buffer,
895 const Kokkos::View<int**, memory_space>& steering,
896 ArrayViews... array_views ) const
897 {
898 auto pp = Cabana::makeParameterPack( array_views... );
899 Kokkos::parallel_for(
900 "Cabana::Grid::Halo::unpack_buffer",
901 Kokkos::RangePolicy<ExecutionSpace>( exec_space, 0,
902 steering.extent( 0 ) ),
903 KOKKOS_LAMBDA( const int i ) {
905 reduce_op, buffer, steering, i,
906 std::integral_constant<std::size_t,
907 sizeof...( ArrayViews ) - 1>(),
908 pp );
909 } );
910 }
911
912 private:
913 // The ranks we will send/receive from.
914 std::vector<int> _neighbor_ranks;
915
916 // The tag we use for sending to each neighbor.
917 std::vector<int> _send_tags;
918
919 // The tag we use for receiving from each neighbor.
920 std::vector<int> _receive_tags;
921
922 // For each neighbor, send/receive buffers for data we own.
923 std::vector<Kokkos::View<char*, memory_space>> _owned_buffers;
924
925 // For each neighbor, send/receive buffers for data we ghost.
926 std::vector<Kokkos::View<char*, memory_space>> _ghosted_buffers;
927
928 // For each neighbor, steering vector for the owned buffer.
929 std::vector<Kokkos::View<int**, memory_space>> _owned_steering;
930
931 // For each neighbor, steering vector for the ghosted buffer.
932 std::vector<Kokkos::View<int**, memory_space>> _ghosted_steering;
933};
934
935//---------------------------------------------------------------------------//
936// Creation function.
937//---------------------------------------------------------------------------//
939template <class ArrayT, class... Types>
941{
943 using type = typename ArrayT::memory_space;
944};
945
946//---------------------------------------------------------------------------//
954template <class Pattern, class... ArrayTypes>
955auto createHalo( const Pattern& pattern, const int width,
956 const ArrayTypes&... arrays )
957{
958 using memory_space = typename ArrayPackMemorySpace<ArrayTypes...>::type;
959 return std::make_shared<Halo<memory_space>>( pattern, width, arrays... );
960}
961
962//---------------------------------------------------------------------------//
963
964} // namespace Grid
965} // namespace Cabana
966
967#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:955
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:774
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:715
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:809
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:800
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:680
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:853
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:782
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:791
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:698
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:510
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:730
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:892
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:751
auto getLocalGrid(const Array_t &array, const ArrayTypes &... arrays)
Definition Cabana_Grid_Halo.hpp:490
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:832
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:555
auto getLocalGrid(const Array_t &array)
Definition Cabana_Grid_Halo.hpp:482
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:869
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:619
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:941
typename ArrayT::memory_space type
Memory space.
Definition Cabana_Grid_Halo.hpp:943
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