Ifpack2 Templated Preconditioning Package Version 1.0
Loading...
Searching...
No Matches
Ifpack2_BlockComputeResidualAndSolve_def.hpp
1// @HEADER
2// *****************************************************************************
3// Ifpack2: Templated Object-Oriented Algebraic Preconditioner Package
4//
5// Copyright 2009 NTESS and the Ifpack2 contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef IFPACK2_BLOCKCOMPUTERES_AND_SOLVE_DEF_HPP
11#define IFPACK2_BLOCKCOMPUTERES_AND_SOLVE_DEF_HPP
12
13#include "Ifpack2_BlockComputeResidualAndSolve_decl.hpp"
14
15namespace Ifpack2::BlockHelperDetails {
16
17template <typename MatrixType, int B>
18struct ComputeResidualAndSolve_SinglePass_Impl {
19 using impl_type = BlockHelperDetails::ImplType<MatrixType>;
20 using node_device_type = typename impl_type::node_device_type;
21 using execution_space = typename impl_type::execution_space;
22 using memory_space = typename impl_type::memory_space;
23
24 using local_ordinal_type = typename impl_type::local_ordinal_type;
25 using size_type = typename impl_type::size_type;
26 using impl_scalar_type = typename impl_type::impl_scalar_type;
27 using magnitude_type = typename impl_type::magnitude_type;
29 using local_ordinal_type_1d_view =
30 typename impl_type::local_ordinal_type_1d_view;
31 using size_type_1d_view = typename impl_type::size_type_1d_view;
32 using tpetra_block_access_view_type =
33 typename impl_type::tpetra_block_access_view_type; // block crs (layout
34 // right)
35 using impl_scalar_type_1d_view = typename impl_type::impl_scalar_type_1d_view;
36 using impl_scalar_type_2d_view_tpetra =
37 typename impl_type::impl_scalar_type_2d_view_tpetra; // block multivector
38 // (layout left)
39 using btdm_scalar_type_3d_view = typename impl_type::btdm_scalar_type_3d_view;
40 using btdm_scalar_type_4d_view = typename impl_type::btdm_scalar_type_4d_view;
41 using i64_3d_view = typename impl_type::i64_3d_view;
42
44 using member_type = typename Kokkos::TeamPolicy<execution_space>::member_type;
45
46 private:
47 ConstUnmanaged<impl_scalar_type_2d_view_tpetra> b;
48 ConstUnmanaged<impl_scalar_type_2d_view_tpetra> x; // x_owned
49 ConstUnmanaged<impl_scalar_type_2d_view_tpetra> x_remote;
50 Unmanaged<impl_scalar_type_2d_view_tpetra> y;
51
52 // AmD information
53 const ConstUnmanaged<impl_scalar_type_1d_view> tpetra_values;
54
55 // blocksize
56 const local_ordinal_type blocksize_requested;
57
58 // block offsets
59 const ConstUnmanaged<i64_3d_view> A_x_offsets;
60 const ConstUnmanaged<i64_3d_view> A_x_offsets_remote;
61
62 // diagonal block inverses
63 const ConstUnmanaged<btdm_scalar_type_3d_view> d_inv;
64
65 // squared update norms
66 const Unmanaged<impl_scalar_type_1d_view> W;
67
68 impl_scalar_type damping_factor;
69
70 public:
71 ComputeResidualAndSolve_SinglePass_Impl(const AmD<MatrixType>& amd,
72 const btdm_scalar_type_3d_view& d_inv_,
73 const impl_scalar_type_1d_view& W_,
74 const local_ordinal_type& blocksize_requested_,
75 const impl_scalar_type& damping_factor_)
76 : tpetra_values(amd.tpetra_values)
77 , blocksize_requested(blocksize_requested_)
78 , A_x_offsets(amd.A_x_offsets)
79 , A_x_offsets_remote(amd.A_x_offsets_remote)
80 , d_inv(d_inv_)
81 , W(W_)
82 , damping_factor(damping_factor_) {}
83
84 KOKKOS_INLINE_FUNCTION
85 void operator()(const member_type& member) const {
86 const local_ordinal_type blocksize = (B == 0 ? blocksize_requested : B);
87 const local_ordinal_type rowidx = member.league_rank();
88 const local_ordinal_type row = rowidx * blocksize;
89 const local_ordinal_type num_vectors = b.extent(1);
90 const local_ordinal_type num_local_rows = d_inv.extent(0);
91
92 const impl_scalar_type* xx;
93 auto A_block_cst = ConstUnmanaged<tpetra_block_access_view_type>(
94 tpetra_values.data(), blocksize, blocksize);
95
96 // Get shared allocation for a local copy of x, residual, and A
97 impl_scalar_type* local_residual = reinterpret_cast<impl_scalar_type*>(
98 member.team_scratch(0).get_shmem(blocksize * sizeof(impl_scalar_type)));
99 impl_scalar_type* local_Dinv_residual = reinterpret_cast<impl_scalar_type*>(
100 member.team_scratch(0).get_shmem(blocksize * sizeof(impl_scalar_type)));
101 impl_scalar_type* local_x =
102 reinterpret_cast<impl_scalar_type*>(member.thread_scratch(0).get_shmem(
103 blocksize * sizeof(impl_scalar_type)));
104
105 magnitude_type norm = 0;
106 for (local_ordinal_type col = 0; col < num_vectors; ++col) {
107 if (col) member.team_barrier();
108 // y -= Rx
109 // Initialize accumulation arrays
110 Kokkos::parallel_for(Kokkos::TeamVectorRange(member, blocksize),
111 [&](const local_ordinal_type& i) {
112 local_Dinv_residual[i] = 0;
113 local_residual[i] = b(row + i, col);
114 });
115 member.team_barrier();
116
117 int numEntries = A_x_offsets.extent(2);
118
119 Kokkos::parallel_for(
120 Kokkos::TeamThreadRange(member, 0, numEntries), [&](const int k) {
121 int64_t A_offset = A_x_offsets(rowidx, 0, k);
122 int64_t x_offset = A_x_offsets(rowidx, 1, k);
123 if (A_offset != KokkosKernels::ArithTraits<int64_t>::min()) {
124 A_block_cst.assign_data(tpetra_values.data() + A_offset);
125 // Pull x into local memory
126 int64_t remote_cutoff = blocksize * num_local_rows;
127 if (x_offset >= remote_cutoff)
128 xx = &x_remote(x_offset - remote_cutoff, col);
129 else
130 xx = &x(x_offset, col);
131
132 Kokkos::parallel_for(
133 Kokkos::ThreadVectorRange(member, blocksize),
134 [&](const local_ordinal_type& i) { local_x[i] = xx[i]; });
135
136 // matvec on block: local_residual -= A_block_cst * local_x
137 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize),
138 [&](const int k0) {
139 impl_scalar_type val = 0;
140 for (int k1 = 0; k1 < blocksize; k1++)
141 val += A_block_cst(k0, k1) * local_x[k1];
142 Kokkos::atomic_add(local_residual + k0, -val);
143 });
144 }
145 });
146 member.team_barrier();
147 // Compute local_Dinv_residual = D^-1 * local_residual
148 Kokkos::parallel_for(
149 Kokkos::TeamThreadRange(member, blocksize),
150 [&](const local_ordinal_type& k0) {
151 Kokkos::parallel_reduce(
152 Kokkos::ThreadVectorRange(member, blocksize),
153 [&](const local_ordinal_type& k1, impl_scalar_type& update) {
154 update += d_inv(rowidx, k0, k1) * local_residual[k1];
155 },
156 local_Dinv_residual[k0]);
157 });
158 member.team_barrier();
159 // local_Dinv_residual is fully computed. Now compute the
160 // squared y update norm and update y (using damping factor).
161 magnitude_type colNorm;
162 Kokkos::parallel_reduce(
163 Kokkos::TeamVectorRange(member, blocksize),
164 [&](const local_ordinal_type& k, magnitude_type& update) {
165 // Compute the change in y (assuming damping_factor == 1) for this
166 // entry.
167 impl_scalar_type old_y = x(row + k, col);
168 impl_scalar_type y_update = local_Dinv_residual[k] - old_y;
169 if constexpr (KokkosKernels::ArithTraits<impl_scalar_type>::is_complex) {
170 magnitude_type ydiff =
171 KokkosKernels::ArithTraits<impl_scalar_type>::abs(y_update);
172 update += ydiff * ydiff;
173 } else {
174 update += y_update * y_update;
175 }
176 y(row + k, col) = old_y + damping_factor * y_update;
177 },
178 colNorm);
179 norm += colNorm;
180 }
181 Kokkos::single(Kokkos::PerTeam(member), [&]() { W(rowidx) = norm; });
182 }
183
184 void run(const ConstUnmanaged<impl_scalar_type_2d_view_tpetra>& b_,
185 const ConstUnmanaged<impl_scalar_type_2d_view_tpetra>& x_,
186 const ConstUnmanaged<impl_scalar_type_2d_view_tpetra>& x_remote_,
187 const Unmanaged<impl_scalar_type_2d_view_tpetra>& y_) {
188 IFPACK2_BLOCKHELPER_PROFILER_REGION_BEGIN;
189 IFPACK2_BLOCKHELPER_TIMER_WITH_FENCE(
190 "BlockTriDi::ComputeResidualAndSolve::RunSinglePass",
191 ComputeResidualAndSolve0, execution_space);
192
193 y = y_;
194 b = b_;
195 x = x_;
196 x_remote = x_remote_;
197
198 const local_ordinal_type blocksize = blocksize_requested;
199 const local_ordinal_type nrows = d_inv.extent(0);
200
201 const local_ordinal_type team_size = 8;
202 const local_ordinal_type vector_size = 8;
203 // team: local_residual, local_Dinv_residual
204 const size_t shmem_team_size = 2 * blocksize * sizeof(impl_scalar_type);
205 // thread: local_x
206 const size_t shmem_thread_size = blocksize * sizeof(impl_scalar_type);
207 Kokkos::TeamPolicy<execution_space> policy(nrows, team_size, vector_size);
208 policy.set_scratch_size(0, Kokkos::PerTeam(shmem_team_size),
209 Kokkos::PerThread(shmem_thread_size));
210 Kokkos::parallel_for("ComputeResidualAndSolve::TeamPolicy::SinglePass",
211 policy, *this);
212
213 IFPACK2_BLOCKHELPER_PROFILER_REGION_END;
214 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
215 }
216};
217
218template <typename MatrixType, int B>
219struct ComputeResidualAndSolve_2Pass_Impl {
220 using impl_type = BlockHelperDetails::ImplType<MatrixType>;
221 using node_device_type = typename impl_type::node_device_type;
222 using execution_space = typename impl_type::execution_space;
223 using memory_space = typename impl_type::memory_space;
224
225 using local_ordinal_type = typename impl_type::local_ordinal_type;
226 using size_type = typename impl_type::size_type;
227 using impl_scalar_type = typename impl_type::impl_scalar_type;
228 using magnitude_type = typename impl_type::magnitude_type;
230 using local_ordinal_type_1d_view =
231 typename impl_type::local_ordinal_type_1d_view;
232 using size_type_1d_view = typename impl_type::size_type_1d_view;
233 using tpetra_block_access_view_type =
234 typename impl_type::tpetra_block_access_view_type; // block crs (layout
235 // right)
236 using impl_scalar_type_1d_view = typename impl_type::impl_scalar_type_1d_view;
237 using impl_scalar_type_2d_view_tpetra =
238 typename impl_type::impl_scalar_type_2d_view_tpetra; // block multivector
239 // (layout left)
240 using btdm_scalar_type_3d_view = typename impl_type::btdm_scalar_type_3d_view;
241 using btdm_scalar_type_4d_view = typename impl_type::btdm_scalar_type_4d_view;
242 using i64_3d_view = typename impl_type::i64_3d_view;
243
245 using member_type = typename Kokkos::TeamPolicy<execution_space>::member_type;
246
247 // Tag for computing residual with owned columns only (pass 1)
248 struct OwnedTag {};
249
250 // Tag for finishing the residual with nonowned columns, and solving/norming
251 // (pass 2)
252 struct NonownedTag {};
253
254 private:
255 ConstUnmanaged<impl_scalar_type_2d_view_tpetra> b;
256 ConstUnmanaged<impl_scalar_type_2d_view_tpetra> x; // x_owned
257 ConstUnmanaged<impl_scalar_type_2d_view_tpetra> x_remote;
258 Unmanaged<impl_scalar_type_2d_view_tpetra> y;
259
260 // AmD information
261 const ConstUnmanaged<impl_scalar_type_1d_view> tpetra_values;
262
263 // blocksize
264 const local_ordinal_type blocksize_requested;
265
266 // block offsets
267 const ConstUnmanaged<i64_3d_view> A_x_offsets;
268 const ConstUnmanaged<i64_3d_view> A_x_offsets_remote;
269
270 // diagonal block inverses
271 const ConstUnmanaged<btdm_scalar_type_3d_view> d_inv;
272
273 // squared update norms
274 const Unmanaged<impl_scalar_type_1d_view> W;
275
276 impl_scalar_type damping_factor;
277
278 public:
279 ComputeResidualAndSolve_2Pass_Impl(
280 const AmD<MatrixType>& amd,
281 const btdm_scalar_type_3d_view& d_inv_,
282 const impl_scalar_type_1d_view& W_,
283 const local_ordinal_type& blocksize_requested_,
284 const impl_scalar_type& damping_factor_)
285 : tpetra_values(amd.tpetra_values)
286 , blocksize_requested(blocksize_requested_)
287 , A_x_offsets(amd.A_x_offsets)
288 , A_x_offsets_remote(amd.A_x_offsets_remote)
289 , d_inv(d_inv_)
290 , W(W_)
291 , damping_factor(damping_factor_) {}
292
293 KOKKOS_INLINE_FUNCTION
294 void operator()(const OwnedTag, const member_type& member) const {
295 const local_ordinal_type blocksize = (B == 0 ? blocksize_requested : B);
296 const local_ordinal_type rowidx = member.league_rank();
297 const local_ordinal_type row = rowidx * blocksize;
298 const local_ordinal_type num_vectors = b.extent(1);
299
300 auto A_block_cst = ConstUnmanaged<tpetra_block_access_view_type>(
301 tpetra_values.data(), blocksize, blocksize);
302
303 // Get shared allocation for a local copy of x, Ax, and A
304 impl_scalar_type* local_residual = reinterpret_cast<impl_scalar_type*>(
305 member.team_scratch(0).get_shmem(blocksize * sizeof(impl_scalar_type)));
306 impl_scalar_type* local_x =
307 reinterpret_cast<impl_scalar_type*>(member.thread_scratch(0).get_shmem(
308 blocksize * sizeof(impl_scalar_type)));
309
310 for (local_ordinal_type col = 0; col < num_vectors; ++col) {
311 if (col) member.team_barrier();
312 // y -= Rx
313 // Initialize accumulation arrays
314 Kokkos::parallel_for(
315 Kokkos::TeamVectorRange(member, blocksize),
316 [&](const local_ordinal_type& i) { local_residual[i] = b(row + i, col); });
317 member.team_barrier();
318
319 int numEntries = A_x_offsets.extent(2);
320
321 Kokkos::parallel_for(
322 Kokkos::TeamThreadRange(member, 0, numEntries), [&](const int k) {
323 int64_t A_offset = A_x_offsets(rowidx, 0, k);
324 int64_t x_offset = A_x_offsets(rowidx, 1, k);
325 if (A_offset != KokkosKernels::ArithTraits<int64_t>::min()) {
326 A_block_cst.assign_data(tpetra_values.data() + A_offset);
327 // Pull x into local memory
328 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize),
329 [&](const local_ordinal_type& i) {
330 local_x[i] = x(x_offset + i, col);
331 });
332
333 // MatVec op Ax += A*x
334 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize),
335 [&](const local_ordinal_type& k0) {
336 impl_scalar_type val = 0;
337 for (int k1 = 0; k1 < blocksize; k1++)
338 val += A_block_cst(k0, k1) * local_x[k1];
339 Kokkos::atomic_add(local_residual + k0, -val);
340 });
341 }
342 });
343 member.team_barrier();
344 // Write back the partial residual to y
345 if (member.team_rank() == 0) {
346 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize),
347 [&](const local_ordinal_type& k) {
348 y(row + k, col) = local_residual[k];
349 });
350 }
351 }
352 }
353
354 KOKKOS_INLINE_FUNCTION
355 void operator()(const NonownedTag, const member_type& member) const {
356 const local_ordinal_type blocksize = (B == 0 ? blocksize_requested : B);
357 const local_ordinal_type rowidx = member.league_rank();
358 const local_ordinal_type row = rowidx * blocksize;
359 const local_ordinal_type num_vectors = y.extent(1);
360
361 auto A_block_cst = ConstUnmanaged<tpetra_block_access_view_type>(
362 tpetra_values.data(), blocksize, blocksize);
363
364 // Get shared allocation for a local copy of x, Ax, and A
365 impl_scalar_type* local_residual = reinterpret_cast<impl_scalar_type*>(
366 member.team_scratch(0).get_shmem(blocksize * sizeof(impl_scalar_type)));
367 impl_scalar_type* local_Dinv_residual = reinterpret_cast<impl_scalar_type*>(
368 member.team_scratch(0).get_shmem(blocksize * sizeof(impl_scalar_type)));
369 impl_scalar_type* local_x =
370 reinterpret_cast<impl_scalar_type*>(member.thread_scratch(0).get_shmem(
371 blocksize * sizeof(impl_scalar_type)));
372
373 magnitude_type norm = 0;
374 for (local_ordinal_type col = 0; col < num_vectors; ++col) {
375 if (col) member.team_barrier();
376 // y -= Rx
377 // Initialize accumulation arrays.
378 Kokkos::parallel_for(Kokkos::TeamVectorRange(member, blocksize),
379 [&](const local_ordinal_type& i) {
380 local_Dinv_residual[i] = 0;
381 local_residual[i] = y(row + i, col);
382 });
383 member.team_barrier();
384
385 int numEntries = A_x_offsets_remote.extent(2);
386
387 Kokkos::parallel_for(
388 Kokkos::TeamThreadRange(member, 0, numEntries), [&](const int k) {
389 int64_t A_offset = A_x_offsets_remote(rowidx, 0, k);
390 int64_t x_offset = A_x_offsets_remote(rowidx, 1, k);
391 if (A_offset != KokkosKernels::ArithTraits<int64_t>::min()) {
392 A_block_cst.assign_data(tpetra_values.data() + A_offset);
393 // Pull x into local memory
394 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize),
395 [&](const local_ordinal_type& i) {
396 local_x[i] = x_remote(x_offset + i, col);
397 });
398
399 // matvec on block: local_residual -= A_block_cst * local_x
400 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize),
401 [&](const int k0) {
402 impl_scalar_type val = 0;
403 for (int k1 = 0; k1 < blocksize; k1++)
404 val += A_block_cst(k0, k1) * local_x[k1];
405 Kokkos::atomic_add(local_residual + k0, -val);
406 });
407 }
408 });
409 member.team_barrier();
410 // Compute local_Dinv_residual = D^-1 * local_residual
411 Kokkos::parallel_for(
412 Kokkos::TeamThreadRange(member, blocksize),
413 [&](const local_ordinal_type& k0) {
414 Kokkos::parallel_reduce(
415 Kokkos::ThreadVectorRange(member, blocksize),
416 [&](const local_ordinal_type& k1, impl_scalar_type& update) {
417 update += d_inv(rowidx, k0, k1) * local_residual[k1];
418 },
419 local_Dinv_residual[k0]);
420 });
421 member.team_barrier();
422 // local_Dinv_residual is fully computed. Now compute the
423 // squared y update norm and update y (using damping factor).
424 magnitude_type colNorm;
425 Kokkos::parallel_reduce(
426 Kokkos::TeamVectorRange(member, blocksize),
427 [&](const local_ordinal_type& k, magnitude_type& update) {
428 // Compute the change in y (assuming damping_factor == 1) for this
429 // entry.
430 impl_scalar_type old_y = x(row + k, col);
431 impl_scalar_type y_update = local_Dinv_residual[k] - old_y;
432 if constexpr (KokkosKernels::ArithTraits<impl_scalar_type>::is_complex) {
433 magnitude_type ydiff =
434 KokkosKernels::ArithTraits<impl_scalar_type>::abs(y_update);
435 update += ydiff * ydiff;
436 } else {
437 update += y_update * y_update;
438 }
439 y(row + k, col) = old_y + damping_factor * y_update;
440 },
441 colNorm);
442 norm += colNorm;
443 }
444 Kokkos::single(Kokkos::PerTeam(member), [&]() { W(rowidx) = norm; });
445 }
446
447 // Launch pass 1 of the 2-pass version.
448 // This computes just the owned part of residual and writes that back to y.
449 void run_pass1(const ConstUnmanaged<impl_scalar_type_2d_view_tpetra>& b_,
450 const ConstUnmanaged<impl_scalar_type_2d_view_tpetra>& x_,
451 const Unmanaged<impl_scalar_type_2d_view_tpetra>& y_) {
452 IFPACK2_BLOCKHELPER_PROFILER_REGION_BEGIN;
453 IFPACK2_BLOCKHELPER_TIMER_WITH_FENCE(
454 "BlockTriDi::ComputeResidualAndSolve::RunPass1",
455 ComputeResidualAndSolve0, execution_space);
456
457 b = b_;
458 x = x_;
459 y = y_;
460
461 const local_ordinal_type blocksize = blocksize_requested;
462 const local_ordinal_type nrows = d_inv.extent(0);
463
464 const local_ordinal_type team_size = 8;
465 const local_ordinal_type vector_size = 8;
466 const size_t shmem_team_size = blocksize * sizeof(impl_scalar_type);
467 const size_t shmem_thread_size = blocksize * sizeof(impl_scalar_type);
468 Kokkos::TeamPolicy<execution_space, OwnedTag> policy(nrows, team_size,
469 vector_size);
470 policy.set_scratch_size(0, Kokkos::PerTeam(shmem_team_size),
471 Kokkos::PerThread(shmem_thread_size));
472 Kokkos::parallel_for("ComputeResidualAndSolve::TeamPolicy::Pass1", policy,
473 *this);
474 IFPACK2_BLOCKHELPER_PROFILER_REGION_END;
475 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
476 }
477
478 // Launch pass 2 of the 2-pass version.
479 // This finishes computing residual with x_remote,
480 // and then applies Dinv and computes norm.
481 void run_pass2(
482 const ConstUnmanaged<impl_scalar_type_2d_view_tpetra>& x_,
483 const ConstUnmanaged<impl_scalar_type_2d_view_tpetra>& x_remote_,
484 const Unmanaged<impl_scalar_type_2d_view_tpetra>& y_) {
485 IFPACK2_BLOCKHELPER_PROFILER_REGION_BEGIN;
486 IFPACK2_BLOCKHELPER_TIMER_WITH_FENCE(
487 "BlockTriDi::ComputeResidualAndSolve::RunPass2",
488 ComputeResidualAndSolve0, execution_space);
489
490 x = x_;
491 x_remote = x_remote_;
492 y = y_;
493
494 const local_ordinal_type blocksize = blocksize_requested;
495 const local_ordinal_type nrows = d_inv.extent(0);
496
497 const local_ordinal_type team_size = 8;
498 const local_ordinal_type vector_size = 8;
499 const size_t shmem_team_size = 2 * blocksize * sizeof(impl_scalar_type);
500 const size_t shmem_thread_size = blocksize * sizeof(impl_scalar_type);
501 Kokkos::TeamPolicy<execution_space, NonownedTag> policy(nrows, team_size,
502 vector_size);
503 policy.set_scratch_size(0, Kokkos::PerTeam(shmem_team_size),
504 Kokkos::PerThread(shmem_thread_size));
505 Kokkos::parallel_for("ComputeResidualAndSolve::TeamPolicy::Pass2", policy,
506 *this);
507 IFPACK2_BLOCKHELPER_PROFILER_REGION_END;
508 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
509 }
510};
511
512template <typename MatrixType, int B>
513struct ComputeResidualAndSolve_YZero_Impl {
514 using impl_type = BlockHelperDetails::ImplType<MatrixType>;
515 using node_device_type = typename impl_type::node_device_type;
516 using execution_space = typename impl_type::execution_space;
517 using memory_space = typename impl_type::memory_space;
518
519 using local_ordinal_type = typename impl_type::local_ordinal_type;
520 using size_type = typename impl_type::size_type;
521 using impl_scalar_type = typename impl_type::impl_scalar_type;
522 using magnitude_type = typename impl_type::magnitude_type;
524 using local_ordinal_type_1d_view =
525 typename impl_type::local_ordinal_type_1d_view;
526 using size_type_1d_view = typename impl_type::size_type_1d_view;
527 using tpetra_block_access_view_type =
528 typename impl_type::tpetra_block_access_view_type; // block crs (layout
529 // right)
530 using impl_scalar_type_1d_view = typename impl_type::impl_scalar_type_1d_view;
531 using impl_scalar_type_2d_view_tpetra =
532 typename impl_type::impl_scalar_type_2d_view_tpetra; // block multivector
533 // (layout left)
534 using btdm_scalar_type_3d_view = typename impl_type::btdm_scalar_type_3d_view;
535 using btdm_scalar_type_4d_view = typename impl_type::btdm_scalar_type_4d_view;
536 using i64_3d_view = typename impl_type::i64_3d_view;
537
539 using member_type = typename Kokkos::TeamPolicy<execution_space>::member_type;
540
541 private:
542 ConstUnmanaged<impl_scalar_type_2d_view_tpetra> b;
543 Unmanaged<impl_scalar_type_2d_view_tpetra> y;
544
545 // AmD information
546 const ConstUnmanaged<impl_scalar_type_1d_view> tpetra_values;
547
548 // blocksize
549 const local_ordinal_type blocksize_requested;
550
551 // block offsets
552 const ConstUnmanaged<i64_3d_view> A_x_offsets;
553 const ConstUnmanaged<i64_3d_view> A_x_offsets_remote;
554
555 // diagonal block inverses
556 const ConstUnmanaged<btdm_scalar_type_3d_view> d_inv;
557
558 // squared update norms
559 const Unmanaged<impl_scalar_type_1d_view> W;
560
561 impl_scalar_type damping_factor;
562
563 public:
564 ComputeResidualAndSolve_YZero_Impl(
565 const AmD<MatrixType>& amd, const btdm_scalar_type_3d_view& d_inv_,
566 const impl_scalar_type_1d_view& W_,
567 const local_ordinal_type& blocksize_requested_,
568 const impl_scalar_type& damping_factor_)
569 : tpetra_values(amd.tpetra_values)
570 , blocksize_requested(blocksize_requested_)
571 , A_x_offsets(amd.A_x_offsets)
572 , A_x_offsets_remote(amd.A_x_offsets_remote)
573 , d_inv(d_inv_)
574 , W(W_)
575 , damping_factor(damping_factor_) {}
576
577 KOKKOS_INLINE_FUNCTION
578 void operator()(const member_type& member) const {
579 const local_ordinal_type blocksize = (B == 0 ? blocksize_requested : B);
580 const local_ordinal_type rowidx =
581 member.league_rank() * member.team_size() + member.team_rank();
582 const local_ordinal_type row = rowidx * blocksize;
583 const local_ordinal_type num_vectors = b.extent(1);
584
585 // Get shared allocation for a local copy of x, Ax, and A
586 impl_scalar_type* local_Dinv_residual =
587 reinterpret_cast<impl_scalar_type*>(member.thread_scratch(0).get_shmem(
588 blocksize * sizeof(impl_scalar_type)));
589
590 if (rowidx >= (local_ordinal_type)d_inv.extent(0)) return;
591
592 magnitude_type norm = 0;
593 for (local_ordinal_type col = 0; col < num_vectors; ++col) {
594 // Compute local_Dinv_residual = D^-1 * local_residual
595 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize),
596 [&](const local_ordinal_type& k0) {
597 impl_scalar_type val = 0;
598 for (local_ordinal_type k1 = 0; k1 < blocksize;
599 k1++) {
600 val += d_inv(rowidx, k0, k1) * b(row + k1, col);
601 }
602 local_Dinv_residual[k0] = val;
603 });
604
605 magnitude_type colNorm;
606 Kokkos::parallel_reduce(
607 Kokkos::ThreadVectorRange(member, blocksize),
608 [&](const local_ordinal_type& k, magnitude_type& update) {
609 // Compute the change in y (assuming damping_factor == 1) for this
610 // entry.
611 impl_scalar_type y_update = local_Dinv_residual[k];
612 if constexpr (KokkosKernels::ArithTraits<impl_scalar_type>::is_complex) {
613 magnitude_type ydiff =
614 KokkosKernels::ArithTraits<impl_scalar_type>::abs(y_update);
615 update += ydiff * ydiff;
616 } else {
617 update += y_update * y_update;
618 }
619 y(row + k, col) = damping_factor * y_update;
620 },
621 colNorm);
622 norm += colNorm;
623 }
624 Kokkos::single(Kokkos::PerThread(member), [&]() { W(rowidx) = norm; });
625 }
626
627 // ComputeResidualAndSolve_SolveOnly::run does the solve for the first
628 // iteration, when the initial guess for y is zero. This means the residual
629 // vector is just b. The kernel applies the inverse diags to b to find y, and
630 // also puts the partial squared update norms (1 per row) into W.
631 void run(const ConstUnmanaged<impl_scalar_type_2d_view_tpetra>& b_,
632 const Unmanaged<impl_scalar_type_2d_view_tpetra>& y_) {
633 IFPACK2_BLOCKHELPER_PROFILER_REGION_BEGIN;
634 IFPACK2_BLOCKHELPER_TIMER_WITH_FENCE(
635 "BlockTriDi::ComputeResidualAndSolve::Run_Y_Zero",
636 ComputeResidualAndSolve0, execution_space);
637
638 this->y = y_;
639 this->b = b_;
640
641 const local_ordinal_type blocksize = blocksize_requested;
642 const local_ordinal_type nrows = d_inv.extent(0);
643
644 const local_ordinal_type team_size = 8;
645 const local_ordinal_type vector_size = 8;
646 const size_t shmem_thread_size = blocksize * sizeof(impl_scalar_type);
647 Kokkos::TeamPolicy<execution_space> policy(
648 (nrows + team_size - 1) / team_size, team_size, vector_size);
649 policy.set_scratch_size(0, Kokkos::PerThread(shmem_thread_size));
650 Kokkos::parallel_for("ComputeResidualAndSolve::TeamPolicy::y_zero", policy, *this);
651 IFPACK2_BLOCKHELPER_PROFILER_REGION_END;
652 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
653 }
654};
655
656// run_y_zero does the solve for the first
657// iteration, when the initial guess for y is zero. This means the residual
658// vector is just b. The kernel applies the inverse diags to b to find y, and
659// also puts the partial squared update norms (1 per row) into W.
660template <typename MatrixType>
661void ComputeResidualAndSolve<MatrixType, BlockTriDiContainerDetails::ImplSimdTag>::run_y_zero(
662 const Const<impl_scalar_type_2d_view_tpetra>& b_,
663 const impl_scalar_type_2d_view_tpetra& y_) {
664#define RUN_CASE(B) \
665 { \
666 ComputeResidualAndSolve_YZero_Impl<MatrixType, B> functor(amd, d_inv, W, blocksize_requested, damping_factor); \
667 functor.run(b_, y_); \
668 break; \
669 }
670
671 switch (blocksize_requested) {
672 case 3: RUN_CASE(3);
673 case 5: RUN_CASE(5);
674 case 7: RUN_CASE(7);
675 case 9: RUN_CASE(9);
676 case 10: RUN_CASE(10);
677 case 11: RUN_CASE(11);
678 case 16: RUN_CASE(16);
679 case 17: RUN_CASE(17);
680 case 18: RUN_CASE(18);
681 default: RUN_CASE(0);
682 }
683#undef RUN_CASE
684}
685
686template <typename MatrixType>
687void ComputeResidualAndSolve<MatrixType, BlockTriDiContainerDetails::ImplSimdTag>::run_single_pass(
688 const Const<impl_scalar_type_2d_view_tpetra>& b_,
689 const impl_scalar_type_2d_view_tpetra& x_,
690 const impl_scalar_type_2d_view_tpetra& x_remote_,
691 const impl_scalar_type_2d_view_tpetra& y_) {
692#define RUN_CASE(B) \
693 { \
694 ComputeResidualAndSolve_SinglePass_Impl<MatrixType, B> functor(amd, d_inv, W, blocksize_requested, damping_factor); \
695 functor.run(b_, x_, x_remote_, y_); \
696 break; \
697 }
698
699 switch (blocksize_requested) {
700 case 3: RUN_CASE(3);
701 case 5: RUN_CASE(5);
702 case 7: RUN_CASE(7);
703 case 9: RUN_CASE(9);
704 case 10: RUN_CASE(10);
705 case 11: RUN_CASE(11);
706 case 16: RUN_CASE(16);
707 case 17: RUN_CASE(17);
708 case 18: RUN_CASE(18);
709 default: RUN_CASE(0);
710 }
711#undef RUN_CASE
712}
713
714template <typename MatrixType>
715void ComputeResidualAndSolve<MatrixType, BlockTriDiContainerDetails::ImplSimdTag>::run_pass1_of_2(
716 const Const<impl_scalar_type_2d_view_tpetra>& b_,
717 const impl_scalar_type_2d_view_tpetra& x_,
718 const impl_scalar_type_2d_view_tpetra& y_) {
719#define RUN_CASE(B) \
720 { \
721 ComputeResidualAndSolve_2Pass_Impl<MatrixType, B> functor(amd, d_inv, W, blocksize_requested, damping_factor); \
722 functor.run_pass1(b_, x_, y_); \
723 break; \
724 }
725
726 switch (blocksize_requested) {
727 case 3: RUN_CASE(3);
728 case 5: RUN_CASE(5);
729 case 7: RUN_CASE(7);
730 case 9: RUN_CASE(9);
731 case 10: RUN_CASE(10);
732 case 11: RUN_CASE(11);
733 case 16: RUN_CASE(16);
734 case 17: RUN_CASE(17);
735 case 18: RUN_CASE(18);
736 default: RUN_CASE(0);
737 }
738#undef RUN_CASE
739}
740
741template <typename MatrixType>
742void ComputeResidualAndSolve<MatrixType, BlockTriDiContainerDetails::ImplSimdTag>::run_pass2_of_2(
743 const impl_scalar_type_2d_view_tpetra& x_,
744 const impl_scalar_type_2d_view_tpetra& x_remote_,
745 const impl_scalar_type_2d_view_tpetra& y_) {
746#define RUN_CASE(B) \
747 { \
748 ComputeResidualAndSolve_2Pass_Impl<MatrixType, B> functor(amd, d_inv, W, blocksize_requested, damping_factor); \
749 functor.run_pass2(x_, x_remote_, y_); \
750 break; \
751 }
752
753 switch (blocksize_requested) {
754 case 3: RUN_CASE(3);
755 case 5: RUN_CASE(5);
756 case 7: RUN_CASE(7);
757 case 9: RUN_CASE(9);
758 case 10: RUN_CASE(10);
759 case 11: RUN_CASE(11);
760 case 16: RUN_CASE(16);
761 case 17: RUN_CASE(17);
762 case 18: RUN_CASE(18);
763 default: RUN_CASE(0);
764 }
765#undef RUN_CASE
766}
767
768} // namespace Ifpack2::BlockHelperDetails
769
770#define IFPACK2_BLOCKCOMPUTERESIDUALANDSOLVE_INSTANT(S, LO, GO, N) \
771 template class Ifpack2::BlockHelperDetails::ComputeResidualAndSolve<Tpetra::RowMatrix<S, LO, GO, N> >;
772
773#endif
size_t size_type
Definition Ifpack2_BlockHelper.hpp:278
node_type::device_type node_device_type
Definition Ifpack2_BlockHelper.hpp:302
KokkosKernels::ArithTraits< scalar_type >::val_type impl_scalar_type
Definition Ifpack2_BlockHelper.hpp:288
Kokkos::View< size_type *, device_type > size_type_1d_view
Definition Ifpack2_BlockHelper.hpp:351