Loading...
Searching...
No Matches
restriction_linear.hpp
Go to the documentation of this file.
1
2
3#pragma once
4
6#include "dense/vec.hpp"
12#include "linalg/operator.hpp"
13#include "linalg/vector.hpp"
14#include "linalg/vector_q1.hpp"
15
17
18template < typename ScalarT >
20{
21 public:
24 using ScalarType = ScalarT;
25
26 private:
27 grid::shell::DistributedDomain domain_coarse_;
28
31
34
37
39
40 public:
42 const grid::shell::DistributedDomain& domain_coarse,
44 const grid::Grid2DDataScalar< ScalarType >& radii_fine )
45 : domain_coarse_( domain_coarse )
46 , grid_fine_( grid_fine )
47 , radii_fine_( radii_fine )
48 // TODO: we can reuse the send and recv buffers and pass in from the outside somehow
49 , send_buffers_( domain_coarse )
50 , recv_buffers_( domain_coarse )
51 {
52 if ( 2 * ( domain_coarse.domain_info().subdomain_num_nodes_per_side_laterally() - 1 ) !=
53 grid_fine.extent( 1 ) - 1 )
54 {
55 throw std::runtime_error(
56 "Restriction: domain_coarse and grid_fine must have compatible number of cells." );
57 }
58
59 if ( 2 * ( domain_coarse.domain_info().subdomain_num_nodes_radially() - 1 ) != radii_fine.extent( 1 ) - 1 )
60 {
61 throw std::runtime_error(
62 "Restriction: domain_coarse and radii_fine must have compatible number of cells." );
63 }
64 }
65
66 void apply_impl( const SrcVectorType& src, DstVectorType& dst )
67 {
68 // Not quite sure currently how to implement additive update (like r += R * f).
69 // For now, only implementing replacement update: r = R * f.
70 assign( dst, 0 );
71
72 src_ = src.grid_data();
73 dst_ = dst.grid_data();
74 mask_src_ = src.mask_data();
75
76 if ( src_.extent( 0 ) != dst_.extent( 0 ) )
77 {
78 throw std::runtime_error( "Restriction: src and dst must have the same number of subdomains." );
79 }
80
81 for ( int i = 1; i <= 3; i++ )
82 {
83 if ( src_.extent( i ) - 1 != 2 * ( dst_.extent( i ) - 1 ) )
84 {
85 throw std::runtime_error( "Restriction: src and dst must have a compatible number of cells." );
86 }
87 }
88
89 // Looping over the fine grid.
90 Kokkos::parallel_for(
91 "matvec",
92 Kokkos::MDRangePolicy< Kokkos::Rank< 4 > >(
93 { 0, 0, 0, 0 },
94 {
95 src_.extent( 0 ),
96 src_.extent( 1 ),
97 src_.extent( 2 ),
98 src_.extent( 3 ),
99 } ),
100 *this );
101
102 Kokkos::fence();
103
104 // Additive communication.
105
107 domain_coarse_, dst_, send_buffers_, recv_buffers_ );
108 communication::shell::unpack_and_reduce_local_subdomain_boundaries( domain_coarse_, dst_, recv_buffers_ );
109 }
110
111 KOKKOS_INLINE_FUNCTION void
112 operator()( const int local_subdomain_id, const int x_fine, const int y_fine, const int r_fine ) const
113 {
114 // Only pushing from owned nodes.
115 if ( !util::has_flag(
116 mask_src_( local_subdomain_id, x_fine, y_fine, r_fine ), grid::NodeOwnershipFlag::OWNED ) )
117 {
118 return;
119 }
120
121 if ( x_fine % 2 == 0 && y_fine % 2 == 0 && r_fine % 2 == 0 )
122 {
123 const auto x_coarse = x_fine / 2;
124 const auto y_coarse = y_fine / 2;
125 const auto r_coarse = r_fine / 2;
126
127 Kokkos::atomic_add(
128 &dst_( local_subdomain_id, x_coarse, y_coarse, r_coarse ),
129 src_( local_subdomain_id, x_fine, y_fine, r_fine ) );
130
131 return;
132 }
133
134 const auto r_coarse_bot = r_fine < src_.extent( 3 ) - 1 ? r_fine / 2 : r_fine / 2 - 1;
135 const auto r_coarse_top = r_coarse_bot + 1;
136
137 if ( x_fine % 2 == 0 && y_fine % 2 == 0 )
138 {
139 const auto x_coarse = x_fine / 2;
140 const auto y_coarse = y_fine / 2;
141
143 dense::Vec< int, 4 >{ local_subdomain_id, x_fine, y_fine, r_fine },
144 dense::Vec< int, 4 >{ local_subdomain_id, x_coarse, y_coarse, r_coarse_bot },
145 grid_fine_,
146 radii_fine_ );
147
148 Kokkos::atomic_add(
149 &dst_( local_subdomain_id, x_coarse, y_coarse, r_coarse_bot ),
150 weights( 0 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine ) );
151 Kokkos::atomic_add(
152 &dst_( local_subdomain_id, x_coarse, y_coarse, r_coarse_top ),
153 weights( 1 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine ) );
154
155 return;
156 }
157
158 int x_coarse_0 = -1;
159 int x_coarse_1 = -1;
160
161 int y_coarse_0 = -1;
162 int y_coarse_1 = -1;
163
164 if ( x_fine % 2 == 0 )
165 {
166 // "Vertical" edge.
167 x_coarse_0 = x_fine / 2;
168 x_coarse_1 = x_fine / 2;
169
170 y_coarse_0 = y_fine / 2;
171 y_coarse_1 = y_fine / 2 + 1;
172 }
173 else if ( y_fine % 2 == 0 )
174 {
175 // "Horizontal" edge.
176 x_coarse_0 = x_fine / 2;
177 x_coarse_1 = x_fine / 2 + 1;
178
179 y_coarse_0 = y_fine / 2;
180 y_coarse_1 = y_fine / 2;
181 }
182 else
183 {
184 // "Diagonal" edge.
185 x_coarse_0 = x_fine / 2 + 1;
186 x_coarse_1 = x_fine / 2;
187
188 y_coarse_0 = y_fine / 2;
189 y_coarse_1 = y_fine / 2 + 1;
190 }
191
193 dense::Vec< int, 4 >{ local_subdomain_id, x_fine, y_fine, r_fine },
194 dense::Vec< int, 4 >{ local_subdomain_id, x_coarse_0, y_coarse_0, r_coarse_bot },
195 dense::Vec< int, 4 >{ local_subdomain_id, x_coarse_1, y_coarse_1, r_coarse_bot },
196 grid_fine_,
197 radii_fine_ );
198
199 Kokkos::atomic_add(
200 &dst_( local_subdomain_id, x_coarse_0, y_coarse_0, r_coarse_bot ),
201 weights( 0 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine ) );
202 Kokkos::atomic_add(
203 &dst_( local_subdomain_id, x_coarse_1, y_coarse_1, r_coarse_bot ),
204 weights( 0 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine ) );
205 Kokkos::atomic_add(
206 &dst_( local_subdomain_id, x_coarse_0, y_coarse_0, r_coarse_top ),
207 weights( 1 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine ) );
208 Kokkos::atomic_add(
209 &dst_( local_subdomain_id, x_coarse_1, y_coarse_1, r_coarse_top ),
210 weights( 1 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine ) );
211 }
212
213#if 0
214 KOKKOS_INLINE_FUNCTION void
215 operator()( const int local_subdomain_id, const int x_coarse, const int y_coarse, const int r_coarse ) const
216 {
217 const auto x_fine = 2 * x_coarse;
218 const auto y_fine = 2 * y_coarse;
219 const auto r_fine = 2 * r_coarse;
220
221 dense::Vec< int, 3 > offsets[21];
222 wedge::shell::prolongation_fine_grid_stencil_offsets_at_coarse_vertex( offsets );
223
224 for ( const auto& offset : offsets )
225 {
226 const auto fine_stencil_x = x_fine + offset( 0 );
227 const auto fine_stencil_y = y_fine + offset( 1 );
228 const auto fine_stencil_r = r_fine + offset( 2 );
229
230 if ( fine_stencil_x >= 0 && fine_stencil_x < src_.extent( 1 ) && fine_stencil_y >= 0 &&
231 fine_stencil_y < src_.extent( 2 ) && fine_stencil_r >= 0 && fine_stencil_r < src_.extent( 3 ) )
232 {
233 const auto weight = wedge::shell::prolongation_weight< ScalarType >(
234 fine_stencil_x, fine_stencil_y, fine_stencil_r, x_coarse, y_coarse, r_coarse );
235
236 const auto mask_weight =
237 util::check_bits(
238 mask_src_( local_subdomain_id, fine_stencil_x, fine_stencil_y, fine_stencil_r ),
239 grid::mask_owned() ) ?
240 1.0 :
241 0.0;
242
243 Kokkos::atomic_add(
244 &dst_( local_subdomain_id, x_coarse, y_coarse, r_coarse ),
245 weight * mask_weight * src_( local_subdomain_id, fine_stencil_x, fine_stencil_y, fine_stencil_r ) );
246 }
247 }
248 }
249#endif
250};
251
252template < typename ScalarT >
254{
255 public:
258 using ScalarType = ScalarT;
259
260 private:
261 grid::shell::DistributedDomain domain_coarse_;
262
265
268
271
273
274 public:
276 const grid::shell::DistributedDomain& domain_coarse,
278 const grid::Grid2DDataScalar< ScalarType >& radii_fine )
279 : domain_coarse_( domain_coarse )
280 , grid_fine_( grid_fine )
281 , radii_fine_( radii_fine )
282 // TODO: we can reuse the send and recv buffers and pass in from the outside somehow
283 , send_buffers_( domain_coarse )
284 , recv_buffers_( domain_coarse )
285 {
286 if ( 2 * ( domain_coarse.domain_info().subdomain_num_nodes_per_side_laterally() - 1 ) !=
287 grid_fine.extent( 1 ) - 1 )
288 {
289 throw std::runtime_error(
290 "Restriction: domain_coarse and grid_fine must have compatible number of cells." );
291 }
292
293 if ( 2 * ( domain_coarse.domain_info().subdomain_num_nodes_radially() - 1 ) != radii_fine.extent( 1 ) - 1 )
294 {
295 throw std::runtime_error(
296 "Restriction: domain_coarse and radii_fine must have compatible number of cells." );
297 }
298 }
299
300 void apply_impl( const SrcVectorType& src, DstVectorType& dst )
301 {
302 // Not quite sure currently how to implement additive update (like r += R * f).
303 // For now, only implementing replacement update: r = R * f.
304 assign( dst, 0 );
305
306 src_ = src.grid_data();
307 dst_ = dst.grid_data();
308 mask_src_ = src.mask_data();
309
310 if ( src_.extent( 0 ) != dst_.extent( 0 ) )
311 {
312 throw std::runtime_error( "Restriction: src and dst must have the same number of subdomains." );
313 }
314
315 for ( int i = 1; i <= 3; i++ )
316 {
317 if ( src_.extent( i ) - 1 != 2 * ( dst_.extent( i ) - 1 ) )
318 {
319 throw std::runtime_error( "Restriction: src and dst must have a compatible number of cells." );
320 }
321 }
322
323 // Looping over the fine grid.
324 Kokkos::parallel_for(
325 "matvec",
326 Kokkos::MDRangePolicy< Kokkos::Rank< 4 > >(
327 { 0, 0, 0, 0 },
328 {
329 src_.extent( 0 ),
330 src_.extent( 1 ),
331 src_.extent( 2 ),
332 src_.extent( 3 ),
333 } ),
334 *this );
335
336 Kokkos::fence();
337
338 // Additive communication.
339
341 domain_coarse_, dst_, send_buffers_, recv_buffers_ );
342 communication::shell::unpack_and_reduce_local_subdomain_boundaries( domain_coarse_, dst_, recv_buffers_ );
343 }
344
345 KOKKOS_INLINE_FUNCTION void
346 operator()( const int local_subdomain_id, const int x_fine, const int y_fine, const int r_fine ) const
347 {
348 for ( int dim = 0; dim < 3; ++dim )
349 {
350 // Only pushing from owned nodes.
351 if ( !util::has_flag(
352 mask_src_( local_subdomain_id, x_fine, y_fine, r_fine ), grid::NodeOwnershipFlag::OWNED ) )
353 {
354 return;
355 }
356
357 if ( x_fine % 2 == 0 && y_fine % 2 == 0 && r_fine % 2 == 0 )
358 {
359 const auto x_coarse = x_fine / 2;
360 const auto y_coarse = y_fine / 2;
361 const auto r_coarse = r_fine / 2;
362
363 Kokkos::atomic_add(
364 &dst_( local_subdomain_id, x_coarse, y_coarse, r_coarse, dim ),
365 src_( local_subdomain_id, x_fine, y_fine, r_fine, dim ) );
366
367 return;
368 }
369
370 const auto r_coarse_bot = r_fine < src_.extent( 3 ) - 1 ? r_fine / 2 : r_fine / 2 - 1;
371 const auto r_coarse_top = r_coarse_bot + 1;
372
373 if ( x_fine % 2 == 0 && y_fine % 2 == 0 )
374 {
375 const auto x_coarse = x_fine / 2;
376 const auto y_coarse = y_fine / 2;
377
379 dense::Vec< int, 4 >{ local_subdomain_id, x_fine, y_fine, r_fine },
380 dense::Vec< int, 4 >{ local_subdomain_id, x_coarse, y_coarse, r_coarse_bot },
381 grid_fine_,
382 radii_fine_ );
383
384 Kokkos::atomic_add(
385 &dst_( local_subdomain_id, x_coarse, y_coarse, r_coarse_bot, dim ),
386 weights( 0 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine, dim ) );
387 Kokkos::atomic_add(
388 &dst_( local_subdomain_id, x_coarse, y_coarse, r_coarse_top, dim ),
389 weights( 1 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine, dim ) );
390
391 return;
392 }
393
394 int x_coarse_0 = -1;
395 int x_coarse_1 = -1;
396
397 int y_coarse_0 = -1;
398 int y_coarse_1 = -1;
399
400 if ( x_fine % 2 == 0 )
401 {
402 // "Vertical" edge.
403 x_coarse_0 = x_fine / 2;
404 x_coarse_1 = x_fine / 2;
405
406 y_coarse_0 = y_fine / 2;
407 y_coarse_1 = y_fine / 2 + 1;
408 }
409 else if ( y_fine % 2 == 0 )
410 {
411 // "Horizontal" edge.
412 x_coarse_0 = x_fine / 2;
413 x_coarse_1 = x_fine / 2 + 1;
414
415 y_coarse_0 = y_fine / 2;
416 y_coarse_1 = y_fine / 2;
417 }
418 else
419 {
420 // "Diagonal" edge.
421 x_coarse_0 = x_fine / 2 + 1;
422 x_coarse_1 = x_fine / 2;
423
424 y_coarse_0 = y_fine / 2;
425 y_coarse_1 = y_fine / 2 + 1;
426 }
427
429 dense::Vec< int, 4 >{ local_subdomain_id, x_fine, y_fine, r_fine },
430 dense::Vec< int, 4 >{ local_subdomain_id, x_coarse_0, y_coarse_0, r_coarse_bot },
431 dense::Vec< int, 4 >{ local_subdomain_id, x_coarse_1, y_coarse_1, r_coarse_bot },
432 grid_fine_,
433 radii_fine_ );
434
435 Kokkos::atomic_add(
436 &dst_( local_subdomain_id, x_coarse_0, y_coarse_0, r_coarse_bot, dim ),
437 weights( 0 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine, dim ) );
438 Kokkos::atomic_add(
439 &dst_( local_subdomain_id, x_coarse_1, y_coarse_1, r_coarse_bot, dim ),
440 weights( 0 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine, dim ) );
441 Kokkos::atomic_add(
442 &dst_( local_subdomain_id, x_coarse_0, y_coarse_0, r_coarse_top, dim ),
443 weights( 1 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine, dim ) );
444 Kokkos::atomic_add(
445 &dst_( local_subdomain_id, x_coarse_1, y_coarse_1, r_coarse_top, dim ),
446 weights( 1 ) * src_( local_subdomain_id, x_fine, y_fine, r_fine, dim ) );
447 }
448 }
449};
450} // namespace terra::fe::wedge::operators::shell
Definition restriction_linear.hpp:20
ScalarT ScalarType
Definition restriction_linear.hpp:24
void operator()(const int local_subdomain_id, const int x_fine, const int y_fine, const int r_fine) const
Definition restriction_linear.hpp:112
void apply_impl(const SrcVectorType &src, DstVectorType &dst)
Definition restriction_linear.hpp:66
RestrictionLinear(const grid::shell::DistributedDomain &domain_coarse, const grid::Grid3DDataVec< ScalarType, 3 > &grid_fine, const grid::Grid2DDataScalar< ScalarType > &radii_fine)
Definition restriction_linear.hpp:41
void apply_impl(const SrcVectorType &src, DstVectorType &dst)
Definition restriction_linear.hpp:300
ScalarT ScalarType
Definition restriction_linear.hpp:258
void operator()(const int local_subdomain_id, const int x_fine, const int y_fine, const int r_fine) const
Definition restriction_linear.hpp:346
RestrictionVecLinear(const grid::shell::DistributedDomain &domain_coarse, const grid::Grid3DDataVec< ScalarType, 3 > &grid_fine, const grid::Grid2DDataScalar< ScalarType > &radii_fine)
Definition restriction_linear.hpp:275
Parallel data structure organizing the thick spherical shell metadata for distributed (MPI parallel) ...
Definition spherical_shell.hpp:2498
const DomainInfo & domain_info() const
Returns a const reference.
Definition spherical_shell.hpp:2577
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
Q1 scalar finite element vector on a distributed shell grid.
Definition vector_q1.hpp:21
const grid::Grid4DDataScalar< ScalarType > & grid_data() const
Get const reference to grid data.
Definition vector_q1.hpp:137
const grid::Grid4DDataScalar< grid::NodeOwnershipFlag > & mask_data() const
Get const reference to mask data.
Definition vector_q1.hpp:142
Static assertion: VectorQ1Scalar satisfies VectorLike concept.
Definition vector_q1.hpp:162
const grid::Grid4DDataScalar< grid::NodeOwnershipFlag > & mask_data() const
Get const reference to mask data.
Definition vector_q1.hpp:285
const grid::Grid4DDataVec< ScalarType, VecDim > & grid_data() const
Get const reference to grid data.
Definition vector_q1.hpp:280
void unpack_and_reduce_local_subdomain_boundaries(const grid::shell::DistributedDomain &domain, const GridDataType &data, SubdomainNeighborhoodSendRecvBuffer< typename GridDataType::value_type, grid::grid_data_vec_dim< GridDataType >() > &boundary_recv_buffers, CommunicationReduction reduction=CommunicationReduction::SUM)
Unpacks and reduces local subdomain boundaries.
Definition communication.hpp:672
void pack_send_and_recv_local_subdomain_boundaries(const grid::shell::DistributedDomain &domain, const GridDataType &data, SubdomainNeighborhoodSendRecvBuffer< typename GridDataType::value_type, grid::grid_data_vec_dim< GridDataType >() > &boundary_send_buffers, SubdomainNeighborhoodSendRecvBuffer< typename GridDataType::value_type, grid::grid_data_vec_dim< GridDataType >() > &boundary_recv_buffers)
Packs, sends and recvs local subdomain boundaries using two sets of buffers.
Definition communication.hpp:242
Definition boundary_mass.hpp:14
constexpr dense::Vec< ScalarType, 2 > prolongation_linear_weights(const dense::Vec< int, 4 > &idx_fine, const dense::Vec< int, 4 > &idx_coarse_bot, const grid::Grid3DDataVec< ScalarType, 3 > &subdomain_shell_coords_fine, const grid::Grid2DDataScalar< ScalarType > subdomain_radii_fine)
Computes prolongation weights for the spherical shell.
Definition grid_transfer_linear.hpp:14
Kokkos::View< ScalarType ***[VecDim], Layout > Grid3DDataVec
Definition grid_types.hpp:40
Kokkos::View< ScalarType ****[VecDim], Layout > Grid4DDataVec
Definition grid_types.hpp:43
Kokkos::View< ScalarType ****, Layout > Grid4DDataScalar
Definition grid_types.hpp:25
Kokkos::View< ScalarType **, Layout > Grid2DDataScalar
Definition grid_types.hpp:19
constexpr bool has_flag(E mask_value, E flag) noexcept
Checks if a bitmask value contains a specific flag.
Definition bit_masking.hpp:43
Definition vec.hpp:9