Ifpack2 Templated Preconditioning Package Version 1.0
Loading...
Searching...
No Matches
Ifpack2_BlockTriDiContainer_impl.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_BLOCKTRIDICONTAINER_IMPL_HPP
11#define IFPACK2_BLOCKTRIDICONTAINER_IMPL_HPP
12
13// #define IFPACK2_BLOCKTRIDICONTAINER_WRITE_MM
14// #define IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
15
16#include <Teuchos_Details_MpiTypeTraits.hpp>
17
18#include <Tpetra_Details_extractMpiCommFromTeuchos.hpp>
19#include <Tpetra_Distributor.hpp>
20#include <Tpetra_BlockMultiVector.hpp>
21
22#include <KokkosKernels_ArithTraits.hpp>
23#include <KokkosBatched_Util.hpp>
24#include <KokkosBatched_Vector.hpp>
25#include <KokkosBatched_Copy_Decl.hpp>
26#include <KokkosBatched_Copy_Impl.hpp>
27#include <KokkosBatched_AddRadial_Decl.hpp>
28#include <KokkosBatched_AddRadial_Impl.hpp>
29#include <KokkosBatched_SetIdentity_Decl.hpp>
30#include <KokkosBatched_SetIdentity_Impl.hpp>
31#include <KokkosBatched_Gemm_Decl.hpp>
32#include <KokkosBatched_Gemm_Serial_Impl.hpp>
33#include <KokkosBatched_Gemm_Team_Impl.hpp>
34#include <KokkosBatched_Gemv_Decl.hpp>
35#include <KokkosBatched_Gemv_Team_Impl.hpp>
36#include <KokkosBatched_Trsm_Decl.hpp>
37#include <KokkosBatched_Trsm_Serial_Impl.hpp>
38#include <KokkosBatched_Trsm_Team_Impl.hpp>
39#include <KokkosBatched_Trsv_Decl.hpp>
40#include <KokkosBatched_Trsv_Serial_Impl.hpp>
41#include <KokkosBatched_Trsv_Team_Impl.hpp>
42#include <KokkosBatched_LU_Decl.hpp>
43#include <KokkosBatched_LU_Serial_Impl.hpp>
44#include <KokkosBatched_LU_Team_Impl.hpp>
45
46#include <KokkosBlas1_nrm1.hpp>
47#include <KokkosBlas1_nrm2.hpp>
48
49#include <memory>
50#include <cstdint>
51
52#include "Ifpack2_BlockHelper.hpp"
53#include "Ifpack2_BlockComputeResidualVector.hpp"
54#include "Ifpack2_BlockComputeResidualAndSolve.hpp"
55
56// need to interface this into cmake variable (or only use this flag when it is necessary)
57// #define IFPACK2_BLOCKTRIDICONTAINER_ENABLE_PROFILE
58// #undef IFPACK2_BLOCKTRIDICONTAINER_ENABLE_PROFILE
59#if defined(KOKKOS_ENABLE_CUDA) && defined(IFPACK2_BLOCKTRIDICONTAINER_ENABLE_PROFILE)
60#include "cuda_profiler_api.h"
61#endif
62
63// I am not 100% sure about the mpi 3 on cuda
64#if MPI_VERSION >= 3
65#define IFPACK2_BLOCKTRIDICONTAINER_USE_MPI_3
66#endif
67
68// ::: Experiments :::
69// define either pinned memory or cudamemory for mpi
70// if both macros are disabled, it will use tpetra memory space which is uvm space for cuda
71// if defined, this use pinned memory instead of device pointer
72// by default, we enable pinned memory
73#define IFPACK2_BLOCKTRIDICONTAINER_USE_PINNED_MEMORY_FOR_MPI
74// #define IFPACK2_BLOCKTRIDICONTAINER_USE_CUDA_MEMORY_FOR_MPI
75
76// if defined, all views are allocated on cuda space intead of cuda uvm space
77#define IFPACK2_BLOCKTRIDICONTAINER_USE_CUDA_SPACE
78
79// if defined, btdm_scalar_type is used (if impl_scala_type is double, btdm_scalar_type is float)
80#if defined(HAVE_IFPACK2_BLOCKTRIDICONTAINER_SMALL_SCALAR)
81#define IFPACK2_BLOCKTRIDICONTAINER_USE_SMALL_SCALAR_FOR_BLOCKTRIDIAG
82#endif
83
84// if defined, it uses multiple execution spaces
85#define IFPACK2_BLOCKTRIDICONTAINER_USE_EXEC_SPACE_INSTANCES
86
87namespace Ifpack2 {
88
89namespace BlockTriDiContainerDetails {
90
91namespace KB = KokkosBatched;
92
96using do_not_initialize_tag = Kokkos::ViewAllocateWithoutInitializing;
97
98template <typename MemoryTraitsType, Kokkos::MemoryTraitsFlags flag>
99using MemoryTraits = Kokkos::MemoryTraits<MemoryTraitsType::is_unmanaged |
100 MemoryTraitsType::is_random_access |
101 flag>;
102
103template <typename ViewType>
104using Unmanaged = Kokkos::View<typename ViewType::data_type,
105 typename ViewType::array_layout,
106 typename ViewType::device_type,
107 MemoryTraits<typename ViewType::memory_traits, Kokkos::Unmanaged>>;
108template <typename ViewType>
109using Atomic = Kokkos::View<typename ViewType::data_type,
110 typename ViewType::array_layout,
111 typename ViewType::device_type,
112 MemoryTraits<typename ViewType::memory_traits, Kokkos::Atomic>>;
113template <typename ViewType>
114using Const = Kokkos::View<typename ViewType::const_data_type,
115 typename ViewType::array_layout,
116 typename ViewType::device_type,
117 typename ViewType::memory_traits>;
118template <typename ViewType>
119using ConstUnmanaged = Const<Unmanaged<ViewType>>;
120
121template <typename ViewType>
122using AtomicUnmanaged = Atomic<Unmanaged<ViewType>>;
123
124template <typename ViewType>
125using Unmanaged = Kokkos::View<typename ViewType::data_type,
126 typename ViewType::array_layout,
127 typename ViewType::device_type,
128 MemoryTraits<typename ViewType::memory_traits, Kokkos::Unmanaged>>;
129
130template <typename ViewType>
131using Scratch = Kokkos::View<typename ViewType::data_type,
132 typename ViewType::array_layout,
133 typename ViewType::execution_space::scratch_memory_space,
134 MemoryTraits<typename ViewType::memory_traits, Kokkos::Unmanaged>>;
135
139template <typename T>
141 typedef T type;
142};
143#if defined(IFPACK2_BLOCKTRIDICONTAINER_USE_SMALL_SCALAR_FOR_BLOCKTRIDIAG)
144template <>
145struct BlockTridiagScalarType<double> {
146 typedef float type;
147};
148// template<> struct SmallScalarType<Kokkos::complex<double> > { typedef Kokkos::complex<float> type; };
149#endif
150
151#if defined(KOKKOS_ENABLE_CUDA) && defined(IFPACK2_BLOCKTRIDICONTAINER_ENABLE_PROFILE)
152#define IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_BEGIN \
153 cudaProfilerStart();
154
155#define IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_END \
156 { cudaProfilerStop(); }
157#else
159#define IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_BEGIN
160#define IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_END
161#endif
162
166template <typename MatrixType>
167typename Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_import_type>
168createBlockCrsTpetraImporter(const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_row_matrix_type> &A) {
169 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::CreateBlockCrsTpetraImporter", CreateBlockCrsTpetraImporter);
171 using tpetra_map_type = typename impl_type::tpetra_map_type;
172 using tpetra_mv_type = typename impl_type::tpetra_block_multivector_type;
173 using tpetra_import_type = typename impl_type::tpetra_import_type;
174 using crs_matrix_type = typename impl_type::tpetra_crs_matrix_type;
175 using block_crs_matrix_type = typename impl_type::tpetra_block_crs_matrix_type;
176
177 auto A_crs = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A);
178 auto A_bcrs = Teuchos::rcp_dynamic_cast<const block_crs_matrix_type>(A);
179
180 bool hasBlockCrsMatrix = !A_bcrs.is_null();
181
182 // This is OK here to use the graph of the A_crs matrix and a block size of 1
183 const auto g = hasBlockCrsMatrix ? A_bcrs->getCrsGraph() : *(A_crs->getCrsGraph()); // tpetra crs graph object
184
185 const auto blocksize = hasBlockCrsMatrix ? A_bcrs->getBlockSize() : 1;
186 const auto src = Teuchos::rcp(new tpetra_map_type(tpetra_mv_type::makePointMap(*g.getDomainMap(), blocksize)));
187 const auto tgt = Teuchos::rcp(new tpetra_map_type(tpetra_mv_type::makePointMap(*g.getColMap(), blocksize)));
188 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
189 return Teuchos::rcp(new tpetra_import_type(src, tgt));
190}
191
192// Partial replacement for forward-mode MultiVector::doImport.
193// Permits overlapped communication and computation, but also supports sync'ed.
194// I'm finding that overlapped comm/comp can give quite poor performance on some
195// platforms, so we can't just use it straightforwardly always.
196
197template <typename MatrixType>
198struct AsyncableImport {
199 public:
200 using impl_type = BlockHelperDetails::ImplType<MatrixType>;
201
202 private:
206#if !defined(HAVE_IFPACK2_MPI)
207 typedef int MPI_Request;
208 typedef int MPI_Comm;
209#endif
212 using scalar_type = typename impl_type::scalar_type;
213
214 static int isend(const MPI_Comm comm, const char *buf, int count, int dest, int tag, MPI_Request *ireq) {
215#ifdef HAVE_IFPACK2_MPI
216 MPI_Request ureq;
217 int ret = MPI_Isend(const_cast<char *>(buf), count, MPI_CHAR, dest, tag, comm, ireq == NULL ? &ureq : ireq);
218 if (ireq == NULL) MPI_Request_free(&ureq);
219 return ret;
220#else
221 return 0;
222#endif
223 }
224
225 static int irecv(const MPI_Comm comm, char *buf, int count, int src, int tag, MPI_Request *ireq) {
226#ifdef HAVE_IFPACK2_MPI
227 MPI_Request ureq;
228 int ret = MPI_Irecv(buf, count, MPI_CHAR, src, tag, comm, ireq == NULL ? &ureq : ireq);
229 if (ireq == NULL) MPI_Request_free(&ureq);
230 return ret;
231#else
232 return 0;
233#endif
234 }
235
236 static int waitany(int count, MPI_Request *reqs, int *index) {
237#ifdef HAVE_IFPACK2_MPI
238 return MPI_Waitany(count, reqs, index, MPI_STATUS_IGNORE);
239#else
240 return 0;
241#endif
242 }
243
244 static int waitall(int count, MPI_Request *reqs) {
245#ifdef HAVE_IFPACK2_MPI
246 return MPI_Waitall(count, reqs, MPI_STATUS_IGNORE);
247#else
248 return 0;
249#endif
250 }
251
252 public:
253 using tpetra_map_type = typename impl_type::tpetra_map_type;
254 using tpetra_import_type = typename impl_type::tpetra_import_type;
255
256 using local_ordinal_type = typename impl_type::local_ordinal_type;
257 using global_ordinal_type = typename impl_type::global_ordinal_type;
258 using size_type = typename impl_type::size_type;
259 using impl_scalar_type = typename impl_type::impl_scalar_type;
260
261 using int_1d_view_host = Kokkos::View<int *, Kokkos::HostSpace>;
262 using local_ordinal_type_1d_view_host = Kokkos::View<local_ordinal_type *, Kokkos::HostSpace>;
263
264 using execution_space = typename impl_type::execution_space;
265 using memory_space = typename impl_type::memory_space;
266 using local_ordinal_type_1d_view = typename impl_type::local_ordinal_type_1d_view;
267 using size_type_1d_view = typename impl_type::size_type_1d_view;
268 using size_type_1d_view_host = Kokkos::View<size_type *, Kokkos::HostSpace>;
269
270#if defined(KOKKOS_ENABLE_CUDA)
271 using impl_scalar_type_1d_view =
272 typename std::conditional<std::is_same<execution_space, Kokkos::Cuda>::value,
273#if defined(IFPACK2_BLOCKTRIDICONTAINER_USE_PINNED_MEMORY_FOR_MPI)
274 Kokkos::View<impl_scalar_type *, Kokkos::CudaHostPinnedSpace>,
275#elif defined(IFPACK2_BLOCKTRIDICONTAINER_USE_CUDA_MEMORY_FOR_MPI)
276 Kokkos::View<impl_scalar_type *, Kokkos::CudaSpace>,
277#else // no experimental macros are defined
278 typename impl_type::impl_scalar_type_1d_view,
279#endif
280 typename impl_type::impl_scalar_type_1d_view>::type;
281#else
282 using impl_scalar_type_1d_view = typename impl_type::impl_scalar_type_1d_view;
283#endif
284 using impl_scalar_type_1d_view_host = Kokkos::View<impl_scalar_type *, Kokkos::HostSpace>;
285 using impl_scalar_type_2d_view = typename impl_type::impl_scalar_type_2d_view;
286 using impl_scalar_type_2d_view_tpetra = typename impl_type::impl_scalar_type_2d_view_tpetra;
287
288#ifdef HAVE_IFPACK2_MPI
289 MPI_Comm comm;
290#endif
291
292 impl_scalar_type_2d_view_tpetra remote_multivector;
293 local_ordinal_type blocksize;
294
295 template <typename T>
296 struct SendRecvPair {
297 T send, recv;
298 };
299
300 // (s)end and (r)eceive data:
301 SendRecvPair<int_1d_view_host> pids; // mpi ranks
302 SendRecvPair<std::vector<MPI_Request>> reqs; // MPI_Request is pointer, cannot use kokkos view
303 SendRecvPair<size_type_1d_view> offset; // offsets to local id list and data buffer
304 SendRecvPair<size_type_1d_view_host> offset_host; // offsets to local id list and data buffer
305 SendRecvPair<local_ordinal_type_1d_view> lids; // local id list
306 SendRecvPair<impl_scalar_type_1d_view> buffer; // data buffer
307 SendRecvPair<impl_scalar_type_1d_view_host> buffer_host; // data buffer
308
309 local_ordinal_type_1d_view dm2cm; // permutation
310
311#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || defined(KOKKOS_ENABLE_SYCL)
312 using exec_instance_1d_std_vector = std::vector<execution_space>;
313 exec_instance_1d_std_vector exec_instances;
314#endif
315
316 // for cuda
317 public:
318 void setOffsetValues(const Teuchos::ArrayView<const size_t> &lens,
319 const size_type_1d_view &offs) {
320 // wrap lens to kokkos view and deep copy to device
321 Kokkos::View<size_t *, Kokkos::HostSpace> lens_host(const_cast<size_t *>(lens.getRawPtr()), lens.size());
322 const auto lens_device = Kokkos::create_mirror_view_and_copy(memory_space(), lens_host);
323
324 // exclusive scan
325 const Kokkos::RangePolicy<execution_space> policy(0, offs.extent(0));
326 const local_ordinal_type lens_size = lens_device.extent(0);
327 Kokkos::parallel_scan(
328 "AsyncableImport::RangePolicy::setOffsetValues",
329 policy, KOKKOS_LAMBDA(const local_ordinal_type &i, size_type &update, const bool &final) {
330 if (final)
331 offs(i) = update;
332 update += (i < lens_size ? lens_device[i] : 0);
333 });
334 }
335
336 void setOffsetValuesHost(const Teuchos::ArrayView<const size_t> &lens,
337 const size_type_1d_view_host &offs) {
338 // wrap lens to kokkos view and deep copy to device
339 Kokkos::View<size_t *, Kokkos::HostSpace> lens_host(const_cast<size_t *>(lens.getRawPtr()), lens.size());
340 const auto lens_device = Kokkos::create_mirror_view_and_copy(memory_space(), lens_host);
341
342 // exclusive scan
343 offs(0) = 0;
344 for (local_ordinal_type i = 1, iend = offs.extent(0); i < iend; ++i) {
345 offs(i) = offs(i - 1) + lens[i - 1];
346 }
347 }
348
349 private:
350 void createMpiRequests(const tpetra_import_type &import) {
351 Tpetra::Distributor &distributor = import.getDistributor();
352
353 // copy pids from distributor
354 const auto pids_from = distributor.getProcsFrom();
355 pids.recv = int_1d_view_host(do_not_initialize_tag("pids recv"), pids_from.size());
356 memcpy(pids.recv.data(), pids_from.getRawPtr(), sizeof(int) * pids.recv.extent(0));
357
358 const auto pids_to = distributor.getProcsTo();
359 pids.send = int_1d_view_host(do_not_initialize_tag("pids send"), pids_to.size());
360 memcpy(pids.send.data(), pids_to.getRawPtr(), sizeof(int) * pids.send.extent(0));
361
362 // mpi requests
363 reqs.recv.resize(pids.recv.extent(0));
364 memset(reqs.recv.data(), 0, reqs.recv.size() * sizeof(MPI_Request));
365 reqs.send.resize(pids.send.extent(0));
366 memset(reqs.send.data(), 0, reqs.send.size() * sizeof(MPI_Request));
367
368 // construct offsets
369#if 0
370 const auto lengths_to = distributor.getLengthsTo();
371 offset.send = size_type_1d_view(do_not_initialize_tag("offset send"), lengths_to.size() + 1);
372
373 const auto lengths_from = distributor.getLengthsFrom();
374 offset.recv = size_type_1d_view(do_not_initialize_tag("offset recv"), lengths_from.size() + 1);
375
376 setOffsetValues(lengths_to, offset.send);
377 offset_host.send = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), offset.send);
378
379 setOffsetValues(lengths_from, offset.recv);
380 offset_host.recv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), offset.recv);
381#else
382 const auto lengths_to = distributor.getLengthsTo();
383 offset_host.send = size_type_1d_view_host(do_not_initialize_tag("offset send"), lengths_to.size() + 1);
384
385 const auto lengths_from = distributor.getLengthsFrom();
386 offset_host.recv = size_type_1d_view_host(do_not_initialize_tag("offset recv"), lengths_from.size() + 1);
387
388 setOffsetValuesHost(lengths_to, offset_host.send);
389 // offset.send = Kokkos::create_mirror_view_and_copy(memory_space(), offset_host.send);
390
391 setOffsetValuesHost(lengths_from, offset_host.recv);
392 // offset.recv = Kokkos::create_mirror_view_and_copy(memory_space(), offset_host.recv);
393#endif
394 }
395
396 void createSendRecvIDs(const tpetra_import_type &import) {
397 // For each remote PID, the list of LIDs to receive.
398 const auto remote_lids = import.getRemoteLIDs();
399 const local_ordinal_type_1d_view_host
400 remote_lids_view_host(const_cast<local_ordinal_type *>(remote_lids.getRawPtr()), remote_lids.size());
401 lids.recv = local_ordinal_type_1d_view(do_not_initialize_tag("lids recv"), remote_lids.size());
402 Kokkos::deep_copy(lids.recv, remote_lids_view_host);
403
404 // For each export PID, the list of LIDs to send.
405 auto epids = import.getExportPIDs();
406 auto elids = import.getExportLIDs();
407 TEUCHOS_ASSERT(epids.size() == elids.size());
408 lids.send = local_ordinal_type_1d_view(do_not_initialize_tag("lids send"), elids.size());
409 auto lids_send_host = Kokkos::create_mirror_view(lids.send);
410
411 // naive search (not sure if pids or epids are sorted)
412 for (local_ordinal_type cnt = 0, i = 0, iend = pids.send.extent(0); i < iend; ++i) {
413 const auto pid_send_value = pids.send[i];
414 for (local_ordinal_type j = 0, jend = epids.size(); j < jend; ++j)
415 if (epids[j] == pid_send_value) lids_send_host[cnt++] = elids[j];
416 TEUCHOS_ASSERT(static_cast<size_t>(cnt) == offset_host.send[i + 1]);
417 }
418 Kokkos::deep_copy(lids.send, lids_send_host);
419 }
420
421 void createExecutionSpaceInstances() {
422#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || defined(KOKKOS_ENABLE_SYCL)
423 // The following line creates 8 streams:
424 exec_instances =
425 Kokkos::Experimental::partition_space(execution_space(), std::vector<int>(8, 1));
426#endif
427 }
428
429 public:
430 // for cuda, all tag types are public
431 struct ToBuffer {};
432 struct ToMultiVector {};
433
434 AsyncableImport(const Teuchos::RCP<const tpetra_map_type> &src_map,
435 const Teuchos::RCP<const tpetra_map_type> &tgt_map,
436 const local_ordinal_type blocksize_,
437 const local_ordinal_type_1d_view dm2cm_) {
438 blocksize = blocksize_;
439 dm2cm = dm2cm_;
440
441#ifdef HAVE_IFPACK2_MPI
442 comm = Tpetra::Details::extractMpiCommFromTeuchos(*tgt_map->getComm());
443#endif
444 const tpetra_import_type import(src_map, tgt_map);
445
446 createMpiRequests(import);
447 createSendRecvIDs(import);
448 createExecutionSpaceInstances();
449 }
450
451 void createDataBuffer(const local_ordinal_type &num_vectors) {
452 const size_type extent_0 = lids.recv.extent(0) * blocksize;
453 const size_type extent_1 = num_vectors;
454 if (remote_multivector.extent(0) == extent_0 &&
455 remote_multivector.extent(1) == extent_1) {
456 // skip
457 } else {
458 remote_multivector =
459 impl_scalar_type_2d_view_tpetra(do_not_initialize_tag("remote multivector"), extent_0, extent_1);
460
461 const auto send_buffer_size = offset_host.send[offset_host.send.extent(0) - 1] * blocksize * num_vectors;
462 const auto recv_buffer_size = offset_host.recv[offset_host.recv.extent(0) - 1] * blocksize * num_vectors;
463
464 buffer.send = impl_scalar_type_1d_view(do_not_initialize_tag("buffer send"), send_buffer_size);
465 buffer.recv = impl_scalar_type_1d_view(do_not_initialize_tag("buffer recv"), recv_buffer_size);
466
467 if (!Tpetra::Details::Behavior::assumeMpiIsGPUAware()) {
468 buffer_host.send = impl_scalar_type_1d_view_host(do_not_initialize_tag("buffer send"), send_buffer_size);
469 buffer_host.recv = impl_scalar_type_1d_view_host(do_not_initialize_tag("buffer recv"), recv_buffer_size);
470 }
471 }
472 }
473
474 void cancel() {
475#ifdef HAVE_IFPACK2_MPI
476 waitall(reqs.recv.size(), reqs.recv.data());
477 waitall(reqs.send.size(), reqs.send.data());
478#endif
479 }
480
481 // ======================================================================
482 // Async version using execution space instances
483 // ======================================================================
484
485#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || defined(KOKKOS_ENABLE_SYCL)
486 template <typename PackTag>
487 static void copy(const local_ordinal_type_1d_view &lids_,
488 const impl_scalar_type_1d_view &buffer_,
489 const local_ordinal_type ibeg_,
490 const local_ordinal_type iend_,
491 const impl_scalar_type_2d_view_tpetra &multivector_,
492 const local_ordinal_type blocksize_,
493 const execution_space &exec_instance_) {
494 const local_ordinal_type num_vectors = multivector_.extent(1);
495 const local_ordinal_type mv_blocksize = blocksize_ * num_vectors;
496 const local_ordinal_type idiff = iend_ - ibeg_;
497 const auto abase = buffer_.data() + mv_blocksize * ibeg_;
498
499 using team_policy_type = Kokkos::TeamPolicy<execution_space>;
500 local_ordinal_type vector_size(0);
501 if (blocksize_ <= 4)
502 vector_size = 4;
503 else if (blocksize_ <= 8)
504 vector_size = 8;
505 else if (blocksize_ <= 16)
506 vector_size = 16;
507 else
508 vector_size = 32;
509
510 const auto work_item_property = Kokkos::Experimental::WorkItemProperty::HintLightWeight;
511 const team_policy_type policy(exec_instance_, idiff, 1, vector_size);
512 Kokkos::parallel_for( //"AsyncableImport::TeamPolicy::copyViaCudaStream",
513 Kokkos::Experimental::require(policy, work_item_property),
514 KOKKOS_LAMBDA(const typename team_policy_type::member_type &member) {
515 const local_ordinal_type i = member.league_rank();
516 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, num_vectors), [&](const local_ordinal_type &j) {
517 auto aptr = abase + blocksize_ * (i + idiff * j);
518 auto bptr = &multivector_(blocksize_ * lids_(i + ibeg_), j);
519 if (std::is_same<PackTag, ToBuffer>::value)
520 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize_), [&](const local_ordinal_type &k) {
521 aptr[k] = bptr[k];
522 });
523 else
524 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize_), [&](const local_ordinal_type &k) {
525 bptr[k] = aptr[k];
526 });
527 });
528 });
529 }
530
531 void asyncSendRecvVar1(const impl_scalar_type_2d_view_tpetra &mv) {
532 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::AsyncableImport::AsyncSendRecv", AsyncSendRecv);
533
534#ifdef HAVE_IFPACK2_MPI
535 // constants and reallocate data buffers if necessary
536 const local_ordinal_type num_vectors = mv.extent(1);
537 const local_ordinal_type mv_blocksize = blocksize * num_vectors;
538
539 // 0. post receive async
540 for (local_ordinal_type i = 0, iend = pids.recv.extent(0); i < iend; ++i) {
541 if (Tpetra::Details::Behavior::assumeMpiIsGPUAware()) {
542 irecv(comm,
543 reinterpret_cast<char *>(buffer.recv.data() + offset_host.recv[i] * mv_blocksize),
544 (offset_host.recv[i + 1] - offset_host.recv[i]) * mv_blocksize * sizeof(impl_scalar_type),
545 pids.recv[i],
546 42,
547 &reqs.recv[i]);
548 } else {
549 irecv(comm,
550 reinterpret_cast<char *>(buffer_host.recv.data() + offset_host.recv[i] * mv_blocksize),
551 (offset_host.recv[i + 1] - offset_host.recv[i]) * mv_blocksize * sizeof(impl_scalar_type),
552 pids.recv[i],
553 42,
554 &reqs.recv[i]);
555 }
556 }
557
559 execution_space().fence();
560
561 // 1. async memcpy
562 for (local_ordinal_type i = 0; i < static_cast<local_ordinal_type>(pids.send.extent(0)); ++i) {
563 // 1.0. enqueue pack buffer
564 if (i < 8) exec_instances[i % 8].fence();
565 copy<ToBuffer>(lids.send, buffer.send,
566 offset_host.send(i), offset_host.send(i + 1),
567 mv, blocksize,
568 // execution_space());
569 exec_instances[i % 8]);
570 if (!Tpetra::Details::Behavior::assumeMpiIsGPUAware()) {
571 // if (i<8) exec_instances[i%8].fence();
572 const local_ordinal_type num_vectors = mv.extent(1);
573 const local_ordinal_type mv_blocksize = blocksize * num_vectors;
574
575 Kokkos::deep_copy(exec_instances[i % 8],
576 Kokkos::subview(buffer_host.send,
577 Kokkos::pair<local_ordinal_type, local_ordinal_type>(
578 offset_host.send(i) * mv_blocksize,
579 offset_host.send(i + 1) * mv_blocksize)),
580 Kokkos::subview(buffer.send,
581 Kokkos::pair<local_ordinal_type, local_ordinal_type>(
582 offset_host.send(i) * mv_blocksize,
583 offset_host.send(i + 1) * mv_blocksize)));
584 }
585 }
587 // execution_space().fence();
588 for (local_ordinal_type i = 0; i < static_cast<local_ordinal_type>(pids.send.extent(0)); ++i) {
589 // 1.1. sync the stream and isend
590 if (i < 8) exec_instances[i % 8].fence();
591 if (Tpetra::Details::Behavior::assumeMpiIsGPUAware()) {
592 isend(comm,
593 reinterpret_cast<const char *>(buffer.send.data() + offset_host.send[i] * mv_blocksize),
594 (offset_host.send[i + 1] - offset_host.send[i]) * mv_blocksize * sizeof(impl_scalar_type),
595 pids.send[i],
596 42,
597 &reqs.send[i]);
598 } else {
599 isend(comm,
600 reinterpret_cast<const char *>(buffer_host.send.data() + offset_host.send[i] * mv_blocksize),
601 (offset_host.send[i + 1] - offset_host.send[i]) * mv_blocksize * sizeof(impl_scalar_type),
602 pids.send[i],
603 42,
604 &reqs.send[i]);
605 }
606 }
607
608 // 2. poke communication
609 for (local_ordinal_type i = 0, iend = pids.recv.extent(0); i < iend; ++i) {
610 int flag;
611 MPI_Status stat;
612 MPI_Iprobe(pids.recv[i], 42, comm, &flag, &stat);
613 }
614#endif // HAVE_IFPACK2_MPI
615 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
616 }
617
618 void syncRecvVar1() {
619 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::AsyncableImport::SyncRecv", SyncRecv);
620#ifdef HAVE_IFPACK2_MPI
621 // 0. wait for receive async.
622 for (local_ordinal_type i = 0; i < static_cast<local_ordinal_type>(pids.recv.extent(0)); ++i) {
623 local_ordinal_type idx = i;
624
625 // 0.0. wait any
626 waitany(pids.recv.extent(0), reqs.recv.data(), &idx);
627
628 if (!Tpetra::Details::Behavior::assumeMpiIsGPUAware()) {
629 const local_ordinal_type num_vectors = remote_multivector.extent(1);
630 const local_ordinal_type mv_blocksize = blocksize * num_vectors;
631
632 Kokkos::deep_copy(
633 Kokkos::subview(buffer.recv,
634 Kokkos::pair<local_ordinal_type, local_ordinal_type>(
635 offset_host.recv(idx) * mv_blocksize,
636 offset_host.recv(idx + 1) * mv_blocksize)),
637 Kokkos::subview(buffer_host.recv,
638 Kokkos::pair<local_ordinal_type, local_ordinal_type>(
639 offset_host.recv(idx) * mv_blocksize,
640 offset_host.recv(idx + 1) * mv_blocksize)));
641 }
642
643 // 0.1. unpack data after data is moved into a device
644 copy<ToMultiVector>(lids.recv, buffer.recv,
645 offset_host.recv(idx), offset_host.recv(idx + 1),
646 remote_multivector, blocksize,
647 exec_instances[idx % 8]);
648 }
649
650 // 1. fire up all cuda events
651 Kokkos::fence();
652
653 // 2. cleanup all open comm
654 waitall(reqs.send.size(), reqs.send.data());
655#endif // HAVE_IFPACK2_MPI
656 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
657 }
658#endif // defined(KOKKOS_ENABLE_CUDA|HIP|SYCL)
659
660 // ======================================================================
661 // Generic version without using execution space instances
662 // - only difference between device and host architecture is on using team
663 // or range policies.
664 // ======================================================================
665 template <typename PackTag>
666 static void copy(const local_ordinal_type_1d_view &lids_,
667 const impl_scalar_type_1d_view &buffer_,
668 const local_ordinal_type &ibeg_,
669 const local_ordinal_type &iend_,
670 const impl_scalar_type_2d_view_tpetra &multivector_,
671 const local_ordinal_type blocksize_) {
672 const local_ordinal_type num_vectors = multivector_.extent(1);
673 const local_ordinal_type mv_blocksize = blocksize_ * num_vectors;
674 const local_ordinal_type idiff = iend_ - ibeg_;
675 const auto abase = buffer_.data() + mv_blocksize * ibeg_;
676 if constexpr (BlockHelperDetails::is_device<execution_space>::value) {
677 using team_policy_type = Kokkos::TeamPolicy<execution_space>;
678 local_ordinal_type vector_size(0);
679 if (blocksize_ <= 4)
680 vector_size = 4;
681 else if (blocksize_ <= 8)
682 vector_size = 8;
683 else if (blocksize_ <= 16)
684 vector_size = 16;
685 else
686 vector_size = 32;
687 const team_policy_type policy(idiff, 1, vector_size);
688 Kokkos::parallel_for(
689 "AsyncableImport::TeamPolicy::copy",
690 policy, KOKKOS_LAMBDA(const typename team_policy_type::member_type &member) {
691 const local_ordinal_type i = member.league_rank();
692 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, num_vectors), [&](const local_ordinal_type &j) {
693 auto aptr = abase + blocksize_ * (i + idiff * j);
694 auto bptr = &multivector_(blocksize_ * lids_(i + ibeg_), j);
695 if (std::is_same<PackTag, ToBuffer>::value)
696 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize_), [&](const local_ordinal_type &k) {
697 aptr[k] = bptr[k];
698 });
699 else
700 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, blocksize_), [&](const local_ordinal_type &k) {
701 bptr[k] = aptr[k];
702 });
703 });
704 });
705 } else {
706 const Kokkos::RangePolicy<execution_space> policy(0, idiff * num_vectors);
707 Kokkos::parallel_for(
708 "AsyncableImport::RangePolicy::copy",
709 policy, KOKKOS_LAMBDA(const local_ordinal_type &ij) {
710 const local_ordinal_type i = ij % idiff;
711 const local_ordinal_type j = ij / idiff;
712 auto aptr = abase + blocksize_ * (i + idiff * j);
713 auto bptr = &multivector_(blocksize_ * lids_(i + ibeg_), j);
714 auto from = std::is_same<PackTag, ToBuffer>::value ? bptr : aptr;
715 auto to = std::is_same<PackTag, ToBuffer>::value ? aptr : bptr;
716 memcpy(to, from, sizeof(impl_scalar_type) * blocksize_);
717 });
718 }
719 }
720
724 void asyncSendRecvVar0(const impl_scalar_type_2d_view_tpetra &mv) {
725 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::AsyncableImport::AsyncSendRecv", AsyncSendRecv);
726
727#ifdef HAVE_IFPACK2_MPI
728 // constants and reallocate data buffers if necessary
729 const local_ordinal_type num_vectors = mv.extent(1);
730 const local_ordinal_type mv_blocksize = blocksize * num_vectors;
731
732 // receive async
733 for (local_ordinal_type i = 0, iend = pids.recv.extent(0); i < iend; ++i) {
734 if (Tpetra::Details::Behavior::assumeMpiIsGPUAware()) {
735 irecv(comm,
736 reinterpret_cast<char *>(buffer.recv.data() + offset_host.recv[i] * mv_blocksize),
737 (offset_host.recv[i + 1] - offset_host.recv[i]) * mv_blocksize * sizeof(impl_scalar_type),
738 pids.recv[i],
739 42,
740 &reqs.recv[i]);
741 } else {
742 irecv(comm,
743 reinterpret_cast<char *>(buffer_host.recv.data() + offset_host.recv[i] * mv_blocksize),
744 (offset_host.recv[i + 1] - offset_host.recv[i]) * mv_blocksize * sizeof(impl_scalar_type),
745 pids.recv[i],
746 42,
747 &reqs.recv[i]);
748 }
749 }
750
751 // send async
752 for (local_ordinal_type i = 0, iend = pids.send.extent(0); i < iend; ++i) {
753 copy<ToBuffer>(lids.send, buffer.send, offset_host.send(i), offset_host.send(i + 1),
754 mv, blocksize);
755 Kokkos::fence();
756 if (Tpetra::Details::Behavior::assumeMpiIsGPUAware()) {
757 isend(comm,
758 reinterpret_cast<const char *>(buffer.send.data() + offset_host.send[i] * mv_blocksize),
759 (offset_host.send[i + 1] - offset_host.send[i]) * mv_blocksize * sizeof(impl_scalar_type),
760 pids.send[i],
761 42,
762 &reqs.send[i]);
763 } else {
764 Kokkos::deep_copy(
765 Kokkos::subview(buffer_host.send,
766 Kokkos::pair<local_ordinal_type, local_ordinal_type>(
767 offset_host.send(i) * mv_blocksize,
768 offset_host.send(i + 1) * mv_blocksize)),
769 Kokkos::subview(buffer.send,
770 Kokkos::pair<local_ordinal_type, local_ordinal_type>(
771 offset_host.send(i) * mv_blocksize,
772 offset_host.send(i + 1) * mv_blocksize)));
773 isend(comm,
774 reinterpret_cast<const char *>(buffer_host.send.data() + offset_host.send[i] * mv_blocksize),
775 (offset_host.send[i + 1] - offset_host.send[i]) * mv_blocksize * sizeof(impl_scalar_type),
776 pids.send[i],
777 42,
778 &reqs.send[i]);
779 }
780 }
781
782 // I find that issuing an Iprobe seems to nudge some MPIs into action,
783 // which helps with overlapped comm/comp performance.
784 for (local_ordinal_type i = 0, iend = pids.recv.extent(0); i < iend; ++i) {
785 int flag;
786 MPI_Status stat;
787 MPI_Iprobe(pids.recv[i], 42, comm, &flag, &stat);
788 }
789#endif
790 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
791 }
792
793 void syncRecvVar0() {
794 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::AsyncableImport::SyncRecv", SyncRecv);
795#ifdef HAVE_IFPACK2_MPI
796 // receive async.
797 for (local_ordinal_type i = 0, iend = pids.recv.extent(0); i < iend; ++i) {
798 local_ordinal_type idx = i;
799 waitany(pids.recv.extent(0), reqs.recv.data(), &idx);
800 if (!Tpetra::Details::Behavior::assumeMpiIsGPUAware()) {
801 const local_ordinal_type num_vectors = remote_multivector.extent(1);
802 const local_ordinal_type mv_blocksize = blocksize * num_vectors;
803 Kokkos::deep_copy(
804 Kokkos::subview(buffer.recv,
805 Kokkos::pair<local_ordinal_type, local_ordinal_type>(
806 offset_host.recv(idx) * mv_blocksize,
807 offset_host.recv(idx + 1) * mv_blocksize)),
808 Kokkos::subview(buffer_host.recv,
809 Kokkos::pair<local_ordinal_type, local_ordinal_type>(
810 offset_host.recv(idx) * mv_blocksize,
811 offset_host.recv(idx + 1) * mv_blocksize)));
812 }
813 copy<ToMultiVector>(lids.recv, buffer.recv, offset_host.recv(idx), offset_host.recv(idx + 1),
814 remote_multivector, blocksize);
815 }
816 // wait on the sends to match all Isends with a cleanup operation.
817 waitall(reqs.send.size(), reqs.send.data());
818#endif
819 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
820 }
821
825 void asyncSendRecv(const impl_scalar_type_2d_view_tpetra &mv) {
826#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || defined(KOKKOS_ENABLE_SYCL)
827#if defined(IFPACK2_BLOCKTRIDICONTAINER_USE_EXEC_SPACE_INSTANCES)
828 asyncSendRecvVar1(mv);
829#else
830 asyncSendRecvVar0(mv);
831#endif
832#else
833 asyncSendRecvVar0(mv);
834#endif
835 }
836 void syncRecv() {
837#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || defined(KOKKOS_ENABLE_SYCL)
838#if defined(IFPACK2_BLOCKTRIDICONTAINER_USE_EXEC_SPACE_INSTANCES)
839 syncRecvVar1();
840#else
841 syncRecvVar0();
842#endif
843#else
844 syncRecvVar0();
845#endif
846 }
847
848 void syncExchange(const impl_scalar_type_2d_view_tpetra &mv) {
849 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::AsyncableImport::SyncExchange", SyncExchange);
850 asyncSendRecv(mv);
851 syncRecv();
852 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
853 }
854
855 impl_scalar_type_2d_view_tpetra getRemoteMultiVectorLocalView() const { return remote_multivector; }
856};
857
858template <typename ViewType1, typename ViewType2>
859struct are_same_struct {
860 ViewType1 keys1;
861 ViewType2 keys2;
862
863 are_same_struct(ViewType1 keys1_, ViewType2 keys2_)
864 : keys1(keys1_)
865 , keys2(keys2_) {}
866 KOKKOS_INLINE_FUNCTION
867 void operator()(int i, unsigned int &count) const {
868 if (keys1(i) != keys2(i)) count++;
869 }
870};
871
872template <typename ViewType1, typename ViewType2>
873bool are_same(ViewType1 keys1, ViewType2 keys2) {
874 unsigned int are_same_ = 0;
875
876 Kokkos::parallel_reduce(Kokkos::RangePolicy<typename ViewType1::execution_space>(0, keys1.extent(0)),
877 are_same_struct(keys1, keys2),
878 are_same_);
879 return are_same_ == 0;
880}
881
885template <typename MatrixType>
886Teuchos::RCP<AsyncableImport<MatrixType>>
887createBlockCrsAsyncImporter(const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_row_matrix_type> &A) {
888 IFPACK2_BLOCKHELPER_TIMER("createBlockCrsAsyncImporter", createBlockCrsAsyncImporter);
889 using impl_type = BlockHelperDetails::ImplType<MatrixType>;
890 using tpetra_map_type = typename impl_type::tpetra_map_type;
891 using local_ordinal_type = typename impl_type::local_ordinal_type;
892 using global_ordinal_type = typename impl_type::global_ordinal_type;
893 using local_ordinal_type_1d_view = typename impl_type::local_ordinal_type_1d_view;
894 using crs_matrix_type = typename impl_type::tpetra_crs_matrix_type;
895 using block_crs_matrix_type = typename impl_type::tpetra_block_crs_matrix_type;
896 using global_indices_array_device_type = Kokkos::View<const global_ordinal_type *, typename tpetra_map_type::device_type>;
897
898 auto A_crs = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A);
899 auto A_bcrs = Teuchos::rcp_dynamic_cast<const block_crs_matrix_type>(A);
900
901 bool hasBlockCrsMatrix = !A_bcrs.is_null();
902
903 // This is OK here to use the graph of the A_crs matrix and a block size of 1
904 const auto g = hasBlockCrsMatrix ? A_bcrs->getCrsGraph() : *(A_crs->getCrsGraph()); // tpetra crs graph object
905
906 const auto blocksize = hasBlockCrsMatrix ? A_bcrs->getBlockSize() : 1;
907 const auto domain_map = g.getDomainMap();
908 const auto column_map = g.getColMap();
909
910 std::vector<global_ordinal_type> gids;
911
912 Kokkos::Subview<global_indices_array_device_type, std::pair<int, int>> column_map_global_iD_last;
913
914 bool separate_remotes = true, found_first = false, need_owned_permutation = false;
915 {
916 IFPACK2_BLOCKHELPER_TIMER("createBlockCrsAsyncImporter::loop_over_local_elements", loop_over_local_elements);
917
918 global_indices_array_device_type column_map_global_iD = column_map->getMyGlobalIndicesDevice();
919 global_indices_array_device_type domain_map_global_iD = domain_map->getMyGlobalIndicesDevice();
920
921 if (are_same(domain_map_global_iD, column_map_global_iD)) {
922 // this should be the most likely path
923 separate_remotes = true;
924 need_owned_permutation = false;
925
926 column_map_global_iD_last = Kokkos::subview(column_map_global_iD,
927 std::pair<int, int>(domain_map_global_iD.extent(0), column_map_global_iD.extent(0)));
928 } else {
929 // This loop is relatively expensive
930 for (size_t i = 0; i < column_map->getLocalNumElements(); ++i) {
931 const global_ordinal_type gid = column_map->getGlobalElement(i);
932 if (!domain_map->isNodeGlobalElement(gid)) {
933 found_first = true;
934 gids.push_back(gid);
935 } else if (found_first) {
936 separate_remotes = false;
937 break;
938 }
939 if (!found_first && !need_owned_permutation &&
940 domain_map->getLocalElement(gid) != static_cast<local_ordinal_type>(i)) {
941 // The owned part of the domain and column maps are different
942 // orderings. We *could* do a super efficient impl of this case in the
943 // num_sweeps > 1 case by adding complexity to PermuteAndRepack. But,
944 // really, if a caller cares about speed, they wouldn't make different
945 // local permutations like this. So we punt on the best impl and go for
946 // a pretty good one: the permutation is done in place in
947 // compute_b_minus_Rx for the pure-owned part of the MVP. The only cost
948 // is the presumably worse memory access pattern of the input vector.
949 need_owned_permutation = true;
950 }
951 }
952 }
953 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
954 }
955
956 if (separate_remotes) {
957 IFPACK2_BLOCKHELPER_TIMER("createBlockCrsAsyncImporter::separate_remotes", separate_remotes);
958 const auto invalid = Teuchos::OrdinalTraits<global_ordinal_type>::invalid();
959 const auto parsimonious_col_map = need_owned_permutation ? Teuchos::rcp(new tpetra_map_type(invalid, gids.data(), gids.size(), 0, domain_map->getComm())) : Teuchos::rcp(new tpetra_map_type(invalid, column_map_global_iD_last, 0, domain_map->getComm()));
960 if (parsimonious_col_map->getGlobalNumElements() > 0) {
961 // make the importer only if needed.
962 local_ordinal_type_1d_view dm2cm;
963 if (need_owned_permutation) {
964 dm2cm = local_ordinal_type_1d_view(do_not_initialize_tag("dm2cm"), domain_map->getLocalNumElements());
965 const auto dm2cm_host = Kokkos::create_mirror_view(dm2cm);
966 for (size_t i = 0; i < domain_map->getLocalNumElements(); ++i)
967 dm2cm_host(i) = domain_map->getLocalElement(column_map->getGlobalElement(i));
968 Kokkos::deep_copy(dm2cm, dm2cm_host);
969 }
970 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
971 return Teuchos::rcp(new AsyncableImport<MatrixType>(domain_map, parsimonious_col_map, blocksize, dm2cm));
972 }
973 }
974 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
975 return Teuchos::null;
976}
977
978template <typename local_ordinal_type>
979local_ordinal_type costTRSM(const local_ordinal_type block_size) {
980 return block_size * block_size;
981}
982
983template <typename local_ordinal_type>
984local_ordinal_type costGEMV(const local_ordinal_type block_size) {
985 return 2 * block_size * block_size;
986}
987
988template <typename local_ordinal_type>
989local_ordinal_type costTriDiagSolve(const local_ordinal_type subline_length, const local_ordinal_type block_size) {
990 return 2 * subline_length * costTRSM(block_size) + 2 * (subline_length - 1) * costGEMV(block_size);
991}
992
993template <typename local_ordinal_type>
994local_ordinal_type costSolveSchur(const local_ordinal_type num_parts,
995 const local_ordinal_type num_teams,
996 const local_ordinal_type line_length,
997 const local_ordinal_type block_size,
998 const local_ordinal_type n_subparts_per_part) {
999 const local_ordinal_type subline_length = ceil(double(line_length - (n_subparts_per_part - 1) * 2) / n_subparts_per_part);
1000 if (subline_length < 1) {
1001 return INT_MAX;
1002 }
1003
1004 const local_ordinal_type p_n_lines = ceil(double(num_parts) / num_teams);
1005 const local_ordinal_type p_n_sublines = ceil(double(n_subparts_per_part) * num_parts / num_teams);
1006 const local_ordinal_type p_n_sublines_2 = ceil(double(n_subparts_per_part - 1) * num_parts / num_teams);
1007
1008 const local_ordinal_type p_costApplyE = p_n_sublines_2 * subline_length * 2 * costGEMV(block_size);
1009 const local_ordinal_type p_costApplyS = p_n_lines * costTriDiagSolve((n_subparts_per_part - 1) * 2, block_size);
1010 const local_ordinal_type p_costApplyAinv = p_n_sublines * costTriDiagSolve(subline_length, block_size);
1011 const local_ordinal_type p_costApplyC = p_n_sublines_2 * 2 * costGEMV(block_size);
1012
1013 if (n_subparts_per_part == 1) {
1014 return p_costApplyAinv;
1015 }
1016 return p_costApplyE + p_costApplyS + p_costApplyAinv + p_costApplyC;
1017}
1018
1019template <typename local_ordinal_type>
1020local_ordinal_type getAutomaticNSubparts(const local_ordinal_type num_parts,
1021 const local_ordinal_type num_teams,
1022 const local_ordinal_type line_length,
1023 const local_ordinal_type block_size) {
1024 // BMK: replaced theoretical model with empirical model
1025 // This is a linear regression based on data from a grid search.
1026 // The independent terms in the regression are:
1027 // - "parallelism surplus" - smaller when problem has enough lines to saturate GPU, larger otherwise
1028 // - log2 of the line length
1029 // - block size
1030 double parallelismSurplus = Kokkos::sqrt((double)num_teams / num_parts);
1031 double logLineLength = Kokkos::log2((double)line_length);
1032 (void)logLineLength;
1033 // Directly predict with linear model
1034#if defined(KOKKOS_ARCH_AMD_GFX942) || defined(KOKKOS_ARCH_AMD_GFX942_APU)
1035 // MI300-specific data
1036 double modeled = -9.2312 + 4.6946 * parallelismSurplus + 0.4095 * block_size + 0.966 * logLineLength;
1037 // Do not split lines if there is plenty of parallelism
1038 if (parallelismSurplus < 0.3)
1039 modeled = 1;
1040#elif defined(KOKKOS_ARCH_HOPPER) || defined(KOKKOS_ARCH_BLACKWELL)
1041 // Based on H100 data
1042 double modeled = -9.6053 + 4.7477 * parallelismSurplus + 0.2338 * block_size + 1.0794 * logLineLength;
1043 // On H100, performance degrades rapidly if small lines are split too many times
1044 double maxSplit = (double)line_length / 8;
1045 if (modeled > maxSplit)
1046 modeled = maxSplit;
1047#elif defined(KOKKOS_ENABLE_CUDA)
1048 // Based on V100 data, line splitting is profitable in fewer cases
1049 // (only when there are few, long lines)
1050 double modeled = 1;
1051 if (parallelismSurplus > 1 && line_length > 64)
1052 modeled = 4;
1053#elif defined(KOKKOS_ENABLE_HIP)
1054 // Based on MI250X data
1055 double modeled = -8.6214 + 7.3468 * parallelismSurplus + 0.3596 * block_size + 0.6673 * logLineLength;
1056#else
1057 // GPUs other than CUDA or HIP: default to simple model that works for V100
1058 double modeled = 1;
1059 if (parallelismSurplus > 1 && line_length > 64)
1060 modeled = 4;
1061#endif
1062
1063 // Round to nearest integer
1064 local_ordinal_type n_subparts_per_part = 0.5 + modeled;
1065 // Do not split lines if there is plenty of parallelism available
1066 if (parallelismSurplus < 0.3)
1067 n_subparts_per_part = 1;
1068 // Clamp the result to valid range
1069 // Criteria for valid n_subparts_per_part (where connection_length is 2 for wide separators)
1070 // line_length >= n_subparts_per_part + (n_subparts_per_part - 1) * connection_length
1071 // Equivalently:
1072 // line_length >= n_subparts_per_part + n_subparts_per_part * 2 - 2
1073 // line_length >= 3 * n_subparts_per_part - 2
1074 local_ordinal_type min_subparts_per_part = 1;
1075 local_ordinal_type max_subparts_per_part = (line_length + 2) / 3;
1076 // Limit memory usage from too many sublines
1077 if (max_subparts_per_part > 16)
1078 max_subparts_per_part = 16;
1079 if (n_subparts_per_part < min_subparts_per_part)
1080 n_subparts_per_part = min_subparts_per_part;
1081 if (n_subparts_per_part > max_subparts_per_part)
1082 n_subparts_per_part = max_subparts_per_part;
1083 return n_subparts_per_part;
1084}
1085
1086template <typename ArgActiveExecutionMemorySpace>
1087struct SolveTridiagsDefaultModeAndAlgo;
1088
1092template <typename MatrixType>
1093BlockHelperDetails::PartInterface<MatrixType>
1094createPartInterface(const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_row_matrix_type> &A,
1095 const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_crs_graph_type> &G,
1096 const Teuchos::Array<Teuchos::Array<typename BlockHelperDetails::ImplType<MatrixType>::local_ordinal_type>> &partitions,
1097 const typename BlockHelperDetails::ImplType<MatrixType>::local_ordinal_type n_subparts_per_part_in) {
1098 IFPACK2_BLOCKHELPER_TIMER("createPartInterface", createPartInterface);
1100 using local_ordinal_type = typename impl_type::local_ordinal_type;
1101 using local_ordinal_type_1d_view = typename impl_type::local_ordinal_type_1d_view;
1102 using local_ordinal_type_2d_view = typename impl_type::local_ordinal_type_2d_view;
1103 using size_type = typename impl_type::size_type;
1104
1105 auto bA = Teuchos::rcp_dynamic_cast<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_block_crs_matrix_type>(A);
1106
1107 TEUCHOS_ASSERT(!bA.is_null() || G->getLocalNumRows() != 0);
1108 const local_ordinal_type blocksize = bA.is_null() ? A->getLocalNumRows() / G->getLocalNumRows() : A->getBlockSize();
1109 constexpr int vector_length = impl_type::vector_length;
1110 constexpr int internal_vector_length = impl_type::internal_vector_length;
1111
1112 const auto comm = A->getRowMap()->getComm();
1113
1114 BlockHelperDetails::PartInterface<MatrixType> interf;
1115
1116 const local_ordinal_type A_n_lclrows = G->getLocalNumRows();
1117 const bool jacobi = partitions.size() == 0 || partitions.size() == A_n_lclrows;
1118 const local_ordinal_type nparts = jacobi ? A_n_lclrows : partitions.size();
1119
1120 typedef std::pair<local_ordinal_type, local_ordinal_type> size_idx_pair_type;
1121 std::vector<size_idx_pair_type> partsz(nparts);
1122
1123 if (!jacobi) {
1124 for (local_ordinal_type i = 0; i < nparts; ++i)
1125 partsz[i] = size_idx_pair_type(partitions[i].size(), i);
1126 std::sort(partsz.begin(), partsz.end(),
1127 [](const size_idx_pair_type &x, const size_idx_pair_type &y) {
1128 return x.first > y.first;
1129 });
1130 }
1131
1132 local_ordinal_type n_subparts_per_part;
1133 if (jacobi) {
1134 n_subparts_per_part = 1;
1135 } else {
1136 if (n_subparts_per_part_in == -1) {
1137 // If the number of subparts is set to -1, the user let the algorithm
1138 // decides the value automatically
1139 using execution_space = typename impl_type::execution_space;
1140
1141 // Line splitting only benefits GPUs
1142 if constexpr (impl_type::node_type::is_gpu) {
1143 const int line_length = partsz[0].first;
1144
1145 const local_ordinal_type team_size =
1146 SolveTridiagsDefaultModeAndAlgo<typename execution_space::memory_space>::
1147 recommended_team_size(blocksize, vector_length, internal_vector_length);
1148
1149 const local_ordinal_type num_teams = std::max(1, execution_space().concurrency() / (team_size * vector_length));
1150 n_subparts_per_part = getAutomaticNSubparts(nparts, num_teams, line_length, blocksize);
1151#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
1152 printf("Automatically chosen n_subparts_per_part = %d for nparts = %d, num_teams = %d, team_size = %d, line_length = %d, and blocksize = %d;\n", n_subparts_per_part, nparts, num_teams, team_size, line_length, blocksize);
1153#endif
1154 } else {
1155 n_subparts_per_part = 1;
1156#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
1157 printf("Automatically chosen n_subparts_per_part = 1 for CPU backend\n");
1158#endif
1159 }
1160 } else {
1161 n_subparts_per_part = n_subparts_per_part_in;
1162 }
1163 }
1164
1165 // Total number of sub lines:
1166 const local_ordinal_type n_sub_parts = nparts * n_subparts_per_part;
1167 // Total number of sub lines + the Schur complement blocks.
1168 // For a given live 2 sub lines implies one Schur complement, 3 sub lines implies two Schur complements etc.
1169 const local_ordinal_type n_sub_parts_and_schur = n_sub_parts + nparts * (n_subparts_per_part - 1);
1170
1171#if defined(BLOCKTRIDICONTAINER_DEBUG)
1172 local_ordinal_type nrows = 0;
1173 if (jacobi)
1174 nrows = nparts;
1175 else
1176 for (local_ordinal_type i = 0; i < nparts; ++i) nrows += partitions[i].size();
1177
1178 TEUCHOS_TEST_FOR_EXCEPT_MSG(nrows != A_n_lclrows, BlockHelperDetails::get_msg_prefix(comm) << "The #rows implied by the local partition is not "
1179 << "the same as getLocalNumRows: " << nrows << " vs " << A_n_lclrows);
1180#endif
1181
1182 // permutation vector
1183 std::vector<local_ordinal_type> p;
1184 if (jacobi) {
1185 interf.max_partsz = 1;
1186 interf.max_subpartsz = 0;
1187 interf.n_subparts_per_part = 1;
1188 interf.nparts = nparts;
1189 } else {
1190 // reorder parts to maximize simd packing efficiency
1191 p.resize(nparts);
1192
1193 for (local_ordinal_type i = 0; i < nparts; ++i)
1194 p[i] = partsz[i].second;
1195
1196 interf.max_partsz = partsz[0].first;
1197
1198 constexpr local_ordinal_type connection_length = 2;
1199 const local_ordinal_type sub_line_length = (interf.max_partsz - (n_subparts_per_part - 1) * connection_length) / n_subparts_per_part;
1200 const local_ordinal_type last_sub_line_length = interf.max_partsz - (n_subparts_per_part - 1) * (connection_length + sub_line_length);
1201
1202 interf.max_subpartsz = (sub_line_length > last_sub_line_length) ? sub_line_length : last_sub_line_length;
1203 interf.n_subparts_per_part = n_subparts_per_part;
1204 interf.nparts = nparts;
1205 }
1206
1207 // allocate parts
1208 interf.partptr = local_ordinal_type_1d_view(do_not_initialize_tag("partptr"), nparts + 1);
1209 interf.lclrow = local_ordinal_type_1d_view(do_not_initialize_tag("lclrow"), A_n_lclrows);
1210 interf.part2rowidx0 = local_ordinal_type_1d_view(do_not_initialize_tag("part2rowidx0"), nparts + 1);
1211 interf.part2packrowidx0 = local_ordinal_type_1d_view(do_not_initialize_tag("part2packrowidx0"), nparts + 1);
1212 interf.rowidx2part = local_ordinal_type_1d_view(do_not_initialize_tag("rowidx2part"), A_n_lclrows);
1213
1214 interf.part2rowidx0_sub = local_ordinal_type_1d_view(do_not_initialize_tag("part2rowidx0_sub"), n_sub_parts_and_schur + 1);
1215 interf.part2packrowidx0_sub = local_ordinal_type_2d_view(do_not_initialize_tag("part2packrowidx0_sub"), nparts, 2 * n_subparts_per_part);
1216 interf.rowidx2part_sub = local_ordinal_type_1d_view(do_not_initialize_tag("rowidx2part"), A_n_lclrows);
1217
1218 interf.partptr_sub = local_ordinal_type_2d_view(do_not_initialize_tag("partptr_sub"), n_sub_parts_and_schur, 2);
1219
1220 // mirror to host and compute on host execution space
1221 const auto partptr = Kokkos::create_mirror_view(interf.partptr);
1222 const auto partptr_sub = Kokkos::create_mirror_view(interf.partptr_sub);
1223
1224 const auto lclrow = Kokkos::create_mirror_view(interf.lclrow);
1225 const auto part2rowidx0 = Kokkos::create_mirror_view(interf.part2rowidx0);
1226 const auto part2packrowidx0 = Kokkos::create_mirror_view(interf.part2packrowidx0);
1227 const auto rowidx2part = Kokkos::create_mirror_view(interf.rowidx2part);
1228
1229 const auto part2rowidx0_sub = Kokkos::create_mirror_view(interf.part2rowidx0_sub);
1230 const auto part2packrowidx0_sub = Kokkos::create_mirror_view(Kokkos::HostSpace(), interf.part2packrowidx0_sub);
1231 const auto rowidx2part_sub = Kokkos::create_mirror_view(interf.rowidx2part_sub);
1232
1233 // Determine parts.
1234 interf.row_contiguous = true;
1235 partptr(0) = 0;
1236 part2rowidx0(0) = 0;
1237 part2packrowidx0(0) = 0;
1238 local_ordinal_type pack_nrows = 0;
1239 local_ordinal_type pack_nrows_sub = 0;
1240 if (jacobi) {
1241 IFPACK2_BLOCKHELPER_TIMER("compute part indices (Jacobi)", Jacobi);
1242 // Jacobi (all lines have length 1) means that A_n_lclrows == nparts,
1243 // so the mapping between parts and rows is trivial.
1244 // Note: we can leave interf.row_contiguous = true, since for all i: lclrow(i) == i
1245 for (local_ordinal_type i = 0; i <= nparts; ++i) {
1246 part2rowidx0(i) = i;
1247 partptr(i) = i;
1248 }
1249 for (local_ordinal_type i = 0; i < nparts; ++i) {
1250 rowidx2part(i) = i;
1251 lclrow(i) = i;
1252 }
1253 for (local_ordinal_type ip = 0; ip < nparts; ++ip) {
1254 // assume No overlap.
1255 if (ip % vector_length == 0) pack_nrows = 1;
1256 part2packrowidx0(ip + 1) = part2packrowidx0(ip) + ((ip + 1) % vector_length == 0 || ip + 1 == nparts ? pack_nrows : 0);
1257 }
1258 part2rowidx0_sub(0) = 0;
1259 partptr_sub(0, 0) = 0;
1260
1261 for (local_ordinal_type ip = 0; ip < nparts; ++ip) {
1262 constexpr local_ordinal_type ipnrows = 1;
1263 const local_ordinal_type full_line_length = partptr(ip + 1) - partptr(ip);
1264
1265 TEUCHOS_TEST_FOR_EXCEPTION(full_line_length != ipnrows, std::logic_error,
1266 "In the part " << ip);
1267
1268 constexpr local_ordinal_type connection_length = 2;
1269
1270 if (full_line_length < n_subparts_per_part + (n_subparts_per_part - 1) * connection_length)
1271 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
1272 "The part " << ip << " is too short to use " << n_subparts_per_part << " sub parts.");
1273
1274 const local_ordinal_type sub_line_length = (full_line_length - (n_subparts_per_part - 1) * connection_length) / n_subparts_per_part;
1275 const local_ordinal_type last_sub_line_length = full_line_length - (n_subparts_per_part - 1) * (connection_length + sub_line_length);
1276
1277 if (ip % vector_length == 0) pack_nrows_sub = ipnrows;
1278
1279 for (local_ordinal_type local_sub_ip = 0; local_sub_ip < n_subparts_per_part; ++local_sub_ip) {
1280 const local_ordinal_type sub_ip = nparts * (2 * local_sub_ip) + ip;
1281 const local_ordinal_type schur_ip = nparts * (2 * local_sub_ip + 1) + ip;
1282 if (local_sub_ip != n_subparts_per_part - 1) {
1283 if (local_sub_ip != 0) {
1284 partptr_sub(sub_ip, 0) = partptr_sub(nparts * (2 * local_sub_ip - 1) + ip, 1);
1285 } else if (ip != 0) {
1286 partptr_sub(sub_ip, 0) = partptr_sub(nparts * 2 * (n_subparts_per_part - 1) + ip - 1, 1);
1287 }
1288 partptr_sub(sub_ip, 1) = sub_line_length + partptr_sub(sub_ip, 0);
1289 partptr_sub(schur_ip, 0) = partptr_sub(sub_ip, 1);
1290 partptr_sub(schur_ip, 1) = connection_length + partptr_sub(schur_ip, 0);
1291
1292 part2rowidx0_sub(sub_ip + 1) = part2rowidx0_sub(sub_ip) + sub_line_length;
1293 part2rowidx0_sub(sub_ip + 2) = part2rowidx0_sub(sub_ip + 1) + connection_length;
1294
1295#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
1296 printf("Sub Part index = %d, first LID associated to the sub part = %d, sub part size = %d;\n", sub_ip, partptr_sub(ip, 2 * local_sub_ip), sub_line_length);
1297 printf("Sub Part index Schur = %d, first LID associated to the sub part = %d, sub part size = %d;\n", sub_ip + 1, partptr_sub(ip, 2 * local_sub_ip + 1), connection_length);
1298#endif
1299 } else {
1300 if (local_sub_ip != 0) {
1301 partptr_sub(sub_ip, 0) = partptr_sub(nparts * (2 * local_sub_ip - 1) + ip, 1);
1302 } else if (ip != 0) {
1303 partptr_sub(sub_ip, 0) = partptr_sub(nparts * 2 * (n_subparts_per_part - 1) + ip - 1, 1);
1304 }
1305 partptr_sub(sub_ip, 1) = last_sub_line_length + partptr_sub(sub_ip, 0);
1306
1307 part2rowidx0_sub(sub_ip + 1) = part2rowidx0_sub(sub_ip) + last_sub_line_length;
1308
1309#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
1310 printf("Sub Part index = %d, first LID associated to the sub part = %d, sub part size = %d;\n", sub_ip, partptr_sub(ip, 2 * local_sub_ip), last_sub_line_length);
1311#endif
1312 }
1313 }
1314 }
1315
1316#ifdef IFPACK2_BLOCKTRIDICONTAINER_WRITE_MM
1317 std::cout << "partptr_sub = " << std::endl;
1318 for (size_type i = 0; i < partptr_sub.extent(0); ++i) {
1319 for (size_type j = 0; j < partptr_sub.extent(1); ++j) {
1320 std::cout << partptr_sub(i, j) << " ";
1321 }
1322 std::cout << std::endl;
1323 }
1324 std::cout << "partptr_sub end" << std::endl;
1325#endif
1326
1327 {
1328 local_ordinal_type npacks = ceil(float(nparts) / vector_length);
1329
1330 local_ordinal_type ip_max = nparts > vector_length ? vector_length : nparts;
1331 for (local_ordinal_type ip = 0; ip < ip_max; ++ip) {
1332 part2packrowidx0_sub(ip, 0) = 0;
1333 }
1334 for (local_ordinal_type ipack = 0; ipack < npacks; ++ipack) {
1335 if (ipack != 0) {
1336 local_ordinal_type ip_min = ipack * vector_length;
1337 ip_max = nparts > (ipack + 1) * vector_length ? (ipack + 1) * vector_length : nparts;
1338 for (local_ordinal_type ip = ip_min; ip < ip_max; ++ip) {
1339 part2packrowidx0_sub(ip, 0) = part2packrowidx0_sub(ip - vector_length, part2packrowidx0_sub.extent(1) - 1);
1340 }
1341 }
1342
1343 for (size_type local_sub_ip = 0; local_sub_ip < part2packrowidx0_sub.extent(1) - 1; ++local_sub_ip) {
1344 local_ordinal_type ip_min = ipack * vector_length;
1345 ip_max = nparts > (ipack + 1) * vector_length ? (ipack + 1) * vector_length : nparts;
1346
1347 const local_ordinal_type full_line_length = partptr(ip_min + 1) - partptr(ip_min);
1348
1349 constexpr local_ordinal_type connection_length = 2;
1350
1351 const local_ordinal_type sub_line_length = (full_line_length - (n_subparts_per_part - 1) * connection_length) / n_subparts_per_part;
1352 const local_ordinal_type last_sub_line_length = full_line_length - (n_subparts_per_part - 1) * (connection_length + sub_line_length);
1353
1354 if (local_sub_ip % 2 == 0) pack_nrows_sub = sub_line_length;
1355 if (local_sub_ip % 2 == 1) pack_nrows_sub = connection_length;
1356 if (local_sub_ip == part2packrowidx0_sub.extent(1) - 2) pack_nrows_sub = last_sub_line_length;
1357
1358 part2packrowidx0_sub(ip_min, local_sub_ip + 1) = part2packrowidx0_sub(ip_min, local_sub_ip) + pack_nrows_sub;
1359
1360 for (local_ordinal_type ip = ip_min + 1; ip < ip_max; ++ip) {
1361 part2packrowidx0_sub(ip, local_sub_ip + 1) = part2packrowidx0_sub(ip_min, local_sub_ip + 1);
1362 }
1363 }
1364 }
1365
1366 Kokkos::deep_copy(interf.part2packrowidx0_sub, part2packrowidx0_sub);
1367 }
1368 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
1369 } else {
1370 IFPACK2_BLOCKHELPER_TIMER("compute part indices", indices);
1371 for (local_ordinal_type ip = 0; ip < nparts; ++ip) {
1372 const auto *part = &partitions[p[ip]];
1373 const local_ordinal_type ipnrows = part->size();
1374 TEUCHOS_ASSERT(ip == 0 || (ipnrows <= static_cast<local_ordinal_type>(partitions[p[ip - 1]].size())));
1375 TEUCHOS_TEST_FOR_EXCEPT_MSG(ipnrows == 0,
1376 BlockHelperDetails::get_msg_prefix(comm)
1377 << "partition " << p[ip]
1378 << " is empty, which is not allowed.");
1379 // assume No overlap.
1380 part2rowidx0(ip + 1) = part2rowidx0(ip) + ipnrows;
1381 // Since parts are ordered in decreasing size, the size of the first
1382 // part in a pack is the size for all parts in the pack.
1383 if (ip % vector_length == 0) pack_nrows = ipnrows;
1384 part2packrowidx0(ip + 1) = part2packrowidx0(ip) + ((ip + 1) % vector_length == 0 || ip + 1 == nparts ? pack_nrows : 0);
1385 const local_ordinal_type offset = partptr(ip);
1386 for (local_ordinal_type i = 0; i < ipnrows; ++i) {
1387 const auto lcl_row = (*part)[i];
1388 TEUCHOS_TEST_FOR_EXCEPT_MSG(lcl_row < 0 || lcl_row >= A_n_lclrows,
1389 BlockHelperDetails::get_msg_prefix(comm)
1390 << "partitions[" << p[ip] << "]["
1391 << i << "] = " << lcl_row
1392 << " but input matrix implies limits of [0, " << A_n_lclrows - 1
1393 << "].");
1394 lclrow(offset + i) = lcl_row;
1395 rowidx2part(offset + i) = ip;
1396 if (interf.row_contiguous && offset + i > 0 && lclrow((offset + i) - 1) + 1 != lcl_row)
1397 interf.row_contiguous = false;
1398 }
1399 partptr(ip + 1) = offset + ipnrows;
1400
1401#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
1402 printf("Part index = ip = %d, first LID associated to the part = partptr(ip) = offset = %d, part->size() = ipnrows = %d;\n", ip, offset, ipnrows);
1403 printf("partptr(%d+1) = %d\n", ip, partptr(ip + 1));
1404#endif
1405 }
1406
1407 part2rowidx0_sub(0) = 0;
1408 partptr_sub(0, 0) = 0;
1409 // const local_ordinal_type number_pack_per_sub_part = ceil(float(nparts)/vector_length);
1410
1411 for (local_ordinal_type ip = 0; ip < nparts; ++ip) {
1412 const auto *part = &partitions[p[ip]];
1413 const local_ordinal_type ipnrows = part->size();
1414 const local_ordinal_type full_line_length = partptr(ip + 1) - partptr(ip);
1415
1416 TEUCHOS_TEST_FOR_EXCEPTION(full_line_length != ipnrows, std::logic_error,
1417 "In the part " << ip);
1418
1419 constexpr local_ordinal_type connection_length = 2;
1420
1421 if (full_line_length < n_subparts_per_part + (n_subparts_per_part - 1) * connection_length)
1422 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
1423 "The part " << ip << " is too short to use " << n_subparts_per_part << " sub parts.");
1424
1425 const local_ordinal_type sub_line_length = (full_line_length - (n_subparts_per_part - 1) * connection_length) / n_subparts_per_part;
1426 const local_ordinal_type last_sub_line_length = full_line_length - (n_subparts_per_part - 1) * (connection_length + sub_line_length);
1427
1428 if (ip % vector_length == 0) pack_nrows_sub = ipnrows;
1429
1430 for (local_ordinal_type local_sub_ip = 0; local_sub_ip < n_subparts_per_part; ++local_sub_ip) {
1431 const local_ordinal_type sub_ip = nparts * (2 * local_sub_ip) + ip;
1432 const local_ordinal_type schur_ip = nparts * (2 * local_sub_ip + 1) + ip;
1433 if (local_sub_ip != n_subparts_per_part - 1) {
1434 if (local_sub_ip != 0) {
1435 partptr_sub(sub_ip, 0) = partptr_sub(nparts * (2 * local_sub_ip - 1) + ip, 1);
1436 } else if (ip != 0) {
1437 partptr_sub(sub_ip, 0) = partptr_sub(nparts * 2 * (n_subparts_per_part - 1) + ip - 1, 1);
1438 }
1439 partptr_sub(sub_ip, 1) = sub_line_length + partptr_sub(sub_ip, 0);
1440 partptr_sub(schur_ip, 0) = partptr_sub(sub_ip, 1);
1441 partptr_sub(schur_ip, 1) = connection_length + partptr_sub(schur_ip, 0);
1442
1443 part2rowidx0_sub(sub_ip + 1) = part2rowidx0_sub(sub_ip) + sub_line_length;
1444 part2rowidx0_sub(sub_ip + 2) = part2rowidx0_sub(sub_ip + 1) + connection_length;
1445
1446#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
1447 printf("Sub Part index = %d, first LID associated to the sub part = %d, sub part size = %d;\n", sub_ip, partptr_sub(sub_ip, 0), sub_line_length);
1448 printf("Sub Part index Schur = %d, first LID associated to the sub part = %d, sub part size = %d;\n", sub_ip + 1, partptr_sub(ip, 2 * local_sub_ip + 1), connection_length);
1449#endif
1450 } else {
1451 if (local_sub_ip != 0) {
1452 partptr_sub(sub_ip, 0) = partptr_sub(nparts * (2 * local_sub_ip - 1) + ip, 1);
1453 } else if (ip != 0) {
1454 partptr_sub(sub_ip, 0) = partptr_sub(nparts * 2 * (n_subparts_per_part - 1) + ip - 1, 1);
1455 }
1456 partptr_sub(sub_ip, 1) = last_sub_line_length + partptr_sub(sub_ip, 0);
1457
1458 part2rowidx0_sub(sub_ip + 1) = part2rowidx0_sub(sub_ip) + last_sub_line_length;
1459
1460#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
1461 printf("Sub Part index = %d, first LID associated to the sub part = %d, sub part size = %d;\n", sub_ip, partptr_sub(sub_ip, 0), last_sub_line_length);
1462#endif
1463 }
1464 }
1465 }
1466
1467 {
1468 local_ordinal_type npacks = ceil(float(nparts) / vector_length);
1469
1470 local_ordinal_type ip_max = nparts > vector_length ? vector_length : nparts;
1471 for (local_ordinal_type ip = 0; ip < ip_max; ++ip) {
1472 part2packrowidx0_sub(ip, 0) = 0;
1473 }
1474 for (local_ordinal_type ipack = 0; ipack < npacks; ++ipack) {
1475 if (ipack != 0) {
1476 local_ordinal_type ip_min = ipack * vector_length;
1477 ip_max = nparts > (ipack + 1) * vector_length ? (ipack + 1) * vector_length : nparts;
1478 for (local_ordinal_type ip = ip_min; ip < ip_max; ++ip) {
1479 part2packrowidx0_sub(ip, 0) = part2packrowidx0_sub(ip - vector_length, part2packrowidx0_sub.extent(1) - 1);
1480 }
1481 }
1482
1483 for (size_type local_sub_ip = 0; local_sub_ip < part2packrowidx0_sub.extent(1) - 1; ++local_sub_ip) {
1484 local_ordinal_type ip_min = ipack * vector_length;
1485 ip_max = nparts > (ipack + 1) * vector_length ? (ipack + 1) * vector_length : nparts;
1486
1487 const local_ordinal_type full_line_length = partptr(ip_min + 1) - partptr(ip_min);
1488
1489 constexpr local_ordinal_type connection_length = 2;
1490
1491 const local_ordinal_type sub_line_length = (full_line_length - (n_subparts_per_part - 1) * connection_length) / n_subparts_per_part;
1492 const local_ordinal_type last_sub_line_length = full_line_length - (n_subparts_per_part - 1) * (connection_length + sub_line_length);
1493
1494 if (local_sub_ip % 2 == 0) pack_nrows_sub = sub_line_length;
1495 if (local_sub_ip % 2 == 1) pack_nrows_sub = connection_length;
1496 if (local_sub_ip == part2packrowidx0_sub.extent(1) - 2) pack_nrows_sub = last_sub_line_length;
1497
1498 part2packrowidx0_sub(ip_min, local_sub_ip + 1) = part2packrowidx0_sub(ip_min, local_sub_ip) + pack_nrows_sub;
1499
1500 for (local_ordinal_type ip = ip_min + 1; ip < ip_max; ++ip) {
1501 part2packrowidx0_sub(ip, local_sub_ip + 1) = part2packrowidx0_sub(ip_min, local_sub_ip + 1);
1502 }
1503 }
1504 }
1505
1506 Kokkos::deep_copy(interf.part2packrowidx0_sub, part2packrowidx0_sub);
1507 }
1508 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
1509 }
1510#if defined(BLOCKTRIDICONTAINER_DEBUG)
1511 TEUCHOS_ASSERT(partptr(nparts) == nrows);
1512#endif
1513 if (lclrow(0) != 0) interf.row_contiguous = false;
1514
1515 Kokkos::deep_copy(interf.partptr, partptr);
1516 Kokkos::deep_copy(interf.lclrow, lclrow);
1517
1518 Kokkos::deep_copy(interf.partptr_sub, partptr_sub);
1519
1520 // assume No overlap. Thus:
1521 interf.part2rowidx0 = interf.partptr;
1522 Kokkos::deep_copy(interf.part2packrowidx0, part2packrowidx0);
1523
1524 interf.part2packrowidx0_back = part2packrowidx0_sub(part2packrowidx0_sub.extent(0) - 1, part2packrowidx0_sub.extent(1) - 1);
1525 Kokkos::deep_copy(interf.rowidx2part, rowidx2part);
1526
1527 { // Fill packptr.
1528 IFPACK2_BLOCKHELPER_TIMER("Fill packptr", packptr0);
1529 local_ordinal_type npacks = ceil(float(nparts) / vector_length) * (part2packrowidx0_sub.extent(1) - 1);
1530 npacks = 0;
1531 for (local_ordinal_type ip = 1; ip <= nparts; ++ip) // n_sub_parts_and_schur
1532 if (part2packrowidx0(ip) != part2packrowidx0(ip - 1))
1533 ++npacks;
1534
1535 interf.packptr = local_ordinal_type_1d_view(do_not_initialize_tag("packptr"), npacks + 1);
1536 const auto packptr = Kokkos::create_mirror_view(interf.packptr);
1537 packptr(0) = 0;
1538 for (local_ordinal_type ip = 1, k = 1; ip <= nparts; ++ip)
1539 if (part2packrowidx0(ip) != part2packrowidx0(ip - 1))
1540 packptr(k++) = ip;
1541
1542 Kokkos::deep_copy(interf.packptr, packptr);
1543
1544 local_ordinal_type npacks_per_subpart = ceil(float(nparts) / vector_length);
1545 npacks = ceil(float(nparts) / vector_length) * (part2packrowidx0_sub.extent(1) - 1);
1546
1547 interf.packindices_sub = local_ordinal_type_1d_view(do_not_initialize_tag("packindices_sub"), npacks_per_subpart * n_subparts_per_part);
1548 interf.packindices_schur = local_ordinal_type_2d_view(do_not_initialize_tag("packindices_schur"), npacks_per_subpart, n_subparts_per_part - 1);
1549
1550 const auto packindices_sub = Kokkos::create_mirror_view(interf.packindices_sub);
1551 const auto packindices_schur = Kokkos::create_mirror_view(interf.packindices_schur);
1552
1553 // Fill packindices_sub and packindices_schur
1554 for (local_ordinal_type local_sub_ip = 0; local_sub_ip < n_subparts_per_part - 1; ++local_sub_ip) {
1555 for (local_ordinal_type local_pack_ip = 0; local_pack_ip < npacks_per_subpart; ++local_pack_ip) {
1556 packindices_sub(local_sub_ip * npacks_per_subpart + local_pack_ip) = 2 * local_sub_ip * npacks_per_subpart + local_pack_ip;
1557 packindices_schur(local_pack_ip, local_sub_ip) = 2 * local_sub_ip * npacks_per_subpart + local_pack_ip + npacks_per_subpart;
1558 }
1559 }
1560
1561 for (local_ordinal_type local_pack_ip = 0; local_pack_ip < npacks_per_subpart; ++local_pack_ip) {
1562 packindices_sub((n_subparts_per_part - 1) * npacks_per_subpart + local_pack_ip) = 2 * (n_subparts_per_part - 1) * npacks_per_subpart + local_pack_ip;
1563 }
1564
1565#ifdef IFPACK2_BLOCKTRIDICONTAINER_WRITE_MM
1566 std::cout << "packindices_sub = " << std::endl;
1567 for (size_type i = 0; i < packindices_sub.extent(0); ++i) {
1568 std::cout << packindices_sub(i) << " ";
1569 }
1570 std::cout << std::endl;
1571 std::cout << "packindices_sub end" << std::endl;
1572
1573 std::cout << "packindices_schur = " << std::endl;
1574 for (size_type i = 0; i < packindices_schur.extent(0); ++i) {
1575 for (size_type j = 0; j < packindices_schur.extent(1); ++j) {
1576 std::cout << packindices_schur(i, j) << " ";
1577 }
1578 std::cout << std::endl;
1579 }
1580
1581 std::cout << "packindices_schur end" << std::endl;
1582#endif
1583
1584 Kokkos::deep_copy(interf.packindices_sub, packindices_sub);
1585 Kokkos::deep_copy(interf.packindices_schur, packindices_schur);
1586
1587 interf.packptr_sub = local_ordinal_type_1d_view(do_not_initialize_tag("packptr"), npacks + 1);
1588 const auto packptr_sub = Kokkos::create_mirror_view(interf.packptr_sub);
1589 packptr_sub(0) = 0;
1590 for (local_ordinal_type k = 0; k < npacks + 1; ++k)
1591 packptr_sub(k) = packptr(k % npacks_per_subpart) + (k / npacks_per_subpart) * packptr(npacks_per_subpart);
1592
1593 Kokkos::deep_copy(interf.packptr_sub, packptr_sub);
1594 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
1595 }
1596 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
1597
1598 return interf;
1599}
1600
1604template <typename MatrixType>
1605struct BlockTridiags {
1607 using local_ordinal_type_1d_view = typename impl_type::local_ordinal_type_1d_view;
1608 using size_type_1d_view = typename impl_type::size_type_1d_view;
1609 using size_type_2d_view = typename impl_type::size_type_2d_view;
1610 using vector_type_3d_view = typename impl_type::vector_type_3d_view;
1611 using vector_type_4d_view = typename impl_type::vector_type_4d_view;
1612 using btdm_scalar_type_3d_view = typename impl_type::btdm_scalar_type_3d_view;
1613 using internal_vector_type_3d_view = typename impl_type::internal_vector_type_3d_view;
1614
1615 // flat_td_ptr(i) is the index into flat-array values of the start of the
1616 // i'th tridiag. pack_td_ptr is the same, but for packs. If vector_length ==
1617 // 1, pack_td_ptr is the same as flat_td_ptr; if vector_length > 1, then i %
1618 // vector_length is the position in the pack.
1619 size_type_2d_view flat_td_ptr, pack_td_ptr, pack_td_ptr_schur;
1620 // List of local column indices into A from which to grab
1621 // data. flat_td_ptr(i) points to the start of the i'th tridiag's data.
1622 local_ordinal_type_1d_view A_colindsub;
1623 // Tridiag block values. pack_td_ptr(i) points to the start of the i'th
1624 // tridiag's pack, and i % vector_length gives the position in the pack.
1625 vector_type_3d_view values;
1626 // Schur block values. pack_td_ptr_schur(i) points to the start of the i'th
1627 // Schur's pack, and i % vector_length gives the position in the pack.
1628 vector_type_3d_view values_schur;
1629 // inv(A_00)*A_01 block values.
1630 vector_type_4d_view e_values;
1631 // If doing Schur line splitting: space for permuted version of X,
1632 // to be used during the Schur complement block solves (SolveTridiags, SingleVectorSchurTag).
1633 // Otherwise, this is not allocated.
1634 internal_vector_type_3d_view X_internal_vector_values_schur;
1635
1636 // The following are for fused block Jacobi only.
1637 // For block row i, diag_offset(i)...diag_offset(i + bs^2)
1638 // is the range of scalars for the diagonal block.
1639 size_type_1d_view diag_offsets;
1640 // For fused residual+solve block Jacobi case,
1641 // this contains the diagonal block inverses in flat, local row indexing:
1642 // d_inv(row, :, :) gives the row-major block for row.
1643 btdm_scalar_type_3d_view d_inv;
1644
1645 bool is_diagonal_only;
1646
1647 BlockTridiags() = default;
1648 BlockTridiags(const BlockTridiags &b) = default;
1649
1650 // Index into row-major block of a tridiag.
1651 template <typename idx_type>
1652 static KOKKOS_FORCEINLINE_FUNCTION
1653 idx_type
1654 IndexToRow(const idx_type &ind) { return (ind + 1) / 3; }
1655 // Given a row of a row-major tridiag, return the index of the first block
1656 // in that row.
1657 template <typename idx_type>
1658 static KOKKOS_FORCEINLINE_FUNCTION
1659 idx_type
1660 RowToIndex(const idx_type &row) { return row > 0 ? 3 * row - 1 : 0; }
1661 // Number of blocks in a tridiag having a given number of rows.
1662 template <typename idx_type>
1663 static KOKKOS_FORCEINLINE_FUNCTION
1664 idx_type
1665 NumBlocks(const idx_type &nrows) { return nrows > 0 ? 3 * nrows - 2 : 0; }
1666 // Number of blocks associated to a Schur complement having a given number of rows.
1667 template <typename idx_type>
1668 static KOKKOS_FORCEINLINE_FUNCTION
1669 idx_type
1670 NumBlocksSchur(const idx_type &nrows) { return nrows > 0 ? 3 * nrows + 2 : 0; }
1671};
1672
1676template <typename MatrixType>
1678createBlockTridiags(const BlockHelperDetails::PartInterface<MatrixType> &interf) {
1679 IFPACK2_BLOCKHELPER_TIMER("createBlockTridiags", createBlockTridiags0);
1681 using execution_space = typename impl_type::execution_space;
1682 using local_ordinal_type = typename impl_type::local_ordinal_type;
1683 using size_type = typename impl_type::size_type;
1684 using size_type_2d_view = typename impl_type::size_type_2d_view;
1685
1686 constexpr int vector_length = impl_type::vector_length;
1687
1689
1690 const local_ordinal_type ntridiags = interf.partptr_sub.extent(0);
1691
1692 { // construct the flat index pointers into the tridiag values array.
1693 btdm.flat_td_ptr = size_type_2d_view(do_not_initialize_tag("btdm.flat_td_ptr"), interf.nparts, 2 * interf.n_subparts_per_part);
1694 const Kokkos::RangePolicy<execution_space> policy(0, 2 * interf.nparts * interf.n_subparts_per_part);
1695 Kokkos::parallel_scan(
1696 "createBlockTridiags::RangePolicy::flat_td_ptr",
1697 policy, KOKKOS_LAMBDA(const local_ordinal_type &i, size_type &update, const bool &final) {
1698 const local_ordinal_type partidx = i / (2 * interf.n_subparts_per_part);
1699 const local_ordinal_type local_subpartidx = i % (2 * interf.n_subparts_per_part);
1700
1701 if (final) {
1702 btdm.flat_td_ptr(partidx, local_subpartidx) = update;
1703 }
1704 if (local_subpartidx != (2 * interf.n_subparts_per_part - 1)) {
1705 const local_ordinal_type nrows = interf.partptr_sub(interf.nparts * local_subpartidx + partidx, 1) - interf.partptr_sub(interf.nparts * local_subpartidx + partidx, 0);
1706 if (local_subpartidx % 2 == 0)
1707 update += btdm.NumBlocks(nrows);
1708 else
1709 update += btdm.NumBlocksSchur(nrows);
1710 }
1711 });
1712
1713 const auto nblocks = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), Kokkos::subview(btdm.flat_td_ptr, interf.nparts - 1, 2 * interf.n_subparts_per_part - 1));
1714 btdm.is_diagonal_only = (static_cast<local_ordinal_type>(nblocks()) == ntridiags);
1715 }
1716
1717 // And the packed index pointers.
1718 if (vector_length == 1) {
1719 btdm.pack_td_ptr = btdm.flat_td_ptr;
1720 } else {
1721 // const local_ordinal_type npacks = interf.packptr_sub.extent(0) - 1;
1722
1723 local_ordinal_type npacks_per_subpart = 0;
1724 const auto part2packrowidx0 = Kokkos::create_mirror_view(interf.part2packrowidx0);
1725 Kokkos::deep_copy(part2packrowidx0, interf.part2packrowidx0);
1726 for (local_ordinal_type ip = 1; ip <= interf.nparts; ++ip) // n_sub_parts_and_schur
1727 if (part2packrowidx0(ip) != part2packrowidx0(ip - 1))
1728 ++npacks_per_subpart;
1729
1730 btdm.pack_td_ptr = size_type_2d_view(do_not_initialize_tag("btdm.pack_td_ptr"), interf.nparts, 2 * interf.n_subparts_per_part);
1731 const Kokkos::RangePolicy<execution_space> policy(0, npacks_per_subpart);
1732
1733 Kokkos::parallel_for(
1734 "createBlockTridiags::RangePolicy::pack_td_ptr",
1735 policy, KOKKOS_LAMBDA(const local_ordinal_type &i) {
1736 for (local_ordinal_type j = 0; j < 2 * interf.n_subparts_per_part; ++j) {
1737 const local_ordinal_type pack_id = (j == 2 * interf.n_subparts_per_part - 1) ? i + (j - 1) * npacks_per_subpart : i + j * npacks_per_subpart;
1738 const local_ordinal_type nparts_in_pack = interf.packptr_sub(pack_id + 1) - interf.packptr_sub(pack_id);
1739
1740 const local_ordinal_type parti = interf.packptr_sub(pack_id);
1741 const local_ordinal_type partidx = parti % interf.nparts;
1742
1743 for (local_ordinal_type pti = 0; pti < nparts_in_pack; ++pti) {
1744 btdm.pack_td_ptr(partidx + pti, j) = btdm.flat_td_ptr(i, j);
1745 }
1746 }
1747 });
1748 }
1749
1750 btdm.pack_td_ptr_schur = size_type_2d_view(do_not_initialize_tag("btdm.pack_td_ptr_schur"), interf.nparts, interf.n_subparts_per_part);
1751
1752 const auto host_pack_td_ptr_schur = Kokkos::create_mirror_view(btdm.pack_td_ptr_schur);
1753 constexpr local_ordinal_type connection_length = 2;
1754
1755 host_pack_td_ptr_schur(0, 0) = 0;
1756 for (local_ordinal_type i = 0; i < interf.nparts; ++i) {
1757 if (i % vector_length == 0) {
1758 if (i != 0)
1759 host_pack_td_ptr_schur(i, 0) = host_pack_td_ptr_schur(i - 1, host_pack_td_ptr_schur.extent(1) - 1);
1760 for (local_ordinal_type j = 0; j < interf.n_subparts_per_part - 1; ++j) {
1761 host_pack_td_ptr_schur(i, j + 1) = host_pack_td_ptr_schur(i, j) + btdm.NumBlocks(connection_length) + (j != 0 ? 1 : 0) + (j != interf.n_subparts_per_part - 2 ? 1 : 0);
1762 }
1763 } else {
1764 for (local_ordinal_type j = 0; j < interf.n_subparts_per_part; ++j) {
1765 host_pack_td_ptr_schur(i, j) = host_pack_td_ptr_schur(i - 1, j);
1766 }
1767 }
1768 }
1769
1770 Kokkos::deep_copy(btdm.pack_td_ptr_schur, host_pack_td_ptr_schur);
1771
1772#ifdef IFPACK2_BLOCKTRIDICONTAINER_WRITE_MM
1773 const auto host_flat_td_ptr = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), btdm.flat_td_ptr);
1774 std::cout << "flat_td_ptr = " << std::endl;
1775 for (size_type i = 0; i < host_flat_td_ptr.extent(0); ++i) {
1776 for (size_type j = 0; j < host_flat_td_ptr.extent(1); ++j) {
1777 std::cout << host_flat_td_ptr(i, j) << " ";
1778 }
1779 std::cout << std::endl;
1780 }
1781 std::cout << "flat_td_ptr end" << std::endl;
1782
1783 const auto host_pack_td_ptr = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), btdm.pack_td_ptr);
1784
1785 std::cout << "pack_td_ptr = " << std::endl;
1786 for (size_type i = 0; i < host_pack_td_ptr.extent(0); ++i) {
1787 for (size_type j = 0; j < host_pack_td_ptr.extent(1); ++j) {
1788 std::cout << host_pack_td_ptr(i, j) << " ";
1789 }
1790 std::cout << std::endl;
1791 }
1792 std::cout << "pack_td_ptr end" << std::endl;
1793
1794 std::cout << "pack_td_ptr_schur = " << std::endl;
1795 for (size_type i = 0; i < host_pack_td_ptr_schur.extent(0); ++i) {
1796 for (size_type j = 0; j < host_pack_td_ptr_schur.extent(1); ++j) {
1797 std::cout << host_pack_td_ptr_schur(i, j) << " ";
1798 }
1799 std::cout << std::endl;
1800 }
1801 std::cout << "pack_td_ptr_schur end" << std::endl;
1802#endif
1803
1804 // values and A_colindsub are created in the symbolic phase
1805 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
1806
1807 return btdm;
1808}
1809
1810// Set the tridiags to be I to the full pack block size. That way, if a
1811// tridiag within a pack is shorter than the longest one, the extra blocks are
1812// processed in a safe way. Similarly, in the solve phase, if the extra blocks
1813// in the packed multvector are 0, and the tridiag LU reflects the extra I
1814// blocks, then the solve proceeds as though the extra blocks aren't
1815// present. Since this extra work is part of the SIMD calls, it's not actually
1816// extra work. Instead, it means we don't have to put checks or masks in, or
1817// quiet NaNs. This functor has to be called just once, in the symbolic phase,
1818// since the numeric phase fills in only the used entries, leaving these I
1819// blocks intact.
1820template <typename MatrixType>
1821void setTridiagsToIdentity(const BlockTridiags<MatrixType> &btdm,
1822 const typename BlockHelperDetails::ImplType<MatrixType>::local_ordinal_type_1d_view &packptr) {
1824 using execution_space = typename impl_type::execution_space;
1825 using local_ordinal_type = typename impl_type::local_ordinal_type;
1826 using size_type_2d_view = typename impl_type::size_type_2d_view;
1827
1828 const ConstUnmanaged<size_type_2d_view> pack_td_ptr(btdm.pack_td_ptr);
1829 const local_ordinal_type blocksize = btdm.values.extent(1);
1830
1831 {
1832 const int vector_length = impl_type::vector_length;
1833 const int internal_vector_length = impl_type::internal_vector_length;
1834
1835 using btdm_scalar_type = typename impl_type::btdm_scalar_type;
1836 using internal_vector_type = typename impl_type::internal_vector_type;
1837 using internal_vector_type_4d_view =
1838 typename impl_type::internal_vector_type_4d_view;
1839
1840 using team_policy_type = Kokkos::TeamPolicy<execution_space>;
1841 const internal_vector_type_4d_view values(reinterpret_cast<internal_vector_type *>(btdm.values.data()),
1842 btdm.values.extent(0),
1843 btdm.values.extent(1),
1844 btdm.values.extent(2),
1845 vector_length / internal_vector_length);
1846 const local_ordinal_type vector_loop_size = values.extent(3);
1847#if defined(KOKKOS_ENABLE_CUDA) && defined(__CUDA_ARCH__)
1848 local_ordinal_type total_team_size(0);
1849 if (blocksize <= 5)
1850 total_team_size = 32;
1851 else if (blocksize <= 9)
1852 total_team_size = 64;
1853 else if (blocksize <= 12)
1854 total_team_size = 96;
1855 else if (blocksize <= 16)
1856 total_team_size = 128;
1857 else if (blocksize <= 20)
1858 total_team_size = 160;
1859 else
1860 total_team_size = 160;
1861 const local_ordinal_type team_size = total_team_size / vector_loop_size;
1862 const team_policy_type policy(packptr.extent(0) - 1, team_size, vector_loop_size);
1863#elif defined(KOKKOS_ENABLE_HIP)
1864 // FIXME: HIP
1865 // These settings might be completely wrong
1866 // will have to do some experiments to decide
1867 // what makes sense on AMD GPUs
1868 local_ordinal_type total_team_size(0);
1869 if (blocksize <= 5)
1870 total_team_size = 32;
1871 else if (blocksize <= 9)
1872 total_team_size = 64;
1873 else if (blocksize <= 12)
1874 total_team_size = 96;
1875 else if (blocksize <= 16)
1876 total_team_size = 128;
1877 else if (blocksize <= 20)
1878 total_team_size = 160;
1879 else
1880 total_team_size = 160;
1881 const local_ordinal_type team_size = total_team_size / vector_loop_size;
1882 const team_policy_type policy(packptr.extent(0) - 1, team_size, vector_loop_size);
1883#elif defined(KOKKOS_ENABLE_SYCL)
1884 // SYCL: FIXME
1885 local_ordinal_type total_team_size(0);
1886 if (blocksize <= 5)
1887 total_team_size = 32;
1888 else if (blocksize <= 9)
1889 total_team_size = 64;
1890 else if (blocksize <= 12)
1891 total_team_size = 96;
1892 else if (blocksize <= 16)
1893 total_team_size = 128;
1894 else if (blocksize <= 20)
1895 total_team_size = 160;
1896 else
1897 total_team_size = 160;
1898 const local_ordinal_type team_size = total_team_size / vector_loop_size;
1899 const team_policy_type policy(packptr.extent(0) - 1, team_size, vector_loop_size);
1900#else
1901 // Host architecture: team size is always one
1902 const team_policy_type policy(packptr.extent(0) - 1, 1, 1);
1903#endif
1904 Kokkos::parallel_for(
1905 "setTridiagsToIdentity::TeamPolicy",
1906 policy, KOKKOS_LAMBDA(const typename team_policy_type::member_type &member) {
1907 const local_ordinal_type k = member.league_rank();
1908 const local_ordinal_type ibeg = pack_td_ptr(packptr(k), 0);
1909 const local_ordinal_type iend = pack_td_ptr(packptr(k), pack_td_ptr.extent(1) - 1);
1910
1911 const local_ordinal_type diff = iend - ibeg;
1912 const local_ordinal_type icount = diff / 3 + (diff % 3 > 0);
1913 const btdm_scalar_type one(1);
1914 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
1915 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, icount), [&](const local_ordinal_type &ii) {
1916 const local_ordinal_type i = ibeg + ii * 3;
1917 for (local_ordinal_type j = 0; j < blocksize; ++j) {
1918 values(i, j, j, v) = one;
1919 }
1920 });
1921 });
1922 });
1923 }
1924}
1925
1929template <typename MatrixType>
1930void performSymbolicPhase(const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_row_matrix_type> &A,
1931 const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_crs_graph_type> &g,
1932 const BlockHelperDetails::PartInterface<MatrixType> &interf,
1933 BlockTridiags<MatrixType> &btdm,
1935 const bool overlap_communication_and_computation,
1936 const Teuchos::RCP<AsyncableImport<MatrixType>> &async_importer,
1937 bool useSeqMethod,
1938 bool use_fused_jacobi) {
1939 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::SymbolicPhase", SymbolicPhase);
1940
1942
1943 using execution_space = typename impl_type::execution_space;
1944
1945 using local_ordinal_type = typename impl_type::local_ordinal_type;
1946 using global_ordinal_type = typename impl_type::global_ordinal_type;
1947 using size_type = typename impl_type::size_type;
1948 using local_ordinal_type_1d_view = typename impl_type::local_ordinal_type_1d_view;
1949 using size_type_1d_view = typename impl_type::size_type_1d_view;
1950 using vector_type_3d_view = typename impl_type::vector_type_3d_view;
1951 using vector_type_4d_view = typename impl_type::vector_type_4d_view;
1952 using crs_matrix_type = typename impl_type::tpetra_crs_matrix_type;
1953 using block_crs_matrix_type = typename impl_type::tpetra_block_crs_matrix_type;
1954 using btdm_scalar_type_3d_view = typename impl_type::btdm_scalar_type_3d_view;
1955 using internal_vector_type_3d_view = typename impl_type::internal_vector_type_3d_view;
1956 using lo_traits = Tpetra::Details::OrdinalTraits<local_ordinal_type>;
1957
1958 constexpr int vector_length = impl_type::vector_length;
1959 constexpr int internal_vector_length = impl_type::internal_vector_length;
1960
1961 const auto comm = A->getRowMap()->getComm();
1962
1963 auto A_crs = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A);
1964 auto A_bcrs = Teuchos::rcp_dynamic_cast<const block_crs_matrix_type>(A);
1965
1966 bool hasBlockCrsMatrix = !A_bcrs.is_null();
1967 TEUCHOS_ASSERT(hasBlockCrsMatrix || g->getLocalNumRows() != 0);
1968 const local_ordinal_type blocksize = hasBlockCrsMatrix ? A->getBlockSize() : A->getLocalNumRows() / g->getLocalNumRows();
1969
1970 const auto partptr = interf.partptr;
1971 const auto lclrow = interf.lclrow;
1972 const auto rowidx2part = interf.rowidx2part;
1973 const auto part2rowidx0 = interf.part2rowidx0;
1974 const auto packptr = interf.packptr;
1975
1976 // TODO: add nrows as a member of part interface?
1977 const local_ordinal_type nrows = Kokkos::create_mirror_view_and_copy(
1978 Kokkos::HostSpace(), Kokkos::subview(partptr, partptr.extent(0) - 1))();
1979
1980 Kokkos::View<local_ordinal_type *, execution_space> col2row("col2row", A->getLocalNumCols());
1981
1982 // find column to row map on host
1983
1984 Kokkos::deep_copy(execution_space(), col2row, Teuchos::OrdinalTraits<local_ordinal_type>::invalid());
1985 {
1986 TEUCHOS_ASSERT(!(g->getRowMap().is_null() || g->getColMap().is_null() || g->getDomainMap().is_null()));
1987#if defined(BLOCKTRIDICONTAINER_DEBUG)
1988 {
1989 // On host: check that row, col, domain maps are consistent
1990 auto rowmapHost = g->getRowMap();
1991 auto colmapHost = g->getColMap();
1992 auto dommapHost = g->getDomainMap();
1993 for (local_ordinal_type lr = 0; lr < nrows; lr++) {
1994 const global_ordinal_type gid = rowmapHost->getGlobalElement(lr);
1995 TEUCHOS_ASSERT(gid != Teuchos::OrdinalTraits<global_ordinal_type>::invalid());
1996 if (dommapHost->isNodeGlobalElement(gid)) {
1997 const local_ordinal_type lc = colmapHost->getLocalElement(gid);
1998 TEUCHOS_TEST_FOR_EXCEPT_MSG(lc == Teuchos::OrdinalTraits<local_ordinal_type>::invalid(),
1999 BlockHelperDetails::get_msg_prefix(comm) << "GID " << gid
2000 << " gives an invalid local column.");
2001 }
2002 }
2003 }
2004#endif
2005 auto rowmap = g->getRowMap()->getLocalMap();
2006 auto colmap = g->getColMap()->getLocalMap();
2007 auto dommap = g->getDomainMap()->getLocalMap();
2008
2009 const Kokkos::RangePolicy<execution_space> policy(0, nrows);
2010 Kokkos::parallel_for(
2011 "performSymbolicPhase::RangePolicy::col2row",
2012 policy, KOKKOS_LAMBDA(const local_ordinal_type &lr) {
2013 const global_ordinal_type gid = rowmap.getGlobalElement(lr);
2014 if (dommap.getLocalElement(gid) != lo_traits::invalid()) {
2015 const local_ordinal_type lc = colmap.getLocalElement(gid);
2016 col2row(lc) = lr;
2017 }
2018 });
2019 }
2020
2021 // construct the D and R graphs in A = D + R.
2022 {
2023 const auto local_graph = g->getLocalGraphDevice();
2024 const auto local_graph_rowptr = local_graph.row_map;
2025 TEUCHOS_ASSERT(local_graph_rowptr.size() == static_cast<size_t>(nrows + 1));
2026 const auto local_graph_colidx = local_graph.entries;
2027
2028 // assume no overlap.
2029
2030 Kokkos::View<local_ordinal_type *, execution_space> lclrow2idx("lclrow2idx", nrows);
2031 {
2032 const Kokkos::RangePolicy<execution_space> policy(0, nrows);
2033 Kokkos::parallel_for(
2034 "performSymbolicPhase::RangePolicy::lclrow2idx",
2035 policy, KOKKOS_LAMBDA(const local_ordinal_type &i) {
2036 lclrow2idx(lclrow(i)) = i;
2037 });
2038 }
2039
2040 // count (block) nnzs in D and R.
2041 size_type D_nnz, R_nnz_owned, R_nnz_remote;
2042 {
2043 const Kokkos::RangePolicy<execution_space> policy(0, nrows);
2044 Kokkos::parallel_reduce
2045 // profiling interface does not work
2046 ( //"performSymbolicPhase::RangePolicy::count_nnz",
2047 policy, KOKKOS_LAMBDA(const local_ordinal_type &lr, size_type &update_D_nnz, size_type &update_R_nnz_owned, size_type &update_R_nnz_remote) {
2048 // LID -> index.
2049 const local_ordinal_type ri0 = lclrow2idx(lr);
2050 const local_ordinal_type pi0 = rowidx2part(ri0);
2051 for (size_type j = local_graph_rowptr(lr); j < local_graph_rowptr(lr + 1); ++j) {
2052 const local_ordinal_type lc = local_graph_colidx(j);
2053 const local_ordinal_type lc2r = col2row(lc);
2054 bool incr_R = false;
2055 do { // breakable
2056 if (lc2r == (local_ordinal_type)-1) {
2057 incr_R = true;
2058 break;
2059 }
2060 const local_ordinal_type ri = lclrow2idx(lc2r);
2061 const local_ordinal_type pi = rowidx2part(ri);
2062 if (pi != pi0) {
2063 incr_R = true;
2064 break;
2065 }
2066 // Test for being in the tridiag. This is done in index space. In
2067 // LID space, tridiag LIDs in a row are not necessarily related by
2068 // {-1, 0, 1}.
2069 if (ri0 + 1 >= ri && ri0 <= ri + 1)
2070 ++update_D_nnz;
2071 else
2072 incr_R = true;
2073 } while (0);
2074 if (incr_R) {
2075 if (lc < nrows)
2076 ++update_R_nnz_owned;
2077 else
2078 ++update_R_nnz_remote;
2079 }
2080 }
2081 },
2082 D_nnz, R_nnz_owned, R_nnz_remote);
2083 }
2084
2085 if (!overlap_communication_and_computation) {
2086 R_nnz_owned += R_nnz_remote;
2087 R_nnz_remote = 0;
2088 }
2089
2090 // construct the D_00 graph.
2091 {
2092 const auto flat_td_ptr = btdm.flat_td_ptr;
2093
2094 btdm.A_colindsub = local_ordinal_type_1d_view("btdm.A_colindsub", D_nnz);
2095 const auto D_A_colindsub = btdm.A_colindsub;
2096
2097#if defined(BLOCKTRIDICONTAINER_DEBUG)
2098 Kokkos::deep_copy(D_A_colindsub, Teuchos::OrdinalTraits<local_ordinal_type>::invalid());
2099#endif
2100
2101 const local_ordinal_type nparts = partptr.extent(0) - 1;
2102
2103 {
2104 const Kokkos::RangePolicy<execution_space> policy(0, nparts);
2105 Kokkos::parallel_for(
2106 "performSymbolicPhase::RangePolicy<execution_space>::D_graph",
2107 policy, KOKKOS_LAMBDA(const local_ordinal_type &pi0) {
2108 const local_ordinal_type part_ri0 = part2rowidx0(pi0);
2109 local_ordinal_type offset = 0;
2110 for (local_ordinal_type ri0 = partptr(pi0); ri0 < partptr(pi0 + 1); ++ri0) {
2111 const local_ordinal_type td_row_os = btdm.RowToIndex(ri0 - part_ri0) + offset;
2112 offset = 1;
2113 const local_ordinal_type lr0 = lclrow(ri0);
2114 const size_type j0 = local_graph_rowptr(lr0);
2115 for (size_type j = j0; j < local_graph_rowptr(lr0 + 1); ++j) {
2116 const local_ordinal_type lc = local_graph_colidx(j);
2117 const local_ordinal_type lc2r = col2row[lc];
2118 if (lc2r == (local_ordinal_type)-1) continue;
2119 const local_ordinal_type ri = lclrow2idx[lc2r];
2120 const local_ordinal_type pi = rowidx2part(ri);
2121 if (pi != pi0) continue;
2122 if (ri + 1 < ri0 || ri > ri0 + 1) continue;
2123 const local_ordinal_type row_entry = j - j0;
2124 D_A_colindsub(flat_td_ptr(pi0, 0) + ((td_row_os + ri) - ri0)) = row_entry;
2125 }
2126 }
2127 });
2128 }
2129#if defined(BLOCKTRIDICONTAINER_DEBUG)
2130 {
2131 auto D_A_colindsub_host = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), D_A_colindsub);
2132 for (size_t i = 0; i < D_A_colindsub_host.extent(0); ++i)
2133 TEUCHOS_ASSERT(D_A_colindsub_host(i) != Teuchos::OrdinalTraits<local_ordinal_type>::invalid());
2134 }
2135#endif
2136
2137 // Allocate values.
2138 {
2139 const auto pack_td_ptr_last = Kokkos::subview(btdm.pack_td_ptr, btdm.pack_td_ptr.extent(0) - 1, btdm.pack_td_ptr.extent(1) - 1);
2140 const auto num_packed_blocks = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), pack_td_ptr_last);
2141 btdm.values = vector_type_3d_view("btdm.values", num_packed_blocks(), blocksize, blocksize);
2142
2143 if (interf.n_subparts_per_part > 1) {
2144 const auto pack_td_ptr_schur_last = Kokkos::subview(btdm.pack_td_ptr_schur, btdm.pack_td_ptr_schur.extent(0) - 1, btdm.pack_td_ptr_schur.extent(1) - 1);
2145 const auto num_packed_blocks_schur = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), pack_td_ptr_schur_last);
2146 btdm.values_schur = vector_type_3d_view("btdm.values_schur", num_packed_blocks_schur(), blocksize, blocksize);
2147 }
2148
2149 if (vector_length > 1) setTridiagsToIdentity(btdm, interf.packptr);
2150 }
2151 }
2152
2153 // Construct the R graph.
2154 {
2155 amd.rowptr = size_type_1d_view("amd.rowptr", nrows + 1);
2156 amd.A_colindsub = local_ordinal_type_1d_view(do_not_initialize_tag("amd.A_colindsub"), R_nnz_owned);
2157
2158 const auto R_rowptr = amd.rowptr;
2159 const auto R_A_colindsub = amd.A_colindsub;
2160
2161 amd.rowptr_remote = size_type_1d_view("amd.rowptr_remote", overlap_communication_and_computation ? nrows + 1 : 0);
2162 amd.A_colindsub_remote = local_ordinal_type_1d_view(do_not_initialize_tag("amd.A_colindsub_remote"), R_nnz_remote);
2163
2164 const auto R_rowptr_remote = amd.rowptr_remote;
2165 const auto R_A_colindsub_remote = amd.A_colindsub_remote;
2166
2167 {
2168 const Kokkos::RangePolicy<execution_space> policy(0, nrows);
2169 Kokkos::parallel_for(
2170 "performSymbolicPhase::RangePolicy<execution_space>::R_graph_count",
2171 policy, KOKKOS_LAMBDA(const local_ordinal_type &lr) {
2172 const local_ordinal_type ri0 = lclrow2idx[lr];
2173 const local_ordinal_type pi0 = rowidx2part(ri0);
2174 const size_type j0 = local_graph_rowptr(lr);
2175 for (size_type j = j0; j < local_graph_rowptr(lr + 1); ++j) {
2176 const local_ordinal_type lc = local_graph_colidx(j);
2177 const local_ordinal_type lc2r = col2row[lc];
2178 if (lc2r != (local_ordinal_type)-1) {
2179 const local_ordinal_type ri = lclrow2idx[lc2r];
2180 const local_ordinal_type pi = rowidx2part(ri);
2181 if (pi == pi0 && ri + 1 >= ri0 && ri <= ri0 + 1) {
2182 continue;
2183 }
2184 }
2185 // exclusive scan will be performed later
2186 if (!overlap_communication_and_computation || lc < nrows) {
2187 ++R_rowptr(lr);
2188 } else {
2189 ++R_rowptr_remote(lr);
2190 }
2191 }
2192 });
2193 }
2194 // Prefix sums to finish computing R_rowptr and R_rowptr_remote.
2195 // Also check that the final elements of R_rowptr (aka amd.rowptr)
2196 // and R_rowptr_remote (aka amd.rowptr_remote) match the total entry counts computed earlier.
2197 {
2198 size_type R_rowptr_final;
2199#if KOKKOSKERNELS_VERSION >= 50299
2200 KokkosKernels::exclusive_parallel_prefix_sum(execution_space(), R_rowptr, R_rowptr_final);
2201#else
2202 KokkosKernels::Impl::kk_exclusive_parallel_prefix_sum<execution_space>(nrows + 1, R_rowptr, R_rowptr_final);
2203#endif
2204 TEUCHOS_ASSERT(R_rowptr_final == R_nnz_owned);
2205 if (overlap_communication_and_computation) {
2206 size_type R_rowptr_remote_final;
2207#if KOKKOSKERNELS_VERSION >= 50299
2208 KokkosKernels::exclusive_parallel_prefix_sum(execution_space(), R_rowptr_remote, R_rowptr_remote_final);
2209#else
2210 KokkosKernels::Impl::kk_exclusive_parallel_prefix_sum<execution_space>(nrows + 1, R_rowptr_remote, R_rowptr_remote_final);
2211#endif
2212 TEUCHOS_ASSERT(R_rowptr_remote_final == R_nnz_remote);
2213 }
2214 }
2215 {
2216 // Fill R graph entries (R_A_colindsub and R_A_colindsub_remote)
2217 Kokkos::RangePolicy<execution_space> policy(0, nrows);
2218 Kokkos::parallel_for(
2219 "performSymbolicPhase::RangePolicy<execution_space>::R_graph_fill",
2220 policy, KOKKOS_LAMBDA(const local_ordinal_type &lr) {
2221 const local_ordinal_type ri0 = lclrow2idx[lr];
2222 const local_ordinal_type pi0 = rowidx2part(ri0);
2223
2224 size_type cnt_rowptr = R_rowptr(lr);
2225 size_type cnt_rowptr_remote = overlap_communication_and_computation ? R_rowptr_remote(lr) : 0; // when not overlap_communication_and_computation, this value is garbage
2226
2227 const size_type j0 = local_graph_rowptr(lr);
2228 for (size_type j = j0; j < local_graph_rowptr(lr + 1); ++j) {
2229 const local_ordinal_type lc = local_graph_colidx(j);
2230 const local_ordinal_type lc2r = col2row[lc];
2231 if (lc2r != (local_ordinal_type)-1) {
2232 const local_ordinal_type ri = lclrow2idx[lc2r];
2233 const local_ordinal_type pi = rowidx2part(ri);
2234 if (pi == pi0 && ri + 1 >= ri0 && ri <= ri0 + 1)
2235 continue;
2236 }
2237 const local_ordinal_type row_entry = j - j0;
2238 if (!overlap_communication_and_computation || lc < nrows)
2239 R_A_colindsub(cnt_rowptr++) = row_entry;
2240 else
2241 R_A_colindsub_remote(cnt_rowptr_remote++) = row_entry;
2242 }
2243 });
2244 }
2245
2246 // Allocate or view values.
2247 if (hasBlockCrsMatrix)
2248 amd.tpetra_values = (const_cast<block_crs_matrix_type *>(A_bcrs.get())->getValuesDeviceNonConst());
2249 else {
2250 amd.tpetra_values = (const_cast<crs_matrix_type *>(A_crs.get()))->getLocalValuesDevice(Tpetra::Access::ReadWrite);
2251 }
2252 }
2253
2254 if (interf.n_subparts_per_part > 1) {
2255 // If doing Schur complement line splitting, allocate E and space for permuted X
2256 btdm.e_values = vector_type_4d_view("btdm.e_values", 2, interf.part2packrowidx0_back, blocksize, blocksize);
2257 btdm.X_internal_vector_values_schur = internal_vector_type_3d_view(
2258 do_not_initialize_tag("X_internal_vector_values_schur"),
2259 2 * (interf.n_subparts_per_part - 1) * interf.part2packrowidx0_sub.extent(0),
2260 blocksize,
2261 vector_length / internal_vector_length);
2262 }
2263 }
2264 // Precompute offsets of each A and x entry to speed up residual.
2265 // Applies if all of these are true:
2266 // - hasBlockCrsMatrix
2267 // - execution_space is a GPU
2268 // - !useSeqMethod (since this uses a different scheme for indexing A,x)
2269 //
2270 // Reading A, x take up to 4 and 6 levels of indirection respectively,
2271 // but precomputing the offsets reduces it to 2 for both (get index, then value)
2272 if (BlockHelperDetails::is_device<execution_space>::value && !useSeqMethod && hasBlockCrsMatrix) {
2273 bool is_async_importer_active = !async_importer.is_null();
2274 local_ordinal_type_1d_view dm2cm = is_async_importer_active ? async_importer->dm2cm : local_ordinal_type_1d_view();
2275 bool ownedRemoteSeparate = overlap_communication_and_computation || !is_async_importer_active;
2276 BlockHelperDetails::ComputeResidualVector<MatrixType>::precompute_A_x_offsets(amd, interf, g, dm2cm, blocksize, ownedRemoteSeparate);
2277 }
2278
2279 // If using fused block Jacobi path, allocate diagonal inverses here (d_inv) and find diagonal offsets.
2280 if (use_fused_jacobi) {
2281 btdm.d_inv = btdm_scalar_type_3d_view(do_not_initialize_tag("btdm.d_inv"), interf.nparts, blocksize, blocksize);
2282 auto rowptrs = A_bcrs->getCrsGraph().getLocalRowPtrsDevice();
2283 auto entries = A_bcrs->getCrsGraph().getLocalIndicesDevice();
2284 btdm.diag_offsets = BlockHelperDetails::findDiagOffsets<execution_space, size_type_1d_view>(rowptrs, entries, interf.nparts, blocksize);
2285 }
2286 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
2287}
2288
2292template <typename ArgActiveExecutionMemorySpace>
2294
2295template <>
2296struct ExtractAndFactorizeTridiagsDefaultModeAndAlgo<Kokkos::HostSpace> {
2297 typedef KB::Mode::Serial mode_type;
2298#if defined(__KOKKOSBATCHED_INTEL_MKL_COMPACT_BATCHED__)
2299 typedef KB::Algo::Level3::CompactMKL algo_type;
2300#else
2301 typedef KB::Algo::Level3::Blocked algo_type;
2302#endif
2303 static int recommended_team_size(const int /* blksize */,
2304 const int /* vector_length */,
2305 const int /* internal_vector_length */) {
2306 return 1;
2307 }
2308};
2309
2310#if defined(KOKKOS_ENABLE_CUDA)
2311static inline int ExtractAndFactorizeRecommendedCudaTeamSize(const int blksize,
2312 const int vector_length,
2313 const int internal_vector_length) {
2314 const int vector_size = vector_length / internal_vector_length;
2315 int total_team_size(0);
2316 if (blksize <= 5)
2317 total_team_size = 32;
2318 else if (blksize <= 9)
2319 total_team_size = 32; // 64
2320 else if (blksize <= 12)
2321 total_team_size = 96;
2322 else if (blksize <= 16)
2323 total_team_size = 128;
2324 else if (blksize <= 20)
2325 total_team_size = 160;
2326 else
2327 total_team_size = 160;
2328 return 2 * total_team_size / vector_size;
2329}
2330template <>
2331struct ExtractAndFactorizeTridiagsDefaultModeAndAlgo<Kokkos::CudaSpace> {
2332 typedef KB::Mode::Team mode_type;
2333 typedef KB::Algo::Level3::Unblocked algo_type;
2334 static int recommended_team_size(const int blksize,
2335 const int vector_length,
2336 const int internal_vector_length) {
2337 return ExtractAndFactorizeRecommendedCudaTeamSize(blksize, vector_length, internal_vector_length);
2338 }
2339};
2340template <>
2341struct ExtractAndFactorizeTridiagsDefaultModeAndAlgo<Kokkos::CudaUVMSpace> {
2342 typedef KB::Mode::Team mode_type;
2343 typedef KB::Algo::Level3::Unblocked algo_type;
2344 static int recommended_team_size(const int blksize,
2345 const int vector_length,
2346 const int internal_vector_length) {
2347 return ExtractAndFactorizeRecommendedCudaTeamSize(blksize, vector_length, internal_vector_length);
2348 }
2349};
2350#endif
2351
2352#if defined(KOKKOS_ENABLE_HIP)
2353static inline int ExtractAndFactorizeRecommendedHIPTeamSize(const int blksize,
2354 const int vector_length,
2355 const int internal_vector_length) {
2356 const int vector_size = vector_length / internal_vector_length;
2357 int total_team_size(0);
2358 if (blksize <= 5)
2359 total_team_size = 32;
2360 else if (blksize <= 9)
2361 total_team_size = 32; // 64
2362 else if (blksize <= 12)
2363 total_team_size = 96;
2364 else if (blksize <= 16)
2365 total_team_size = 128;
2366 else if (blksize <= 20)
2367 total_team_size = 160;
2368 else
2369 total_team_size = 160;
2370 return 2 * total_team_size / vector_size;
2371}
2372template <>
2373struct ExtractAndFactorizeTridiagsDefaultModeAndAlgo<Kokkos::HIPSpace> {
2374 typedef KB::Mode::Team mode_type;
2375 typedef KB::Algo::Level3::Unblocked algo_type;
2376 static int recommended_team_size(const int blksize,
2377 const int vector_length,
2378 const int internal_vector_length) {
2379 return ExtractAndFactorizeRecommendedHIPTeamSize(blksize, vector_length, internal_vector_length);
2380 }
2381};
2382template <>
2383struct ExtractAndFactorizeTridiagsDefaultModeAndAlgo<Kokkos::HIPHostPinnedSpace> {
2384 typedef KB::Mode::Team mode_type;
2385 typedef KB::Algo::Level3::Unblocked algo_type;
2386 static int recommended_team_size(const int blksize,
2387 const int vector_length,
2388 const int internal_vector_length) {
2389 return ExtractAndFactorizeRecommendedHIPTeamSize(blksize, vector_length, internal_vector_length);
2390 }
2391};
2392#endif
2393
2394#if defined(KOKKOS_ENABLE_SYCL)
2395static inline int ExtractAndFactorizeRecommendedSYCLTeamSize(const int blksize,
2396 const int vector_length,
2397 const int internal_vector_length) {
2398 const int vector_size = vector_length / internal_vector_length;
2399 int total_team_size(0);
2400 if (blksize <= 5)
2401 total_team_size = 32;
2402 else if (blksize <= 9)
2403 total_team_size = 32; // 64
2404 else if (blksize <= 12)
2405 total_team_size = 96;
2406 else if (blksize <= 16)
2407 total_team_size = 128;
2408 else if (blksize <= 20)
2409 total_team_size = 160;
2410 else
2411 total_team_size = 160;
2412 return 2 * total_team_size / vector_size;
2413}
2414template <>
2415struct ExtractAndFactorizeTridiagsDefaultModeAndAlgo<Kokkos::SYCLDeviceUSMSpace> {
2416 typedef KB::Mode::Team mode_type;
2417 typedef KB::Algo::Level3::Unblocked algo_type;
2418 static int recommended_team_size(const int blksize,
2419 const int vector_length,
2420 const int internal_vector_length) {
2421 return ExtractAndFactorizeRecommendedSYCLTeamSize(blksize, vector_length, internal_vector_length);
2422 }
2423};
2424template <>
2425struct ExtractAndFactorizeTridiagsDefaultModeAndAlgo<Kokkos::SYCLSharedUSMSpace> {
2426 typedef KB::Mode::Team mode_type;
2427 typedef KB::Algo::Level3::Unblocked algo_type;
2428 static int recommended_team_size(const int blksize,
2429 const int vector_length,
2430 const int internal_vector_length) {
2431 return ExtractAndFactorizeRecommendedSYCLTeamSize(blksize, vector_length, internal_vector_length);
2432 }
2433};
2434#endif
2435
2436template <typename impl_type, typename WWViewType>
2437KOKKOS_INLINE_FUNCTION void
2438solveMultiVector(const typename Kokkos::TeamPolicy<typename impl_type::execution_space>::member_type &member,
2439 const typename impl_type::local_ordinal_type & /* blocksize */,
2440 const typename impl_type::local_ordinal_type &i0,
2441 const typename impl_type::local_ordinal_type &r0,
2442 const typename impl_type::local_ordinal_type &nrows,
2443 const typename impl_type::local_ordinal_type &v,
2444 const ConstUnmanaged<typename impl_type::internal_vector_type_4d_view> D_internal_vector_values,
2445 const Unmanaged<typename impl_type::internal_vector_type_4d_view> X_internal_vector_values,
2446 const WWViewType &WW,
2447 const bool skip_first_pass = false) {
2448 using execution_space = typename impl_type::execution_space;
2449 using team_policy_type = Kokkos::TeamPolicy<execution_space>;
2450 using member_type = typename team_policy_type::member_type;
2451 using local_ordinal_type = typename impl_type::local_ordinal_type;
2452
2453 typedef SolveTridiagsDefaultModeAndAlgo<typename execution_space::memory_space> default_mode_and_algo_type;
2454
2455 typedef typename default_mode_and_algo_type::mode_type default_mode_type;
2456 typedef typename default_mode_and_algo_type::multi_vector_algo_type default_algo_type;
2457
2458 using btdm_magnitude_type = typename impl_type::btdm_magnitude_type;
2459
2460 // constant
2461 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
2462 const auto zero = KokkosKernels::ArithTraits<btdm_magnitude_type>::zero();
2463
2464 // subview pattern
2465 auto A = Kokkos::subview(D_internal_vector_values, i0, Kokkos::ALL(), Kokkos::ALL(), v);
2466 auto X1 = Kokkos::subview(X_internal_vector_values, r0, Kokkos::ALL(), Kokkos::ALL(), v);
2467 auto X2 = X1;
2468
2469 local_ordinal_type i = i0, r = r0;
2470
2471 if (nrows > 1) {
2472 // solve Lx = x
2473 if (skip_first_pass) {
2474 i += (nrows - 2) * 3;
2475 r += (nrows - 2);
2476 A.assign_data(&D_internal_vector_values(i + 2, 0, 0, v));
2477 X2.assign_data(&X_internal_vector_values(++r, 0, 0, v));
2478 A.assign_data(&D_internal_vector_values(i + 3, 0, 0, v));
2479 KB::Trsm<member_type,
2480 KB::Side::Left, KB::Uplo::Lower, KB::Trans::NoTranspose, KB::Diag::Unit,
2481 default_mode_type, default_algo_type>::invoke(member, one, A, X2);
2482 X1.assign_data(X2.data());
2483 i += 3;
2484 } else {
2485 KB::Trsm<member_type,
2486 KB::Side::Left, KB::Uplo::Lower, KB::Trans::NoTranspose, KB::Diag::Unit,
2487 default_mode_type, default_algo_type>::invoke(member, one, A, X1);
2488 for (local_ordinal_type tr = 1; tr < nrows; ++tr, i += 3) {
2489 A.assign_data(&D_internal_vector_values(i + 2, 0, 0, v));
2490 X2.assign_data(&X_internal_vector_values(++r, 0, 0, v));
2491 member.team_barrier();
2492 KB::Gemm<member_type,
2493 KB::Trans::NoTranspose, KB::Trans::NoTranspose,
2494 default_mode_type, default_algo_type>::invoke(member, -one, A, X1, one, X2);
2495 A.assign_data(&D_internal_vector_values(i + 3, 0, 0, v));
2496 KB::Trsm<member_type,
2497 KB::Side::Left, KB::Uplo::Lower, KB::Trans::NoTranspose, KB::Diag::Unit,
2498 default_mode_type, default_algo_type>::invoke(member, one, A, X2);
2499 X1.assign_data(X2.data());
2500 }
2501 }
2502
2503 // solve Ux = x
2504 KB::Trsm<member_type,
2505 KB::Side::Left, KB::Uplo::Upper, KB::Trans::NoTranspose, KB::Diag::NonUnit,
2506 default_mode_type, default_algo_type>::invoke(member, one, A, X1);
2507 for (local_ordinal_type tr = nrows; tr > 1; --tr) {
2508 i -= 3;
2509 A.assign_data(&D_internal_vector_values(i + 1, 0, 0, v));
2510 X2.assign_data(&X_internal_vector_values(--r, 0, 0, v));
2511 member.team_barrier();
2512 KB::Gemm<member_type,
2513 KB::Trans::NoTranspose, KB::Trans::NoTranspose,
2514 default_mode_type, default_algo_type>::invoke(member, -one, A, X1, one, X2);
2515
2516 A.assign_data(&D_internal_vector_values(i, 0, 0, v));
2517 KB::Trsm<member_type,
2518 KB::Side::Left, KB::Uplo::Upper, KB::Trans::NoTranspose, KB::Diag::NonUnit,
2519 default_mode_type, default_algo_type>::invoke(member, one, A, X2);
2520 X1.assign_data(X2.data());
2521 }
2522 } else {
2523 // matrix is already inverted
2524 auto W = Kokkos::subview(WW, Kokkos::ALL(), Kokkos::ALL(), v);
2525 KB::Copy<member_type, KB::Trans::NoTranspose, default_mode_type>::invoke(member, X1, W);
2526 member.team_barrier();
2527 KB::Gemm<member_type,
2528 KB::Trans::NoTranspose, KB::Trans::NoTranspose,
2529 default_mode_type, default_algo_type>::invoke(member, one, A, W, zero, X1);
2530 }
2531}
2532
2533template <typename impl_type, typename WWViewType, typename XViewType>
2534KOKKOS_INLINE_FUNCTION void
2535solveSingleVectorNew(const typename Kokkos::TeamPolicy<typename impl_type::execution_space>::member_type &member,
2536 const typename impl_type::local_ordinal_type &blocksize,
2537 const typename impl_type::local_ordinal_type &i0,
2538 const typename impl_type::local_ordinal_type &r0,
2539 const typename impl_type::local_ordinal_type &nrows,
2540 const typename impl_type::local_ordinal_type &v,
2541 const ConstUnmanaged<typename impl_type::internal_vector_type_4d_view> D_internal_vector_values,
2542 const XViewType &X_internal_vector_values, // Unmanaged<typename impl_type::internal_vector_type_4d_view>
2543 const WWViewType &WW) {
2544 using execution_space = typename impl_type::execution_space;
2545 // using team_policy_type = Kokkos::TeamPolicy<execution_space>;
2546 // using member_type = typename team_policy_type::member_type;
2547 using local_ordinal_type = typename impl_type::local_ordinal_type;
2548
2549 typedef SolveTridiagsDefaultModeAndAlgo<typename execution_space::memory_space> default_mode_and_algo_type;
2550
2551 typedef typename default_mode_and_algo_type::mode_type default_mode_type;
2552 typedef typename default_mode_and_algo_type::single_vector_algo_type default_algo_type;
2553
2554 using btdm_magnitude_type = typename impl_type::btdm_magnitude_type;
2555
2556 // base pointers
2557 auto A = D_internal_vector_values.data();
2558 auto X = X_internal_vector_values.data();
2559
2560 // constant
2561 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
2562 const auto zero = KokkosKernels::ArithTraits<btdm_magnitude_type>::zero();
2563 // const local_ordinal_type num_vectors = X_scalar_values.extent(2);
2564
2565 // const local_ordinal_type blocksize = D_scalar_values.extent(1);
2566 const local_ordinal_type astep = D_internal_vector_values.stride(0);
2567 const local_ordinal_type as0 = D_internal_vector_values.stride(1); // blocksize*vector_length;
2568 const local_ordinal_type as1 = D_internal_vector_values.stride(2); // vector_length;
2569 const local_ordinal_type xstep = X_internal_vector_values.stride(0);
2570 const local_ordinal_type xs0 = X_internal_vector_values.stride(1); // vector_length;
2571
2572 // move to starting point
2573 A += i0 * astep + v;
2574 X += r0 * xstep + v;
2575
2576 // for (local_ordinal_type col=0;col<num_vectors;++col)
2577 if (nrows > 1) {
2578 // solve Lx = x
2579 KOKKOSBATCHED_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
2580 member,
2581 KB::Diag::Unit,
2582 blocksize, blocksize,
2583 one,
2584 A, as0, as1,
2585 X, xs0);
2586
2587 for (local_ordinal_type tr = 1; tr < nrows; ++tr) {
2588 member.team_barrier();
2589 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
2590 member,
2591 blocksize, blocksize,
2592 -one,
2593 A + 2 * astep, as0, as1,
2594 X, xs0,
2595 one,
2596 X + 1 * xstep, xs0);
2597 KOKKOSBATCHED_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
2598 member,
2599 KB::Diag::Unit,
2600 blocksize, blocksize,
2601 one,
2602 A + 3 * astep, as0, as1,
2603 X + 1 * xstep, xs0);
2604
2605 A += 3 * astep;
2606 X += 1 * xstep;
2607 }
2608
2609 // solve Ux = x
2610 KOKKOSBATCHED_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
2611 member,
2612 KB::Diag::NonUnit,
2613 blocksize, blocksize,
2614 one,
2615 A, as0, as1,
2616 X, xs0);
2617
2618 for (local_ordinal_type tr = nrows; tr > 1; --tr) {
2619 A -= 3 * astep;
2620 member.team_barrier();
2621 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
2622 member,
2623 blocksize, blocksize,
2624 -one,
2625 A + 1 * astep, as0, as1,
2626 X, xs0,
2627 one,
2628 X - 1 * xstep, xs0);
2629 KOKKOSBATCHED_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
2630 member,
2631 KB::Diag::NonUnit,
2632 blocksize, blocksize,
2633 one,
2634 A, as0, as1,
2635 X - 1 * xstep, xs0);
2636 X -= 1 * xstep;
2637 }
2638 // for multiple rhs
2639 // X += xs1;
2640 } else {
2641 const local_ordinal_type ws0 = WW.stride(0);
2642 auto W = WW.data() + v;
2643 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize), [&](int i) { W[i * ws0] = X[i * xs0]; });
2644 member.team_barrier();
2645 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
2646 member,
2647 blocksize, blocksize,
2648 one,
2649 A, as0, as1,
2650 W, xs0,
2651 zero,
2652 X, xs0);
2653 }
2654}
2655
2656template <typename local_ordinal_type, typename ViewType>
2657void writeBTDValuesToFile(const local_ordinal_type &n_parts, const ViewType &scalar_values_device, std::string fileName) {
2658#ifdef IFPACK2_BLOCKTRIDICONTAINER_WRITE_MM
2659 auto scalar_values = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), scalar_values_device);
2660 std::ofstream myfile;
2661 myfile.open(fileName);
2662
2663 const local_ordinal_type n_parts_per_pack = n_parts < (local_ordinal_type)scalar_values.extent(3) ? n_parts : scalar_values.extent(3);
2664 local_ordinal_type nnz = scalar_values.extent(0) * scalar_values.extent(1) * scalar_values.extent(2) * n_parts_per_pack;
2665 const local_ordinal_type n_blocks = scalar_values.extent(0) * n_parts_per_pack;
2666 const local_ordinal_type n_blocks_per_part = n_blocks / n_parts;
2667
2668 const local_ordinal_type block_size = scalar_values.extent(1);
2669
2670 const local_ordinal_type n_rows_per_part = (n_blocks_per_part + 2) / 3 * block_size;
2671 const local_ordinal_type n_rows = n_rows_per_part * n_parts;
2672
2673 const local_ordinal_type n_packs = ceil(float(n_parts) / n_parts_per_pack);
2674
2675 myfile << "%%MatrixMarket matrix coordinate real general" << std::endl;
2676 myfile << "%%nnz = " << nnz;
2677 myfile << " block size = " << block_size;
2678 myfile << " number of blocks = " << n_blocks;
2679 myfile << " number of parts = " << n_parts;
2680 myfile << " number of blocks per part = " << n_blocks_per_part;
2681 myfile << " number of rows = " << n_rows;
2682 myfile << " number of cols = " << n_rows;
2683 myfile << " number of packs = " << n_packs << std::endl;
2684
2685 myfile << n_rows << " " << n_rows << " " << nnz << std::setprecision(9) << std::endl;
2686
2687 local_ordinal_type current_part_idx, current_block_idx, current_row_offset, current_col_offset, current_row, current_col;
2688 for (local_ordinal_type i_pack = 0; i_pack < n_packs; ++i_pack) {
2689 for (local_ordinal_type i_part_in_pack = 0; i_part_in_pack < n_parts_per_pack; ++i_part_in_pack) {
2690 current_part_idx = i_part_in_pack + i_pack * n_parts_per_pack;
2691 for (local_ordinal_type i_block_in_part = 0; i_block_in_part < n_blocks_per_part; ++i_block_in_part) {
2692 current_block_idx = i_block_in_part + i_pack * n_blocks_per_part;
2693 if (current_block_idx >= (local_ordinal_type)scalar_values.extent(0))
2694 continue;
2695 if (i_block_in_part % 3 == 0) {
2696 current_row_offset = i_block_in_part / 3 * block_size;
2697 current_col_offset = i_block_in_part / 3 * block_size;
2698 } else if (i_block_in_part % 3 == 1) {
2699 current_row_offset = (i_block_in_part - 1) / 3 * block_size;
2700 current_col_offset = ((i_block_in_part - 1) / 3 + 1) * block_size;
2701 } else if (i_block_in_part % 3 == 2) {
2702 current_row_offset = ((i_block_in_part - 2) / 3 + 1) * block_size;
2703 current_col_offset = (i_block_in_part - 2) / 3 * block_size;
2704 }
2705 current_row_offset += current_part_idx * n_rows_per_part;
2706 current_col_offset += current_part_idx * n_rows_per_part;
2707 for (local_ordinal_type i_in_block = 0; i_in_block < block_size; ++i_in_block) {
2708 for (local_ordinal_type j_in_block = 0; j_in_block < block_size; ++j_in_block) {
2709 current_row = current_row_offset + i_in_block + 1;
2710 current_col = current_col_offset + j_in_block + 1;
2711 myfile << current_row << " " << current_col << " " << scalar_values(current_block_idx, i_in_block, j_in_block, i_part_in_pack) << std::endl;
2712 }
2713 }
2714 }
2715 }
2716 }
2717
2718 myfile.close();
2719#endif
2720}
2721
2722template <typename local_ordinal_type, typename ViewType>
2723void write4DMultiVectorValuesToFile(const local_ordinal_type &n_parts, const ViewType &scalar_values_device, std::string fileName) {
2724#ifdef IFPACK2_BLOCKTRIDICONTAINER_WRITE_MM
2725 auto scalar_values = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), scalar_values_device);
2726 std::ofstream myfile;
2727 myfile.open(fileName);
2728
2729 const local_ordinal_type n_parts_per_pack = n_parts < scalar_values.extent(3) ? n_parts : scalar_values.extent(3);
2730 const local_ordinal_type n_blocks = scalar_values.extent(0) * n_parts_per_pack;
2731 const local_ordinal_type n_blocks_per_part = n_blocks / n_parts;
2732
2733 const local_ordinal_type block_size = scalar_values.extent(1);
2734 const local_ordinal_type n_cols = scalar_values.extent(2);
2735
2736 const local_ordinal_type n_rows_per_part = n_blocks_per_part * block_size;
2737 const local_ordinal_type n_rows = n_rows_per_part * n_parts;
2738
2739 const local_ordinal_type n_packs = ceil(float(n_parts) / n_parts_per_pack);
2740
2741 myfile << "%%MatrixMarket matrix array real general" << std::endl;
2742 myfile << "%%block size = " << block_size;
2743 myfile << " number of blocks = " << n_blocks;
2744 myfile << " number of parts = " << n_parts;
2745 myfile << " number of blocks per part = " << n_blocks_per_part;
2746 myfile << " number of rows = " << n_rows;
2747 myfile << " number of cols = " << n_cols;
2748 myfile << " number of packs = " << n_packs << std::endl;
2749
2750 myfile << n_rows << " " << n_cols << std::setprecision(9) << std::endl;
2751
2752 local_ordinal_type current_part_idx, current_block_idx, current_row_offset;
2753 (void)current_row_offset;
2754 (void)current_part_idx;
2755 for (local_ordinal_type j_in_block = 0; j_in_block < n_cols; ++j_in_block) {
2756 for (local_ordinal_type i_pack = 0; i_pack < n_packs; ++i_pack) {
2757 for (local_ordinal_type i_part_in_pack = 0; i_part_in_pack < n_parts_per_pack; ++i_part_in_pack) {
2758 current_part_idx = i_part_in_pack + i_pack * n_parts_per_pack;
2759 for (local_ordinal_type i_block_in_part = 0; i_block_in_part < n_blocks_per_part; ++i_block_in_part) {
2760 current_block_idx = i_block_in_part + i_pack * n_blocks_per_part;
2761
2762 if (current_block_idx >= (local_ordinal_type)scalar_values.extent(0))
2763 continue;
2764 for (local_ordinal_type i_in_block = 0; i_in_block < block_size; ++i_in_block) {
2765 myfile << scalar_values(current_block_idx, i_in_block, j_in_block, i_part_in_pack) << std::endl;
2766 }
2767 }
2768 }
2769 }
2770 }
2771 myfile.close();
2772#endif
2773}
2774
2775template <typename local_ordinal_type, typename ViewType>
2776void write5DMultiVectorValuesToFile(const local_ordinal_type &n_parts, const ViewType &scalar_values_device, std::string fileName) {
2777#ifdef IFPACK2_BLOCKTRIDICONTAINER_WRITE_MM
2778 auto scalar_values = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), scalar_values_device);
2779 std::ofstream myfile;
2780 myfile.open(fileName);
2781
2782 const local_ordinal_type n_parts_per_pack = n_parts < scalar_values.extent(4) ? n_parts : scalar_values.extent(4);
2783 const local_ordinal_type n_blocks = scalar_values.extent(1) * n_parts_per_pack;
2784 const local_ordinal_type n_blocks_per_part = n_blocks / n_parts;
2785
2786 const local_ordinal_type block_size = scalar_values.extent(2);
2787 const local_ordinal_type n_blocks_cols = scalar_values.extent(0);
2788 const local_ordinal_type n_cols = n_blocks_cols * block_size;
2789
2790 const local_ordinal_type n_rows_per_part = n_blocks_per_part * block_size;
2791 const local_ordinal_type n_rows = n_rows_per_part * n_parts;
2792
2793 const local_ordinal_type n_packs = ceil(float(n_parts) / n_parts_per_pack);
2794
2795 myfile << "%%MatrixMarket matrix array real general" << std::endl;
2796 myfile << "%%block size = " << block_size;
2797 myfile << " number of blocks = " << n_blocks;
2798 myfile << " number of parts = " << n_parts;
2799 myfile << " number of blocks per part = " << n_blocks_per_part;
2800 myfile << " number of rows = " << n_rows;
2801 myfile << " number of cols = " << n_cols;
2802 myfile << " number of packs = " << n_packs << std::endl;
2803
2804 myfile << n_rows << " " << n_cols << std::setprecision(9) << std::endl;
2805
2806 local_ordinal_type current_part_idx, current_block_idx, current_row_offset;
2807 (void)current_row_offset;
2808 (void)current_part_idx;
2809 for (local_ordinal_type i_block_col = 0; i_block_col < n_blocks_cols; ++i_block_col) {
2810 for (local_ordinal_type j_in_block = 0; j_in_block < block_size; ++j_in_block) {
2811 for (local_ordinal_type i_pack = 0; i_pack < n_packs; ++i_pack) {
2812 for (local_ordinal_type i_part_in_pack = 0; i_part_in_pack < n_parts_per_pack; ++i_part_in_pack) {
2813 current_part_idx = i_part_in_pack + i_pack * n_parts_per_pack;
2814 for (local_ordinal_type i_block_in_part = 0; i_block_in_part < n_blocks_per_part; ++i_block_in_part) {
2815 current_block_idx = i_block_in_part + i_pack * n_blocks_per_part;
2816
2817 if (current_block_idx >= (local_ordinal_type)scalar_values.extent(1))
2818 continue;
2819 for (local_ordinal_type i_in_block = 0; i_in_block < block_size; ++i_in_block) {
2820 myfile << scalar_values(i_block_col, current_block_idx, i_in_block, j_in_block, i_part_in_pack) << std::endl;
2821 }
2822 }
2823 }
2824 }
2825 }
2826 }
2827 myfile.close();
2828#endif
2829}
2830
2831template <typename local_ordinal_type, typename member_type, typename ViewType1, typename ViewType2>
2832KOKKOS_INLINE_FUNCTION void
2833copy3DView(const member_type &member, const ViewType1 &view1, const ViewType2 &view2) {
2834 /*
2835 // Kokkos::Experimental::local_deep_copy
2836 auto teamVectorRange =
2837 Kokkos::TeamVectorMDRange<Kokkos::Rank<3>, member_type>(
2838 member, view1.extent(0), view1.extent(1), view1.extent(2));
2839
2840 Kokkos::parallel_for
2841 (teamVectorRange,
2842 [&](const local_ordinal_type &i, const local_ordinal_type &j, const local_ordinal_type &k) {
2843 view1(i,j,k) = view2(i,j,k);
2844 });
2845 */
2846 Kokkos::Experimental::local_deep_copy(member, view1, view2);
2847}
2848template <typename MatrixType, int ScratchLevel>
2849struct ExtractAndFactorizeTridiags {
2850 public:
2851 using impl_type = BlockHelperDetails::ImplType<MatrixType>;
2852 // a functor cannot have both device_type and execution_space; specialization error in kokkos
2853 using execution_space = typename impl_type::execution_space;
2854 using memory_space = typename impl_type::memory_space;
2856 using local_ordinal_type = typename impl_type::local_ordinal_type;
2857 using size_type = typename impl_type::size_type;
2858 using impl_scalar_type = typename impl_type::impl_scalar_type;
2859 using magnitude_type = typename impl_type::magnitude_type;
2861 using row_matrix_type = typename impl_type::tpetra_row_matrix_type;
2862 using crs_graph_type = typename impl_type::tpetra_crs_graph_type;
2864 using local_ordinal_type_1d_view = typename impl_type::local_ordinal_type_1d_view;
2865 using local_ordinal_type_2d_view = typename impl_type::local_ordinal_type_2d_view;
2866 using size_type_1d_view = typename impl_type::size_type_1d_view;
2867 using size_type_2d_view = typename impl_type::size_type_2d_view;
2868 using impl_scalar_type_1d_view_tpetra = typename impl_type::impl_scalar_type_1d_view_tpetra;
2870 using btdm_scalar_type = typename impl_type::btdm_scalar_type;
2871 using btdm_magnitude_type = typename impl_type::btdm_magnitude_type;
2872 using vector_type_3d_view = typename impl_type::vector_type_3d_view;
2873 using vector_type_4d_view = typename impl_type::vector_type_4d_view;
2874 using internal_vector_type_4d_view = typename impl_type::internal_vector_type_4d_view;
2875 using internal_vector_type_5d_view = typename impl_type::internal_vector_type_5d_view;
2876 using btdm_scalar_type_2d_view = typename impl_type::btdm_scalar_type_2d_view;
2877 using btdm_scalar_type_3d_view = typename impl_type::btdm_scalar_type_3d_view;
2878 using btdm_scalar_type_4d_view = typename impl_type::btdm_scalar_type_4d_view;
2879 using btdm_scalar_type_5d_view = typename impl_type::btdm_scalar_type_5d_view;
2880 using internal_vector_scratch_type_3d_view = Scratch<typename impl_type::internal_vector_type_3d_view>;
2881 using btdm_scalar_scratch_type_3d_view = Scratch<typename impl_type::btdm_scalar_type_3d_view>;
2882 using tpetra_block_access_view_type = typename impl_type::tpetra_block_access_view_type; // block crs (layout right)
2883 using local_crs_graph_type = typename impl_type::local_crs_graph_type;
2884 using colinds_view = typename local_crs_graph_type::entries_type;
2885
2886 using internal_vector_type = typename impl_type::internal_vector_type;
2887 static constexpr int vector_length = impl_type::vector_length;
2888 static constexpr int internal_vector_length = impl_type::internal_vector_length;
2889 static_assert(vector_length >= internal_vector_length, "Ifpack2 BlockTriDi Numeric: vector_length must be at least as large as internal_vector_length");
2890 static_assert(vector_length % internal_vector_length == 0, "Ifpack2 BlockTriDi Numeric: vector_length must be divisible by internal_vector_length");
2891 // half_vector_length is used for block Jacobi factorization.
2892 // Shared memory requirement is twice as large (per vector lane) as for general tridi factorization, so
2893 // reducing vector length (if possible) keeps the shared requirement constant. This avoids the performance
2894 // cliff of switching from level 0 to level 1 scratch.
2895 static constexpr int half_vector_length = impl_type::half_vector_length;
2896
2898 using team_policy_type = Kokkos::TeamPolicy<execution_space>;
2899 using member_type = typename team_policy_type::member_type;
2900
2901 private:
2902 // part interface
2903 const ConstUnmanaged<local_ordinal_type_1d_view> partptr, lclrow, packptr, packindices_sub, packptr_sub;
2904 const ConstUnmanaged<local_ordinal_type_2d_view> partptr_sub, part2packrowidx0_sub, packindices_schur;
2905 const local_ordinal_type max_partsz;
2906 // block crs matrix (it could be Kokkos::UVMSpace::size_type, which is int)
2907 using size_type_1d_view_tpetra = Kokkos::View<size_t *, typename impl_type::node_device_type>;
2908 ConstUnmanaged<size_type_1d_view_tpetra> A_block_rowptr;
2909 ConstUnmanaged<size_type_1d_view_tpetra> A_point_rowptr;
2910 ConstUnmanaged<impl_scalar_type_1d_view_tpetra> A_values;
2911 // block tridiags
2912 const ConstUnmanaged<size_type_2d_view> pack_td_ptr, flat_td_ptr, pack_td_ptr_schur;
2913 const ConstUnmanaged<local_ordinal_type_1d_view> A_colindsub;
2914 const Unmanaged<internal_vector_type_4d_view> internal_vector_values, internal_vector_values_schur;
2915 const Unmanaged<internal_vector_type_5d_view> e_internal_vector_values;
2916 const Unmanaged<btdm_scalar_type_4d_view> scalar_values, scalar_values_schur;
2917 const Unmanaged<btdm_scalar_type_5d_view> e_scalar_values;
2918 const Unmanaged<btdm_scalar_type_3d_view> d_inv;
2919 const Unmanaged<size_type_1d_view> diag_offsets;
2920 // shared information
2921 const local_ordinal_type blocksize, blocksize_square;
2922 // diagonal safety
2923 const magnitude_type tiny;
2924 const local_ordinal_type vector_loop_size;
2925
2926 bool hasBlockCrsMatrix;
2927
2928 public:
2929 ExtractAndFactorizeTridiags(const BlockTridiags<MatrixType> &btdm_,
2930 const BlockHelperDetails::PartInterface<MatrixType> &interf_,
2931 const Teuchos::RCP<const row_matrix_type> &A_,
2932 const Teuchos::RCP<const crs_graph_type> &G_,
2933 const magnitude_type &tiny_)
2934 : // interface
2935 partptr(interf_.partptr)
2936 , lclrow(interf_.lclrow)
2937 , packptr(interf_.packptr)
2938 , packindices_sub(interf_.packindices_sub)
2939 , packptr_sub(interf_.packptr_sub)
2940 , partptr_sub(interf_.partptr_sub)
2941 , part2packrowidx0_sub(interf_.part2packrowidx0_sub)
2942 , packindices_schur(interf_.packindices_schur)
2943 , max_partsz(interf_.max_partsz)
2944 ,
2945 // block tridiags
2946 pack_td_ptr(btdm_.pack_td_ptr)
2947 , flat_td_ptr(btdm_.flat_td_ptr)
2948 , pack_td_ptr_schur(btdm_.pack_td_ptr_schur)
2949 , A_colindsub(btdm_.A_colindsub)
2950 , internal_vector_values((internal_vector_type *)btdm_.values.data(),
2951 btdm_.values.extent(0),
2952 btdm_.values.extent(1),
2953 btdm_.values.extent(2),
2954 vector_length / internal_vector_length)
2955 , internal_vector_values_schur((internal_vector_type *)btdm_.values_schur.data(),
2956 btdm_.values_schur.extent(0),
2957 btdm_.values_schur.extent(1),
2958 btdm_.values_schur.extent(2),
2959 vector_length / internal_vector_length)
2960 , e_internal_vector_values((internal_vector_type *)btdm_.e_values.data(),
2961 btdm_.e_values.extent(0),
2962 btdm_.e_values.extent(1),
2963 btdm_.e_values.extent(2),
2964 btdm_.e_values.extent(3),
2965 vector_length / internal_vector_length)
2966 , scalar_values((btdm_scalar_type *)btdm_.values.data(),
2967 btdm_.values.extent(0),
2968 btdm_.values.extent(1),
2969 btdm_.values.extent(2),
2970 vector_length)
2971 , scalar_values_schur((btdm_scalar_type *)btdm_.values_schur.data(),
2972 btdm_.values_schur.extent(0),
2973 btdm_.values_schur.extent(1),
2974 btdm_.values_schur.extent(2),
2975 vector_length)
2976 , e_scalar_values((btdm_scalar_type *)btdm_.e_values.data(),
2977 btdm_.e_values.extent(0),
2978 btdm_.e_values.extent(1),
2979 btdm_.e_values.extent(2),
2980 btdm_.e_values.extent(3),
2981 vector_length)
2982 , d_inv(btdm_.d_inv)
2983 , diag_offsets(btdm_.diag_offsets)
2984 , blocksize(btdm_.values.extent(1))
2985 , blocksize_square(blocksize * blocksize)
2986 ,
2987 // diagonal weight to avoid zero pivots
2988 tiny(tiny_)
2989 , vector_loop_size(vector_length / internal_vector_length) {
2990 using crs_matrix_type = typename impl_type::tpetra_crs_matrix_type;
2991 using block_crs_matrix_type = typename impl_type::tpetra_block_crs_matrix_type;
2992
2993 auto A_crs = Teuchos::rcp_dynamic_cast<const crs_matrix_type>(A_);
2994 auto A_bcrs = Teuchos::rcp_dynamic_cast<const block_crs_matrix_type>(A_);
2995
2996 hasBlockCrsMatrix = !A_bcrs.is_null();
2997
2998 A_block_rowptr = G_->getLocalGraphDevice().row_map;
2999 if (hasBlockCrsMatrix) {
3000 A_values = const_cast<block_crs_matrix_type *>(A_bcrs.get())->getValuesDeviceNonConst();
3001 } else {
3002 A_point_rowptr = A_crs->getCrsGraph()->getLocalGraphDevice().row_map;
3003 A_values = A_crs->getLocalValuesDevice(Tpetra::Access::ReadOnly);
3004 }
3005 }
3006
3007 private:
3008 KOKKOS_INLINE_FUNCTION
3009 void
3010 extract(local_ordinal_type partidx,
3011 local_ordinal_type local_subpartidx,
3012 local_ordinal_type npacks) const {
3013#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3014 printf("extract partidx = %d, local_subpartidx = %d, npacks = %d;\n", partidx, local_subpartidx, npacks);
3015#endif
3016 using tlb = BlockHelperDetails::TpetraLittleBlock<Tpetra::Impl::BlockCrsMatrixLittleBlockArrayLayout>;
3017 const size_type kps = pack_td_ptr(partidx, local_subpartidx);
3018 local_ordinal_type kfs[vector_length] = {};
3019 local_ordinal_type ri0[vector_length] = {};
3020 local_ordinal_type nrows[vector_length] = {};
3021
3022 for (local_ordinal_type vi = 0; vi < npacks; ++vi, ++partidx) {
3023 kfs[vi] = flat_td_ptr(partidx, local_subpartidx);
3024 ri0[vi] = partptr_sub(pack_td_ptr.extent(0) * local_subpartidx + partidx, 0);
3025 nrows[vi] = partptr_sub(pack_td_ptr.extent(0) * local_subpartidx + partidx, 1) - ri0[vi];
3026#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3027 printf("kfs[%d] = %d;\n", vi, kfs[vi]);
3028 printf("ri0[%d] = %d;\n", vi, ri0[vi]);
3029 printf("nrows[%d] = %d;\n", vi, nrows[vi]);
3030#endif
3031 }
3032 local_ordinal_type tr_min = 0;
3033 local_ordinal_type tr_max = nrows[0];
3034 if (local_subpartidx % 2 == 1) {
3035 tr_min -= 1;
3036 tr_max += 1;
3037 }
3038#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3039 printf("tr_min = %d and tr_max = %d;\n", tr_min, tr_max);
3040#endif
3041 for (local_ordinal_type tr = tr_min, j = 0; tr < tr_max; ++tr) {
3042 for (local_ordinal_type e = 0; e < 3; ++e) {
3043 if (hasBlockCrsMatrix) {
3044 const impl_scalar_type *block[vector_length] = {};
3045 for (local_ordinal_type vi = 0; vi < npacks; ++vi) {
3046 const size_type Aj = A_block_rowptr(lclrow(ri0[vi] + tr)) + A_colindsub(kfs[vi] + j);
3047
3048 block[vi] = &A_values(Aj * blocksize_square);
3049 }
3050 const size_type pi = kps + j;
3051#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3052 printf("Extract pi = %ld, ri0 + tr = %d, kfs + j = %d\n", pi, ri0[0] + tr, kfs[0] + j);
3053#endif
3054 ++j;
3055 for (local_ordinal_type ii = 0; ii < blocksize; ++ii) {
3056 for (local_ordinal_type jj = 0; jj < blocksize; ++jj) {
3057 const auto idx = tlb::getFlatIndex(ii, jj, blocksize);
3058 auto &v = internal_vector_values(pi, ii, jj, 0);
3059 for (local_ordinal_type vi = 0; vi < npacks; ++vi) {
3060 v[vi] = static_cast<btdm_scalar_type>(block[vi][idx]);
3061 }
3062 }
3063 }
3064 } else {
3065 const size_type pi = kps + j;
3066
3067 for (local_ordinal_type vi = 0; vi < npacks; ++vi) {
3068 const size_type Aj_c = A_colindsub(kfs[vi] + j);
3069
3070 for (local_ordinal_type ii = 0; ii < blocksize; ++ii) {
3071 auto point_row_offset = A_point_rowptr(lclrow(ri0[vi] + tr) * blocksize + ii);
3072
3073 for (local_ordinal_type jj = 0; jj < blocksize; ++jj) {
3074 scalar_values(pi, ii, jj, vi) = A_values(point_row_offset + Aj_c * blocksize + jj);
3075 }
3076 }
3077 }
3078 ++j;
3079 }
3080 if (nrows[0] == 1) break;
3081 if (local_subpartidx % 2 == 0) {
3082 if (e == 1 && (tr == 0 || tr + 1 == nrows[0])) break;
3083 for (local_ordinal_type vi = 1; vi < npacks; ++vi) {
3084 if ((e == 0 && nrows[vi] == 1) || (e == 1 && tr + 1 == nrows[vi])) {
3085 npacks = vi;
3086 break;
3087 }
3088 }
3089 } else {
3090 if (e == 0 && (tr == -1 || tr == nrows[0])) break;
3091 for (local_ordinal_type vi = 1; vi < npacks; ++vi) {
3092 if ((e == 0 && nrows[vi] == 1) || (e == 0 && tr == nrows[vi])) {
3093 npacks = vi;
3094 break;
3095 }
3096 }
3097 }
3098 }
3099 }
3100 }
3101
3102 KOKKOS_INLINE_FUNCTION
3103 void
3104 extract(const member_type &member,
3105 const local_ordinal_type &partidxbeg,
3106 local_ordinal_type local_subpartidx,
3107 const local_ordinal_type &npacks,
3108 const local_ordinal_type &vbeg) const {
3109#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3110 printf("extract partidxbeg = %d, local_subpartidx = %d, npacks = %d, vbeg = %d;\n", partidxbeg, local_subpartidx, npacks, vbeg);
3111#endif
3112 using tlb = BlockHelperDetails::TpetraLittleBlock<Tpetra::Impl::BlockCrsMatrixLittleBlockArrayLayout>;
3113 local_ordinal_type kfs_vals[internal_vector_length] = {};
3114 local_ordinal_type ri0_vals[internal_vector_length] = {};
3115 local_ordinal_type nrows_vals[internal_vector_length] = {};
3116
3117 const size_type kps = pack_td_ptr(partidxbeg, local_subpartidx);
3118 for (local_ordinal_type v = vbeg, vi = 0; v < npacks && vi < internal_vector_length; ++v, ++vi) {
3119 kfs_vals[vi] = flat_td_ptr(partidxbeg + vi, local_subpartidx);
3120 ri0_vals[vi] = partptr_sub(pack_td_ptr.extent(0) * local_subpartidx + partidxbeg + vi, 0);
3121 nrows_vals[vi] = partptr_sub(pack_td_ptr.extent(0) * local_subpartidx + partidxbeg + vi, 1) - ri0_vals[vi];
3122#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3123 printf("kfs_vals[%d] = %d;\n", vi, kfs_vals[vi]);
3124 printf("ri0_vals[%d] = %d;\n", vi, ri0_vals[vi]);
3125 printf("nrows_vals[%d] = %d;\n", vi, nrows_vals[vi]);
3126#endif
3127 }
3128
3129 local_ordinal_type j_vals[internal_vector_length] = {};
3130
3131 local_ordinal_type tr_min = 0;
3132 local_ordinal_type tr_max = nrows_vals[0];
3133 if (local_subpartidx % 2 == 1) {
3134 tr_min -= 1;
3135 tr_max += 1;
3136 }
3137#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3138 printf("tr_min = %d and tr_max = %d;\n", tr_min, tr_max);
3139#endif
3140 for (local_ordinal_type tr = tr_min; tr < tr_max; ++tr) {
3141 for (local_ordinal_type v = vbeg, vi = 0; v < npacks && vi < internal_vector_length; ++v, ++vi) {
3142 const local_ordinal_type nrows = (local_subpartidx % 2 == 0 ? nrows_vals[vi] : nrows_vals[vi]);
3143 if ((local_subpartidx % 2 == 0 && tr < nrows) || (local_subpartidx % 2 == 1 && tr < nrows + 1)) {
3144 auto &j = j_vals[vi];
3145 const local_ordinal_type kfs = kfs_vals[vi];
3146 const local_ordinal_type ri0 = ri0_vals[vi];
3147 local_ordinal_type lbeg, lend;
3148 if (local_subpartidx % 2 == 0) {
3149 lbeg = (tr == tr_min ? 1 : 0);
3150 lend = (tr == nrows - 1 ? 2 : 3);
3151 } else {
3152 lbeg = 0;
3153 lend = 3;
3154 if (tr == tr_min) {
3155 lbeg = 1;
3156 lend = 2;
3157 } else if (tr == nrows) {
3158 lbeg = 0;
3159 lend = 1;
3160 }
3161 }
3162 if (hasBlockCrsMatrix) {
3163 for (local_ordinal_type l = lbeg; l < lend; ++l, ++j) {
3164 const size_type Aj = A_block_rowptr(lclrow(ri0 + tr)) + A_colindsub(kfs + j);
3165 const impl_scalar_type *block = &A_values(Aj * blocksize_square);
3166 const size_type pi = kps + j;
3167#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3168 printf("Extract pi = %ld, ri0 + tr = %d, kfs + j = %d, tr = %d, lbeg = %d, lend = %d, l = %d\n", pi, ri0 + tr, kfs + j, tr, lbeg, lend, l);
3169#endif
3170 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize),
3171 [&](const local_ordinal_type &ii) {
3172 for (local_ordinal_type jj = 0; jj < blocksize; ++jj) {
3173 scalar_values(pi, ii, jj, v) = static_cast<btdm_scalar_type>(block[tlb::getFlatIndex(ii, jj, blocksize)]);
3174 }
3175 });
3176 }
3177 } else {
3178 for (local_ordinal_type l = lbeg; l < lend; ++l, ++j) {
3179 const size_type Aj_c = A_colindsub(kfs + j);
3180 const size_type pi = kps + j;
3181 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize),
3182 [&](const local_ordinal_type &ii) {
3183 auto point_row_offset = A_point_rowptr(lclrow(ri0 + tr) * blocksize + ii);
3184 for (local_ordinal_type jj = 0; jj < blocksize; ++jj) {
3185 scalar_values(pi, ii, jj, v) = A_values(point_row_offset + Aj_c * blocksize + jj);
3186 }
3187 });
3188 }
3189 }
3190 }
3191 }
3192 }
3193 }
3194
3195 template <typename AAViewType,
3196 typename WWViewType>
3197 KOKKOS_INLINE_FUNCTION void
3198 factorize_subline(const member_type &member,
3199 const local_ordinal_type &i0,
3200 const local_ordinal_type &nrows,
3201 const local_ordinal_type &v,
3202 const AAViewType &AA,
3203 const WWViewType &WW) const {
3204 typedef ExtractAndFactorizeTridiagsDefaultModeAndAlgo<typename execution_space::memory_space> default_mode_and_algo_type;
3205
3206 typedef typename default_mode_and_algo_type::mode_type default_mode_type;
3207 typedef typename default_mode_and_algo_type::algo_type default_algo_type;
3208
3209 // constant
3210 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
3211
3212#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3213 printf("i0 = %d, nrows = %d, v = %d, AA.extent(0) = %ld;\n", i0, nrows, v, AA.extent(0));
3214#endif
3215
3216 // subview pattern
3217 auto A = Kokkos::subview(AA, i0, Kokkos::ALL(), Kokkos::ALL(), v);
3218 KB::LU<member_type,
3219 default_mode_type, KB::Algo::LU::Unblocked>::invoke(member, A, tiny);
3220
3221 if (nrows > 1) {
3222 auto B = A;
3223 auto C = A;
3224 local_ordinal_type i = i0;
3225 for (local_ordinal_type tr = 1; tr < nrows; ++tr, i += 3) {
3226#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3227 printf("tr = %d, i = %d;\n", tr, i);
3228#endif
3229 B.assign_data(&AA(i + 1, 0, 0, v));
3230 KB::Trsm<member_type,
3231 KB::Side::Left, KB::Uplo::Lower, KB::Trans::NoTranspose, KB::Diag::Unit,
3232 default_mode_type, default_algo_type>::invoke(member, one, A, B);
3233 C.assign_data(&AA(i + 2, 0, 0, v));
3234 KB::Trsm<member_type,
3235 KB::Side::Right, KB::Uplo::Upper, KB::Trans::NoTranspose, KB::Diag::NonUnit,
3236 default_mode_type, default_algo_type>::invoke(member, one, A, C);
3237 A.assign_data(&AA(i + 3, 0, 0, v));
3238
3239 member.team_barrier();
3240 KB::Gemm<member_type,
3241 KB::Trans::NoTranspose, KB::Trans::NoTranspose,
3242 default_mode_type, default_algo_type>::invoke(member, -one, C, B, one, A);
3243 KB::LU<member_type,
3244 default_mode_type, KB::Algo::LU::Unblocked>::invoke(member, A, tiny);
3245 }
3246 } else {
3247 // for block jacobi invert a matrix here
3248 auto W = Kokkos::subview(WW, Kokkos::ALL(), Kokkos::ALL(), v);
3249 KB::Copy<member_type, KB::Trans::NoTranspose, default_mode_type>::invoke(member, A, W);
3250 KB::SetIdentity<member_type, default_mode_type>::invoke(member, A);
3251 member.team_barrier();
3252 KB::Trsm<member_type,
3253 KB::Side::Left, KB::Uplo::Lower, KB::Trans::NoTranspose, KB::Diag::Unit,
3254 default_mode_type, default_algo_type>::invoke(member, one, W, A);
3255 KB::Trsm<member_type,
3256 KB::Side::Left, KB::Uplo::Upper, KB::Trans::NoTranspose, KB::Diag::NonUnit,
3257 default_mode_type, default_algo_type>::invoke(member, one, W, A);
3258 }
3259 }
3260
3261 public:
3262 struct ExtractAndFactorizeSubLineTag {};
3263 struct ExtractAndFactorizeFusedJacobiTag {};
3264 struct ExtractBCDTag {};
3265 struct ComputeETag {};
3266 struct ComputeSchurTag {};
3267 struct FactorizeSchurTag {};
3268
3269 KOKKOS_INLINE_FUNCTION
3270 void
3271 operator()(const ExtractAndFactorizeSubLineTag &, const member_type &member) const {
3272 // btdm is packed and sorted from largest one
3273 const local_ordinal_type packidx = packindices_sub(member.league_rank());
3274
3275 const local_ordinal_type subpartidx = packptr_sub(packidx);
3276 const local_ordinal_type n_parts = part2packrowidx0_sub.extent(0);
3277 const local_ordinal_type local_subpartidx = subpartidx / n_parts;
3278 const local_ordinal_type partidx = subpartidx % n_parts;
3279
3280 const local_ordinal_type npacks = packptr_sub(packidx + 1) - subpartidx;
3281 const local_ordinal_type i0 = pack_td_ptr(partidx, local_subpartidx);
3282 const local_ordinal_type nrows = partptr_sub(subpartidx, 1) - partptr_sub(subpartidx, 0);
3283
3284 internal_vector_scratch_type_3d_view
3285 WW(member.team_scratch(ScratchLevel), blocksize, blocksize, vector_loop_size);
3286
3287#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3288 printf("rank = %d, i0 = %d, npacks = %d, nrows = %d, packidx = %d, subpartidx = %d, partidx = %d, local_subpartidx = %d;\n", member.league_rank(), i0, npacks, nrows, packidx, subpartidx, partidx, local_subpartidx);
3289 printf("vector_loop_size = %d\n", vector_loop_size);
3290#endif
3291
3292 if (vector_loop_size == 1) {
3293 extract(partidx, local_subpartidx, npacks);
3294 factorize_subline(member, i0, nrows, 0, internal_vector_values, WW);
3295 } else {
3296 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size),
3297 [&](const local_ordinal_type &v) {
3298 const local_ordinal_type vbeg = v * internal_vector_length;
3299#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3300 printf("i0 = %d, npacks = %d, vbeg = %d;\n", i0, npacks, vbeg);
3301#endif
3302 if (vbeg < npacks)
3303 extract(member, partidx + vbeg, local_subpartidx, npacks, vbeg);
3304 // this is not safe if vector loop size is different from vector size of
3305 // the team policy. we always make sure this when constructing the team policy
3306 member.team_barrier();
3307 factorize_subline(member, i0, nrows, v, internal_vector_values, WW);
3308 });
3309 }
3310 }
3311
3312 KOKKOS_INLINE_FUNCTION
3313 void
3314 operator()(const ExtractAndFactorizeFusedJacobiTag &, const member_type &member) const {
3315 using default_mode_and_algo_type = ExtractAndFactorizeTridiagsDefaultModeAndAlgo<typename execution_space::memory_space>;
3316 using default_mode_type = typename default_mode_and_algo_type::mode_type;
3317 using default_algo_type = typename default_mode_and_algo_type::algo_type;
3318 // When fused block Jacobi can be used, the mapping between local rows and parts is trivial (i <-> i)
3319 // We can simply pull the diagonal entry from A into d_inv
3320 btdm_scalar_scratch_type_3d_view WW1(member.team_scratch(ScratchLevel), half_vector_length, blocksize, blocksize);
3321 btdm_scalar_scratch_type_3d_view WW2(member.team_scratch(ScratchLevel), half_vector_length, blocksize, blocksize);
3322 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
3323 const local_ordinal_type nrows = lclrow.extent(0);
3324 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, half_vector_length),
3325 [&](const local_ordinal_type &v) {
3326 local_ordinal_type row = member.league_rank() * half_vector_length + v;
3327 // diagEntry has index of diagonal within row
3328 auto W1 = Kokkos::subview(WW1, v, Kokkos::ALL(), Kokkos::ALL());
3329 auto W2 = Kokkos::subview(WW2, v, Kokkos::ALL(), Kokkos::ALL());
3330 if (row < nrows) {
3331 // View the diagonal block of A in row as 2D row-major
3332 const impl_scalar_type *A_diag = A_values.data() + diag_offsets(row);
3333 // Copy the diag into scratch slice W1
3334 // (copying elements directly is better than KokkosBatched copy)
3335 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize * blocksize),
3336 [&](int i) {
3337 W1.data()[i] = A_diag[i];
3338 });
3339 // and set W2 to identity in preparation to invert with 2 x Trsm
3340 KB::SetIdentity<member_type, default_mode_type>::invoke(member, W2);
3341 } else {
3342 // if this vector lane has no block to invert, then set W1 to identity
3343 // so that LU still has a matrix to work on. LU uses team barriers so
3344 // having some lanes run it and some not will deadlock.
3345 KB::SetIdentity<member_type, default_mode_type>::invoke(member, W1);
3346 }
3347 member.team_barrier();
3348 // LU factorize in-place
3349 KB::LU<member_type, default_mode_type, KB::Algo::LU::Unblocked>::invoke(member, W1, tiny);
3350 member.team_barrier();
3351 KB::Trsm<member_type,
3352 KB::Side::Left, KB::Uplo::Lower, KB::Trans::NoTranspose, KB::Diag::Unit,
3353 default_mode_type, default_algo_type>::invoke(member, one, W1, W2);
3354 KB::Trsm<member_type,
3355 KB::Side::Left, KB::Uplo::Upper, KB::Trans::NoTranspose, KB::Diag::NonUnit,
3356 default_mode_type, default_algo_type>::invoke(member, one, W1, W2);
3357 member.team_barrier();
3358 if (row < nrows) {
3359 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize * blocksize),
3360 [&](int i) {
3361 auto d_inv_block = &d_inv(row, 0, 0);
3362 d_inv_block[i] = W2.data()[i];
3363 });
3364 }
3365 });
3366 }
3367
3368 KOKKOS_INLINE_FUNCTION
3369 void
3370 operator()(const ExtractBCDTag &, const member_type &member) const {
3371 // btdm is packed and sorted from largest one
3372 const local_ordinal_type packindices_schur_i = member.league_rank() % packindices_schur.extent(0);
3373 const local_ordinal_type packindices_schur_j = member.league_rank() / packindices_schur.extent(0);
3374 const local_ordinal_type packidx = packindices_schur(packindices_schur_i, packindices_schur_j);
3375
3376 const local_ordinal_type subpartidx = packptr_sub(packidx);
3377 const local_ordinal_type n_parts = part2packrowidx0_sub.extent(0);
3378 const local_ordinal_type local_subpartidx = subpartidx / n_parts;
3379 const local_ordinal_type partidx = subpartidx % n_parts;
3380
3381 const local_ordinal_type npacks = packptr_sub(packidx + 1) - subpartidx;
3382 // const local_ordinal_type i0 = pack_td_ptr(partidx,local_subpartidx);
3383 // const local_ordinal_type nrows = partptr_sub(subpartidx,1) - partptr_sub(subpartidx,0);
3384
3385 if (vector_loop_size == 1) {
3386 extract(partidx, local_subpartidx, npacks);
3387 } else {
3388 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size),
3389 [&](const local_ordinal_type &v) {
3390 const local_ordinal_type vbeg = v * internal_vector_length;
3391#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3392 const local_ordinal_type i0 = pack_td_ptr(partidx, local_subpartidx);
3393 printf("i0 = %d, npacks = %d, vbeg = %d;\n", i0, npacks, vbeg);
3394#endif
3395 if (vbeg < npacks)
3396 extract(member, partidx + vbeg, local_subpartidx, npacks, vbeg);
3397 });
3398 }
3399
3400 member.team_barrier();
3401
3402 const size_type kps1 = pack_td_ptr(partidx, local_subpartidx);
3403 const size_type kps2 = pack_td_ptr(partidx, local_subpartidx + 1) - 1;
3404
3405 const local_ordinal_type r1 = part2packrowidx0_sub(partidx, local_subpartidx) - 1;
3406 const local_ordinal_type r2 = part2packrowidx0_sub(partidx, local_subpartidx) + 2;
3407
3408#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3409 printf("Copy for Schur complement part id = %d from kps1 = %ld to r1 = %d and from kps2 = %ld to r2 = %d partidx = %d local_subpartidx = %d;\n", packidx, kps1, r1, kps2, r2, partidx, local_subpartidx);
3410#endif
3411
3412 // Need to copy D to e_internal_vector_values.
3413 copy3DView<local_ordinal_type>(member, Kokkos::subview(e_internal_vector_values, 0, r1, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()),
3414 Kokkos::subview(internal_vector_values, kps1, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()));
3415
3416 copy3DView<local_ordinal_type>(member, Kokkos::subview(e_internal_vector_values, 1, r2, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()),
3417 Kokkos::subview(internal_vector_values, kps2, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()));
3418 }
3419
3420 KOKKOS_INLINE_FUNCTION
3421 void
3422 operator()(const ComputeETag &, const member_type &member) const {
3423 // btdm is packed and sorted from largest one
3424 const local_ordinal_type packidx = packindices_sub(member.league_rank());
3425
3426 const local_ordinal_type subpartidx = packptr_sub(packidx);
3427 const local_ordinal_type n_parts = part2packrowidx0_sub.extent(0);
3428 const local_ordinal_type local_subpartidx = subpartidx / n_parts;
3429 const local_ordinal_type partidx = subpartidx % n_parts;
3430
3431 const local_ordinal_type npacks = packptr_sub(packidx + 1) - subpartidx;
3432 const local_ordinal_type i0 = pack_td_ptr(partidx, local_subpartidx);
3433 const local_ordinal_type r0 = part2packrowidx0_sub(partidx, local_subpartidx);
3434 const local_ordinal_type nrows = partptr_sub(subpartidx, 1) - partptr_sub(subpartidx, 0);
3435 const local_ordinal_type num_vectors = blocksize;
3436
3437 (void)npacks;
3438
3439 internal_vector_scratch_type_3d_view
3440 WW(member.team_scratch(ScratchLevel), blocksize, num_vectors, vector_loop_size);
3441 if (local_subpartidx == 0) {
3442 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
3443 solveMultiVector<impl_type, internal_vector_scratch_type_3d_view>(member, blocksize, i0, r0, nrows, v, internal_vector_values, Kokkos::subview(e_internal_vector_values, 0, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()), WW, true);
3444 });
3445 } else if (local_subpartidx == (local_ordinal_type)part2packrowidx0_sub.extent(1) - 2) {
3446 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
3447 solveMultiVector<impl_type, internal_vector_scratch_type_3d_view>(member, blocksize, i0, r0, nrows, v, internal_vector_values, Kokkos::subview(e_internal_vector_values, 1, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()), WW);
3448 });
3449 } else {
3450 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
3451 solveMultiVector<impl_type, internal_vector_scratch_type_3d_view>(member, blocksize, i0, r0, nrows, v, internal_vector_values, Kokkos::subview(e_internal_vector_values, 0, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()), WW, true);
3452 solveMultiVector<impl_type, internal_vector_scratch_type_3d_view>(member, blocksize, i0, r0, nrows, v, internal_vector_values, Kokkos::subview(e_internal_vector_values, 1, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()), WW);
3453 });
3454 }
3455 }
3456
3457 KOKKOS_INLINE_FUNCTION
3458 void
3459 operator()(const ComputeSchurTag &, const member_type &member) const {
3460 // btdm is packed and sorted from largest one
3461 const local_ordinal_type packindices_schur_i = member.league_rank() % packindices_schur.extent(0);
3462 const local_ordinal_type packindices_schur_j = member.league_rank() / packindices_schur.extent(0);
3463 const local_ordinal_type packidx = packindices_schur(packindices_schur_i, packindices_schur_j);
3464
3465 const local_ordinal_type subpartidx = packptr_sub(packidx);
3466 const local_ordinal_type n_parts = part2packrowidx0_sub.extent(0);
3467 const local_ordinal_type local_subpartidx = subpartidx / n_parts;
3468 const local_ordinal_type partidx = subpartidx % n_parts;
3469
3470 // const local_ordinal_type npacks = packptr_sub(packidx+1) - subpartidx;
3471 const local_ordinal_type i0 = pack_td_ptr(partidx, local_subpartidx);
3472 // const local_ordinal_type r0 = part2packrowidx0_sub(partidx,local_subpartidx);
3473 // const local_ordinal_type nrows = partptr_sub(subpartidx,1) - partptr_sub(subpartidx,0);
3474
3475 // Compute S = D - C E
3476
3477 const local_ordinal_type local_subpartidx_schur = (local_subpartidx - 1) / 2;
3478 const local_ordinal_type i0_schur = local_subpartidx_schur == 0 ? pack_td_ptr_schur(partidx, local_subpartidx_schur) : pack_td_ptr_schur(partidx, local_subpartidx_schur) + 1;
3479 const local_ordinal_type i0_offset = local_subpartidx_schur == 0 ? i0 + 2 : i0 + 2;
3480
3481 for (local_ordinal_type i = 0; i < 4; ++i) { // pack_td_ptr_schur(partidx,local_subpartidx_schur+1)-i0_schur
3482 copy3DView<local_ordinal_type>(member, Kokkos::subview(internal_vector_values_schur, i0_schur + i, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()),
3483 Kokkos::subview(internal_vector_values, i0_offset + i, Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL()));
3484 }
3485
3486 member.team_barrier();
3487
3488 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
3489
3490 const size_type c_kps1 = pack_td_ptr(partidx, local_subpartidx) + 1;
3491 const size_type c_kps2 = pack_td_ptr(partidx, local_subpartidx + 1) - 2;
3492
3493 const local_ordinal_type e_r1 = part2packrowidx0_sub(partidx, local_subpartidx) - 1;
3494 const local_ordinal_type e_r2 = part2packrowidx0_sub(partidx, local_subpartidx) + 2;
3495
3496 typedef ExtractAndFactorizeTridiagsDefaultModeAndAlgo<typename execution_space::memory_space> default_mode_and_algo_type;
3497
3498 typedef typename default_mode_and_algo_type::mode_type default_mode_type;
3499 typedef typename default_mode_and_algo_type::algo_type default_algo_type;
3500
3501 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
3502 for (size_type i = 0; i < pack_td_ptr_schur(partidx, local_subpartidx_schur + 1) - pack_td_ptr_schur(partidx, local_subpartidx_schur); ++i) {
3503 local_ordinal_type e_r, e_c, c_kps;
3504
3505 if (local_subpartidx_schur == 0) {
3506 if (i == 0) {
3507 e_r = e_r1;
3508 e_c = 0;
3509 c_kps = c_kps1;
3510 } else if (i == 3) {
3511 e_r = e_r2;
3512 e_c = 1;
3513 c_kps = c_kps2;
3514 } else if (i == 4) {
3515 e_r = e_r2;
3516 e_c = 0;
3517 c_kps = c_kps2;
3518 } else {
3519 continue;
3520 }
3521 } else {
3522 if (i == 0) {
3523 e_r = e_r1;
3524 e_c = 1;
3525 c_kps = c_kps1;
3526 } else if (i == 1) {
3527 e_r = e_r1;
3528 e_c = 0;
3529 c_kps = c_kps1;
3530 } else if (i == 4) {
3531 e_r = e_r2;
3532 e_c = 1;
3533 c_kps = c_kps2;
3534 } else if (i == 5) {
3535 e_r = e_r2;
3536 e_c = 0;
3537 c_kps = c_kps2;
3538 } else {
3539 continue;
3540 }
3541 }
3542
3543 auto S = Kokkos::subview(internal_vector_values_schur, pack_td_ptr_schur(partidx, local_subpartidx_schur) + i, Kokkos::ALL(), Kokkos::ALL(), v);
3544 auto C = Kokkos::subview(internal_vector_values, c_kps, Kokkos::ALL(), Kokkos::ALL(), v);
3545 auto E = Kokkos::subview(e_internal_vector_values, e_c, e_r, Kokkos::ALL(), Kokkos::ALL(), v);
3546 KB::Gemm<member_type,
3547 KB::Trans::NoTranspose, KB::Trans::NoTranspose,
3548 default_mode_type, default_algo_type>::invoke(member, -one, C, E, one, S);
3549 }
3550 });
3551 }
3552
3553 KOKKOS_INLINE_FUNCTION
3554 void
3555 operator()(const FactorizeSchurTag &, const member_type &member) const {
3556 const local_ordinal_type packidx = packindices_schur(member.league_rank(), 0);
3557
3558 const local_ordinal_type subpartidx = packptr_sub(packidx);
3559
3560 const local_ordinal_type n_parts = part2packrowidx0_sub.extent(0);
3561 const local_ordinal_type partidx = subpartidx % n_parts;
3562
3563 const local_ordinal_type i0 = pack_td_ptr_schur(partidx, 0);
3564 const local_ordinal_type nrows = 2 * (pack_td_ptr_schur.extent(1) - 1);
3565
3566 internal_vector_scratch_type_3d_view
3567 WW(member.team_scratch(ScratchLevel), blocksize, blocksize, vector_loop_size);
3568
3569#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3570 printf("FactorizeSchurTag rank = %d, i0 = %d, nrows = %d, vector_loop_size = %d;\n", member.league_rank(), i0, nrows, vector_loop_size);
3571#endif
3572
3573 if (vector_loop_size == 1) {
3574 factorize_subline(member, i0, nrows, 0, internal_vector_values_schur, WW);
3575 } else {
3576 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size),
3577 [&](const local_ordinal_type &v) {
3578 factorize_subline(member, i0, nrows, v, internal_vector_values_schur, WW);
3579 });
3580 }
3581 }
3582
3583 void run() {
3584 IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_BEGIN;
3585 const local_ordinal_type team_size =
3586 ExtractAndFactorizeTridiagsDefaultModeAndAlgo<typename execution_space::memory_space>::
3587 recommended_team_size(blocksize, vector_length, internal_vector_length);
3588 const local_ordinal_type per_team_scratch = internal_vector_scratch_type_3d_view::
3589 shmem_size(blocksize, blocksize, vector_loop_size);
3590
3591 {
3592#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3593 printf("Start ExtractAndFactorizeSubLineTag\n");
3594#endif
3595 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::NumericPhase::ExtractAndFactorizeSubLineTag", ExtractAndFactorizeSubLineTag0);
3596 Kokkos::TeamPolicy<execution_space, ExtractAndFactorizeSubLineTag>
3597 policy(packindices_sub.extent(0), team_size, vector_loop_size);
3598
3599 const local_ordinal_type n_parts = part2packrowidx0_sub.extent(0);
3600 writeBTDValuesToFile(n_parts, scalar_values, "before.mm");
3601
3602 policy.set_scratch_size(ScratchLevel, Kokkos::PerTeam(per_team_scratch));
3603 Kokkos::parallel_for("ExtractAndFactorize::TeamPolicy::run<ExtractAndFactorizeSubLineTag>",
3604 policy, *this);
3605 execution_space().fence();
3606
3607 writeBTDValuesToFile(n_parts, scalar_values, "after.mm");
3608#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3609 printf("End ExtractAndFactorizeSubLineTag\n");
3610#endif
3611 }
3612
3613 if (packindices_schur.extent(1) > 0) {
3614 {
3615#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3616 printf("Start ExtractBCDTag\n");
3617#endif
3618 Kokkos::deep_copy(e_scalar_values, KokkosKernels::ArithTraits<btdm_magnitude_type>::zero());
3619 Kokkos::deep_copy(scalar_values_schur, KokkosKernels::ArithTraits<btdm_magnitude_type>::zero());
3620
3621 write5DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), e_scalar_values, "e_scalar_values_before_extract.mm");
3622
3623 {
3624 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::NumericPhase::ExtractBCDTag", ExtractBCDTag0);
3625 Kokkos::TeamPolicy<execution_space, ExtractBCDTag>
3626 policy(packindices_schur.extent(0) * packindices_schur.extent(1), team_size, vector_loop_size);
3627
3628 policy.set_scratch_size(ScratchLevel, Kokkos::PerTeam(per_team_scratch));
3629 Kokkos::parallel_for("ExtractAndFactorize::TeamPolicy::run<ExtractBCDTag>",
3630 policy, *this);
3631 execution_space().fence();
3632 }
3633
3634#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3635 printf("End ExtractBCDTag\n");
3636#endif
3637 writeBTDValuesToFile(part2packrowidx0_sub.extent(0), scalar_values, "after_extraction_of_BCD.mm");
3638#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3639 printf("Start ComputeETag\n");
3640#endif
3641 write5DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), e_scalar_values, "e_scalar_values_after_extract.mm");
3642 {
3643 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::NumericPhase::ComputeETag", ComputeETag0);
3644 Kokkos::TeamPolicy<execution_space, ComputeETag>
3645 policy(packindices_sub.extent(0), team_size, vector_loop_size);
3646
3647 policy.set_scratch_size(ScratchLevel, Kokkos::PerTeam(per_team_scratch));
3648 Kokkos::parallel_for("ExtractAndFactorize::TeamPolicy::run<ComputeETag>",
3649 policy, *this);
3650 execution_space().fence();
3651 }
3652 write5DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), e_scalar_values, "e_scalar_values_after_compute.mm");
3653
3654#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3655 printf("End ComputeETag\n");
3656#endif
3657 }
3658
3659 {
3660#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3661 printf("Start ComputeSchurTag\n");
3662#endif
3663 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::NumericPhase::ComputeSchurTag", ComputeSchurTag0);
3664 writeBTDValuesToFile(part2packrowidx0_sub.extent(0), scalar_values_schur, "before_schur.mm");
3665 Kokkos::TeamPolicy<execution_space, ComputeSchurTag>
3666 policy(packindices_schur.extent(0) * packindices_schur.extent(1), team_size, vector_loop_size);
3667
3668 Kokkos::parallel_for("ExtractAndFactorize::TeamPolicy::run<ComputeSchurTag>",
3669 policy, *this);
3670 writeBTDValuesToFile(part2packrowidx0_sub.extent(0), scalar_values_schur, "after_schur.mm");
3671 execution_space().fence();
3672#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3673 printf("End ComputeSchurTag\n");
3674#endif
3675 }
3676
3677 {
3678#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3679 printf("Start FactorizeSchurTag\n");
3680#endif
3681 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::NumericPhase::FactorizeSchurTag", FactorizeSchurTag0);
3682 Kokkos::TeamPolicy<execution_space, FactorizeSchurTag>
3683 policy(packindices_schur.extent(0), team_size, vector_loop_size);
3684 policy.set_scratch_size(ScratchLevel, Kokkos::PerTeam(per_team_scratch));
3685 Kokkos::parallel_for("ExtractAndFactorize::TeamPolicy::run<FactorizeSchurTag>",
3686 policy, *this);
3687 execution_space().fence();
3688 writeBTDValuesToFile(part2packrowidx0_sub.extent(0), scalar_values_schur, "after_factor_schur.mm");
3689#ifdef IFPACK2_BLOCKTRIDICONTAINER_USE_PRINTF
3690 printf("End FactorizeSchurTag\n");
3691#endif
3692 }
3693 }
3694
3695 IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_END;
3696 }
3697
3698 void run_fused_jacobi() {
3699 IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_BEGIN;
3700 const local_ordinal_type team_size =
3701 ExtractAndFactorizeTridiagsDefaultModeAndAlgo<typename execution_space::memory_space>::
3702 recommended_team_size(blocksize, half_vector_length, 1);
3703 const local_ordinal_type per_team_scratch =
3704 btdm_scalar_scratch_type_3d_view::shmem_size(blocksize, blocksize, 2 * half_vector_length);
3705 {
3706 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::NumericPhase::ExtractAndFactorizeFusedJacobi", ExtractAndFactorizeFusedJacobiTag);
3707 Kokkos::TeamPolicy<execution_space, ExtractAndFactorizeFusedJacobiTag>
3708 policy((lclrow.extent(0) + half_vector_length - 1) / half_vector_length, team_size, half_vector_length);
3709
3710 policy.set_scratch_size(ScratchLevel, Kokkos::PerTeam(per_team_scratch));
3711 Kokkos::parallel_for("ExtractAndFactorize::TeamPolicy::run<ExtractAndFactorizeFusedJacobiTag>",
3712 policy, *this);
3713 }
3714 IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_END;
3715 }
3716};
3717
3721template <typename MatrixType>
3722void performNumericPhase(const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_row_matrix_type> &A,
3723 const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_crs_graph_type> &G,
3724 const BlockHelperDetails::PartInterface<MatrixType> &interf,
3725 BlockTridiags<MatrixType> &btdm,
3726 const typename BlockHelperDetails::ImplType<MatrixType>::magnitude_type tiny,
3727 bool use_fused_jacobi) {
3729 using execution_space = typename impl_type::execution_space;
3730 using team_policy_type = Kokkos::TeamPolicy<execution_space>;
3731 using internal_vector_scratch_type_3d_view = Scratch<typename impl_type::internal_vector_type_3d_view>;
3732 using btdm_scalar_scratch_type_3d_view = Scratch<typename impl_type::btdm_scalar_type_3d_view>;
3733
3734 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::NumericPhase", NumericPhase);
3735
3736 int blocksize = btdm.values.extent(1);
3737 // Both Kokkos policy vector length and SIMD type vector length are hardcoded in KokkosBatched.
3738 // For large block sizes, have to fall back to level 1 scratch.
3739 int scratch_required;
3740 if (!use_fused_jacobi) {
3741 // General path scratch requirement
3742 scratch_required = internal_vector_scratch_type_3d_view::shmem_size(blocksize, blocksize, impl_type::vector_length / impl_type::internal_vector_length);
3743 } else {
3744 // Block Jacobi scratch requirement: measured in scalars, and uses twice as much (in bytes) per vector lane as the general path.
3745 scratch_required = btdm_scalar_scratch_type_3d_view::shmem_size(blocksize, blocksize, 2 * impl_type::half_vector_length);
3746 }
3747
3748 int max_scratch = team_policy_type::scratch_size_max(0);
3749
3750 if (scratch_required < max_scratch) {
3751 // Can use level 0 scratch
3752 ExtractAndFactorizeTridiags<MatrixType, 0> function(btdm, interf, A, G, tiny);
3753 if (!use_fused_jacobi)
3754 function.run();
3755 else
3756 function.run_fused_jacobi();
3757 } else {
3758 // Not enough level 0 scratch, so fall back to level 1
3759 ExtractAndFactorizeTridiags<MatrixType, 1> function(btdm, interf, A, G, tiny);
3760 if (!use_fused_jacobi)
3761 function.run();
3762 else
3763 function.run_fused_jacobi();
3764 }
3765 IFPACK2_BLOCKHELPER_TIMER_FENCE(typename BlockHelperDetails::ImplType<MatrixType>::execution_space)
3766}
3767
3771template <typename MatrixType>
3772struct MultiVectorConverter {
3773 public:
3775 using execution_space = typename impl_type::execution_space;
3776 using memory_space = typename impl_type::memory_space;
3777
3778 using local_ordinal_type = typename impl_type::local_ordinal_type;
3779 using impl_scalar_type = typename impl_type::impl_scalar_type;
3780 using btdm_scalar_type = typename impl_type::btdm_scalar_type;
3781 using tpetra_multivector_type = typename impl_type::tpetra_multivector_type;
3782 using local_ordinal_type_1d_view = typename impl_type::local_ordinal_type_1d_view;
3783 using vector_type_3d_view = typename impl_type::vector_type_3d_view;
3784 using impl_scalar_type_2d_view_tpetra = typename impl_type::impl_scalar_type_2d_view_tpetra;
3785 using const_impl_scalar_type_2d_view_tpetra = typename impl_scalar_type_2d_view_tpetra::const_type;
3786 static constexpr int vector_length = impl_type::vector_length;
3787
3788 using member_type = typename Kokkos::TeamPolicy<execution_space>::member_type;
3789
3790 private:
3791 // part interface
3792 const ConstUnmanaged<local_ordinal_type_1d_view> partptr;
3793 const ConstUnmanaged<local_ordinal_type_1d_view> packptr;
3794 const ConstUnmanaged<local_ordinal_type_1d_view> part2packrowidx0;
3795 const ConstUnmanaged<local_ordinal_type_1d_view> part2rowidx0;
3796 const ConstUnmanaged<local_ordinal_type_1d_view> lclrow;
3797 const local_ordinal_type blocksize;
3798 const local_ordinal_type num_vectors;
3799
3800 // packed multivector output (or input)
3801 vector_type_3d_view packed_multivector;
3802 const_impl_scalar_type_2d_view_tpetra scalar_multivector;
3803
3804 template <typename TagType>
3805 KOKKOS_INLINE_FUNCTION void copy_multivectors(const local_ordinal_type &j,
3806 const local_ordinal_type &vi,
3807 const local_ordinal_type &pri,
3808 const local_ordinal_type &ri0) const {
3809 for (local_ordinal_type col = 0; col < num_vectors; ++col)
3810 for (local_ordinal_type i = 0; i < blocksize; ++i)
3811 packed_multivector(pri, i, col)[vi] = static_cast<btdm_scalar_type>(scalar_multivector(blocksize * lclrow(ri0 + j) + i, col));
3812 }
3813
3814 public:
3815 MultiVectorConverter(const BlockHelperDetails::PartInterface<MatrixType> &interf,
3816 const vector_type_3d_view &pmv)
3817 : partptr(interf.partptr)
3818 , packptr(interf.packptr)
3819 , part2packrowidx0(interf.part2packrowidx0)
3820 , part2rowidx0(interf.part2rowidx0)
3821 , lclrow(interf.lclrow)
3822 , blocksize(pmv.extent(1))
3823 , num_vectors(pmv.extent(2))
3824 , packed_multivector(pmv) {}
3825
3826 // TODO:: modify this routine similar to the team level functions
3827 KOKKOS_INLINE_FUNCTION
3828 void
3829 operator()(const local_ordinal_type &packidx) const {
3830 local_ordinal_type partidx = packptr(packidx);
3831 local_ordinal_type npacks = packptr(packidx + 1) - partidx;
3832 const local_ordinal_type pri0 = part2packrowidx0(partidx);
3833
3834 local_ordinal_type ri0[vector_length] = {};
3835 local_ordinal_type nrows[vector_length] = {};
3836 for (local_ordinal_type v = 0; v < npacks; ++v, ++partidx) {
3837 ri0[v] = part2rowidx0(partidx);
3838 nrows[v] = part2rowidx0(partidx + 1) - ri0[v];
3839 }
3840 for (local_ordinal_type j = 0; j < nrows[0]; ++j) {
3841 local_ordinal_type cnt = 1;
3842 for (; cnt < npacks && j != nrows[cnt]; ++cnt)
3843 ;
3844 npacks = cnt;
3845 const local_ordinal_type pri = pri0 + j;
3846 for (local_ordinal_type col = 0; col < num_vectors; ++col)
3847 for (local_ordinal_type i = 0; i < blocksize; ++i)
3848 for (local_ordinal_type v = 0; v < npacks; ++v)
3849 packed_multivector(pri, i, col)[v] = static_cast<btdm_scalar_type>(scalar_multivector(blocksize * lclrow(ri0[v] + j) + i, col));
3850 }
3851 }
3852
3853 KOKKOS_INLINE_FUNCTION
3854 void
3855 operator()(const member_type &member) const {
3856 const local_ordinal_type packidx = member.league_rank();
3857 const local_ordinal_type partidx_begin = packptr(packidx);
3858 const local_ordinal_type npacks = packptr(packidx + 1) - partidx_begin;
3859 const local_ordinal_type pri0 = part2packrowidx0(partidx_begin);
3860 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, npacks), [&](const local_ordinal_type &v) {
3861 const local_ordinal_type partidx = partidx_begin + v;
3862 const local_ordinal_type ri0 = part2rowidx0(partidx);
3863 const local_ordinal_type nrows = part2rowidx0(partidx + 1) - ri0;
3864
3865 if (nrows == 1) {
3866 const local_ordinal_type pri = pri0;
3867 for (local_ordinal_type col = 0; col < num_vectors; ++col) {
3868 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize), [&](const local_ordinal_type &i) {
3869 packed_multivector(pri, i, col)[v] = static_cast<btdm_scalar_type>(scalar_multivector(blocksize * lclrow(ri0) + i, col));
3870 });
3871 }
3872 } else {
3873 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, nrows), [&](const local_ordinal_type &j) {
3874 const local_ordinal_type pri = pri0 + j;
3875 for (local_ordinal_type col = 0; col < num_vectors; ++col)
3876 for (local_ordinal_type i = 0; i < blocksize; ++i)
3877 packed_multivector(pri, i, col)[v] = static_cast<btdm_scalar_type>(scalar_multivector(blocksize * lclrow(ri0 + j) + i, col));
3878 });
3879 }
3880 });
3881 }
3882
3883 void run(const const_impl_scalar_type_2d_view_tpetra &scalar_multivector_) {
3884 IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_BEGIN;
3885 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::MultiVectorConverter", MultiVectorConverter0);
3886
3887 scalar_multivector = scalar_multivector_;
3888 if constexpr (BlockHelperDetails::is_device<execution_space>::value) {
3889 const local_ordinal_type vl = vector_length;
3890 const Kokkos::TeamPolicy<execution_space> policy(packptr.extent(0) - 1, Kokkos::AUTO(), vl);
3891 Kokkos::parallel_for("MultiVectorConverter::TeamPolicy", policy, *this);
3892 } else {
3893 const Kokkos::RangePolicy<execution_space> policy(0, packptr.extent(0) - 1);
3894 Kokkos::parallel_for("MultiVectorConverter::RangePolicy", policy, *this);
3895 }
3896 IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_END;
3897 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
3898 }
3899};
3900
3904
3905template <>
3906struct SolveTridiagsDefaultModeAndAlgo<Kokkos::HostSpace> {
3907 typedef KB::Mode::Serial mode_type;
3908 typedef KB::Algo::Level2::Unblocked single_vector_algo_type;
3909#if defined(__KOKKOSBATCHED_INTEL_MKL_COMPACT_BATCHED__)
3910 typedef KB::Algo::Level3::CompactMKL multi_vector_algo_type;
3911#else
3912 typedef KB::Algo::Level3::Blocked multi_vector_algo_type;
3913#endif
3914 static int recommended_team_size(const int /* blksize */,
3915 const int /* vector_length */,
3916 const int /* internal_vector_length */) {
3917 return 1;
3918 }
3919};
3920
3921#if defined(KOKKOS_ENABLE_CUDA)
3922static inline int SolveTridiagsRecommendedCudaTeamSize(const int blksize,
3923 const int vector_length,
3924 const int internal_vector_length) {
3925 const int vector_size = vector_length / internal_vector_length;
3926 int total_team_size(0);
3927 if (blksize <= 5)
3928 total_team_size = 32;
3929 else if (blksize <= 9)
3930 total_team_size = 32; // 64
3931 else if (blksize <= 12)
3932 total_team_size = 96;
3933 else if (blksize <= 16)
3934 total_team_size = 128;
3935 else if (blksize <= 20)
3936 total_team_size = 160;
3937 else
3938 total_team_size = 160;
3939 return total_team_size / vector_size;
3940}
3941
3942template <>
3943struct SolveTridiagsDefaultModeAndAlgo<Kokkos::CudaSpace> {
3944 typedef KB::Mode::Team mode_type;
3945 typedef KB::Algo::Level2::Unblocked single_vector_algo_type;
3946 typedef KB::Algo::Level3::Unblocked multi_vector_algo_type;
3947 static int recommended_team_size(const int blksize,
3948 const int vector_length,
3949 const int internal_vector_length) {
3950 return SolveTridiagsRecommendedCudaTeamSize(blksize, vector_length, internal_vector_length);
3951 }
3952};
3953template <>
3954struct SolveTridiagsDefaultModeAndAlgo<Kokkos::CudaUVMSpace> {
3955 typedef KB::Mode::Team mode_type;
3956 typedef KB::Algo::Level2::Unblocked single_vector_algo_type;
3957 typedef KB::Algo::Level3::Unblocked multi_vector_algo_type;
3958 static int recommended_team_size(const int blksize,
3959 const int vector_length,
3960 const int internal_vector_length) {
3961 return SolveTridiagsRecommendedCudaTeamSize(blksize, vector_length, internal_vector_length);
3962 }
3963};
3964#endif
3965
3966#if defined(KOKKOS_ENABLE_HIP)
3967static inline int SolveTridiagsRecommendedHIPTeamSize(const int blksize,
3968 const int vector_length,
3969 const int internal_vector_length) {
3970 const int vector_size = vector_length / internal_vector_length;
3971 int total_team_size(0);
3972 if (blksize <= 5)
3973 total_team_size = 32;
3974 else if (blksize <= 9)
3975 total_team_size = 32; // 64
3976 else if (blksize <= 12)
3977 total_team_size = 96;
3978 else if (blksize <= 16)
3979 total_team_size = 128;
3980 else if (blksize <= 20)
3981 total_team_size = 160;
3982 else
3983 total_team_size = 160;
3984 return total_team_size / vector_size;
3985}
3986
3987template <>
3988struct SolveTridiagsDefaultModeAndAlgo<Kokkos::HIPSpace> {
3989 typedef KB::Mode::Team mode_type;
3990 typedef KB::Algo::Level2::Unblocked single_vector_algo_type;
3991 typedef KB::Algo::Level3::Unblocked multi_vector_algo_type;
3992 static int recommended_team_size(const int blksize,
3993 const int vector_length,
3994 const int internal_vector_length) {
3995 return SolveTridiagsRecommendedHIPTeamSize(blksize, vector_length, internal_vector_length);
3996 }
3997};
3998template <>
3999struct SolveTridiagsDefaultModeAndAlgo<Kokkos::HIPHostPinnedSpace> {
4000 typedef KB::Mode::Team mode_type;
4001 typedef KB::Algo::Level2::Unblocked single_vector_algo_type;
4002 typedef KB::Algo::Level3::Unblocked multi_vector_algo_type;
4003 static int recommended_team_size(const int blksize,
4004 const int vector_length,
4005 const int internal_vector_length) {
4006 return SolveTridiagsRecommendedHIPTeamSize(blksize, vector_length, internal_vector_length);
4007 }
4008};
4009#endif
4010
4011#if defined(KOKKOS_ENABLE_SYCL)
4012static inline int SolveTridiagsRecommendedSYCLTeamSize(const int blksize,
4013 const int vector_length,
4014 const int internal_vector_length) {
4015 const int vector_size = vector_length / internal_vector_length;
4016 int total_team_size(0);
4017 if (blksize <= 5)
4018 total_team_size = 32;
4019 else if (blksize <= 9)
4020 total_team_size = 32; // 64
4021 else if (blksize <= 12)
4022 total_team_size = 96;
4023 else if (blksize <= 16)
4024 total_team_size = 128;
4025 else if (blksize <= 20)
4026 total_team_size = 160;
4027 else
4028 total_team_size = 160;
4029 return total_team_size / vector_size;
4030}
4031
4032template <>
4033struct SolveTridiagsDefaultModeAndAlgo<Kokkos::SYCLSharedUSMSpace> {
4034 typedef KB::Mode::Team mode_type;
4035 typedef KB::Algo::Level2::Unblocked single_vector_algo_type;
4036 typedef KB::Algo::Level3::Unblocked multi_vector_algo_type;
4037 static int recommended_team_size(const int blksize,
4038 const int vector_length,
4039 const int internal_vector_length) {
4040 return SolveTridiagsRecommendedSYCLTeamSize(blksize, vector_length, internal_vector_length);
4041 }
4042};
4043template <>
4044struct SolveTridiagsDefaultModeAndAlgo<Kokkos::SYCLDeviceUSMSpace> {
4045 typedef KB::Mode::Team mode_type;
4046 typedef KB::Algo::Level2::Unblocked single_vector_algo_type;
4047 typedef KB::Algo::Level3::Unblocked multi_vector_algo_type;
4048 static int recommended_team_size(const int blksize,
4049 const int vector_length,
4050 const int internal_vector_length) {
4051 return SolveTridiagsRecommendedSYCLTeamSize(blksize, vector_length, internal_vector_length);
4052 }
4053};
4054#endif
4055
4056template <typename MatrixType>
4057struct SolveTridiags {
4058 public:
4059 using impl_type = BlockHelperDetails::ImplType<MatrixType>;
4060 using execution_space = typename impl_type::execution_space;
4061
4062 using local_ordinal_type = typename impl_type::local_ordinal_type;
4063 using size_type = typename impl_type::size_type;
4064 using impl_scalar_type = typename impl_type::impl_scalar_type;
4065 using magnitude_type = typename impl_type::magnitude_type;
4066 using btdm_scalar_type = typename impl_type::btdm_scalar_type;
4067 using btdm_magnitude_type = typename impl_type::btdm_magnitude_type;
4069 using local_ordinal_type_1d_view = typename impl_type::local_ordinal_type_1d_view;
4070 using local_ordinal_type_2d_view = typename impl_type::local_ordinal_type_2d_view;
4071 using size_type_2d_view = typename impl_type::size_type_2d_view;
4073 using vector_type_3d_view = typename impl_type::vector_type_3d_view;
4074 using internal_vector_type_3d_view = typename impl_type::internal_vector_type_3d_view;
4075 using internal_vector_type_4d_view = typename impl_type::internal_vector_type_4d_view;
4076 using internal_vector_type_5d_view = typename impl_type::internal_vector_type_5d_view;
4077 using btdm_scalar_type_4d_view = typename impl_type::btdm_scalar_type_4d_view;
4078
4079 using internal_vector_scratch_type_3d_view = Scratch<typename impl_type::internal_vector_type_3d_view>;
4080
4081 using internal_vector_type = typename impl_type::internal_vector_type;
4082 static constexpr int vector_length = impl_type::vector_length;
4083 static constexpr int internal_vector_length = impl_type::internal_vector_length;
4084
4086 using impl_scalar_type_1d_view = typename impl_type::impl_scalar_type_1d_view;
4087 using impl_scalar_type_2d_view_tpetra = typename impl_type::impl_scalar_type_2d_view_tpetra;
4088
4090 using team_policy_type = Kokkos::TeamPolicy<execution_space>;
4091 using member_type = typename team_policy_type::member_type;
4092
4093 private:
4094 // part interface
4095 local_ordinal_type n_subparts_per_part;
4096 const ConstUnmanaged<local_ordinal_type_1d_view> partptr;
4097 const ConstUnmanaged<local_ordinal_type_1d_view> packptr;
4098 const ConstUnmanaged<local_ordinal_type_1d_view> packindices_sub;
4099 const ConstUnmanaged<local_ordinal_type_2d_view> packindices_schur;
4100 const ConstUnmanaged<local_ordinal_type_1d_view> part2packrowidx0;
4101 const ConstUnmanaged<local_ordinal_type_2d_view> part2packrowidx0_sub;
4102 const ConstUnmanaged<local_ordinal_type_1d_view> lclrow;
4103 const ConstUnmanaged<local_ordinal_type_1d_view> packptr_sub;
4104
4105 const ConstUnmanaged<local_ordinal_type_2d_view> partptr_sub;
4106 const ConstUnmanaged<size_type_2d_view> pack_td_ptr_schur;
4107
4108 // block tridiags
4109 const ConstUnmanaged<size_type_2d_view> pack_td_ptr;
4110
4111 // block tridiags values
4112 const ConstUnmanaged<internal_vector_type_4d_view> D_internal_vector_values;
4113 const Unmanaged<internal_vector_type_4d_view> X_internal_vector_values;
4114 const Unmanaged<btdm_scalar_type_4d_view> X_internal_scalar_values;
4115
4116 const Unmanaged<internal_vector_type_3d_view> X_internal_vector_values_schur;
4117
4118 const ConstUnmanaged<internal_vector_type_4d_view> D_internal_vector_values_schur;
4119 const ConstUnmanaged<internal_vector_type_5d_view> e_internal_vector_values;
4120
4121 const local_ordinal_type vector_loop_size;
4122
4123 // copy to multivectors : damping factor and Y_scalar_multivector
4124 Unmanaged<impl_scalar_type_2d_view_tpetra> Y_scalar_multivector;
4125#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) || defined(__SYCL_DEVICE_ONLY__)
4126 AtomicUnmanaged<impl_scalar_type_1d_view> Z_scalar_vector;
4127#else
4128 /* */ Unmanaged<impl_scalar_type_1d_view> Z_scalar_vector;
4129#endif
4130 const impl_scalar_type df;
4131 const bool compute_diff;
4132 // Schur solve only supports solving one vector at a time (currently).
4133 // If solving on a multivector, we loop over each vec in the solve.
4134 // This is the current vec being solved.
4135 local_ordinal_type active_schur_solve_vec;
4136
4137 public:
4138 SolveTridiags(const BlockHelperDetails::PartInterface<MatrixType> &interf,
4139 const BlockTridiags<MatrixType> &btdm,
4140 const vector_type_3d_view &pmv,
4141 const impl_scalar_type damping_factor,
4142 const bool is_norm_manager_active)
4143 : // interface
4144 n_subparts_per_part(interf.n_subparts_per_part)
4145 , partptr(interf.partptr)
4146 , packptr(interf.packptr)
4147 , packindices_sub(interf.packindices_sub)
4148 , packindices_schur(interf.packindices_schur)
4149 , part2packrowidx0(interf.part2packrowidx0)
4150 , part2packrowidx0_sub(interf.part2packrowidx0_sub)
4151 , lclrow(interf.lclrow)
4152 , packptr_sub(interf.packptr_sub)
4153 , partptr_sub(interf.partptr_sub)
4154 , pack_td_ptr_schur(btdm.pack_td_ptr_schur)
4155 ,
4156 // block tridiags and multivector
4157 pack_td_ptr(btdm.pack_td_ptr)
4158 , D_internal_vector_values((internal_vector_type *)btdm.values.data(),
4159 btdm.values.extent(0),
4160 btdm.values.extent(1),
4161 btdm.values.extent(2),
4162 vector_length / internal_vector_length)
4163 , X_internal_vector_values((internal_vector_type *)pmv.data(),
4164 pmv.extent(0),
4165 pmv.extent(1),
4166 pmv.extent(2),
4167 vector_length / internal_vector_length)
4168 , X_internal_scalar_values((btdm_scalar_type *)pmv.data(),
4169 pmv.extent(0),
4170 pmv.extent(1),
4171 pmv.extent(2),
4172 vector_length)
4173 , X_internal_vector_values_schur(btdm.X_internal_vector_values_schur)
4174 , D_internal_vector_values_schur((internal_vector_type *)btdm.values_schur.data(),
4175 btdm.values_schur.extent(0),
4176 btdm.values_schur.extent(1),
4177 btdm.values_schur.extent(2),
4178 vector_length / internal_vector_length)
4179 , e_internal_vector_values((internal_vector_type *)btdm.e_values.data(),
4180 btdm.e_values.extent(0),
4181 btdm.e_values.extent(1),
4182 btdm.e_values.extent(2),
4183 btdm.e_values.extent(3),
4184 vector_length / internal_vector_length)
4185 , vector_loop_size(vector_length / internal_vector_length)
4186 , Y_scalar_multivector()
4187 , Z_scalar_vector()
4188 , df(damping_factor)
4189 , compute_diff(is_norm_manager_active)
4190 , active_schur_solve_vec(0) {}
4191
4192 public:
4194 KOKKOS_INLINE_FUNCTION
4195 void
4196 copyToFlatMultiVector(const member_type &member,
4197 const local_ordinal_type partidxbeg, // partidx for v = 0
4198 const local_ordinal_type npacks,
4199 const local_ordinal_type pri0,
4200 const local_ordinal_type v, // index with a loop of vector_loop_size
4201 const local_ordinal_type blocksize,
4202 const local_ordinal_type num_vectors) const {
4203 const local_ordinal_type vbeg = v * internal_vector_length;
4204 if (vbeg < npacks) {
4205 local_ordinal_type ri0_vals[internal_vector_length] = {};
4206 local_ordinal_type nrows_vals[internal_vector_length] = {};
4207 for (local_ordinal_type vv = vbeg, vi = 0; vv < npacks && vi < internal_vector_length; ++vv, ++vi) {
4208 const local_ordinal_type partidx = partidxbeg + vv;
4209 ri0_vals[vi] = partptr(partidx);
4210 nrows_vals[vi] = partptr(partidx + 1) - ri0_vals[vi];
4211 }
4212
4213 impl_scalar_type z_partial_sum(0);
4214 if (nrows_vals[0] == 1) {
4215 const local_ordinal_type j = 0, pri = pri0;
4216 {
4217 for (local_ordinal_type vv = vbeg, vi = 0; vv < npacks && vi < internal_vector_length; ++vv, ++vi) {
4218 const local_ordinal_type ri0 = ri0_vals[vi];
4219 const local_ordinal_type nrows = nrows_vals[vi];
4220 if (j < nrows) {
4221 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize),
4222 [&](const local_ordinal_type &i) {
4223 const local_ordinal_type row = blocksize * lclrow(ri0 + j) + i;
4224 for (local_ordinal_type col = 0; col < num_vectors; ++col) {
4225 impl_scalar_type &y = Y_scalar_multivector(row, col);
4226 const impl_scalar_type yd = X_internal_vector_values(pri, i, col, v)[vi] - y;
4227 y += df * yd;
4228
4229 { // if (compute_diff) {
4230 const auto yd_abs = KokkosKernels::ArithTraits<impl_scalar_type>::abs(yd);
4231 z_partial_sum += yd_abs * yd_abs;
4232 }
4233 }
4234 });
4235 }
4236 }
4237 }
4238 } else {
4239 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, nrows_vals[0]),
4240 [&](const local_ordinal_type &j) {
4241 const local_ordinal_type pri = pri0 + j;
4242 for (local_ordinal_type vv = vbeg, vi = 0; vv < npacks && vi < internal_vector_length; ++vv, ++vi) {
4243 const local_ordinal_type ri0 = ri0_vals[vi];
4244 const local_ordinal_type nrows = nrows_vals[vi];
4245 if (j < nrows) {
4246 for (local_ordinal_type col = 0; col < num_vectors; ++col) {
4247 for (local_ordinal_type i = 0; i < blocksize; ++i) {
4248 const local_ordinal_type row = blocksize * lclrow(ri0 + j) + i;
4249 impl_scalar_type &y = Y_scalar_multivector(row, col);
4250 const impl_scalar_type yd = X_internal_vector_values(pri, i, col, v)[vi] - y;
4251 y += df * yd;
4252
4253 { // if (compute_diff) {
4254 const auto yd_abs = KokkosKernels::ArithTraits<impl_scalar_type>::abs(yd);
4255 z_partial_sum += yd_abs * yd_abs;
4256 }
4257 }
4258 }
4259 }
4260 }
4261 });
4262 }
4263 // if (compute_diff)
4264 Z_scalar_vector(member.league_rank()) += z_partial_sum;
4265 }
4266 }
4267
4271 template <typename WWViewType>
4272 KOKKOS_INLINE_FUNCTION void
4273 solveSingleVector(const member_type &member,
4274 const local_ordinal_type &blocksize,
4275 const local_ordinal_type &i0,
4276 const local_ordinal_type &r0,
4277 const local_ordinal_type &nrows,
4278 const local_ordinal_type &v,
4279 const WWViewType &WW) const {
4280 typedef SolveTridiagsDefaultModeAndAlgo<typename execution_space::memory_space> default_mode_and_algo_type;
4281
4282 typedef typename default_mode_and_algo_type::mode_type default_mode_type;
4283 typedef typename default_mode_and_algo_type::single_vector_algo_type default_algo_type;
4284
4285 // base pointers
4286 auto A = D_internal_vector_values.data();
4287 auto X = X_internal_vector_values.data();
4288
4289 // constant
4290 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
4291 const auto zero = KokkosKernels::ArithTraits<btdm_magnitude_type>::zero();
4292 // const local_ordinal_type num_vectors = X_scalar_values.extent(2);
4293
4294 // const local_ordinal_type blocksize = D_scalar_values.extent(1);
4295 const local_ordinal_type astep = D_internal_vector_values.stride(0);
4296 const local_ordinal_type as0 = D_internal_vector_values.stride(1); // blocksize*vector_length;
4297 const local_ordinal_type as1 = D_internal_vector_values.stride(2); // vector_length;
4298 const local_ordinal_type xstep = X_internal_vector_values.stride(0);
4299 const local_ordinal_type xs0 = X_internal_vector_values.stride(1); // vector_length;
4300
4301 // move to starting point
4302 A += i0 * astep + v;
4303 X += r0 * xstep + v;
4304
4305 // for (local_ordinal_type col=0;col<num_vectors;++col)
4306 if (nrows > 1) {
4307 // solve Lx = x
4308 KOKKOSBATCHED_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4309 member,
4310 KB::Diag::Unit,
4311 blocksize, blocksize,
4312 one,
4313 A, as0, as1,
4314 X, xs0);
4315
4316 for (local_ordinal_type tr = 1; tr < nrows; ++tr) {
4317 member.team_barrier();
4318 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4319 member,
4320 blocksize, blocksize,
4321 -one,
4322 A + 2 * astep, as0, as1,
4323 X, xs0,
4324 one,
4325 X + 1 * xstep, xs0);
4326 KOKKOSBATCHED_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4327 member,
4328 KB::Diag::Unit,
4329 blocksize, blocksize,
4330 one,
4331 A + 3 * astep, as0, as1,
4332 X + 1 * xstep, xs0);
4333
4334 A += 3 * astep;
4335 X += 1 * xstep;
4336 }
4337
4338 // solve Ux = x
4339 KOKKOSBATCHED_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4340 member,
4341 KB::Diag::NonUnit,
4342 blocksize, blocksize,
4343 one,
4344 A, as0, as1,
4345 X, xs0);
4346
4347 for (local_ordinal_type tr = nrows; tr > 1; --tr) {
4348 A -= 3 * astep;
4349 member.team_barrier();
4350 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4351 member,
4352 blocksize, blocksize,
4353 -one,
4354 A + 1 * astep, as0, as1,
4355 X, xs0,
4356 one,
4357 X - 1 * xstep, xs0);
4358 KOKKOSBATCHED_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4359 member,
4360 KB::Diag::NonUnit,
4361 blocksize, blocksize,
4362 one,
4363 A, as0, as1,
4364 X - 1 * xstep, xs0);
4365 X -= 1 * xstep;
4366 }
4367 // for multiple rhs
4368 // X += xs1;
4369 } else {
4370 const local_ordinal_type ws0 = WW.stride(0);
4371 auto W = WW.data() + v;
4372 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize), [&](int i) { W[i * ws0] = X[i * xs0]; });
4373 member.team_barrier();
4374 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4375 member,
4376 blocksize, blocksize,
4377 one,
4378 A, as0, as1,
4379 W, xs0,
4380 zero,
4381 X, xs0);
4382 }
4383 }
4384
4385 template <typename WWViewType>
4386 KOKKOS_INLINE_FUNCTION void
4387 solveMultiVector(const member_type &member,
4388 const local_ordinal_type & /* blocksize */,
4389 const local_ordinal_type &i0,
4390 const local_ordinal_type &r0,
4391 const local_ordinal_type &nrows,
4392 const local_ordinal_type &v,
4393 const WWViewType &WW) const {
4394 typedef SolveTridiagsDefaultModeAndAlgo<typename execution_space::memory_space> default_mode_and_algo_type;
4395
4396 typedef typename default_mode_and_algo_type::mode_type default_mode_type;
4397 typedef typename default_mode_and_algo_type::multi_vector_algo_type default_algo_type;
4398
4399 // constant
4400 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
4401 const auto zero = KokkosKernels::ArithTraits<btdm_magnitude_type>::zero();
4402
4403 // subview pattern
4404 auto A = Kokkos::subview(D_internal_vector_values, i0, Kokkos::ALL(), Kokkos::ALL(), v);
4405 auto X1 = Kokkos::subview(X_internal_vector_values, r0, Kokkos::ALL(), Kokkos::ALL(), v);
4406 auto X2 = X1;
4407
4408 local_ordinal_type i = i0, r = r0;
4409
4410 if (nrows > 1) {
4411 // solve Lx = x
4412 KB::Trsm<member_type,
4413 KB::Side::Left, KB::Uplo::Lower, KB::Trans::NoTranspose, KB::Diag::Unit,
4414 default_mode_type, default_algo_type>::invoke(member, one, A, X1);
4415 for (local_ordinal_type tr = 1; tr < nrows; ++tr, i += 3) {
4416 A.assign_data(&D_internal_vector_values(i + 2, 0, 0, v));
4417 X2.assign_data(&X_internal_vector_values(++r, 0, 0, v));
4418 member.team_barrier();
4419 KB::Gemm<member_type,
4420 KB::Trans::NoTranspose, KB::Trans::NoTranspose,
4421 default_mode_type, default_algo_type>::invoke(member, -one, A, X1, one, X2);
4422 A.assign_data(&D_internal_vector_values(i + 3, 0, 0, v));
4423 KB::Trsm<member_type,
4424 KB::Side::Left, KB::Uplo::Lower, KB::Trans::NoTranspose, KB::Diag::Unit,
4425 default_mode_type, default_algo_type>::invoke(member, one, A, X2);
4426 X1.assign_data(X2.data());
4427 }
4428
4429 // solve Ux = x
4430 KB::Trsm<member_type,
4431 KB::Side::Left, KB::Uplo::Upper, KB::Trans::NoTranspose, KB::Diag::NonUnit,
4432 default_mode_type, default_algo_type>::invoke(member, one, A, X1);
4433 for (local_ordinal_type tr = nrows; tr > 1; --tr) {
4434 i -= 3;
4435 A.assign_data(&D_internal_vector_values(i + 1, 0, 0, v));
4436 X2.assign_data(&X_internal_vector_values(--r, 0, 0, v));
4437 member.team_barrier();
4438 KB::Gemm<member_type,
4439 KB::Trans::NoTranspose, KB::Trans::NoTranspose,
4440 default_mode_type, default_algo_type>::invoke(member, -one, A, X1, one, X2);
4441
4442 A.assign_data(&D_internal_vector_values(i, 0, 0, v));
4443 KB::Trsm<member_type,
4444 KB::Side::Left, KB::Uplo::Upper, KB::Trans::NoTranspose, KB::Diag::NonUnit,
4445 default_mode_type, default_algo_type>::invoke(member, one, A, X2);
4446 X1.assign_data(X2.data());
4447 }
4448 } else {
4449 // matrix is already inverted
4450 auto W = Kokkos::subview(WW, Kokkos::ALL(), Kokkos::ALL(), v);
4451 KB::Copy<member_type, KB::Trans::NoTranspose, default_mode_type>::invoke(member, X1, W);
4452 member.team_barrier();
4453 KB::Gemm<member_type,
4454 KB::Trans::NoTranspose, KB::Trans::NoTranspose,
4455 default_mode_type, default_algo_type>::invoke(member, one, A, W, zero, X1);
4456 }
4457 }
4458
4459 template <int B, int ScratchLevel>
4460 struct MultiVectorTag {};
4461
4462 template <int B, int ScratchLevel>
4463 struct SingleVectorSubLineTag {};
4464 template <int B>
4465 struct SingleVectorApplyCTag {};
4466 template <int B, int ScratchLevel>
4467 struct SingleVectorSchurTag {};
4468 template <int B>
4469 struct SingleVectorApplyETag {};
4470 template <int B>
4471 struct CopyVectorToFlatTag {};
4472
4473 template <int B, int ScratchLevel>
4474 struct SingleVectorFunctor {
4475 SingleVectorFunctor(const SolveTridiags<MatrixType> &solve)
4476 : packptr(solve.packptr)
4477 , part2packrowidx0(solve.part2packrowidx0)
4478 , pack_td_ptr(solve.pack_td_ptr)
4479 , partptr(solve.partptr)
4480 , D_internal_vector_values(solve.D_internal_vector_values)
4481 , lclrow(solve.lclrow)
4482 , X_internal_vector_values(solve.X_internal_vector_values)
4483 , Y_scalar_multivector(solve.Y_scalar_multivector)
4484 , Z_scalar_vector(solve.Z_scalar_vector)
4485 , df(solve.df)
4486 , vector_loop_size(solve.vector_loop_size) {}
4487
4488 // Members use uint32_t-indexed Views when mdspan is enabled, or plain
4489 // unmanaged Views otherwise. Both types support data(), extent(), stride(),
4490 // and operator(), so the functor body is identical in either case.
4491 BlockHelperDetails::Uint32IndexedConstView<local_ordinal_type_1d_view> packptr;
4492 BlockHelperDetails::Uint32IndexedConstView<local_ordinal_type_1d_view> part2packrowidx0;
4493 BlockHelperDetails::Uint32IndexedConstView<size_type_2d_view> pack_td_ptr;
4494 BlockHelperDetails::Uint32IndexedConstView<local_ordinal_type_1d_view> partptr;
4495 BlockHelperDetails::Uint32IndexedConstView<internal_vector_type_4d_view> D_internal_vector_values;
4496 BlockHelperDetails::Uint32IndexedConstView<local_ordinal_type_1d_view> lclrow;
4497 BlockHelperDetails::Uint32IndexedView<internal_vector_type_4d_view> X_internal_vector_values;
4498 BlockHelperDetails::Uint32IndexedView<impl_scalar_type_2d_view_tpetra> Y_scalar_multivector;
4499 BlockHelperDetails::Uint32IndexedView<impl_scalar_type_1d_view> Z_scalar_vector;
4500 const impl_scalar_type df;
4501 const local_ordinal_type vector_loop_size;
4502
4503 template <typename WWViewType>
4504 KOKKOS_INLINE_FUNCTION void
4505 solveSingleVector(const member_type &member,
4506 const local_ordinal_type &blocksize,
4507 const local_ordinal_type &i0,
4508 const local_ordinal_type &r0,
4509 const local_ordinal_type &nrows,
4510 const local_ordinal_type &v,
4511 const WWViewType &WW) const {
4512 typedef SolveTridiagsDefaultModeAndAlgo<typename execution_space::memory_space> default_mode_and_algo_type;
4513
4514 typedef typename default_mode_and_algo_type::mode_type default_mode_type;
4515 typedef typename default_mode_and_algo_type::single_vector_algo_type default_algo_type;
4516
4517 // base pointers
4518 auto A = D_internal_vector_values.data();
4519 auto X = X_internal_vector_values.data();
4520
4521 // constant
4522 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
4523 const auto zero = KokkosKernels::ArithTraits<btdm_magnitude_type>::zero();
4524 // const local_ordinal_type num_vectors = X_scalar_values.extent(2);
4525
4526 // const local_ordinal_type blocksize = D_scalar_values.extent(1);
4527 const local_ordinal_type astep = D_internal_vector_values.stride(0);
4528 const local_ordinal_type as0 = D_internal_vector_values.stride(1); // blocksize*vector_length;
4529 const local_ordinal_type as1 = D_internal_vector_values.stride(2); // vector_length;
4530 const local_ordinal_type xstep = X_internal_vector_values.stride(0);
4531 const local_ordinal_type xs0 = X_internal_vector_values.stride(1); // vector_length;
4532
4533 // move to starting point
4534 A += i0 * astep + v;
4535 X += r0 * xstep + v;
4536
4537 // for (local_ordinal_type col=0;col<num_vectors;++col)
4538 if (nrows > 1) {
4539 // solve Lx = x
4540 KOKKOSBATCHED_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4541 member,
4542 KB::Diag::Unit,
4543 blocksize, blocksize,
4544 one,
4545 A, as0, as1,
4546 X, xs0);
4547
4548 for (local_ordinal_type tr = 1; tr < nrows; ++tr) {
4549 member.team_barrier();
4550 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4551 member,
4552 blocksize, blocksize,
4553 -one,
4554 A + 2 * astep, as0, as1,
4555 X, xs0,
4556 one,
4557 X + 1 * xstep, xs0);
4558 KOKKOSBATCHED_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4559 member,
4560 KB::Diag::Unit,
4561 blocksize, blocksize,
4562 one,
4563 A + 3 * astep, as0, as1,
4564 X + 1 * xstep, xs0);
4565
4566 A += 3 * astep;
4567 X += 1 * xstep;
4568 }
4569
4570 // solve Ux = x
4571 KOKKOSBATCHED_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4572 member,
4573 KB::Diag::NonUnit,
4574 blocksize, blocksize,
4575 one,
4576 A, as0, as1,
4577 X, xs0);
4578
4579 for (local_ordinal_type tr = nrows; tr > 1; --tr) {
4580 A -= 3 * astep;
4581 member.team_barrier();
4582 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4583 member,
4584 blocksize, blocksize,
4585 -one,
4586 A + 1 * astep, as0, as1,
4587 X, xs0,
4588 one,
4589 X - 1 * xstep, xs0);
4590 KOKKOSBATCHED_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4591 member,
4592 KB::Diag::NonUnit,
4593 blocksize, blocksize,
4594 one,
4595 A, as0, as1,
4596 X - 1 * xstep, xs0);
4597 X -= 1 * xstep;
4598 }
4599 // for multiple rhs
4600 // X += xs1;
4601 } else {
4602 const local_ordinal_type ws0 = WW.stride(0);
4603 auto W = WW.data() + v;
4604 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize), [&](int i) { W[i * ws0] = X[i * xs0]; });
4605 member.team_barrier();
4606 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4607 member,
4608 blocksize, blocksize,
4609 one,
4610 A, as0, as1,
4611 W, xs0,
4612 zero,
4613 X, xs0);
4614 }
4615 }
4616
4617 KOKKOS_INLINE_FUNCTION
4618 void
4619 copyToFlatMultiVector(const member_type &member,
4620 const local_ordinal_type partidxbeg, // partidx for v = 0
4621 const local_ordinal_type npacks,
4622 const local_ordinal_type pri0,
4623 const local_ordinal_type v, // index with a loop of vector_loop_size
4624 const local_ordinal_type blocksize) const {
4625 const local_ordinal_type vbeg = v * internal_vector_length;
4626 if (vbeg < npacks) {
4627 local_ordinal_type ri0_vals[internal_vector_length] = {};
4628 local_ordinal_type nrows_vals[internal_vector_length] = {};
4629 for (local_ordinal_type vv = vbeg, vi = 0; vv < npacks && vi < internal_vector_length; ++vv, ++vi) {
4630 const local_ordinal_type partidx = partidxbeg + vv;
4631 ri0_vals[vi] = partptr(partidx);
4632 nrows_vals[vi] = partptr(partidx + 1) - ri0_vals[vi];
4633 }
4634
4635 impl_scalar_type z_partial_sum(0);
4636 if (nrows_vals[0] == 1) {
4637 const local_ordinal_type j = 0, pri = pri0;
4638 {
4639 for (local_ordinal_type vv = vbeg, vi = 0; vv < npacks && vi < internal_vector_length; ++vv, ++vi) {
4640 const local_ordinal_type ri0 = ri0_vals[vi];
4641 const local_ordinal_type nrows = nrows_vals[vi];
4642 if (j < nrows) {
4643 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, blocksize),
4644 [&](const local_ordinal_type &i) {
4645 const local_ordinal_type row = blocksize * lclrow(ri0 + j) + i;
4646 impl_scalar_type &y = Y_scalar_multivector(row, 0);
4647 const impl_scalar_type yd = X_internal_vector_values(pri, i, 0, v)[vi] - y;
4648 y += df * yd;
4649
4650 { // if (compute_diff) {
4651 const auto yd_abs = KokkosKernels::ArithTraits<impl_scalar_type>::abs(yd);
4652 z_partial_sum += yd_abs * yd_abs;
4653 }
4654 });
4655 }
4656 }
4657 }
4658 } else {
4659 Kokkos::parallel_for(Kokkos::TeamThreadRange(member, nrows_vals[0]),
4660 [&](const local_ordinal_type &j) {
4661 const local_ordinal_type pri = pri0 + j;
4662 for (local_ordinal_type vv = vbeg, vi = 0; vv < npacks && vi < internal_vector_length; ++vv, ++vi) {
4663 const local_ordinal_type ri0 = ri0_vals[vi];
4664 const local_ordinal_type nrows = nrows_vals[vi];
4665 if (j < nrows) {
4666 for (local_ordinal_type i = 0; i < blocksize; ++i) {
4667 const local_ordinal_type row = blocksize * lclrow(ri0 + j) + i;
4668 impl_scalar_type &y = Y_scalar_multivector(row, 0);
4669 const impl_scalar_type yd = X_internal_vector_values(pri, i, 0, v)[vi] - y;
4670 y += df * yd;
4671
4672 { // if (compute_diff) {
4673 const auto yd_abs = KokkosKernels::ArithTraits<impl_scalar_type>::abs(yd);
4674 z_partial_sum += yd_abs * yd_abs;
4675 }
4676 }
4677 }
4678 }
4679 });
4680 }
4681 // if (compute_diff)
4682 Kokkos::atomic_add(&Z_scalar_vector(member.league_rank()), z_partial_sum);
4683 }
4684 }
4685
4686 KOKKOS_INLINE_FUNCTION void
4687 operator()(const member_type &member) const {
4688 const local_ordinal_type packidx = member.league_rank();
4689 const local_ordinal_type partidx = packptr(packidx);
4690 const local_ordinal_type npacks = packptr(packidx + 1) - partidx;
4691 const local_ordinal_type pri0 = part2packrowidx0(partidx);
4692 const local_ordinal_type i0 = pack_td_ptr(partidx, 0);
4693 const local_ordinal_type r0 = pri0;
4694 const local_ordinal_type nrows = partptr(partidx + 1) - partptr(partidx);
4695 const local_ordinal_type blocksize = (B == 0 ? D_internal_vector_values.extent(1) : B);
4696 // Raw scratch allocation wrapped in a 32-bit-indexed View
4697 // (LayoutRight, extents blocksize x vector_loop_size) instead of a Kokkos
4698 // scratch View, to keep indices/strides 32-bit for the single-vector
4699 // solve. The middle dimension (num_vectors == 1 here) is elided since we
4700 // specialize for the single-vector case.
4701#if defined(KOKKOS_ENABLE_IMPL_MDSPAN) || (KOKKOS_VERSION >= 50299)
4702 using WW_u32_view_type = Kokkos::View<
4703 internal_vector_type,
4704 Kokkos::extents<uint32_t, Kokkos::dynamic_extent, Kokkos::dynamic_extent>,
4705 Kokkos::layout_right,
4706 Kokkos::Experimental::Accessor<internal_vector_type,
4707 typename execution_space::scratch_memory_space,
4708 Kokkos::MemoryTraits<Kokkos::Unmanaged>>>;
4709#else
4710 using WW_u32_view_type = Kokkos::View<
4711 internal_vector_type **,
4712 Kokkos::LayoutRight,
4713 typename execution_space::scratch_memory_space,
4714 Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
4715#endif
4716 constexpr size_t WW_alignment =
4717 Kokkos::max({sizeof(internal_vector_type), alignof(internal_vector_type),
4718 static_cast<size_t>(execution_space::scratch_memory_space::ALIGN)});
4719 internal_vector_type *WW_ptr = reinterpret_cast<internal_vector_type *>(
4720 member.team_scratch(ScratchLevel)
4721 .get_shmem_aligned(sizeof(internal_vector_type) *
4722 static_cast<size_t>(blocksize) *
4723 static_cast<size_t>(vector_loop_size),
4724 WW_alignment));
4725 WW_u32_view_type WW(WW_ptr,
4726 static_cast<uint32_t>(blocksize),
4727 static_cast<uint32_t>(vector_loop_size));
4728 Kokkos::single(Kokkos::PerTeam(member), [&]() {
4729 Z_scalar_vector(member.league_rank()) = impl_scalar_type(0);
4730 });
4731 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4732 solveSingleVector(member, blocksize, i0, r0, nrows, v, WW);
4733 copyToFlatMultiVector(member, partidx, npacks, pri0, v, blocksize);
4734 });
4735 }
4736 };
4737
4738 template <int B, int ScratchLevel>
4739 KOKKOS_INLINE_FUNCTION void
4740 operator()(const MultiVectorTag<B, ScratchLevel> &, const member_type &member) const {
4741 const local_ordinal_type packidx = member.league_rank();
4742 const local_ordinal_type partidx = packptr(packidx);
4743 const local_ordinal_type npacks = packptr(packidx + 1) - partidx;
4744 const local_ordinal_type pri0 = part2packrowidx0(partidx);
4745 const local_ordinal_type i0 = pack_td_ptr(partidx, 0);
4746 const local_ordinal_type r0 = part2packrowidx0(partidx);
4747 const local_ordinal_type nrows = partptr(partidx + 1) - partptr(partidx);
4748 const local_ordinal_type blocksize = (B == 0 ? D_internal_vector_values.extent(1) : B);
4749 const local_ordinal_type num_vectors = X_internal_vector_values.extent(2);
4750
4751 internal_vector_scratch_type_3d_view
4752 WW(member.team_scratch(ScratchLevel), blocksize, num_vectors, vector_loop_size);
4753 Kokkos::single(Kokkos::PerTeam(member), [&]() {
4754 Z_scalar_vector(member.league_rank()) = impl_scalar_type(0);
4755 });
4756 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4757 solveMultiVector(member, blocksize, i0, r0, nrows, v, WW);
4758 copyToFlatMultiVector(member, partidx, npacks, pri0, v, blocksize, num_vectors);
4759 });
4760 }
4761
4762 template <int B, int ScratchLevel>
4763 KOKKOS_INLINE_FUNCTION void
4764 operator()(const SingleVectorSubLineTag<B, ScratchLevel> &, const member_type &member) const {
4765 // btdm is packed and sorted from largest one
4766 const local_ordinal_type packidx = packindices_sub(member.league_rank());
4767
4768 const local_ordinal_type subpartidx = packptr_sub(packidx);
4769 const local_ordinal_type n_parts = part2packrowidx0_sub.extent(0);
4770 const local_ordinal_type local_subpartidx = subpartidx / n_parts;
4771 const local_ordinal_type partidx = subpartidx % n_parts;
4772
4773 const local_ordinal_type npacks = packptr_sub(packidx + 1) - subpartidx;
4774 const local_ordinal_type i0 = pack_td_ptr(partidx, local_subpartidx);
4775 const local_ordinal_type r0 = part2packrowidx0_sub(partidx, local_subpartidx);
4776 const local_ordinal_type nrows = partptr_sub(subpartidx, 1) - partptr_sub(subpartidx, 0);
4777 const local_ordinal_type blocksize = e_internal_vector_values.extent(2);
4778
4779 //(void) i0;
4780 //(void) nrows;
4781 (void)npacks;
4782
4783 internal_vector_scratch_type_3d_view
4784 WW(member.team_scratch(ScratchLevel), blocksize, 1, vector_loop_size);
4785
4786 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4787 auto X_internal_vec = Kokkos::subview(X_internal_vector_values, Kokkos::ALL(), Kokkos::ALL(), active_schur_solve_vec, Kokkos::ALL());
4788 solveSingleVectorNew<impl_type, internal_vector_scratch_type_3d_view>(member, blocksize, i0, r0, nrows, v, D_internal_vector_values, X_internal_vec, WW);
4789 });
4790 }
4791
4792 template <int B>
4793 KOKKOS_INLINE_FUNCTION void
4794 operator()(const SingleVectorApplyCTag<B> &, const member_type &member) const {
4795 // btdm is packed and sorted from largest one
4796 // const local_ordinal_type packidx = packindices_schur(member.league_rank());
4797 const local_ordinal_type packidx = packindices_sub(member.league_rank());
4798
4799 const local_ordinal_type subpartidx = packptr_sub(packidx);
4800 const local_ordinal_type n_parts = part2packrowidx0_sub.extent(0);
4801 const local_ordinal_type local_subpartidx = subpartidx / n_parts;
4802 const local_ordinal_type partidx = subpartidx % n_parts;
4803 const local_ordinal_type blocksize = e_internal_vector_values.extent(2);
4804
4805 // const local_ordinal_type npacks = packptr_sub(packidx+1) - subpartidx;
4806 const local_ordinal_type i0 = pack_td_ptr(partidx, local_subpartidx);
4807 const local_ordinal_type r0 = part2packrowidx0_sub(partidx, local_subpartidx);
4808 const local_ordinal_type nrows = partptr_sub(subpartidx, 1) - partptr_sub(subpartidx, 0);
4809
4810 // Compute v_2 = v_2 - C v_1
4811
4812 const local_ordinal_type local_subpartidx_schur = (local_subpartidx - 1) / 2;
4813 const local_ordinal_type i0_schur = local_subpartidx_schur == 0 ? pack_td_ptr_schur(partidx, local_subpartidx_schur) : pack_td_ptr_schur(partidx, local_subpartidx_schur) + 1;
4814 const local_ordinal_type i0_offset = local_subpartidx_schur == 0 ? i0 + 2 : i0 + 2;
4815
4816 (void)i0_schur;
4817 (void)i0_offset;
4818
4819 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
4820
4821 const size_type c_kps2 = local_subpartidx > 0 ? pack_td_ptr(partidx, local_subpartidx) - 2 : 0;
4822 const size_type c_kps1 = pack_td_ptr(partidx, local_subpartidx + 1) + 1;
4823
4824 typedef SolveTridiagsDefaultModeAndAlgo<typename execution_space::memory_space> default_mode_and_algo_type;
4825
4826 typedef typename default_mode_and_algo_type::mode_type default_mode_type;
4827 typedef typename default_mode_and_algo_type::single_vector_algo_type default_algo_type;
4828
4829 if (local_subpartidx == 0) {
4830 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4831 auto v_1 = Kokkos::subview(X_internal_vector_values, r0 + nrows - 1, Kokkos::ALL(), active_schur_solve_vec, v);
4832 auto v_2 = Kokkos::subview(X_internal_vector_values, r0 + nrows, Kokkos::ALL(), active_schur_solve_vec, v);
4833 auto C = Kokkos::subview(D_internal_vector_values, c_kps1, Kokkos::ALL(), Kokkos::ALL(), v);
4834
4835 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4836 member,
4837 blocksize, blocksize,
4838 -one,
4839 C.data(), C.stride(0), C.stride(1),
4840 v_1.data(), v_1.stride(0),
4841 one,
4842 v_2.data(), v_2.stride(0));
4843 });
4844 } else if (local_subpartidx == (local_ordinal_type)part2packrowidx0_sub.extent(1) - 2) {
4845 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4846 auto v_1 = Kokkos::subview(X_internal_vector_values, r0, Kokkos::ALL(), active_schur_solve_vec, v);
4847 auto v_2 = Kokkos::subview(X_internal_vector_values, r0 - 1, Kokkos::ALL(), active_schur_solve_vec, v);
4848 auto C = Kokkos::subview(D_internal_vector_values, c_kps2, Kokkos::ALL(), Kokkos::ALL(), v);
4849
4850 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4851 member,
4852 blocksize, blocksize,
4853 -one,
4854 C.data(), C.stride(0), C.stride(1),
4855 v_1.data(), v_1.stride(0),
4856 one,
4857 v_2.data(), v_2.stride(0));
4858 });
4859 } else {
4860 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4861 {
4862 auto v_1 = Kokkos::subview(X_internal_vector_values, r0 + nrows - 1, Kokkos::ALL(), active_schur_solve_vec, v);
4863 auto v_2 = Kokkos::subview(X_internal_vector_values, r0 + nrows, Kokkos::ALL(), active_schur_solve_vec, v);
4864 auto C = Kokkos::subview(D_internal_vector_values, c_kps1, Kokkos::ALL(), Kokkos::ALL(), v);
4865
4866 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4867 member,
4868 blocksize, blocksize,
4869 -one,
4870 C.data(), C.stride(0), C.stride(1),
4871 v_1.data(), v_1.stride(0),
4872 one,
4873 v_2.data(), v_2.stride(0));
4874 }
4875 {
4876 auto v_1 = Kokkos::subview(X_internal_vector_values, r0, Kokkos::ALL(), active_schur_solve_vec, v);
4877 auto v_2 = Kokkos::subview(X_internal_vector_values, r0 - 1, Kokkos::ALL(), active_schur_solve_vec, v);
4878 auto C = Kokkos::subview(D_internal_vector_values, c_kps2, Kokkos::ALL(), Kokkos::ALL(), v);
4879
4880 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4881 member,
4882 blocksize, blocksize,
4883 -one,
4884 C.data(), C.stride(0), C.stride(1),
4885 v_1.data(), v_1.stride(0),
4886 one,
4887 v_2.data(), v_2.stride(0));
4888 }
4889 });
4890 }
4891 }
4892
4893 template <int B, int ScratchLevel>
4894 KOKKOS_INLINE_FUNCTION void
4895 operator()(const SingleVectorSchurTag<B, ScratchLevel> &, const member_type &member) const {
4896 const local_ordinal_type packidx = packindices_sub(member.league_rank());
4897
4898 const local_ordinal_type partidx = packptr_sub(packidx);
4899
4900 const local_ordinal_type blocksize = e_internal_vector_values.extent(2);
4901
4902 const local_ordinal_type i0_schur = pack_td_ptr_schur(partidx, 0);
4903 const local_ordinal_type nrows = 2 * (n_subparts_per_part - 1);
4904
4905 const local_ordinal_type r0_schur = nrows * member.league_rank();
4906
4907 internal_vector_scratch_type_3d_view
4908 WW(member.team_scratch(ScratchLevel), blocksize, blocksize, vector_loop_size);
4909
4910 for (local_ordinal_type schur_sub_part = 0; schur_sub_part < n_subparts_per_part - 1; ++schur_sub_part) {
4911 const local_ordinal_type r0 = part2packrowidx0_sub(partidx, 2 * schur_sub_part + 1);
4912 for (local_ordinal_type i = 0; i < 2; ++i) {
4913 copy3DView<local_ordinal_type>(member,
4914 Kokkos::subview(X_internal_vector_values_schur, r0_schur + 2 * schur_sub_part + i, Kokkos::ALL(), Kokkos::ALL()),
4915 Kokkos::subview(X_internal_vector_values, r0 + i, Kokkos::ALL(), active_schur_solve_vec, Kokkos::ALL()));
4916 }
4917 }
4918
4919 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4920 solveSingleVectorNew<impl_type, internal_vector_scratch_type_3d_view>(member, blocksize, i0_schur, r0_schur, nrows, v, D_internal_vector_values_schur, X_internal_vector_values_schur, WW);
4921 });
4922
4923 for (local_ordinal_type schur_sub_part = 0; schur_sub_part < n_subparts_per_part - 1; ++schur_sub_part) {
4924 const local_ordinal_type r0 = part2packrowidx0_sub(partidx, 2 * schur_sub_part + 1);
4925 for (local_ordinal_type i = 0; i < 2; ++i) {
4926 copy3DView<local_ordinal_type>(member,
4927 Kokkos::subview(X_internal_vector_values, r0 + i, Kokkos::ALL(), active_schur_solve_vec, Kokkos::ALL()),
4928 Kokkos::subview(X_internal_vector_values_schur, r0_schur + 2 * schur_sub_part + i, Kokkos::ALL(), Kokkos::ALL()));
4929 }
4930 }
4931 }
4932
4933 template <int B>
4934 KOKKOS_INLINE_FUNCTION void
4935 operator()(const SingleVectorApplyETag<B> &, const member_type &member) const {
4936 const local_ordinal_type packidx = packindices_sub(member.league_rank());
4937
4938 const local_ordinal_type subpartidx = packptr_sub(packidx);
4939 const local_ordinal_type n_parts = part2packrowidx0_sub.extent(0);
4940 const local_ordinal_type local_subpartidx = subpartidx / n_parts;
4941 const local_ordinal_type partidx = subpartidx % n_parts;
4942 const local_ordinal_type blocksize = e_internal_vector_values.extent(2);
4943
4944 const local_ordinal_type r0 = part2packrowidx0_sub(partidx, local_subpartidx);
4945 const local_ordinal_type nrows = partptr_sub(subpartidx, 1) - partptr_sub(subpartidx, 0);
4946
4947 // Compute v_2 = v_2 - C v_1
4948
4949 const auto one = KokkosKernels::ArithTraits<btdm_magnitude_type>::one();
4950
4951 typedef SolveTridiagsDefaultModeAndAlgo<typename execution_space::memory_space> default_mode_and_algo_type;
4952
4953 typedef typename default_mode_and_algo_type::mode_type default_mode_type;
4954 typedef typename default_mode_and_algo_type::single_vector_algo_type default_algo_type;
4955
4956 if (local_subpartidx == 0) {
4957 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4958 auto v_2 = Kokkos::subview(X_internal_vector_values, r0 + nrows, Kokkos::ALL(), active_schur_solve_vec, v);
4959
4960 for (local_ordinal_type row = 0; row < nrows; ++row) {
4961 auto v_1 = Kokkos::subview(X_internal_vector_values, r0 + row, Kokkos::ALL(), active_schur_solve_vec, v);
4962 auto E = Kokkos::subview(e_internal_vector_values, 0, r0 + row, Kokkos::ALL(), Kokkos::ALL(), v);
4963
4964 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4965 member,
4966 blocksize, blocksize,
4967 -one,
4968 E.data(), E.stride(0), E.stride(1),
4969 v_2.data(), v_2.stride(0),
4970 one,
4971 v_1.data(), v_1.stride(0));
4972 }
4973 });
4974 } else if (local_subpartidx == (local_ordinal_type)part2packrowidx0_sub.extent(1) - 2) {
4975 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4976 auto v_2 = Kokkos::subview(X_internal_vector_values, r0 - 1, Kokkos::ALL(), active_schur_solve_vec, v);
4977
4978 for (local_ordinal_type row = 0; row < nrows; ++row) {
4979 auto v_1 = Kokkos::subview(X_internal_vector_values, r0 + row, Kokkos::ALL(), active_schur_solve_vec, v);
4980 auto E = Kokkos::subview(e_internal_vector_values, 1, r0 + row, Kokkos::ALL(), Kokkos::ALL(), v);
4981
4982 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
4983 member,
4984 blocksize, blocksize,
4985 -one,
4986 E.data(), E.stride(0), E.stride(1),
4987 v_2.data(), v_2.stride(0),
4988 one,
4989 v_1.data(), v_1.stride(0));
4990 }
4991 });
4992 } else {
4993 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
4994 {
4995 auto v_2 = Kokkos::subview(X_internal_vector_values, r0 + nrows, Kokkos::ALL(), active_schur_solve_vec, v);
4996
4997 for (local_ordinal_type row = 0; row < nrows; ++row) {
4998 auto v_1 = Kokkos::subview(X_internal_vector_values, r0 + row, Kokkos::ALL(), active_schur_solve_vec, v);
4999 auto E = Kokkos::subview(e_internal_vector_values, 0, r0 + row, Kokkos::ALL(), Kokkos::ALL(), v);
5000
5001 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
5002 member,
5003 blocksize, blocksize,
5004 -one,
5005 E.data(), E.stride(0), E.stride(1),
5006 v_2.data(), v_2.stride(0),
5007 one,
5008 v_1.data(), v_1.stride(0));
5009 }
5010 }
5011 {
5012 auto v_2 = Kokkos::subview(X_internal_vector_values, r0 - 1, Kokkos::ALL(), active_schur_solve_vec, v);
5013
5014 for (local_ordinal_type row = 0; row < nrows; ++row) {
5015 auto v_1 = Kokkos::subview(X_internal_vector_values, r0 + row, Kokkos::ALL(), active_schur_solve_vec, v);
5016 auto E = Kokkos::subview(e_internal_vector_values, 1, r0 + row, Kokkos::ALL(), Kokkos::ALL(), v);
5017
5018 KOKKOSBATCHED_GEMV_NO_TRANSPOSE_INTERNAL_INVOKE(default_mode_type, default_algo_type,
5019 member,
5020 blocksize, blocksize,
5021 -one,
5022 E.data(), E.stride(0), E.stride(1),
5023 v_2.data(), v_2.stride(0),
5024 one,
5025 v_1.data(), v_1.stride(0));
5026 }
5027 }
5028 });
5029 }
5030 }
5031
5032 template <int B>
5033 KOKKOS_INLINE_FUNCTION void
5034 operator()(const CopyVectorToFlatTag<B> &, const member_type &member) const {
5035 const local_ordinal_type packidx = member.league_rank();
5036 const local_ordinal_type partidx = packptr(packidx);
5037 const local_ordinal_type npacks = packptr(packidx + 1) - partidx;
5038 const local_ordinal_type pri0 = part2packrowidx0(partidx);
5039 const local_ordinal_type blocksize = (B == 0 ? D_internal_vector_values.extent(1) : B);
5040 const local_ordinal_type num_vectors = X_internal_vector_values.extent(2);
5041
5042 Kokkos::parallel_for(Kokkos::ThreadVectorRange(member, vector_loop_size), [&](const int &v) {
5043 copyToFlatMultiVector(member, partidx, npacks, pri0, v, blocksize, num_vectors);
5044 });
5045 }
5046
5047 void run(const impl_scalar_type_2d_view_tpetra &Y,
5048 const impl_scalar_type_1d_view &Z) {
5049 IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_BEGIN;
5050 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::SolveTridiags", SolveTridiags);
5051
5053 this->Y_scalar_multivector = Y;
5054 this->Z_scalar_vector = Z;
5055
5056 const local_ordinal_type num_vectors = X_internal_vector_values.extent(2);
5057 const local_ordinal_type blocksize = D_internal_vector_values.extent(1);
5058
5059 const local_ordinal_type team_size =
5060 SolveTridiagsDefaultModeAndAlgo<typename execution_space::memory_space>::
5061 recommended_team_size(blocksize, vector_length, internal_vector_length);
5062 const int per_team_scratch = internal_vector_scratch_type_3d_view ::shmem_size(blocksize, num_vectors, vector_loop_size);
5063 const int max_scratch = team_policy_type::scratch_size_max(0);
5064
5065#define BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(B) \
5066 if (packindices_schur.extent(1) <= 0) { \
5067 if (num_vectors == 1) { \
5068 if (per_team_scratch < max_scratch) { \
5069 Kokkos::TeamPolicy<execution_space> \
5070 policy(packptr.extent(0) - 1, team_size, vector_loop_size); \
5071 policy.set_scratch_size(0, Kokkos::PerTeam(per_team_scratch)); \
5072 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<SingleVector,0>", \
5073 policy, \
5074 SingleVectorFunctor<B, 0>(*this)); \
5075 } else { \
5076 Kokkos::TeamPolicy<execution_space> \
5077 policy(packptr.extent(0) - 1, team_size, vector_loop_size); \
5078 policy.set_scratch_size(1, Kokkos::PerTeam(per_team_scratch)); \
5079 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<SingleVector,1>", \
5080 policy, \
5081 SingleVectorFunctor<B, 1>(*this)); \
5082 } \
5083 } else { \
5084 if (per_team_scratch < max_scratch) { \
5085 Kokkos::TeamPolicy<execution_space, MultiVectorTag<B, 0>> \
5086 policy(packptr.extent(0) - 1, team_size, vector_loop_size); \
5087 policy.set_scratch_size(0, Kokkos::PerTeam(per_team_scratch)); \
5088 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<MultiVector,0>", \
5089 policy, *this); \
5090 } else { \
5091 Kokkos::TeamPolicy<execution_space, MultiVectorTag<B, 1>> \
5092 policy(packptr.extent(0) - 1, team_size, vector_loop_size); \
5093 policy.set_scratch_size(1, Kokkos::PerTeam(per_team_scratch)); \
5094 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<MultiVector,1>", \
5095 policy, *this); \
5096 } \
5097 } \
5098 } else { \
5099 Kokkos::deep_copy(execution_space(), Z, impl_scalar_type(0)); \
5100 for (local_ordinal_type vec = 0; vec < num_vectors; vec++) { \
5101 this->active_schur_solve_vec = vec; \
5102 { \
5103 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::ApplyInverseJacobi::SingleVectorSubLineTag", SingleVectorSubLineTag0); \
5104 write4DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), X_internal_scalar_values, "x_scalar_values_before_SingleVectorSubLineTag.mm"); \
5105 if (per_team_scratch < max_scratch) { \
5106 Kokkos::TeamPolicy<execution_space, SingleVectorSubLineTag<B, 0>> \
5107 policy(packindices_sub.extent(0), team_size, vector_loop_size); \
5108 policy.set_scratch_size(0, Kokkos::PerTeam(per_team_scratch)); \
5109 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<SingleVector,0>", \
5110 policy, *this); \
5111 } else { \
5112 Kokkos::TeamPolicy<execution_space, SingleVectorSubLineTag<B, 1>> \
5113 policy(packindices_sub.extent(0), team_size, vector_loop_size); \
5114 policy.set_scratch_size(1, Kokkos::PerTeam(per_team_scratch)); \
5115 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<SingleVector,1>", \
5116 policy, *this); \
5117 } \
5118 write4DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), X_internal_scalar_values, "x_scalar_values_after_SingleVectorSubLineTag.mm"); \
5119 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space) \
5120 } \
5121 { \
5122 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::ApplyInverseJacobi::SingleVectorApplyCTag", SingleVectorApplyCTag0); \
5123 write4DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), X_internal_scalar_values, "x_scalar_values_before_SingleVectorApplyCTag.mm"); \
5124 Kokkos::TeamPolicy<execution_space, SingleVectorApplyCTag<B>> \
5125 policy(packindices_sub.extent(0), team_size, vector_loop_size); \
5126 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<SingleVector>", \
5127 policy, *this); \
5128 write4DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), X_internal_scalar_values, "x_scalar_values_after_SingleVectorApplyCTag.mm"); \
5129 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space) \
5130 } \
5131 { \
5132 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::ApplyInverseJacobi::SingleVectorSchurTag", SingleVectorSchurTag0); \
5133 write4DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), X_internal_scalar_values, "x_scalar_values_before_SingleVectorSchurTag.mm"); \
5134 if (per_team_scratch < max_scratch) { \
5135 Kokkos::TeamPolicy<execution_space, SingleVectorSchurTag<B, 0>> \
5136 policy(packindices_schur.extent(0), team_size, vector_loop_size); \
5137 policy.set_scratch_size(0, Kokkos::PerTeam(per_team_scratch)); \
5138 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<SingleVector,0>", \
5139 policy, *this); \
5140 } else { \
5141 Kokkos::TeamPolicy<execution_space, SingleVectorSchurTag<B, 1>> \
5142 policy(packindices_schur.extent(0), team_size, vector_loop_size); \
5143 policy.set_scratch_size(1, Kokkos::PerTeam(per_team_scratch)); \
5144 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<SingleVector,1>", \
5145 policy, *this); \
5146 } \
5147 write4DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), X_internal_scalar_values, "x_scalar_values_after_SingleVectorSchurTag.mm"); \
5148 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space) \
5149 } \
5150 { \
5151 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::ApplyInverseJacobi::SingleVectorApplyETag", SingleVectorApplyETag0); \
5152 write4DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), X_internal_scalar_values, "x_scalar_values_before_SingleVectorApplyETag.mm"); \
5153 Kokkos::TeamPolicy<execution_space, SingleVectorApplyETag<B>> \
5154 policy(packindices_sub.extent(0), team_size, vector_loop_size); \
5155 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<SingleVector>", \
5156 policy, *this); \
5157 write4DMultiVectorValuesToFile(part2packrowidx0_sub.extent(0), X_internal_scalar_values, "x_scalar_values_after_SingleVectorApplyETag.mm"); \
5158 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space) \
5159 } \
5160 } \
5161 { \
5162 Kokkos::TeamPolicy<execution_space, CopyVectorToFlatTag<B>> \
5163 policy(packptr.extent(0) - 1, team_size, vector_loop_size); \
5164 Kokkos::parallel_for("SolveTridiags::TeamPolicy::run<CopyVectorToFlatTag>", \
5165 policy, *this); \
5166 } \
5167 } \
5168 break
5169 switch (blocksize) {
5170 case 3: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(3);
5171 case 5: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(5);
5172 case 6: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(6);
5173 case 7: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(7);
5174 case 10: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(10);
5175 case 11: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(11);
5176 case 12: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(12);
5177 case 13: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(13);
5178 case 16: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(16);
5179 case 17: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(17);
5180 case 18: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(18);
5181 case 19: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(19);
5182 default: BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS(0);
5183 }
5184#undef BLOCKTRIDICONTAINER_DETAILS_SOLVETRIDIAGS
5185
5186 IFPACK2_BLOCKTRIDICONTAINER_PROFILER_REGION_END;
5187 IFPACK2_BLOCKHELPER_TIMER_FENCE(execution_space)
5188 }
5189};
5190
5194template <typename MatrixType>
5195int applyInverseJacobi( // importer
5196 const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_row_matrix_type> &A,
5197 const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_crs_graph_type> &G,
5198 const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_import_type> &tpetra_importer,
5199 const Teuchos::RCP<AsyncableImport<MatrixType>> &async_importer,
5200 const bool overlap_communication_and_computation,
5201 // tpetra interface
5202 const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_multivector_type &X, // tpetra interface
5203 /* */ typename BlockHelperDetails::ImplType<MatrixType>::tpetra_multivector_type &Y, // tpetra interface
5204 /* */ typename BlockHelperDetails::ImplType<MatrixType>::tpetra_multivector_type &Z, // temporary tpetra interface (seq_method)
5205 /* */ typename BlockHelperDetails::ImplType<MatrixType>::impl_scalar_type_1d_view &W, // temporary tpetra interface (diff)
5206 // local object interface
5207 const BlockHelperDetails::PartInterface<MatrixType> &interf, // mesh interface
5208 const BlockTridiags<MatrixType> &btdm, // packed block tridiagonal matrices
5209 const BlockHelperDetails::AmD<MatrixType> &amd, // R = A - D
5210 /* */ typename BlockHelperDetails::ImplType<MatrixType>::vector_type_1d_view &work, // workspace for packed multivector of right hand side
5212 // preconditioner parameters
5214 /* */ bool is_y_zero,
5215 const int max_num_sweeps,
5216 const typename BlockHelperDetails::ImplType<MatrixType>::magnitude_type tol,
5217 const int check_tol_every) {
5218 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::ApplyInverseJacobi", ApplyInverseJacobi);
5219
5221 using node_memory_space = typename impl_type::node_memory_space;
5222 using local_ordinal_type = typename impl_type::local_ordinal_type;
5223 using size_type = typename impl_type::size_type;
5224 using impl_scalar_type = typename impl_type::impl_scalar_type;
5225 using magnitude_type = typename impl_type::magnitude_type;
5226 using local_ordinal_type_1d_view = typename impl_type::local_ordinal_type_1d_view;
5227 using vector_type_1d_view = typename impl_type::vector_type_1d_view;
5228 using vector_type_3d_view = typename impl_type::vector_type_3d_view;
5229 using tpetra_multivector_type = typename impl_type::tpetra_multivector_type;
5230
5231 using impl_scalar_type_1d_view = typename impl_type::impl_scalar_type_1d_view;
5232
5233 // either tpetra importer or async importer must be active
5234 TEUCHOS_TEST_FOR_EXCEPT_MSG(!tpetra_importer.is_null() && !async_importer.is_null(),
5235 "Neither Tpetra importer nor Async importer is null.");
5236 // max number of sweeps should be positive number
5237 TEUCHOS_TEST_FOR_EXCEPT_MSG(max_num_sweeps <= 0,
5238 "Maximum number of sweeps must be >= 1.");
5239
5240 // const parameters
5241 const bool is_seq_method_requested = !tpetra_importer.is_null();
5242 const bool is_async_importer_active = !async_importer.is_null();
5243 const bool is_norm_manager_active = tol > KokkosKernels::ArithTraits<magnitude_type>::zero();
5244 const magnitude_type tolerance = tol * tol;
5245 const local_ordinal_type blocksize = btdm.values.extent(1);
5246 const local_ordinal_type num_vectors = Y.getNumVectors();
5247 const local_ordinal_type num_blockrows = interf.part2packrowidx0_back;
5248
5249 const impl_scalar_type zero(0.0);
5250
5251 TEUCHOS_TEST_FOR_EXCEPT_MSG(is_norm_manager_active && is_seq_method_requested,
5252 "The seq method for applyInverseJacobi, "
5253 << "which in any case is for developer use only, "
5254 << "does not support norm-based termination.");
5255 const bool device_accessible_from_host = Kokkos::SpaceAccessibility<
5256 Kokkos::DefaultHostExecutionSpace, node_memory_space>::accessible;
5257 TEUCHOS_TEST_FOR_EXCEPTION(is_seq_method_requested && !device_accessible_from_host,
5258 std::invalid_argument,
5259 "The seq method for applyInverseJacobi, "
5260 << "which in any case is for developer use only, "
5261 << "only supports memory spaces accessible from host.");
5262
5263 // if workspace is needed more, resize it
5264 const size_type work_span_required = num_blockrows * num_vectors * blocksize;
5265 if (work.span() < work_span_required)
5266 work = vector_type_1d_view("vector workspace 1d view", work_span_required);
5267
5268 // construct W
5269 const local_ordinal_type W_size = interf.packptr.extent(0) - 1;
5270 if (local_ordinal_type(W.extent(0)) < W_size)
5271 W = impl_scalar_type_1d_view("W", W_size);
5272
5273 typename impl_type::impl_scalar_type_2d_view_tpetra remote_multivector;
5274 {
5275 if (is_seq_method_requested) {
5276 if (Z.getNumVectors() != Y.getNumVectors())
5277 Z = tpetra_multivector_type(tpetra_importer->getTargetMap(), num_vectors, false);
5278 } else {
5279 if (is_async_importer_active) {
5280 // create comm data buffer and keep it here
5281 async_importer->createDataBuffer(num_vectors);
5282 remote_multivector = async_importer->getRemoteMultiVectorLocalView();
5283 }
5284 }
5285 }
5286
5287 // wrap the workspace with 3d view
5288 vector_type_3d_view pmv(work.data(), num_blockrows, blocksize, num_vectors);
5289 const auto XX = X.getLocalViewDevice(Tpetra::Access::ReadOnly);
5290 const auto YY = Y.getLocalViewDevice(Tpetra::Access::ReadWrite);
5291 const auto ZZ = Z.getLocalViewDevice(Tpetra::Access::ReadWrite);
5292 if (is_y_zero) Kokkos::deep_copy(YY, zero);
5293
5294 MultiVectorConverter<MatrixType> multivector_converter(interf, pmv);
5295 SolveTridiags<MatrixType> solve_tridiags(interf, btdm, pmv,
5296 damping_factor, is_norm_manager_active);
5297
5298 const local_ordinal_type_1d_view dummy_local_ordinal_type_1d_view;
5299
5300 auto A_crs = Teuchos::rcp_dynamic_cast<const typename impl_type::tpetra_crs_matrix_type>(A);
5301 auto A_bcrs = Teuchos::rcp_dynamic_cast<const typename impl_type::tpetra_block_crs_matrix_type>(A);
5302
5303 bool hasBlockCrsMatrix = !A_bcrs.is_null();
5304
5305 // This is OK here to use the graph of the A_crs matrix and a block size of 1
5306 const auto g = hasBlockCrsMatrix ? A_bcrs->getCrsGraph() : *(A_crs->getCrsGraph()); // tpetra crs graph object
5307
5308 BlockHelperDetails::ComputeResidualVector<MatrixType>
5309 compute_residual_vector(amd, G->getLocalGraphDevice(), g.getLocalGraphDevice(), blocksize, interf,
5310 is_async_importer_active ? async_importer->dm2cm : dummy_local_ordinal_type_1d_view,
5311 hasBlockCrsMatrix);
5312
5313 // norm manager workspace resize
5314 if (is_norm_manager_active)
5315 norm_manager.setCheckFrequency(check_tol_every);
5316
5317 // iterate
5318 int sweep = 0;
5319 for (; sweep < max_num_sweeps; ++sweep) {
5320 {
5321 if (is_y_zero) {
5322 // pmv := x(lclrow)
5323 multivector_converter.run(XX);
5324 } else {
5325 if (is_seq_method_requested) {
5326 // SEQ METHOD IS TESTING ONLY
5327
5328 // y := x - R y
5329 Z.doImport(Y, *tpetra_importer, Tpetra::REPLACE);
5330 compute_residual_vector.run(YY, XX, ZZ);
5331
5332 // pmv := y(lclrow).
5333 multivector_converter.run(YY);
5334 } else {
5335 // fused y := x - R y and pmv := y(lclrow);
5336 // real use case does not use overlap comp and comm
5337 if (overlap_communication_and_computation || !is_async_importer_active) {
5338 if (is_async_importer_active) async_importer->asyncSendRecv(YY);
5339 // OverlapTag, compute_owned = true
5340 compute_residual_vector.run(pmv, XX, YY, remote_multivector, true);
5341 if (is_norm_manager_active && norm_manager.checkDone(sweep, tolerance)) {
5342 if (is_async_importer_active) async_importer->cancel();
5343 break;
5344 }
5345 if (is_async_importer_active) {
5346 async_importer->syncRecv();
5347 // OverlapTag, compute_owned = false
5348 compute_residual_vector.run(pmv, XX, YY, remote_multivector, false);
5349 }
5350 } else {
5351 if (is_async_importer_active)
5352 async_importer->syncExchange(YY);
5353 if (is_norm_manager_active && norm_manager.checkDone(sweep, tolerance)) break;
5354 // AsyncTag
5355 compute_residual_vector.run(pmv, XX, YY, remote_multivector);
5356 }
5357 }
5358 }
5359 }
5360
5361 // pmv := inv(D) pmv.
5362 {
5363 solve_tridiags.run(YY, W);
5364 }
5365 {
5366 if (is_norm_manager_active) {
5367 // y(lclrow) = (b - a) y(lclrow) + a pmv, with b = 1 always.
5368 BlockHelperDetails::reduceVector<MatrixType>(W, norm_manager.getBuffer());
5369 if (sweep + 1 == max_num_sweeps) {
5370 norm_manager.ireduce(sweep, true);
5371 norm_manager.checkDone(sweep + 1, tolerance, true);
5372 } else {
5373 norm_manager.ireduce(sweep);
5374 }
5375 }
5376 }
5377 is_y_zero = false;
5378 }
5379
5380 // sqrt the norms for the caller's use.
5381 if (is_norm_manager_active) norm_manager.finalize();
5382
5383 return sweep;
5384}
5385
5389template <typename MatrixType>
5390int applyFusedBlockJacobi(
5391 const Teuchos::RCP<const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_import_type> &tpetra_importer,
5392 const Teuchos::RCP<AsyncableImport<MatrixType>> &async_importer,
5393 const bool overlap_communication_and_computation,
5394 // tpetra interface
5395 const typename BlockHelperDetails::ImplType<MatrixType>::tpetra_multivector_type &X, // tpetra interface
5396 /* */ typename BlockHelperDetails::ImplType<MatrixType>::tpetra_multivector_type &Y, // tpetra interface
5397 /* */ typename BlockHelperDetails::ImplType<MatrixType>::impl_scalar_type_1d_view &W, // temporary tpetra interface (diff)
5398 // local object interface
5399 const BlockHelperDetails::PartInterface<MatrixType> &interf, // mesh interface
5400 const BlockTridiags<MatrixType> &btdm, // packed block tridiagonal matrices
5401 const BlockHelperDetails::AmD<MatrixType> &amd, // R = A - D
5402 /* */ typename BlockHelperDetails::ImplType<MatrixType>::impl_scalar_type_1d_view &work, // workspace
5404 // preconditioner parameters
5406 /* */ bool is_y_zero,
5407 const int max_num_sweeps,
5408 const typename BlockHelperDetails::ImplType<MatrixType>::magnitude_type tol,
5409 const int check_tol_every) {
5411 using local_ordinal_type = typename impl_type::local_ordinal_type;
5412 using size_type = typename impl_type::size_type;
5413 using magnitude_type = typename impl_type::magnitude_type;
5414 using impl_scalar_type_1d_view = typename impl_type::impl_scalar_type_1d_view;
5415 using impl_scalar_type_2d_view_tpetra = typename impl_type::impl_scalar_type_2d_view_tpetra;
5416
5417 IFPACK2_BLOCKHELPER_TIMER("BlockTriDi::ApplyFusedBlockJacobi", ApplyFusedBlockJacobi);
5418
5419 // the tpetra importer and async importer can't both be active
5420 TEUCHOS_TEST_FOR_EXCEPT_MSG(!tpetra_importer.is_null() && !async_importer.is_null(),
5421 "Neither Tpetra importer nor Async importer is null.");
5422 // max number of sweeps should be positive number
5423 TEUCHOS_TEST_FOR_EXCEPT_MSG(max_num_sweeps <= 0,
5424 "Maximum number of sweeps must be >= 1.");
5425
5426 // const parameters
5427 const bool is_async_importer_active = !async_importer.is_null();
5428 const bool is_norm_manager_active = tol > KokkosKernels::ArithTraits<magnitude_type>::zero();
5429 const magnitude_type tolerance = tol * tol;
5430 const local_ordinal_type blocksize = btdm.d_inv.extent(1);
5431 const local_ordinal_type num_vectors = Y.getNumVectors();
5432 const local_ordinal_type num_blockrows = interf.nparts;
5433
5434 typename impl_type::impl_scalar_type_2d_view_tpetra remote_multivector;
5435 {
5436 if (is_async_importer_active) {
5437 // create comm data buffer and keep it here
5438 async_importer->createDataBuffer(num_vectors);
5439 remote_multivector = async_importer->getRemoteMultiVectorLocalView();
5440 }
5441 }
5442
5443 const auto XX = X.getLocalViewDevice(Tpetra::Access::ReadOnly);
5444 const auto YY = Y.getLocalViewDevice(Tpetra::Access::ReadWrite);
5445
5446 const bool two_pass_residual =
5447 overlap_communication_and_computation && is_async_importer_active;
5448
5449 // Calculate the required work size and reallocate it if not already big enough.
5450 // Check that our assumptions about YY dimension are correct.
5451 TEUCHOS_TEST_FOR_EXCEPT_MSG(
5452 size_t(num_blockrows) * blocksize * num_vectors != YY.extent(0) * YY.extent(1),
5453 "Local LHS vector (YY) has total size " << YY.extent(0) << "x" << YY.extent(1) << " = " << YY.extent(0) * YY.extent(1) << ",\n"
5454 << "but expected " << num_blockrows << "x" << blocksize << "x" << num_vectors << " = " << size_t(num_blockrows) * blocksize * num_vectors << '\n');
5455 size_type work_required = size_type(num_blockrows) * blocksize * num_vectors;
5456 if (work.extent(0) < work_required) {
5457 work = impl_scalar_type_1d_view(do_not_initialize_tag("flat workspace 1d view"), work_required);
5458 }
5459
5460 Unmanaged<impl_scalar_type_2d_view_tpetra> y_doublebuf(work.data(), num_blockrows * blocksize, num_vectors);
5461
5462 // construct W
5463 if (W.extent(0) != size_t(num_blockrows))
5464 W = impl_scalar_type_1d_view(do_not_initialize_tag("W"), num_blockrows);
5465
5466 BlockHelperDetails::ComputeResidualAndSolve<MatrixType>
5467 residualAndSolve(amd, btdm.d_inv, W, blocksize, damping_factor);
5468
5469 // norm manager workspace resize
5470 if (is_norm_manager_active)
5471 norm_manager.setCheckFrequency(check_tol_every);
5472
5473 // For double-buffering.
5474 // yy_buffers[current_y] has the current iterate of y.
5475 // yy_buffers[1-current_y] has the next iterate of y.
5476 Unmanaged<impl_scalar_type_2d_view_tpetra> y_buffers[2] = {YY, y_doublebuf};
5477 int current_y = 0;
5478
5479 // iterate
5480 int sweep = 0;
5481 for (; sweep < max_num_sweeps; ++sweep) {
5482 if (is_y_zero) {
5483 // If y is initially zero, then we are just computing y := damping_factor * Dinv * x
5484 residualAndSolve.run_y_zero(XX, y_buffers[1 - current_y]);
5485 } else {
5486 // real use case does not use overlap comp and comm
5487 if (overlap_communication_and_computation || !is_async_importer_active) {
5488 if (is_async_importer_active) async_importer->asyncSendRecv(y_buffers[current_y]);
5489 if (two_pass_residual) {
5490 // Pass 1 computes owned residual and stores into new y buffer,
5491 // but doesn't apply Dinv or produce a norm yet
5492 residualAndSolve.run_pass1_of_2(XX, y_buffers[current_y], y_buffers[1 - current_y]);
5493 } else {
5494 // This case happens if running with single rank.
5495 // There are no remote columns, so residual and solve can happen in one step.
5496 residualAndSolve.run_single_pass(XX, y_buffers[current_y], remote_multivector, y_buffers[1 - current_y]);
5497 }
5498 if (is_norm_manager_active && norm_manager.checkDone(sweep, tolerance)) {
5499 if (is_async_importer_active) async_importer->cancel();
5500 break;
5501 }
5502 if (is_async_importer_active) {
5503 async_importer->syncRecv();
5504 // Stage 2 finishes computing the residual, then applies Dinv and computes norm.
5505 residualAndSolve.run_pass2_of_2(y_buffers[current_y], remote_multivector, y_buffers[1 - current_y]);
5506 }
5507 } else {
5508 if (is_async_importer_active)
5509 async_importer->syncExchange(y_buffers[current_y]);
5510 if (is_norm_manager_active && norm_manager.checkDone(sweep, tolerance)) break;
5511 // Full residual, Dinv apply, and norm in one kernel
5512 residualAndSolve.run_single_pass(XX, y_buffers[current_y], remote_multivector, y_buffers[1 - current_y]);
5513 }
5514 }
5515
5516 // Compute global norm.
5517 if (is_norm_manager_active) {
5518 BlockHelperDetails::reduceVector<MatrixType>(W, norm_manager.getBuffer());
5519 if (sweep + 1 == max_num_sweeps) {
5520 norm_manager.ireduce(sweep, true);
5521 norm_manager.checkDone(sweep + 1, tolerance, true);
5522 } else {
5523 norm_manager.ireduce(sweep);
5524 }
5525 }
5526 is_y_zero = false;
5527 // flip y buffers for next iteration, or termination if we reached max_num_sweeps.
5528 current_y = 1 - current_y;
5529 }
5530 if (current_y == 1) {
5531 // We finished iterating with y in the double buffer, so copy it to the user's vector.
5532 Kokkos::deep_copy(YY, y_doublebuf);
5533 }
5534
5535 // sqrt the norms for the caller's use.
5536 if (is_norm_manager_active) norm_manager.finalize();
5537
5538 return sweep;
5539}
5540
5541template <typename MatrixType>
5544 using part_interface_type = BlockHelperDetails::PartInterface<MatrixType>;
5545 using block_tridiags_type = BlockTridiags<MatrixType>;
5546 using amd_type = BlockHelperDetails::AmD<MatrixType>;
5547 using norm_manager_type = BlockHelperDetails::NormManager<MatrixType>;
5548 using async_import_type = AsyncableImport<MatrixType>;
5549
5550 // distructed objects
5551 Teuchos::RCP<const typename impl_type::tpetra_row_matrix_type> A;
5552 Teuchos::RCP<const typename impl_type::tpetra_crs_graph_type> blockGraph;
5553 Teuchos::RCP<const typename impl_type::tpetra_import_type> tpetra_importer;
5554 Teuchos::RCP<async_import_type> async_importer;
5555 bool overlap_communication_and_computation;
5556
5557 // copy of Y (mutable to penentrate const)
5558 mutable typename impl_type::tpetra_multivector_type Z;
5559 mutable typename impl_type::impl_scalar_type_1d_view W;
5560
5561 // local objects
5562 part_interface_type part_interface;
5563 block_tridiags_type block_tridiags; // D
5564 amd_type a_minus_d; // R = A - D
5565
5566 // whether to use fused block Jacobi path
5567 bool use_fused_jacobi;
5568
5569 // vector workspace is used for general block tridi case
5570 mutable typename impl_type::vector_type_1d_view work; // right hand side workspace (1D view of vector)
5571 // scalar workspace is used for fused block jacobi case
5572 mutable typename impl_type::impl_scalar_type_1d_view work_flat; // right hand side workspace (1D view of scalar)
5573 mutable norm_manager_type norm_manager;
5574};
5575
5576} // namespace BlockTriDiContainerDetails
5577
5578} // namespace Ifpack2
5579
5580#endif
Preconditioners and smoothers for Tpetra sparse matrices.
Definition Ifpack2_AdditiveSchwarz_decl.hpp:40
Definition Ifpack2_BlockHelper.hpp:421
Definition Ifpack2_BlockHelper.hpp:313
size_t size_type
Definition Ifpack2_BlockHelper.hpp:317
KokkosKernels::ArithTraits< scalar_type >::val_type impl_scalar_type
Definition Ifpack2_BlockHelper.hpp:327
Kokkos::View< size_type *, device_type > size_type_1d_view
Definition Ifpack2_BlockHelper.hpp:390
Definition Ifpack2_BlockHelper.hpp:519
Definition Ifpack2_BlockTriDiContainer_impl.hpp:140
Definition Ifpack2_BlockTriDiContainer_impl.hpp:1605
forward declaration
Definition Ifpack2_BlockTriDiContainer_impl.hpp:5542