60template <
typename ScalarType >
64 const int local_subdomain_id,
69 const auto pos_x = boundary_position_from_boundary_type_x( face );
70 const auto pos_y = boundary_position_from_boundary_type_y( face );
71 const auto pos_r = boundary_position_from_boundary_type_r( face );
73 const int sx =
static_cast< int >( data.extent( 1 ) );
74 const int sy =
static_cast< int >( data.extent( 2 ) );
75 const int sr =
static_cast< int >( data.extent( 3 ) );
76 const int ni =
static_cast< int >( buffer.extent( 0 ) );
77 const int nj =
static_cast< int >( buffer.extent( 1 ) );
78 const int id = local_subdomain_id;
81 "fv_pack_inner_cells",
82 Kokkos::MDRangePolicy< Kokkos::Rank< 2 > >( { 0, 0 }, { ni, nj } ),
83 KOKKOS_LAMBDA(
const int i,
const int j ) {
84 int x = 0, y = 0, r = 0;
86 if ( pos_x != BoundaryPosition::PV )
88 x = ( pos_x == BoundaryPosition::P0 ) ? 1 : ( sx - 2 );
92 else if ( pos_y != BoundaryPosition::PV )
95 y = ( pos_y == BoundaryPosition::P0 ) ? 1 : ( sy - 2 );
102 r = ( pos_r == BoundaryPosition::P0 ) ? 1 : ( sr - 2 );
105 buffer( i, j ) = data(
id, x, y, r );
119template <
typename ScalarType >
123 const int local_subdomain_id,
128 using namespace grid;
130 const auto pos_x = boundary_position_from_boundary_type_x( face );
131 const auto pos_y = boundary_position_from_boundary_type_y( face );
132 const auto pos_r = boundary_position_from_boundary_type_r( face );
134 const int sx =
static_cast< int >( data.extent( 1 ) );
135 const int sy =
static_cast< int >( data.extent( 2 ) );
136 const int sr =
static_cast< int >( data.extent( 3 ) );
137 const int ni =
static_cast< int >( buffer.extent( 0 ) );
138 const int nj =
static_cast< int >( buffer.extent( 1 ) );
139 const int id = local_subdomain_id;
141 Kokkos::parallel_for(
142 "fv_unpack_to_ghost",
143 Kokkos::MDRangePolicy< Kokkos::Rank< 2 > >( { 0, 0 }, { ni, nj } ),
144 KOKKOS_LAMBDA(
const int i,
const int j ) {
145 int x = 0, y = 0, r = 0;
147 if ( pos_x != BoundaryPosition::PV )
149 x = ( pos_x == BoundaryPosition::P0 ) ? 0 : ( sx - 1 );
150 y = ( dir0 == BoundaryDirection::FORWARD ) ? ( i + 1 ) : ( sy - 2 - i );
151 r = ( dir1 == BoundaryDirection::FORWARD ) ? ( j + 1 ) : ( sr - 2 - j );
153 else if ( pos_y != BoundaryPosition::PV )
155 x = ( dir0 == BoundaryDirection::FORWARD ) ? ( i + 1 ) : ( sx - 2 - i );
156 y = ( pos_y == BoundaryPosition::P0 ) ? 0 : ( sy - 1 );
157 r = ( dir1 == BoundaryDirection::FORWARD ) ? ( j + 1 ) : ( sr - 2 - j );
161 x = ( dir0 == BoundaryDirection::FORWARD ) ? ( i + 1 ) : ( sx - 2 - i );
162 y = ( dir1 == BoundaryDirection::FORWARD ) ? ( j + 1 ) : ( sy - 2 - j );
163 r = ( pos_r == BoundaryPosition::P0 ) ? 0 : ( sr - 1 );
166 data(
id, x, y, r ) = buffer( i, j );
182template <
typename ScalarType >
208 const std::vector< FacePair >&
recv_ordered()
const {
return recv_ordered_; }
211 const std::vector< FacePair >&
send_ordered()
const {
return send_ordered_; }
217 std::vector< Buffer > send_bufs_;
218 std::vector< Buffer > recv_bufs_;
219 std::vector< FacePair > recv_ordered_;
220 std::vector< FacePair > send_ordered_;
224 using namespace grid;
228 const int n_lat_cells = N_lat - 1;
229 const int n_rad_cells = N_rad - 1;
234 std::vector< FacePair > all_pairs;
235 all_pairs.reserve( domain.
subdomains().size() * 6 );
237 for (
const auto& [subdomain_info, idx_and_neighborhood] : domain.subdomains() )
239 const auto& [local_id, neighborhood] = idx_and_neighborhood;
241 for (
const auto& [local_face, neighbor_tuple] : neighborhood.neighborhood_face() )
243 const auto& [neighbor_info, neighbor_face, unpack_dirs, neighbor_rank] = neighbor_tuple;
246 int neighbor_local_id = -1;
247 if ( neighbor_rank == my_rank && domain.
subdomains().contains( neighbor_info ) )
248 neighbor_local_id = std::get< 0 >( domain.
subdomains().at( neighbor_info ) );
251 const auto px = boundary_position_from_boundary_type_x( local_face );
252 const auto py = boundary_position_from_boundary_type_y( local_face );
253 const bool r_normal = ( px == BoundaryPosition::PV && py == BoundaryPosition::PV );
254 const int ni = n_lat_cells;
255 const int nj = r_normal ? n_lat_cells : n_rad_cells;
257 const int buf_idx =
static_cast< int >( all_pairs.size() );
258 send_bufs_.emplace_back(
"fv_ghost_send", ni, nj );
259 recv_bufs_.emplace_back(
"fv_ghost_recv", ni, nj );
261 all_pairs.push_back( FacePair{
263 .local_rank = my_rank,
264 .local_subdomain = subdomain_info,
265 .local_subdomain_id = local_id,
266 .local_face = local_face,
267 .neighbor_rank = neighbor_rank,
268 .neighbor_subdomain = neighbor_info,
269 .neighbor_subdomain_id = neighbor_local_id,
270 .neighbor_face = neighbor_face,
271 .dir0 = std::get< 0 >( unpack_dirs ),
272 .dir1 = std::get< 1 >( unpack_dirs ),
279 recv_ordered_ = all_pairs;
280 std::sort( recv_ordered_.begin(), recv_ordered_.end(), [](
const FacePair& a,
const FacePair& b ) {
281 if ( a.neighbor_subdomain != b.neighbor_subdomain )
282 return a.neighbor_subdomain < b.neighbor_subdomain;
283 if ( a.neighbor_face != b.neighbor_face )
284 return static_cast< int >( a.neighbor_face ) < static_cast< int >( b.neighbor_face );
285 if ( a.local_subdomain != b.local_subdomain )
286 return a.local_subdomain < b.local_subdomain;
287 return static_cast< int >( a.local_face ) < static_cast< int >( b.local_face );
291 send_ordered_ = all_pairs;
292 std::sort( send_ordered_.begin(), send_ordered_.end(), [](
const FacePair& a,
const FacePair& b ) {
293 if ( a.local_subdomain != b.local_subdomain )
294 return a.local_subdomain < b.local_subdomain;
295 if ( a.local_face != b.local_face )
296 return static_cast< int >( a.local_face ) < static_cast< int >( b.local_face );
297 if ( a.neighbor_subdomain != b.neighbor_subdomain )
298 return a.neighbor_subdomain < b.neighbor_subdomain;
299 return static_cast< int >( a.neighbor_face ) < static_cast< int >( b.neighbor_face );
315template <
typename ScalarType >
321 using namespace fv_detail;
323 std::vector< MPI_Request > recv_requests;
324 std::vector< MPI_Request > send_requests;
329 if ( fp.local_rank == fp.neighbor_rank )
332 auto& recv_buf = bufs.
recv_buf( fp );
336 static_cast< int >( recv_buf.span() ),
337 mpi::mpi_datatype< ScalarType >(),
342 recv_requests.push_back( req );
348 if ( fp.local_rank == fp.neighbor_rank )
351 pack_inner_cells( bufs.
recv_buf( fp ), data, fp.neighbor_subdomain_id, fp.neighbor_face );
356 auto& send_buf = bufs.
send_buf( fp );
357 pack_inner_cells( send_buf, data, fp.local_subdomain_id, fp.local_face );
363 static_cast< int >( send_buf.span() ),
364 mpi::mpi_datatype< ScalarType >(),
369 send_requests.push_back( req );
373 MPI_Waitall(
static_cast< int >( send_requests.size() ), send_requests.data(), MPI_STATUSES_IGNORE );
374 MPI_Waitall(
static_cast< int >( recv_requests.size() ), recv_requests.data(), MPI_STATUSES_IGNORE );
379 unpack_to_ghost( bufs.
recv_buf( fp ), data, fp.local_subdomain_id, fp.local_face, fp.dir0, fp.dir1 );
387template <
typename ScalarType >
409template <
typename ScalarType,
int VecDim >
413 const int local_subdomain_id,
416 using namespace grid;
418 const auto pos_x = boundary_position_from_boundary_type_x( face );
419 const auto pos_y = boundary_position_from_boundary_type_y( face );
420 const auto pos_r = boundary_position_from_boundary_type_r( face );
422 const int sx =
static_cast< int >( data.
extent( 1 ) );
423 const int sy =
static_cast< int >( data.
extent( 2 ) );
424 const int sr =
static_cast< int >( data.
extent( 3 ) );
425 const int ni =
static_cast< int >( buffer.extent( 0 ) );
426 const int nj =
static_cast< int >( buffer.extent( 1 ) );
427 const int id = local_subdomain_id;
429 Kokkos::parallel_for(
430 "fv_pack_inner_cells_vec",
431 Kokkos::MDRangePolicy< Kokkos::Rank< 2 > >( { 0, 0 }, { ni, nj } ),
432 KOKKOS_LAMBDA(
const int i,
const int j ) {
433 int x = 0, y = 0, r = 0;
435 if ( pos_x != BoundaryPosition::PV )
437 x = ( pos_x == BoundaryPosition::P0 ) ? 1 : ( sx - 2 );
441 else if ( pos_y != BoundaryPosition::PV )
444 y = ( pos_y == BoundaryPosition::P0 ) ? 1 : ( sy - 2 );
451 r = ( pos_r == BoundaryPosition::P0 ) ? 1 : ( sr - 2 );
454 for (
int d = 0; d < VecDim; ++d )
455 buffer( i, j, d ) = data(
id, x, y, r, d );
462template <
typename ScalarType,
int VecDim >
466 const int local_subdomain_id,
471 using namespace grid;
473 const auto pos_x = boundary_position_from_boundary_type_x( face );
474 const auto pos_y = boundary_position_from_boundary_type_y( face );
475 const auto pos_r = boundary_position_from_boundary_type_r( face );
477 const int sx =
static_cast< int >( data.
extent( 1 ) );
478 const int sy =
static_cast< int >( data.
extent( 2 ) );
479 const int sr =
static_cast< int >( data.
extent( 3 ) );
480 const int ni =
static_cast< int >( buffer.extent( 0 ) );
481 const int nj =
static_cast< int >( buffer.extent( 1 ) );
482 const int id = local_subdomain_id;
484 Kokkos::parallel_for(
485 "fv_unpack_to_ghost_vec",
486 Kokkos::MDRangePolicy< Kokkos::Rank< 2 > >( { 0, 0 }, { ni, nj } ),
487 KOKKOS_LAMBDA(
const int i,
const int j ) {
488 int x = 0, y = 0, r = 0;
490 if ( pos_x != BoundaryPosition::PV )
492 x = ( pos_x == BoundaryPosition::P0 ) ? 0 : ( sx - 1 );
493 y = ( dir0 == BoundaryDirection::FORWARD ) ? ( i + 1 ) : ( sy - 2 - i );
494 r = ( dir1 == BoundaryDirection::FORWARD ) ? ( j + 1 ) : ( sr - 2 - j );
496 else if ( pos_y != BoundaryPosition::PV )
498 x = ( dir0 == BoundaryDirection::FORWARD ) ? ( i + 1 ) : ( sx - 2 - i );
499 y = ( pos_y == BoundaryPosition::P0 ) ? 0 : ( sy - 1 );
500 r = ( dir1 == BoundaryDirection::FORWARD ) ? ( j + 1 ) : ( sr - 2 - j );
504 x = ( dir0 == BoundaryDirection::FORWARD ) ? ( i + 1 ) : ( sx - 2 - i );
505 y = ( dir1 == BoundaryDirection::FORWARD ) ? ( j + 1 ) : ( sy - 2 - j );
506 r = ( pos_r == BoundaryPosition::P0 ) ? 0 : ( sr - 1 );
509 for (
int d = 0; d < VecDim; ++d )
510 data(
id, x, y, r, d ) = buffer( i, j, d );
522template <
typename ScalarType,
int VecDim >
545 const std::vector< FacePair >&
recv_ordered()
const {
return recv_ordered_; }
546 const std::vector< FacePair >&
send_ordered()
const {
return send_ordered_; }
552 std::vector< Buffer > send_bufs_;
553 std::vector< Buffer > recv_bufs_;
554 std::vector< FacePair > recv_ordered_;
555 std::vector< FacePair > send_ordered_;
559 using namespace grid;
563 const int n_lat_cells = N_lat - 1;
564 const int n_rad_cells = N_rad - 1;
568 std::vector< FacePair > all_pairs;
569 all_pairs.reserve( domain.
subdomains().size() * 6 );
571 for (
const auto& [subdomain_info, idx_and_neighborhood] : domain.subdomains() )
573 const auto& [local_id, neighborhood] = idx_and_neighborhood;
575 for (
const auto& [local_face, neighbor_tuple] : neighborhood.neighborhood_face() )
577 const auto& [neighbor_info, neighbor_face, unpack_dirs, neighbor_rank] = neighbor_tuple;
579 int neighbor_local_id = -1;
580 if ( neighbor_rank == my_rank && domain.
subdomains().contains( neighbor_info ) )
581 neighbor_local_id = std::get< 0 >( domain.
subdomains().at( neighbor_info ) );
583 const auto px = boundary_position_from_boundary_type_x( local_face );
584 const auto py = boundary_position_from_boundary_type_y( local_face );
585 const bool r_normal = ( px == BoundaryPosition::PV && py == BoundaryPosition::PV );
586 const int ni = n_lat_cells;
587 const int nj = r_normal ? n_lat_cells : n_rad_cells;
589 const int buf_idx =
static_cast< int >( all_pairs.size() );
590 send_bufs_.emplace_back(
"fv_ghost_vec_send", ni, nj, VecDim );
591 recv_bufs_.emplace_back(
"fv_ghost_vec_recv", ni, nj, VecDim );
593 all_pairs.push_back( FacePair{
595 .local_rank = my_rank,
596 .local_subdomain = subdomain_info,
597 .local_subdomain_id = local_id,
598 .local_face = local_face,
599 .neighbor_rank = neighbor_rank,
600 .neighbor_subdomain = neighbor_info,
601 .neighbor_subdomain_id = neighbor_local_id,
602 .neighbor_face = neighbor_face,
603 .dir0 = std::get< 0 >( unpack_dirs ),
604 .dir1 = std::get< 1 >( unpack_dirs ),
609 recv_ordered_ = all_pairs;
610 std::sort( recv_ordered_.begin(), recv_ordered_.end(), [](
const FacePair& a,
const FacePair& b ) {
611 if ( a.neighbor_subdomain != b.neighbor_subdomain )
612 return a.neighbor_subdomain < b.neighbor_subdomain;
613 if ( a.neighbor_face != b.neighbor_face )
614 return static_cast< int >( a.neighbor_face ) < static_cast< int >( b.neighbor_face );
615 if ( a.local_subdomain != b.local_subdomain )
616 return a.local_subdomain < b.local_subdomain;
617 return static_cast< int >( a.local_face ) < static_cast< int >( b.local_face );
620 send_ordered_ = all_pairs;
621 std::sort( send_ordered_.begin(), send_ordered_.end(), [](
const FacePair& a,
const FacePair& b ) {
622 if ( a.local_subdomain != b.local_subdomain )
623 return a.local_subdomain < b.local_subdomain;
624 if ( a.local_face != b.local_face )
625 return static_cast< int >( a.local_face ) < static_cast< int >( b.local_face );
626 if ( a.neighbor_subdomain != b.neighbor_subdomain )
627 return a.neighbor_subdomain < b.neighbor_subdomain;
628 return static_cast< int >( a.neighbor_face ) < static_cast< int >( b.neighbor_face );
638template <
typename ScalarType,
int VecDim >
644 using namespace fv_detail;
646 std::vector< MPI_Request > recv_requests;
647 std::vector< MPI_Request > send_requests;
651 if ( fp.local_rank == fp.neighbor_rank )
654 auto& recv_buf = bufs.
recv_buf( fp );
658 static_cast< int >( recv_buf.span() ),
659 mpi::mpi_datatype< ScalarType >(),
664 recv_requests.push_back( req );
669 if ( fp.local_rank == fp.neighbor_rank )
671 pack_inner_cells_vec( bufs.
recv_buf( fp ), data, fp.neighbor_subdomain_id, fp.neighbor_face );
675 auto& send_buf = bufs.
send_buf( fp );
676 pack_inner_cells_vec( send_buf, data, fp.local_subdomain_id, fp.local_face );
681 static_cast< int >( send_buf.span() ),
682 mpi::mpi_datatype< ScalarType >(),
687 send_requests.push_back( req );
691 MPI_Waitall(
static_cast< int >( send_requests.size() ), send_requests.data(), MPI_STATUSES_IGNORE );
692 MPI_Waitall(
static_cast< int >( recv_requests.size() ), recv_requests.data(), MPI_STATUSES_IGNORE );
696 unpack_to_ghost_vec( bufs.
recv_buf( fp ), data, fp.local_subdomain_id, fp.local_face, fp.dir0, fp.dir1 );
704template <
typename ScalarType,
int VecDim >
Pre-allocated send/recv buffers and sorted face-pair lists for FV ghost layer comm.
Definition fv_communication.hpp:184
Buffer & send_buf(const FacePair &fp)
Definition fv_communication.hpp:213
grid::Grid2DDataScalar< ScalarType > Buffer
Definition fv_communication.hpp:186
Buffer & recv_buf(const FacePair &fp)
Definition fv_communication.hpp:214
const std::vector< FacePair > & send_ordered() const
Face pairs sorted for consistent MPI_Isend / local-comm packing order.
Definition fv_communication.hpp:211
const std::vector< FacePair > & recv_ordered() const
Face pairs sorted for consistent MPI_Irecv posting order (by sending subdomain).
Definition fv_communication.hpp:208
FVGhostLayerBuffers(const grid::shell::DistributedDomain &domain)
Allocates all send/recv buffers and builds the sorted face pair lists.
Definition fv_communication.hpp:205
Pre-allocated send/recv buffers for vector-valued FV ghost layer communication.
Definition fv_communication.hpp:524
const std::vector< FacePair > & send_ordered() const
Definition fv_communication.hpp:546
const std::vector< FacePair > & recv_ordered() const
Definition fv_communication.hpp:545
Buffer & send_buf(const FacePair &fp)
Definition fv_communication.hpp:548
grid::Grid3DDataScalar< ScalarType > Buffer
Definition fv_communication.hpp:526
Buffer & recv_buf(const FacePair &fp)
Definition fv_communication.hpp:549
FVGhostLayerVecBuffers(const grid::shell::DistributedDomain &domain)
Definition fv_communication.hpp:543
Parallel data structure organizing the thick spherical shell metadata for distributed (MPI parallel) ...
Definition spherical_shell.hpp:2518
const std::map< SubdomainInfo, std::tuple< LocalSubdomainIdx, SubdomainNeighborhood > > & subdomains() const
Definition spherical_shell.hpp:2650
const DomainInfo & domain_info() const
Returns a const reference.
Definition spherical_shell.hpp:2647
int subdomain_num_nodes_radially() const
Equivalent to calling subdomain_num_nodes_radially( subdomain_refinement_level() )
Definition spherical_shell.hpp:861
int subdomain_num_nodes_per_side_laterally() const
Equivalent to calling subdomain_num_nodes_per_side_laterally( subdomain_refinement_level() )
Definition spherical_shell.hpp:852
(Sortable) Globally unique identifier for a single subdomain of a diamond.
Definition spherical_shell.hpp:595
void unpack_to_ghost_vec(const grid::Grid3DDataScalar< ScalarType > &buffer, const grid::Grid4DDataVec< ScalarType, VecDim > &data, const int local_subdomain_id, const grid::BoundaryFace face, const grid::BoundaryDirection dir0, const grid::BoundaryDirection dir1)
Writes buffer into the ghost cell layer at face for a vector field.
Definition fv_communication.hpp:463
void pack_inner_cells(const grid::Grid2DDataScalar< ScalarType > &buffer, const grid::Grid4DDataScalar< ScalarType > &data, const int local_subdomain_id, const grid::BoundaryFace face)
Packs the innermost real-cell layer adjacent to face into buffer.
Definition fv_communication.hpp:61
void unpack_to_ghost(const grid::Grid2DDataScalar< ScalarType > &buffer, const grid::Grid4DDataScalar< ScalarType > &data, const int local_subdomain_id, const grid::BoundaryFace face, const grid::BoundaryDirection dir0, const grid::BoundaryDirection dir1)
Writes buffer into the ghost cell layer at face.
Definition fv_communication.hpp:120
void pack_inner_cells_vec(const grid::Grid3DDataScalar< ScalarType > &buffer, const grid::Grid4DDataVec< ScalarType, VecDim > &data, const int local_subdomain_id, const grid::BoundaryFace face)
Packs the innermost real-cell layer adjacent to face into buffer for a vector field.
Definition fv_communication.hpp:410
Definition communication.hpp:14
constexpr int MPI_TAG_FV_GHOST_LAYERS
MPI tag for FV ghost layer messages (distinct from MPI_TAG_BOUNDARY_DATA = 100).
Definition fv_communication.hpp:42
constexpr int MPI_TAG_FV_VEC_GHOST_LAYERS
MPI tag for vector-valued FV ghost layer messages.
Definition fv_communication.hpp:401
void update_fv_ghost_layers(const grid::shell::DistributedDomain &domain, const grid::Grid4DDataScalar< ScalarType > &data, FVGhostLayerBuffers< ScalarType > &bufs)
Fills all ghost layers of a scalar FV field from neighbouring subdomains.
Definition fv_communication.hpp:316
Kokkos::View< ScalarType ***, Layout > Grid3DDataScalar
Definition grid_types.hpp:24
BoundaryDirection
Enum for the iteration direction at a boundary.
Definition grid_types.hpp:311
BoundaryFace
Enum for identification of the 6 boundary faces of a subdomain.
Definition grid_types.hpp:297
Kokkos::View< ScalarType ****, Layout > Grid4DDataScalar
Definition grid_types.hpp:27
Kokkos::View< ScalarType **, Layout > Grid2DDataScalar
Definition grid_types.hpp:21
int MPIRank
Definition mpi.hpp:11
MPIRank rank()
Definition mpi.hpp:13
All metadata needed to drive communication for one subdomain face.
Definition fv_communication.hpp:190
mpi::MPIRank neighbor_rank
Definition fv_communication.hpp:196
grid::shell::SubdomainInfo neighbor_subdomain
Definition fv_communication.hpp:197
grid::BoundaryFace local_face
Definition fv_communication.hpp:195
int buf_idx
Index into send_bufs_ / recv_bufs_.
Definition fv_communication.hpp:191
grid::BoundaryDirection dir0
Unpack direction for first varying dimension.
Definition fv_communication.hpp:200
int local_subdomain_id
Definition fv_communication.hpp:194
mpi::MPIRank local_rank
Definition fv_communication.hpp:192
int neighbor_subdomain_id
Local array index; -1 if on remote rank.
Definition fv_communication.hpp:198
grid::shell::SubdomainInfo local_subdomain
Definition fv_communication.hpp:193
grid::BoundaryDirection dir1
Unpack direction for second varying dimension.
Definition fv_communication.hpp:201
grid::BoundaryFace neighbor_face
Definition fv_communication.hpp:199
Definition fv_communication.hpp:529
grid::shell::SubdomainInfo neighbor_subdomain
Definition fv_communication.hpp:536
mpi::MPIRank neighbor_rank
Definition fv_communication.hpp:535
grid::shell::SubdomainInfo local_subdomain
Definition fv_communication.hpp:532
int neighbor_subdomain_id
Definition fv_communication.hpp:537
grid::BoundaryDirection dir1
Definition fv_communication.hpp:540
int buf_idx
Definition fv_communication.hpp:530
grid::BoundaryFace local_face
Definition fv_communication.hpp:534
grid::BoundaryDirection dir0
Definition fv_communication.hpp:539
mpi::MPIRank local_rank
Definition fv_communication.hpp:531
int local_subdomain_id
Definition fv_communication.hpp:533
grid::BoundaryFace neighbor_face
Definition fv_communication.hpp:538
SoA (Structure-of-Arrays) 4D vector grid data.
Definition grid_types.hpp:51
auto extent(int i) const
Definition grid_types.hpp:75