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