Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_SpMatrix.H
Go to the documentation of this file.
1#ifndef AMREX_SP_MATRIX_H_
2#define AMREX_SP_MATRIX_H_
3#include <AMReX_Config.H>
4
6#include <AMReX_AlgVector.H>
7#include <AMReX_CSR.H>
8#include <AMReX_Gpu.H>
9#include <AMReX_Scan.H>
10
11#if defined(AMREX_USE_CUDA)
12# include <cusparse.h>
13#elif defined(AMREX_USE_HIP)
14# include <rocsparse/rocsparse.h>
15#elif defined(AMREX_USE_SYCL)
16# include <mkl_version.h>
17# include <oneapi/mkl/spblas.hpp>
18#endif
19
20#include <fstream>
21#include <string>
22#include <type_traits>
23#include <unordered_map>
24
25namespace amrex {
26
34template <typename T>
35struct ParCsr {
36 CsrView<T> csr0; // diagonal part
37 CsrView<T> csr1; // off-diagonal part
38 Long row_begin = 0; // global row index begin
39 Long col_begin = 0; // global col index begin
40 Long const* AMREX_RESTRICT row_map = nullptr; // mapping from csr0's row to csr1
41 Long const* AMREX_RESTRICT col_map = nullptr; // mapping from csr1's col to global
42};
43
45struct CsrSorted {
46 bool b = true;
47 explicit operator bool() const { return b; }
48};
49
51struct CsrValid {
52 bool b = true;
53 explicit operator bool() const { return b; }
54};
55
59template <typename T, template<typename> class Allocator = DefaultAllocator>
61{
62public:
63 using value_type = T;
64 template <class U> using allocator_type = Allocator<U>;
65 template <class U> using container_type = PODVector<U,Allocator<U> >;
67 using AllocT = Allocator<T>;
68
69 SpMatrix () = default;
70
89 SpMatrix (AlgPartition partition, int nnz_per_row);
90
102
103 SpMatrix (SpMatrix const&) = delete;
104 SpMatrix& operator= (SpMatrix const&) = delete;
105
106 SpMatrix (SpMatrix &&) = default;
108
109 ~SpMatrix () = default;
110
125 void define (AlgPartition partition, int nnz_per_row);
126
159 void define (AlgPartition partition, T const* mat, Long const* col_index,
160 Long nentries, Long const* row_offset, CsrSorted is_sorted,
161 CsrValid is_valid);
162
174 void define (AlgPartition partition, csr_type csr, CsrSorted is_sorted);
175
177 [[nodiscard]] AlgPartition const& partition () const { return m_partition; }
178
186 [[nodiscard]] AlgPartition const& columnPartition () const { return m_col_partition; }
187
189 [[nodiscard]] Long numLocalRows () const { return m_row_end - m_row_begin; }
191 [[nodiscard]] Long numGlobalRows () const { return m_partition.numGlobalRows(); }
193 [[nodiscard]] Long numLocalNonZeros () const { return m_nnz; }
194
196 [[nodiscard]] Long globalRowBegin () const { return m_row_begin; }
198 [[nodiscard]] Long globalRowEnd () const { return m_row_end; }
199
201 [[nodiscard]] T* data () {
202 AMREX_ALWAYS_ASSERT(m_split == false);
203 return m_csr.mat.data();
204 }
205
207 [[nodiscard]] Long* columnIndex () {
208 AMREX_ALWAYS_ASSERT(m_split == false);
209 return m_csr.col_index.data();
210 }
211
213 [[nodiscard]] Long* rowOffset () {
214 AMREX_ALWAYS_ASSERT(m_split == false);
215 return m_csr.row_offset.data();
216 }
217
218 /*
219 * \brief Print the matrix to files.
220 *
221 * Each process writes its local portion of the matrix to a separate
222 * file. This function is provided for debugging purpose. Using it in
223 * large-scale runs may overwhelm the file system.
224 *
225 * File format:
226 * - The first line contains three integers describing the local matrix:
227 * the global row begin index, the global row end index, and the
228 * number of local nonzeros.
229 * - This is followed by one line per nonzero entry. Each line contains:
230 * the global row index, global column index, and matrix value.
231 *
232 * \param file Base file name. The full name on process `i` is `{file}.{i}`.
233 */
234 void printToFile (std::string const& file) const;
235
258 template <typename F>
259 void setVal (F const& f, CsrSorted is_sorted);
260
263 void sortCSR ();
264
270 [[nodiscard]] AlgVector<T,AllocT> const& diagonalVector () const;
271
278 [[nodiscard]] AlgVector<T,AllocT> rowSum () const;
279
285 [[nodiscard]] ParCsr<T > parcsr () ;
287 [[nodiscard]] ParCsr<T const> parcsr () const;
289 [[nodiscard]] ParCsr<T const> const_parcsr () const;
290
291 template <typename U, template<typename> class M, typename N> friend
293
294 template <typename U, template<typename> class M> friend
296
297 template <typename U> friend class AMG;
298
300 void define_doit (int nnz_per_row);
301
311 template <typename I>
312 void define_and_filter_doit (T const* mat, Long const* col_index,
313 Long nentries, Long const* row_offset);
314
320 void startComm_mv (AlgVector<T,AllocT> const& x);
323
325 void startComm_tr (AlgPartition const& col_partition);
328
329private:
330
331 void set_num_neighbors ();
332
333 AlgPartition m_partition;
334 AlgPartition m_col_partition;
335 Long m_row_begin = 0;
336 Long m_row_end = 0;
337 Long m_col_begin = 0;
338 Long m_col_end = 0;
339 Long m_nnz = 0;
340 csr_type m_csr;
341
342 mutable AlgVector<T,AllocT> m_diagonal;
343
344 bool m_split = false; // Has the matrix been split into diagonal and off-diagonal parts?
345
346#ifdef AMREX_USE_MPI
347 csr_type m_csr_remote;
348
349 // It should be noted that m_csr and m_csr_remote may have different
350 // number of rows, because some rows may be purely local and they do not
351 // appear in m_csr_remote. Thus, we need a mapping from local row index
352 // in m_csr_remote to local row index in m_csr. Then we will be able to
353 // know its global row index by adding m_row_begin. For example,
354 // m_row_begin + m_ri_rtol[i] is the global index for local row i in
355 // m_csr_remote.
356 container_type<Long> m_ri_rtol; // size: m_csr_remote.nrows()
357
358 // This is local row index mapping from m_csr to m_csr_remote. -1 means
359 // the row does not exist in m_csr_remote.
360 container_type<Long> m_ri_ltor; // size: m_csr.nrows()
361
362 // For column index, we also need to be careful with local vs. global,
363 // and there two types of locals: local in m_csr and local in
364 // m_csr_remote. For m_csr, col_index is the global index if m_split is
365 // false, and it becomes the local index if m_split is true. The
366 // conversion is global_col_index = local_col_index + m_col_begin.
367 //
368 // For m_csr_remote, col_index is also local. For a give col_index j,
369 // m_remote_cols_v[j] gives us the global index.
370 Vector<Long> m_remote_cols_v;
371#ifdef AMREX_USE_GPU
372 container_type<Long> m_remote_cols_dv;
373#endif
374 // The size of outer vector is # of procs. The indices are global.
375 Vector<Vector<Long>> m_remote_cols_vv;
376
377 int m_num_neighbors = -1; // No. of other processes involved in communication
378
379public: // NOLINT Private functions, but public for cuda
380
399
400 struct CommTR {
401 CsrView<T> csrt; // Own the memory inside
402
406
410
411 std::array<Long,2> total_counts_recv = {0,0};
413 // The raw pointers below are owning.
414 // xxxxx TODO GPU: Currently we use pinned memory. In the future, we
415 // may explore device memory for GPU aware MPI.
416 T* recv_buffer_mat = nullptr;
419 Long* recv_buffer_idx_map = nullptr; // local -> global for row of A^T.
420
422
423 void split_csr (AlgPartition const& col_partition);
424 template <typename C>
425 void update_remote_col_index (C& csrr, bool in_device_memory);
426 void prepare_comm_mv (AlgPartition const& col_partition);
427 void pack_buffer_mv (AlgVector<T,AllocT> const& v);
429 void unpack_buffer_tr (CommTR const& ctr, AlgPartition const& col_partition);
430
431#endif
432};
433
434namespace detail {
435
436// Communication may be skipped only when all rows and all columns live on
437// one common rank. Two partitions with a single active rank each may still
438// name different ranks.
439inline bool spmat_comm_is_local (AlgPartition const& row_partition,
440 AlgPartition const& col_partition)
441{
442 int const rp = row_partition.singleActiveProc();
443 int const cp = col_partition.singleActiveProc();
444 return row_partition.numActiveProcs() <= 1
445 && col_partition.numActiveProcs() <= 1
446 && (rp < 0 || cp < 0 || rp == cp);
447}
448
449template <typename T>
450void transpose (CsrView<T> const& csrt, CsrView<T const> const& csr)
451{
452 Long nrows = csr.nrows;
453 Long ncols = csrt.nrows;
454 Long nnz = csr.nnz;
455
456 if (nrows <= 0 || ncols <= 0 || nnz <= 0) {
457 if (ncols > 0) {
458 auto* p = csrt.row_offset;
459 ParallelForOMP(ncols+1, [=] AMREX_GPU_DEVICE (Long i) { p[i] = 0; });
460 }
461 return;
462 }
463
464#ifdef AMREX_USE_GPU
465
466#if defined(AMREX_USE_CUDA)
467
468 cusparseHandle_t handle;
469 AMREX_CUSPARSE_SAFE_CALL(cusparseCreate(&handle));
470 AMREX_CUSPARSE_SAFE_CALL(cusparseSetStream(handle, Gpu::gpuStream()));
471
472 cudaDataType data_type;
473 if constexpr (std::is_same_v<T,float>) {
474 data_type = CUDA_R_32F;
475 } else if constexpr (std::is_same_v<T,double>) {
476 data_type = CUDA_R_64F;
477 } else if constexpr (std::is_same_v<T,GpuComplex<float>>) {
478 data_type = CUDA_C_32F;
479 } else if constexpr (std::is_same_v<T,GpuComplex<double>>) {
480 data_type = CUDA_C_64F;
481 } else {
482 amrex::Abort("SpMatrix transpose: unsupported data type");
483 }
484
485 AMREX_ALWAYS_ASSERT(nrows < Long(std::numeric_limits<int>::max()) &&
486 ncols < Long(std::numeric_limits<int>::max()) &&
487 nnz < Long(std::numeric_limits<int>::max()));
488
489 auto* csr_col_index = (int*)The_Arena()->alloc(csr.nnz*sizeof(int));
490 auto* csr_row_offset = (int*)The_Arena()->alloc((csr.nrows+1)*sizeof(int));
491 auto* csrt_col_index = (int*)The_Arena()->alloc(csrt.nnz*sizeof(int));
492 auto* csrt_row_offset = (int*)The_Arena()->alloc((csrt.nrows+1)*sizeof(int));
493
494 ParallelFor(std::max(csr.nnz, csr.nrows+1), [=] AMREX_GPU_DEVICE (Long i)
495 {
496 if (i < csr.nnz) {
497 csr_col_index[i] = int(csr.col_index[i]);
498 }
499 if (i < csr.nrows+1) {
500 csr_row_offset[i] = int(csr.row_offset[i]);
501 }
502 });
503
504 std::size_t buffer_size;
506 cusparseCsr2cscEx2_bufferSize(handle, int(nrows), int(ncols), int(nnz),
507 csr.mat, csr_row_offset, csr_col_index,
508 csrt.mat, csrt_row_offset, csrt_col_index,
509 data_type, CUSPARSE_ACTION_NUMERIC,
510 CUSPARSE_INDEX_BASE_ZERO,
511 CUSPARSE_CSR2CSC_ALG1,
512 &buffer_size));
513
514 auto* pbuffer = (void*)The_Arena()->alloc(buffer_size);
515
517 cusparseCsr2cscEx2(handle, int(nrows), int(ncols), int(nnz),
518 csr.mat, csr_row_offset, csr_col_index,
519 csrt.mat, csrt_row_offset, csrt_col_index,
520 data_type, CUSPARSE_ACTION_NUMERIC,
521 CUSPARSE_INDEX_BASE_ZERO,
522 CUSPARSE_CSR2CSC_ALG1,
523 pbuffer));
524
525 ParallelFor(std::max(csrt.nnz, csrt.nrows+1), [=] AMREX_GPU_DEVICE (Long i)
526 {
527 if (i < csrt.nnz) {
528 csrt.col_index[i] = csrt_col_index[i];
529 }
530 if (i < csrt.nrows+1) {
531 csrt.row_offset[i] = csrt_row_offset[i];
532 }
533 });
534
536 AMREX_CUSPARSE_SAFE_CALL(cusparseDestroy(handle));
537 The_Arena()->free(pbuffer);
538 The_Arena()->free(csr_row_offset);
539 The_Arena()->free(csr_col_index);
540 The_Arena()->free(csrt_row_offset);
541 The_Arena()->free(csrt_col_index);
542
543#elif defined(AMREX_USE_HIP)
544
545 rocsparse_handle handle;
546 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_create_handle(&handle));
547 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_set_stream(handle, Gpu::gpuStream()));
548
549 AMREX_ALWAYS_ASSERT(nrows < Long(std::numeric_limits<rocsparse_int>::max()) &&
550 ncols < Long(std::numeric_limits<rocsparse_int>::max()) &&
551 nnz < Long(std::numeric_limits<rocsparse_int>::max()));
552
553 rocsparse_int* csr_col_index;
554 rocsparse_int* csr_row_offset;
555 rocsparse_int* csrt_col_index;
556 rocsparse_int* csrt_row_offset;
557 if (std::is_same_v<rocsparse_int,Long>) {
558 csr_col_index = (rocsparse_int*)csr.col_index;
559 csr_row_offset = (rocsparse_int*)csr.row_offset;
560 csrt_col_index = (rocsparse_int*)csrt.col_index;
561 csrt_row_offset = (rocsparse_int*)csrt.row_offset;
562 } else {
563 csr_col_index = (rocsparse_int*)The_Arena()->alloc(csr.nnz*sizeof(rocsparse_int));
564 csr_row_offset = (rocsparse_int*)The_Arena()->alloc((csr.nrows+1)*sizeof(rocsparse_int));
565 csrt_col_index = (rocsparse_int*)The_Arena()->alloc(csrt.nnz*sizeof(rocsparse_int));
566 csrt_row_offset = (rocsparse_int*)The_Arena()->alloc((csrt.nrows+1)*sizeof(rocsparse_int));
567 ParallelFor(std::max(csr.nnz, csr.nrows+1), [=] AMREX_GPU_DEVICE (Long i)
568 {
569 if (i < csr.nnz) {
570 csr_col_index[i] = rocsparse_int(csr.col_index[i]);
571 }
572 if (i < csr.nrows+1) {
573 csr_row_offset[i] = rocsparse_int(csr.row_offset[i]);
574 }
575 });
576 }
577
578 std::size_t buffer_size;
579 AMREX_ROCSPARSE_SAFE_CALL(
580 rocsparse_csr2csc_buffer_size(handle, rocsparse_int(nrows),
581 rocsparse_int(ncols), rocsparse_int(nnz),
582 csr_row_offset, csr_col_index,
583 rocsparse_action_numeric,
584 &buffer_size));
585
586 auto* pbuffer = (void*)The_Arena()->alloc(buffer_size);
587
588 if constexpr (std::is_same_v<T,float>) {
589 AMREX_ROCSPARSE_SAFE_CALL(
590 rocsparse_scsr2csc(handle, rocsparse_int(nrows),
591 rocsparse_int(ncols), rocsparse_int(nnz),
592 csr.mat, csr_row_offset, csr_col_index,
593 csrt.mat, csrt_col_index, csrt_row_offset,
594 rocsparse_action_numeric,
595 rocsparse_index_base_zero,
596 pbuffer));
597 } else if constexpr (std::is_same_v<T,double>) {
598 AMREX_ROCSPARSE_SAFE_CALL(
599 rocsparse_dcsr2csc(handle, rocsparse_int(nrows),
600 rocsparse_int(ncols), rocsparse_int(nnz),
601 csr.mat, csr_row_offset, csr_col_index,
602 csrt.mat, csrt_col_index, csrt_row_offset,
603 rocsparse_action_numeric,
604 rocsparse_index_base_zero,
605 pbuffer));
606 } else if constexpr (std::is_same_v<T,GpuComplex<float>>) {
607 AMREX_ROCSPARSE_SAFE_CALL(
608 rocsparse_ccsr2csc(handle, rocsparse_int(nrows),
609 rocsparse_int(ncols), rocsparse_int(nnz),
610 (rocsparse_float_complex*)csr.mat, csr_row_offset, csr_col_index,
611 (rocsparse_float_complex*)csrt.mat, csrt_col_index, csrt_row_offset,
612 rocsparse_action_numeric,
613 rocsparse_index_base_zero,
614 pbuffer));
615 } else if constexpr (std::is_same_v<T,GpuComplex<double>>) {
616 AMREX_ROCSPARSE_SAFE_CALL(
617 rocsparse_zcsr2csc(handle, rocsparse_int(nrows),
618 rocsparse_int(ncols), rocsparse_int(nnz),
619 (rocsparse_double_complex*)csr.mat, csr_row_offset, csr_col_index,
620 (rocsparse_double_complex*)csrt.mat, csrt_col_index, csrt_row_offset,
621 rocsparse_action_numeric,
622 rocsparse_index_base_zero,
623 pbuffer));
624 } else {
625 amrex::Abort("SpMatrix transpose: unsupported data type");
626 }
627
628 if (! std::is_same_v<rocsparse_int,Long>) {
629 ParallelFor(std::max(csrt.nnz, csrt.nrows+1), [=] AMREX_GPU_DEVICE (Long i)
630 {
631 if (i < csrt.nnz) {
632 csrt.col_index[i] = csrt_col_index[i];
633 }
634 if (i < csrt.nrows+1) {
635 csrt.row_offset[i] = csrt_row_offset[i];
636 }
637 });
638 }
639
641 AMREX_ROCSPARSE_SAFE_CALL(rocsparse_destroy_handle(handle));
642 The_Arena()->free(pbuffer);
643 if (! std::is_same_v<rocsparse_int,Long>) {
644 The_Arena()->free(csr_row_offset);
645 The_Arena()->free(csr_col_index);
646 The_Arena()->free(csrt_row_offset);
647 The_Arena()->free(csrt_col_index);
648 }
649
650#elif defined(AMREX_USE_SYCL)
651
652 mkl::sparse::matrix_handle_t handle_in{};
653 mkl::sparse::matrix_handle_t handle_out{};
654 mkl::sparse::init_matrix_handle(&handle_in);
655 mkl::sparse::init_matrix_handle(&handle_out);
656
657#if defined(INTEL_MKL_VERSION) && (INTEL_MKL_VERSION < 20250300)
659 mkl::sparse::set_csr_data(Gpu::Device::streamQueue(), handle_in, nrows, ncols,
660 mkl::index_base::zero, (Long*)csr.row_offset,
661 (Long*)csr.col_index, (T*)csr.mat);
662 mkl::sparse::set_csr_data(Gpu::Device::streamQueue(), handle_out, ncols, nrows,
663 mkl::index_base::zero, (Long*)csrt.row_offset,
664 (Long*)csrt.col_index, (T*)csrt.mat);
665#else
666 mkl::sparse::set_csr_data(Gpu::Device::streamQueue(), handle_in, nrows, ncols, nnz,
667 mkl::index_base::zero, (Long*)csr.row_offset,
668 (Long*)csr.col_index, (T*)csr.mat);
669 mkl::sparse::set_csr_data(Gpu::Device::streamQueue(), handle_out, ncols, nrows, nnz,
670 mkl::index_base::zero, (Long*)csrt.row_offset,
671 (Long*)csrt.col_index, (T*)csrt.mat);
672#endif
673
674 mkl::sparse::omatcopy(Gpu::Device::streamQueue(), mkl::transpose::trans,
675 handle_in, handle_out);
676
677 mkl::sparse::release_matrix_handle(Gpu::Device::streamQueue(), &handle_in);
678 auto ev = mkl::sparse::release_matrix_handle(Gpu::Device::streamQueue(), &handle_out);
679 ev.wait();
680
681#endif
682
684
685#else
686
687 auto* p = csrt.row_offset;
688
689 ParallelForOMP(ncols+1, [=] AMREX_GPU_DEVICE (Long i) { p[i] = 0; });
690
691 // nonzeros per column
692#ifdef AMREX_USE_OMP
693#pragma omp parallel for
694#endif
695 for (Long i = 0; i < nnz; ++i) {
696 auto col = csr.col_index[i];
697#ifdef AMREX_USE_OMP
698#pragma omp atomic update
699#endif
700 ++p[col+1];
701 }
702
703 // build row_offset for transposed matrix. Also save a copy.
704 Vector<Long> current_pos(ncols+1);
705 current_pos[0] = 0;
706 for (Long i = 0; i < ncols; ++i) {
707 p[i+1] += p[i];
708 current_pos[i+1] = p[i+1];
709 }
710
711 // The following code is not OMP safe. It's difficult to use OMP and
712 // still keep CSR sorted.
713 for (Long i = 0; i < nrows; ++i) {
714 for (Long idx = csr.row_offset[i]; idx < csr.row_offset[i+1]; ++idx) {
715 auto col = csr.col_index[idx];
716 Long dest = current_pos[col]++;
717 csrt.mat[dest] = csr.mat[idx];
718 csrt.col_index[dest] = i;
719 }
720 }
721
722#endif
723}
724}
725
726template <typename T, template<typename> class Allocator>
728 : m_partition(std::move(partition)),
729 m_row_begin(m_partition[ParallelDescriptor::MyProc()]),
730 m_row_end(m_partition[ParallelDescriptor::MyProc()+1])
731{
732 define_doit(nnz_per_row);
733}
734
735template <typename T, template<typename> class Allocator>
737 : m_partition(std::move(partition)),
738 m_row_begin(m_partition[ParallelDescriptor::MyProc()]),
739 m_row_end(m_partition[ParallelDescriptor::MyProc()+1]),
740 m_nnz(csr.nnz),
741 m_csr(std::move(csr))
742{}
743
744template <typename T, template<typename> class Allocator>
745void SpMatrix<T,Allocator>::define (AlgPartition partition, int nnz_per_row)
746{
747 m_partition = std::move(partition);
748 m_row_begin = m_partition[ParallelDescriptor::MyProc()];
749 m_row_end = m_partition[ParallelDescriptor::MyProc()+1];
750 m_diagonal = AlgVector<T,AllocT>{};
751 define_doit(nnz_per_row);
752}
753
754template <typename T, template<typename> class Allocator>
756 CsrSorted is_sorted)
757{
758 AMREX_ALWAYS_ASSERT(m_split == false);
759
760 m_partition = std::move(partition);
761 m_row_begin = m_partition[ParallelDescriptor::MyProc()];
762 m_row_end = m_partition[ParallelDescriptor::MyProc()+1];
763 m_nnz = csr.nnz;
764 m_csr = std::move(csr);
765 m_diagonal = AlgVector<T,AllocT>{};
766 if (! is_sorted) { m_csr.sort(); }
767}
768
769template <typename T, template<typename> class Allocator>
770void
772{
773 if (nnz_per_row <= 0) { return; };
774
775 AMREX_ALWAYS_ASSERT(m_split == false);
776
777 Long nlocalrows = this->numLocalRows();
778 m_nnz = nlocalrows*nnz_per_row;
779 m_csr.mat.resize(m_nnz);
780 m_csr.col_index.resize(m_nnz);
781 m_csr.row_offset.resize(nlocalrows+1);
782 m_csr.nnz = m_nnz;
783
784 auto* poffset = m_csr.row_offset.data();
785 ParallelForOMP(nlocalrows+1, [=] AMREX_GPU_DEVICE (Long lrow) noexcept
786 {
787 poffset[lrow] = lrow*nnz_per_row;
788 });
789}
790
791template <typename T, template<typename> class Allocator>
792void
794 Long const* col_index, Long nentries,
795 Long const* row_offset, CsrSorted is_sorted,
796 CsrValid is_valid)
797{
798 AMREX_ALWAYS_ASSERT(m_split == false);
799
800 m_partition = std::move(partition);
801 m_row_begin = m_partition[ParallelDescriptor::MyProc()];
802 m_row_end = m_partition[ParallelDescriptor::MyProc()+1];
803 m_diagonal = AlgVector<T,AllocT>{};
804
805 bool synced = false;
806
807 if (is_valid) {
808 m_nnz = nentries;
809 Long nlocalrows = this->numLocalRows();
810 m_csr.mat.resize(nentries);
811 m_csr.col_index.resize(nentries);
812 m_csr.row_offset.resize(nlocalrows+1);
813 m_csr.nnz = nentries;
814 Gpu::copyAsync(Gpu::deviceToDevice, mat, mat+nentries, m_csr.mat.begin());
815 Gpu::copyAsync(Gpu::deviceToDevice, col_index, col_index+nentries,
816 m_csr.col_index.begin());
817 Gpu::copyAsync(Gpu::deviceToDevice, row_offset, row_offset+nlocalrows+1,
818 m_csr.row_offset.begin());
819 } else {
820 if (nentries < Long(std::numeric_limits<int>::max())) {
821 define_and_filter_doit<int>(mat, col_index, nentries, row_offset);
822 } else {
823 define_and_filter_doit<Long>(mat, col_index, nentries, row_offset);
824 }
825 synced = true;
826 }
827
828 if (! is_sorted) {
829 m_csr.sort();
830 synced = true;
831 }
832
833 if (! synced) {
835 }
836}
837
838template <typename T, template<typename> class Allocator>
839void
841{
842 m_csr.sort();
843}
844
845template <typename T, template<typename> class Allocator>
846template <typename I>
847void
849 Long nentries, Long const* row_offset)
850{
851 Gpu::DeviceVector<I> psum(nentries);
852 auto* ps = psum.data();
853 m_nnz = Scan::PrefixSum<I>(I(nentries),
854 [=] AMREX_GPU_DEVICE (I i) -> I {
855 return col_index[i] >= 0 && mat[i] != 0; },
856 [=] AMREX_GPU_DEVICE (I i, I x) {
857 ps[i] = x; },
859 Long nlocalrows = this->numLocalRows();
860 m_csr.mat.resize(m_nnz);
861 m_csr.col_index.resize(m_nnz);
862 m_csr.row_offset.resize(nlocalrows+1);
863 m_csr.nnz = m_nnz;
864 auto* pmat = m_csr.mat.data();
865 auto* pcol = m_csr.col_index.data();
866 auto* prow = m_csr.row_offset.data();
867 auto actual_nnz = m_nnz;
868 ParallelFor(std::max(nentries,nlocalrows), [=] AMREX_GPU_DEVICE (Long i)
869 {
870 if (i < nentries) {
871 if (col_index[i] >= 0 && mat[i] != 0) {
872 pmat[ps[i]] = mat[i];
873 pcol[ps[i]] = col_index[i];
874 }
875 }
876 if (i < nlocalrows) {
877 prow[i] = (row_offset[i] < nentries) ? Long(ps[row_offset[i]]) : actual_nnz;
878 if (i == nlocalrows - 1) {
879 prow[nlocalrows] = actual_nnz;
880 }
881 }
882 });
884}
885
886template <typename T, template<typename> class Allocator>
887void
888SpMatrix<T,Allocator>::printToFile (std::string const& file) const
889{
890#ifdef AMREX_USE_GPU
893
894# ifdef AMREX_USE_MPI
896 if (m_split) {
897 amrex::duplicateCSR(Gpu::deviceToHost, csr_r, m_csr_remote);
898 }
899
900 Gpu::PinnedVector<Long> ri_ltor(m_ri_ltor.size());
901 if (m_split) {
903 m_ri_ltor.begin(),
904 m_ri_ltor.end(),
905 ri_ltor.begin());
906 }
907 auto const& remote_cols = m_remote_cols_v;
908# endif
909
911
912#else
913
914 auto const& csr = m_csr;
915# ifdef AMREX_USE_MPI
916 auto const& csr_r = m_csr_remote;
917 auto const& ri_ltor = m_ri_ltor;
918 auto const& remote_cols = m_remote_cols_v;
919# endif
920
921#endif
922
923 Long nnz = m_csr.nnz;
924#ifdef AMREX_USE_MPI
925 nnz += m_csr_remote.nnz;
926#endif
927
928 std::ofstream ofs(file+"."+std::to_string(ParallelDescriptor::MyProc()));
929 ofs << m_row_begin << " " << m_row_end << " " << nnz << "\n";
930 for (Long i = 0, nrows = numLocalRows(); i < nrows; ++i) {
931 Long nnz_row = csr.row_offset[i+1] - csr.row_offset[i];
932 T const* mat = csr.mat.data() + csr.row_offset[i];
933 Long const* col = csr.col_index.data() + csr.row_offset[i];
934 for (Long j = 0; j < nnz_row; ++j) {
935 ofs << i+m_row_begin << " " << col[j]+m_col_begin << " " << mat[j] << "\n";
936 }
937#ifdef AMREX_USE_MPI
938 if (i < Long(ri_ltor.size()) && ri_ltor[i] >= 0) {
939 Long ii = ri_ltor[i];
940 nnz_row = csr_r.row_offset[ii+1] - csr_r.row_offset[ii];
941 mat = csr_r.mat.data() + csr_r.row_offset[ii];
942 col = csr_r.col_index.data() + csr_r.row_offset[ii];
943 for (Long j = 0; j < nnz_row; ++j) {
944 ofs << i+m_row_begin << " " << remote_cols[col[j]] << " " << mat[j] << "\n";
945 }
946 }
947#endif
948 }
949}
950
951template <typename T, template<typename> class Allocator>
952template <typename F>
953void SpMatrix<T,Allocator>::setVal (F const& f, CsrSorted is_sorted)
954{
955 // xxxxx TODO: We can try to optimize this later by using shared memory.
956
957 AMREX_ALWAYS_ASSERT(m_split == false);
958 m_diagonal = AlgVector<T,AllocT>{};
959
960 Long nlocalrows = this->numLocalRows();
961 Long rowbegin = this->globalRowBegin();
962 auto* pmat = m_csr.mat.data();
963 auto* pcolindex = m_csr.col_index.data();
964 auto* prowoffset = m_csr.row_offset.data();
965 ParallelForOMP(nlocalrows, [=] AMREX_GPU_DEVICE (int lrow) noexcept
966 {
967 f(rowbegin+lrow, pcolindex+prowoffset[lrow], pmat+prowoffset[lrow]);
968 });
969
970 if (! is_sorted) { m_csr.sort(); }
971}
972
973template <typename T, template<typename> class Allocator>
975{
976 if (m_diagonal.empty()) {
977 m_diagonal.define(this->partition());
978 auto* AMREX_RESTRICT p = m_diagonal.data();
979 auto const* AMREX_RESTRICT mat = m_csr.mat.data();
980 auto const* AMREX_RESTRICT col = m_csr.col_index.data();
981 auto const* AMREX_RESTRICT row = m_csr.row_offset.data();
982 auto offset = m_split ? Long(0) : m_row_begin; // assuming square matrix
983 Long nrows = this->numLocalRows();
985 {
986 T d = 0;
987 for (Long j = row[i]; j < row[i+1]; ++j) {
988 if (i == col[j] - offset) {
989 d = mat[j];
990 break;
991 }
992 }
993 p[i] = d;
994 });
995 }
996 return m_diagonal;
997}
998
999template <typename T, template<typename> class Allocator>
1001{
1002 AlgVector<T,Allocator<T>> r(this->partition());
1003 auto* p = r.data();
1004 auto const& a = this->const_parcsr();
1006 {
1007 T s = 0;
1008 for (auto idx = a.csr0.row_offset[i];
1009 idx < a.csr0.row_offset[i+1]; ++idx) {
1010 s += a.csr0.mat[idx];
1011 }
1012 if (a.csr1.nnz > 0 && a.row_map[i] >= 0) {
1013 auto ii = a.row_map[i];
1014 for (auto idx = a.csr1.row_offset[ii];
1015 idx < a.csr1.row_offset[ii+1]; ++idx) {
1016 s += a.csr1.mat[idx];
1017 }
1018 }
1019 p[i] = s;
1020 });
1021 return r;
1022}
1023
1024template <typename T, template<typename> class Allocator>
1026{
1027 return ParCsr<T>{m_csr.view(),
1028#ifdef AMREX_USE_MPI
1029 m_csr_remote.view(),
1030#else
1031 CsrView<T>{},
1032#endif
1033 m_row_begin,
1034 m_col_begin,
1035#ifdef AMREX_USE_MPI
1036 m_ri_ltor.data(),
1037# ifdef AMREX_USE_GPU
1038 m_remote_cols_dv.data()
1039# else
1040 m_remote_cols_v.data()
1041# endif
1042#else
1043 nullptr, nullptr
1044#endif
1045 };
1046}
1047
1048template <typename T, template<typename> class Allocator>
1050{
1051 using U = T const;
1052 return ParCsr<U>{m_csr.const_view(),
1053#ifdef AMREX_USE_MPI
1054 m_csr_remote.const_view(),
1055#else
1056 CsrView<U>{},
1057#endif
1058 m_row_begin,
1059 m_col_begin,
1060#ifdef AMREX_USE_MPI
1061 m_ri_ltor.data(),
1062# ifdef AMREX_USE_GPU
1063 m_remote_cols_dv.data()
1064# else
1065 m_remote_cols_v.data()
1066# endif
1067#else
1068 nullptr, nullptr
1069#endif
1070 };
1071}
1072
1073template <typename T, template<typename> class Allocator>
1075{
1076 return this->const_parcsr();
1077}
1078
1079template <typename T, template<typename> class Allocator>
1081{
1082#ifndef AMREX_USE_MPI
1084#else
1085 if (detail::spmat_comm_is_local(this->partition(), x.partition())) { return; }
1086
1087 this->prepare_comm_mv(x.partition());
1088
1089 auto const mpi_tag = ParallelDescriptor::SeqNum();
1090 auto const mpi_t_type = ParallelDescriptor::Mpi_typemap<T>::type();
1091 auto const mpi_comm = ParallelContext::CommunicatorSub();
1092
1093 auto const nrecvs = int(m_comm_mv.recv_from.size());
1094 if (nrecvs > 0) {
1095 m_comm_mv.recv_buffer = (T*)The_Comms_Arena()->alloc(sizeof(T)*m_comm_mv.total_counts_recv);
1096 m_comm_mv.recv_reqs.resize(nrecvs, MPI_REQUEST_NULL);
1097 auto* p_recv = m_comm_mv.recv_buffer;
1098 for (int irecv = 0; irecv < nrecvs; ++irecv) {
1099 BL_MPI_REQUIRE(MPI_Irecv(p_recv,
1100 m_comm_mv.recv_counts[irecv], mpi_t_type,
1101 m_comm_mv.recv_from[irecv], mpi_tag, mpi_comm,
1102 &(m_comm_mv.recv_reqs[irecv])));
1103 p_recv += m_comm_mv.recv_counts[irecv];
1104 }
1105 AMREX_ASSERT(p_recv == m_comm_mv.recv_buffer + m_comm_mv.total_counts_recv);
1106 }
1107
1108 auto const nsends = int(m_comm_mv.send_to.size());
1109 if (nsends > 0) {
1110 m_comm_mv.send_buffer = (T*)The_Comms_Arena()->alloc(sizeof(T)*m_comm_mv.total_counts_send);
1111
1112 pack_buffer_mv(x);
1114
1115 m_comm_mv.send_reqs.resize(nsends, MPI_REQUEST_NULL);
1116 auto* p_send = m_comm_mv.send_buffer;
1117 for (int isend = 0; isend < nsends; ++isend) {
1118 auto count = m_comm_mv.send_counts[isend];
1119 BL_MPI_REQUIRE(MPI_Isend(p_send, count, mpi_t_type, m_comm_mv.send_to[isend],
1120 mpi_tag, mpi_comm, &(m_comm_mv.send_reqs[isend])));
1121 p_send += count;
1122 }
1123 AMREX_ASSERT(p_send == m_comm_mv.send_buffer + m_comm_mv.total_counts_send);
1124 }
1125#endif
1126}
1127
1128template <typename T, template<typename> class Allocator>
1130{
1131#ifndef AMREX_USE_MPI
1133#else
1134 if (detail::spmat_comm_is_local(this->partition(), m_col_partition)) { return; }
1135
1136 if ( ! m_comm_mv.recv_reqs.empty()) {
1137 Vector<MPI_Status> mpi_statuses(m_comm_mv.recv_reqs.size());
1138 BL_MPI_REQUIRE(MPI_Waitall(int(m_comm_mv.recv_reqs.size()),
1139 m_comm_mv.recv_reqs.data(),
1140 mpi_statuses.data()));
1141 }
1142
1143 unpack_buffer_mv(y);
1144
1145 if ( ! m_comm_mv.send_reqs.empty()) {
1146 Vector<MPI_Status> mpi_statuses(m_comm_mv.send_reqs.size());
1147 BL_MPI_REQUIRE(MPI_Waitall(int(m_comm_mv.send_reqs.size()),
1148 m_comm_mv.send_reqs.data(),
1149 mpi_statuses.data()));
1150 }
1151
1153 The_Comms_Arena()->free(m_comm_mv.send_buffer);
1154 The_Comms_Arena()->free(m_comm_mv.recv_buffer);
1155 m_comm_mv.send_reqs.clear();
1156 m_comm_mv.recv_reqs.clear();
1157#endif
1158}
1159
1160template <typename T, template<typename> class Allocator>
1162{
1163#ifdef AMREX_USE_MPI
1164 if (detail::spmat_comm_is_local(this->partition(), col_partition)) { return; }
1165
1166 this->split_csr(col_partition);
1167
1168 int const nprocs = ParallelContext::NProcsSub();
1169 auto const mpi_tag = ParallelDescriptor::SeqNum();
1170 auto const mpi_long = ParallelDescriptor::Mpi_typemap<Long>::type();
1171 auto const mpi_t = ParallelDescriptor::Mpi_typemap<T>::type();
1172 auto const mpi_comm = ParallelContext::CommunicatorSub();
1173
1174 // transpose the off-diagonal part
1175 if (m_csr_remote.nnz > 0) {
1176 m_comm_tr.csrt.nnz = m_csr_remote.nnz;
1177 m_comm_tr.csrt.nrows = m_remote_cols_v.size();
1178 m_comm_tr.csrt.mat = (T*)The_Pinned_Arena()->alloc
1179 (sizeof(T)*m_comm_tr.csrt.nnz);
1180 m_comm_tr.csrt.col_index = (Long*)The_Pinned_Arena()->alloc
1181 (sizeof(Long)*m_comm_tr.csrt.nnz);
1182 m_comm_tr.csrt.row_offset = (Long*)The_Pinned_Arena()->alloc
1183 (sizeof(Long)*(m_comm_tr.csrt.nrows+1));
1184#ifdef AMREX_USE_GPU
1185 csr_type csr_comm;
1186 csr_comm.resize(m_comm_tr.csrt.nrows, m_comm_tr.csrt.nnz);
1187 auto const& csrv_comm = csr_comm.view();
1188#else
1189 auto const& csrv_comm = m_comm_tr.csrt;
1190#endif
1191 detail::transpose(csrv_comm, m_csr_remote.const_view());
1192 auto row_begin = m_row_begin;
1193 auto ri_rtol = m_ri_rtol.data();
1194 auto* col_index = csrv_comm.col_index;
1195 ParallelForOMP(csrv_comm.nnz, [=] AMREX_GPU_DEVICE (Long idx)
1196 {
1197 auto gjt =ri_rtol[col_index[idx]] + row_begin;
1198 col_index[idx] = gjt; // global index
1199 });
1200#ifdef AMREX_USE_GPU
1202 csrv_comm. mat,
1203 csrv_comm. mat + csrv_comm.nnz,
1204 m_comm_tr.csrt.mat);
1206 csrv_comm. col_index,
1207 csrv_comm. col_index + csrv_comm.nnz,
1208 m_comm_tr.csrt.col_index);
1210 csrv_comm. row_offset,
1211 csrv_comm. row_offset + csrv_comm.nrows+1,
1212 m_comm_tr.csrt.row_offset);
1214#endif
1215 }
1216
1217 if (m_num_neighbors < 0) { set_num_neighbors(); }
1218
1219 // As a sender, I need to let other processes know that how many
1220 // elements I will send them.
1221
1222 Vector<MPI_Request> mpi_requests;
1223 mpi_requests.reserve(nprocs);
1224 if (m_csr_remote.nnz > 0) {
1225 Long it = 0;
1226 for (int iproc = 0; iproc < nprocs; ++iproc) {
1227 Long n = 0;
1228 for (Long i = 0; i < Long(m_remote_cols_vv[iproc].size()); ++i) {
1229 n += m_comm_tr.csrt.row_offset[it+1] - m_comm_tr.csrt.row_offset[it];
1230 ++it;
1231 }
1232 if (n > 0) {
1233 mpi_requests.push_back(MPI_REQUEST_NULL);
1234 AMREX_ALWAYS_ASSERT(n < std::numeric_limits<int>::max());
1235 std::array<int,2> nn{int(n), int(m_remote_cols_vv[iproc].size())};
1236 BL_MPI_REQUIRE(MPI_Isend(nn.data(), 2, MPI_INT, iproc, mpi_tag,
1237 mpi_comm, &(mpi_requests.back())));
1238 m_comm_tr.send_to.push_back(iproc);
1239 m_comm_tr.send_counts.push_back(nn);
1240 }
1241 }
1242 }
1243
1244 // As a receiver, m_num_neighbors is the number of processes from which
1245 // I will receive data.
1246
1247 for (int irecv = 0; irecv < m_num_neighbors; ++irecv) {
1248 MPI_Status mpi_status;
1249 BL_MPI_REQUIRE(MPI_Probe(MPI_ANY_SOURCE, mpi_tag, mpi_comm, &mpi_status));
1250 int sender = mpi_status.MPI_SOURCE;
1251 std::array<int,2> nn;
1252 BL_MPI_REQUIRE(MPI_Recv(nn.data(), 2, MPI_INT, sender, mpi_tag,
1253 mpi_comm, &mpi_status));
1254 m_comm_tr.recv_from.push_back(sender);
1255 m_comm_tr.recv_counts.push_back(nn);
1256 m_comm_tr.total_counts_recv[0] += nn[0];
1257 m_comm_tr.total_counts_recv[1] += nn[1];
1258 }
1259
1260 if (! mpi_requests.empty()) {
1261 Vector<MPI_Status> mpi_statuses(mpi_requests.size());
1262 BL_MPI_REQUIRE(MPI_Waitall(int(mpi_requests.size()), mpi_requests.data(),
1263 mpi_statuses.data()));
1264 }
1265
1266 auto const mpi_tag_m = ParallelDescriptor::SeqNum();
1267 auto const mpi_tag_c = ParallelDescriptor::SeqNum();
1268 auto const mpi_tag_r = ParallelDescriptor::SeqNum();
1269 auto const mpi_tag_p = ParallelDescriptor::SeqNum();
1270
1271 // We need to send m_comm_tr.csrt.mat, col_index & row_offset. We also
1272 // need to send m_remote_cols_vv, which maps row index (in transposed
1273 // matrix) form local to global.
1274
1275 auto const nrecvs = int(m_comm_tr.recv_from.size());
1276 if (nrecvs > 0) {
1277 m_comm_tr.recv_buffer_mat = (T*) The_Pinned_Arena()->alloc
1278 (sizeof(T) * m_comm_tr.total_counts_recv[0]);
1279 m_comm_tr.recv_buffer_col_index = (Long*) The_Pinned_Arena()->alloc
1280 (sizeof(Long) * m_comm_tr.total_counts_recv[0]);
1281 m_comm_tr.recv_buffer_row_offset = (Long*) The_Pinned_Arena()->alloc
1282 (sizeof(Long) * (m_comm_tr.total_counts_recv[1]+nrecvs));
1283 m_comm_tr.recv_buffer_idx_map = (Long*) The_Pinned_Arena()->alloc
1284 (sizeof(Long) * m_comm_tr.total_counts_recv[1]);
1285 m_comm_tr.recv_buffer_offset.push_back({0,0,0,0});
1286 m_comm_tr.recv_reqs.resize(4*nrecvs, MPI_REQUEST_NULL);
1287 for (int irecv = 0; irecv < nrecvs; ++irecv) {
1288 auto [os0, os1, os2, os3] = m_comm_tr.recv_buffer_offset.back();
1289 auto [n0, n1] = m_comm_tr.recv_counts[irecv];
1290 auto recv_from_rank = m_comm_tr.recv_from[irecv];
1291 BL_MPI_REQUIRE(MPI_Irecv(m_comm_tr.recv_buffer_mat + os0,
1292 n0,
1293 mpi_t,
1294 recv_from_rank,
1295 mpi_tag_m,
1296 mpi_comm,
1297 &(m_comm_tr.recv_reqs[irecv*4])));
1298 BL_MPI_REQUIRE(MPI_Irecv(m_comm_tr.recv_buffer_col_index + os1,
1299 n0,
1300 mpi_long,
1301 recv_from_rank,
1302 mpi_tag_c,
1303 mpi_comm,
1304 &(m_comm_tr.recv_reqs[irecv*4+1])));
1305 BL_MPI_REQUIRE(MPI_Irecv(m_comm_tr.recv_buffer_row_offset + os2,
1306 n1+1,
1307 mpi_long,
1308 recv_from_rank,
1309 mpi_tag_r,
1310 mpi_comm,
1311 &(m_comm_tr.recv_reqs[irecv*4+2])));
1312 BL_MPI_REQUIRE(MPI_Irecv(m_comm_tr.recv_buffer_idx_map + os3,
1313 n1,
1314 mpi_long,
1315 recv_from_rank,
1316 mpi_tag_p,
1317 mpi_comm,
1318 &(m_comm_tr.recv_reqs[irecv*4+3])));
1319 m_comm_tr.recv_buffer_offset.push_back({os0 + n0,
1320 os1 + n0,
1321 os2 + n1+1,
1322 os3 + n1});
1323 }
1324 }
1325
1326 auto const nsends = int(m_comm_tr.send_to.size());
1327 if (nsends > 0) {
1328 m_comm_tr.send_reqs.resize(4*nsends, MPI_REQUEST_NULL);
1329 Long os0 = 0, os1 = 0;
1330 for (int isend = 0; isend < nsends; ++isend) {
1331 auto [n0, n1] = m_comm_tr.send_counts[isend];
1332 auto send_to_rank = m_comm_tr.send_to[isend];
1333 BL_MPI_REQUIRE(MPI_Isend(m_comm_tr.csrt.mat + os0,
1334 n0,
1335 mpi_t,
1336 send_to_rank,
1337 mpi_tag_m,
1338 mpi_comm,
1339 &(m_comm_tr.send_reqs[isend*4])));
1340 BL_MPI_REQUIRE(MPI_Isend(m_comm_tr.csrt.col_index + os0,
1341 n0,
1342 mpi_long,
1343 send_to_rank,
1344 mpi_tag_c,
1345 mpi_comm,
1346 &(m_comm_tr.send_reqs[isend*4+1])));
1347 BL_MPI_REQUIRE(MPI_Isend(m_comm_tr.csrt.row_offset + os1,
1348 n1+1,
1349 mpi_long,
1350 send_to_rank,
1351 mpi_tag_r,
1352 mpi_comm,
1353 &(m_comm_tr.send_reqs[isend*4+2])));
1354 BL_MPI_REQUIRE(MPI_Isend(m_remote_cols_vv[send_to_rank].data(),
1355 n1,
1356 mpi_long,
1357 send_to_rank,
1358 mpi_tag_p,
1359 mpi_comm,
1360 &(m_comm_tr.send_reqs[isend*4+3])));
1361 os0 += n0;
1362 os1 += n1;
1363 }
1364 }
1365#else
1366 amrex::ignore_unused(col_partition);
1367#endif
1368}
1369
1370template <typename T, template<typename> class Allocator>
1372{
1373#ifdef AMREX_USE_MPI
1374 if (detail::spmat_comm_is_local(this->partition(), AT.partition())) { return; }
1375
1376 if (! m_comm_tr.recv_reqs.empty()) {
1377 Vector<MPI_Status> mpi_statuses(m_comm_tr.recv_reqs.size());
1378 BL_MPI_REQUIRE(MPI_Waitall(int(m_comm_tr.recv_reqs.size()),
1379 m_comm_tr.recv_reqs.data(),
1380 mpi_statuses.data()));
1381 }
1382
1383 AT.unpack_buffer_tr(m_comm_tr, this->m_partition);
1384
1385 if (! m_comm_tr.send_reqs.empty()) {
1386 Vector<MPI_Status> mpi_statuses(m_comm_tr.send_reqs.size());
1387 BL_MPI_REQUIRE(MPI_Waitall(int(m_comm_tr.send_reqs.size()),
1388 m_comm_tr.send_reqs.data(),
1389 mpi_statuses.data()));
1390 }
1391
1392 if (m_comm_tr.csrt.nnz > 0) {
1393 The_Pinned_Arena()->free(m_comm_tr.csrt.mat);
1394 The_Pinned_Arena()->free(m_comm_tr.csrt.col_index);
1395 The_Pinned_Arena()->free(m_comm_tr.csrt.row_offset);
1396 }
1397 if (m_comm_tr.recv_buffer_mat) {
1398 The_Pinned_Arena()->free(m_comm_tr.recv_buffer_mat);
1399 The_Pinned_Arena()->free(m_comm_tr.recv_buffer_col_index);
1400 The_Pinned_Arena()->free(m_comm_tr.recv_buffer_row_offset);
1401 The_Pinned_Arena()->free(m_comm_tr.recv_buffer_idx_map);
1402 }
1403 m_comm_tr = CommTR{};
1404#else
1406#endif
1407}
1408
1409#ifdef AMREX_USE_MPI
1410
1411template <typename T, template<typename> class Allocator>
1413{
1414 if (m_split) {
1416 (m_col_begin == col_partition[ParallelDescriptor::MyProc()] &&
1417 m_col_end == col_partition[ParallelDescriptor::MyProc()+1]);
1418 return;
1419 }
1420
1421 AMREX_ALWAYS_ASSERT(m_col_partition.empty());
1422
1423 m_col_partition = col_partition;
1424 m_col_begin = col_partition[ParallelDescriptor::MyProc()];
1425 m_col_end = col_partition[ParallelDescriptor::MyProc()+1];
1426
1427 // This function needs to be safe when nnz is zero.
1428
1429 // We need to split the matrix into two parts, a diagonal part for pure
1430 // local operations and another part for remote operations in
1431 // matrix-vector or matrix-matrix multiplication.
1432
1433 Long local_nnz;
1434 Gpu::DeviceVector<Long> pfsum(m_nnz);
1435 auto* p_pfsum = pfsum.data();
1436 auto col_begin = m_col_begin;
1437 auto col_end = m_col_end;
1438 if (m_csr.nnz < Long(std::numeric_limits<int>::max())) {
1439 auto const* pcol = m_csr.col_index.data();
1440 local_nnz = Scan::PrefixSum<int>(int(m_nnz),
1441 [=] AMREX_GPU_DEVICE (int i) -> int {
1442 return (pcol[i] >= col_begin &&
1443 pcol[i] < col_end); },
1444 [=] AMREX_GPU_DEVICE (int i, int const& x) {
1445 p_pfsum[i] = x; },
1447 } else {
1448 auto const* pcol = m_csr.col_index.data();
1449 local_nnz = Scan::PrefixSum<Long>(m_nnz,
1450 [=] AMREX_GPU_DEVICE (Long i) -> Long {
1451 return (pcol[i] >= col_begin &&
1452 pcol[i] < col_end); },
1453 [=] AMREX_GPU_DEVICE (Long i, Long const& x) {
1454 p_pfsum[i] = x; },
1456 }
1457
1458 m_csr.nnz = local_nnz;
1459 Long remote_nnz = m_nnz - local_nnz;
1460 m_csr_remote.nnz = remote_nnz;
1461
1462 if (local_nnz != m_nnz) {
1463 m_csr_remote.mat.resize(remote_nnz);
1464 m_csr_remote.col_index.resize(remote_nnz);
1465 container_type<T> new_mat(local_nnz);
1466 container_type<Long> new_col(local_nnz);
1467 auto const* pmat = m_csr.mat.data();
1468 auto const* pcol = m_csr.col_index.data();
1469 auto* pmat_l = new_mat.data();
1470 auto* pcol_l = new_col.data();
1471 auto* pmat_r = m_csr_remote.mat.data();
1472 auto* pcol_r = m_csr_remote.col_index.data();
1473 ParallelForOMP(m_nnz, [=] AMREX_GPU_DEVICE (Long i)
1474 {
1475 auto ps = p_pfsum[i];
1476 auto local = (pcol[i] >= col_begin &&
1477 pcol[i] < col_end);
1478 if (local) {
1479 pmat_l[ps] = pmat[i];
1480 pcol_l[ps] = pcol[i] - col_begin; // shift the column index to local
1481 } else {
1482 pmat_r[i-ps] = pmat[i];
1483 pcol_r[i-ps] = pcol[i];
1484 }
1485 });
1486 auto noffset = Long(m_csr.row_offset.size());
1487 auto* pro = m_csr.row_offset.data();
1488 m_csr_remote.row_offset.resize(noffset);
1489 auto* pro_r = m_csr_remote.row_offset.data();
1490 auto total_nnz = m_nnz;
1491 ParallelForOMP(noffset, [=] AMREX_GPU_DEVICE (Long i)
1492 {
1493 if (i < noffset-1) {
1494 auto ro_l = (pro[i] < total_nnz) ? p_pfsum[pro[i]] : local_nnz;
1495 pro_r[i] = pro[i] - ro_l;
1496 pro[i] = ro_l;
1497 } else {
1498 pro[i] = local_nnz;
1499 pro_r[i] = remote_nnz;
1500 }
1501 });
1503 m_csr.mat.swap(new_mat);
1504 m_csr.col_index.swap(new_col);
1505
1506 // In the remote part, it's expected that some rows don't have
1507 // nonzeros. So we trim them off.
1508 {
1509 Long old_size = m_csr_remote.row_offset.size();
1510 m_ri_ltor.resize(old_size-1);
1511 m_ri_rtol.resize(old_size-1);
1512 auto* p_ltor = m_ri_ltor.data();
1513 auto* p_rtol = m_ri_rtol.data();
1514 container_type<Long> trimmed_row_offset(old_size);
1515 auto const* p_ro = m_csr_remote.row_offset.data();
1516 auto* p_tro = trimmed_row_offset.data();
1517 Long new_size;
1518 if (old_size < Long(std::numeric_limits<int>::max())) {
1519 // This is basically std::unique.
1520 new_size = Scan::PrefixSum<int>(int(old_size),
1521 [=] AMREX_GPU_DEVICE (int i) -> int {
1522 if (i+1 < old_size) {
1523 return (p_ro[i+1] > p_ro[i]);
1524 } else {
1525 return 1;
1526 }
1527 },
1528 [=] AMREX_GPU_DEVICE (int i, int const& x) {
1529 if (i == 0) {
1530 p_tro[0] = 0;
1531 } else if (p_ro[i] > p_ro[i-1]) {
1532 p_tro[x] = p_ro[i];
1533 }
1534 if (i+1 < old_size) {
1535 if (p_ro[i+1] > p_ro[i]) {
1536 p_rtol[x] = i;
1537 p_ltor[i] = x;
1538 } else {
1539 p_ltor[i] = -1;
1540 }
1541 }
1542 },
1544 } else {
1545 // This is basically std::unique.
1546 new_size = Scan::PrefixSum<Long>(old_size,
1547 [=] AMREX_GPU_DEVICE (Long i) -> Long {
1548 if (i+1 < old_size) {
1549 return (p_ro[i+1] > p_ro[i]);
1550 } else {
1551 return 1;
1552 }
1553 },
1554 [=] AMREX_GPU_DEVICE (Long i, Long const& x) {
1555 if (i == 0) {
1556 p_tro[0] = 0;
1557 } else if (p_ro[i] > p_ro[i-1]) {
1558 p_tro[x] = p_ro[i];
1559 }
1560 if (i+1 < old_size) {
1561 if (p_ro[i+1] > p_ro[i]) {
1562 p_rtol[x] = i;
1563 p_ltor[i] = x;
1564 } else {
1565 p_ltor[i] = -1;
1566 }
1567 }
1568 },
1570 }
1571
1572 m_ri_rtol.resize(new_size-1);
1573 trimmed_row_offset.resize(new_size);
1574#ifdef AMREX_USE_GPU
1575 m_ri_rtol.shrink_to_fit();
1576 trimmed_row_offset.shrink_to_fit();
1577#endif
1578 m_csr_remote.row_offset.swap(trimmed_row_offset);
1579 }
1580
1581 } else if (col_begin > 0) {
1582 auto* pcol = m_csr.col_index.data();
1583 ParallelForOMP(m_nnz, [=] AMREX_GPU_DEVICE (Long i) { pcol[i] -= col_begin; });
1584 }
1585
1586 update_remote_col_index(m_csr_remote, true);
1587
1588 m_split = true;
1589}
1590
1591template <typename T, template<typename> class Allocator>
1592template <typename C>
1593void SpMatrix<T,Allocator>::update_remote_col_index (C& csrr, bool in_device_memory)
1594{
1595 int const nprocs = ParallelContext::NProcsSub();
1596
1597 // This function also needs to update m_remote_cols_*.
1598
1599 m_remote_cols_v.clear();
1600 m_remote_cols_vv.clear();
1601 m_remote_cols_vv.resize(nprocs);
1602#ifdef AMREX_USE_GPU
1603 m_remote_cols_dv.clear();
1604#endif
1605
1606 if (csrr.nnz == 0) { return; }
1607
1608 amrex::ignore_unused(in_device_memory);
1609
1610#ifdef AMREX_USE_GPU
1611 if (in_device_memory) {
1612 m_remote_cols_v.resize(csrr.nnz);
1614 csrr.col_index.begin(),
1615 csrr.col_index.end(),
1616 m_remote_cols_v.begin());
1618 } else
1619#endif
1620 {
1621 m_remote_cols_v.assign(csrr.col_index.begin(),
1622 csrr.col_index.end());
1623 }
1624
1625 amrex::RemoveDuplicates(m_remote_cols_v);
1626
1627#ifdef AMREX_USE_GPU
1628 m_remote_cols_dv.resize(m_remote_cols_v.size());
1630 m_remote_cols_v.begin(),
1631 m_remote_cols_v.end(),
1632 m_remote_cols_dv.data());
1633#endif
1634
1635 // Note that amrex::RemoveDuplicates sorts the data.
1636 auto const& cp = this->m_col_partition.dataVector();
1637 AMREX_ALWAYS_ASSERT(m_remote_cols_v.front() >= cp.front() &&
1638 m_remote_cols_v.back() < cp.back());
1639 auto it = cp.cbegin();
1640 for (auto c : m_remote_cols_v) {
1641 it = std::find_if(it, cp.cend(), [&] (auto x) { return x > c; });
1642 if (it != cp.cend()) {
1643 int iproc = int(std::distance(cp.cbegin(),it)) - 1;
1644 m_remote_cols_vv[iproc].push_back(c);
1645 } else {
1646 amrex::Abort("SpMatrix::update_remote_col_index: how did this happen?");
1647 }
1648 }
1649
1650 // Now we convert the remote indices from global to local.
1651 std::map<Long,Long> gtol;
1652 for (Long i = 0, N = Long(m_remote_cols_v.size()); i < N; ++i) {
1653 gtol[m_remote_cols_v[i]] = i;
1654 }
1655
1656#ifdef AMREX_USE_GPU
1657 if (in_device_memory) {
1658 Gpu::PinnedVector<Long> host_col_index(csrr.nnz);
1660 csrr.col_index.begin(),
1661 csrr.col_index.end(),
1662 host_col_index.begin());
1664 for (auto& c : host_col_index) {
1665 c = gtol[c];
1666 }
1668 host_col_index.begin(),
1669 host_col_index.end(),
1670 csrr.col_index.begin());
1672 } else
1673#endif
1674 {
1675 for (auto& c : csrr.col_index) {
1676 c = gtol[c];
1677 }
1678 }
1679}
1680
1681template <typename T, template<typename> class Allocator>
1683{
1684 if (m_num_neighbors >= 0) { return; }
1685
1686 int const nprocs = ParallelContext::NProcsSub();
1687 auto const mpi_int = ParallelDescriptor::Mpi_typemap<int>::type();
1688 auto const mpi_comm = ParallelContext::CommunicatorSub();
1689
1690 amrex::Vector<int> connection(nprocs);
1691 for (int iproc = 0; iproc < nprocs; ++iproc) {
1692 connection[iproc] = m_remote_cols_vv[iproc].empty() ? 0 : 1;
1693 }
1694 amrex::Vector<int> reduce_scatter_counts(nprocs,1);
1695 m_num_neighbors = 0;
1696 BL_MPI_REQUIRE(MPI_Reduce_scatter
1697 (connection.data(), &m_num_neighbors, reduce_scatter_counts.data(),
1698 mpi_int, MPI_SUM, mpi_comm));
1699}
1700
1701template <typename T, template<typename> class Allocator>
1703{
1704 if (m_comm_mv.prepared) { return; }
1705
1706 // This function needs to be safe when nnz is zero.
1707
1708 this->split_csr(col_partition);
1709
1710 int const nprocs = ParallelContext::NProcsSub();
1711 auto const mpi_tag = ParallelDescriptor::SeqNum();
1712 auto const mpi_long = ParallelDescriptor::Mpi_typemap<Long>::type();
1713 auto const mpi_comm = ParallelContext::CommunicatorSub();
1714
1715 if (m_num_neighbors < 0) { set_num_neighbors(); }
1716
1717 Vector<MPI_Request> mpi_requests;
1718 mpi_requests.reserve(nprocs);
1719 for (int iproc = 0; iproc < nprocs; ++iproc) {
1720 if ( ! m_remote_cols_vv[iproc].empty()) {
1721 mpi_requests.push_back(MPI_REQUEST_NULL);
1722 auto const sz = m_remote_cols_vv[iproc].size();
1723 if (sz > static_cast<Long>(std::numeric_limits<int>::max())) {
1724 amrex::Abort("SpMatrix::prepare_comm_mv: remote column payload exceeds MPI int count range.");
1725 }
1726 auto const msg_count = static_cast<int>(sz);
1727 // I need to let other processes know what I need from them.
1728 BL_MPI_REQUIRE(MPI_Isend(m_remote_cols_vv[iproc].data(),
1729 msg_count,
1730 mpi_long, iproc, mpi_tag, mpi_comm,
1731 &(mpi_requests.back())));
1732 m_comm_mv.recv_from.push_back(iproc);
1733 m_comm_mv.recv_counts.push_back(msg_count);
1734 }
1735 }
1736
1737 m_comm_mv.total_counts_recv = Long(m_remote_cols_v.size());
1738
1739 Vector<Vector<Long>> send_indices(m_num_neighbors);
1740 m_comm_mv.total_counts_send = 0;
1741 for (int isend = 0; isend < m_num_neighbors; ++isend) {
1742 MPI_Status mpi_status;
1743 BL_MPI_REQUIRE(MPI_Probe(MPI_ANY_SOURCE, mpi_tag, mpi_comm, &mpi_status));
1744 int receiver = mpi_status.MPI_SOURCE;
1745 int count;
1746 BL_MPI_REQUIRE(MPI_Get_count(&mpi_status, mpi_long, &count));
1747 m_comm_mv.send_to.push_back(receiver);
1748 m_comm_mv.send_counts.push_back(count);
1749 send_indices[isend].resize(count);
1750 BL_MPI_REQUIRE(MPI_Recv(send_indices[isend].data(), count, mpi_long,
1751 receiver, mpi_tag, mpi_comm, &mpi_status));
1752 m_comm_mv.total_counts_send += count;
1753 }
1754
1755 m_comm_mv.send_indices.resize(m_comm_mv.total_counts_send);
1756 Gpu::PinnedVector<Long> send_indices_all;
1757 send_indices_all.reserve(m_comm_mv.total_counts_send);
1758 for (auto const& vl : send_indices) {
1759 for (auto x : vl) {
1760 send_indices_all.push_back(x);
1761 }
1762 }
1763 Gpu::copyAsync(Gpu::hostToDevice, send_indices_all.begin(), send_indices_all.end(),
1764 m_comm_mv.send_indices.begin());
1766
1767 if (! mpi_requests.empty()) {
1768 Vector<MPI_Status> mpi_statuses(mpi_requests.size());
1769 BL_MPI_REQUIRE(MPI_Waitall(int(mpi_requests.size()), mpi_requests.data(),
1770 mpi_statuses.data()));
1771 }
1772
1773 m_comm_mv.prepared = true;
1774}
1775
1776template <typename T, template<typename> class Allocator>
1778{
1779 auto* pdst = m_comm_mv.send_buffer;
1780 auto* pidx = m_comm_mv.send_indices.data();
1781 auto const& vv = v.view();
1782 auto const nsends = Long(m_comm_mv.send_indices.size());
1783 ParallelForOMP(nsends, [=] AMREX_GPU_DEVICE (Long i)
1784 {
1785 pdst[i] = vv(pidx[i]);
1786 });
1787}
1788
1789template <typename T, template<typename> class Allocator>
1791{
1792 auto const& csr = m_csr_remote;
1793 if (csr.nnz > 0) {
1794 T const* AMREX_RESTRICT mat = csr.mat.data();
1795 auto const* AMREX_RESTRICT col = csr.col_index.data();
1796 auto const* AMREX_RESTRICT row = csr.row_offset.data();
1797
1798 auto const* rtol = m_ri_rtol.data();
1799
1800 auto const* AMREX_RESTRICT px = m_comm_mv.recv_buffer;
1801 auto * AMREX_RESTRICT py = v.data();
1802
1803 auto const nrr = Long(csr.row_offset.size())-1;
1805 {
1806 T r = 0;
1807 for (Long j = row[i]; j < row[i+1]; ++j) {
1808 r += mat[j] * px[col[j]];
1809 }
1810 py[rtol[i]] += r;
1811 });
1812 }
1813}
1814
1815template <typename T, template<typename> class Allocator>
1817 AlgPartition const& col_partition)
1818{
1819 m_split = true;
1820 m_col_partition = col_partition;
1821 m_col_begin = m_col_partition[ParallelDescriptor::MyProc() ];
1822 m_col_end = m_col_partition[ParallelDescriptor::MyProc()+1];
1823
1824 m_ri_ltor.resize(m_csr.nrows(), -1);
1825 m_remote_cols_vv.resize(ParallelDescriptor::NProcs());
1826
1827 auto nnz = ctr.total_counts_recv[0];
1828 if (nnz == 0) { return; }
1829
1830 m_nnz += nnz;
1831 auto nb = int(ctr.recv_from.size()); // # of blocked CSRs to be merged
1832 auto total_local_rows = ctr.total_counts_recv[1];
1833
1834 // Build compressed row index map
1836 ctr.recv_buffer_idx_map + total_local_rows);
1837 RemoveDuplicates(ri_map);
1838 Long nrows = ri_map.size(); // # of unique rows.
1839
1840#ifdef AMREX_USE_GPU
1842#else
1843 auto& csrr = m_csr_remote;
1844#endif
1845 csrr.mat.resize(nnz);
1846 csrr.col_index.resize(nnz);
1847 csrr.row_offset.resize(nrows+1);
1848 csrr.nnz = nnz;
1849
1850 // Count nnz per compressed row
1851 Vector<int> row_nnz(nrows, 0);
1852 for (int i = 0; i < nb; ++i) {
1853 auto nrow_i = ctr.recv_counts[i][1];
1854 Long const* row_offset = ctr.recv_buffer_row_offset
1855 + ctr.recv_buffer_offset[i][2];
1856 Long const* idx_map = ctr.recv_buffer_idx_map
1857 + ctr.recv_buffer_offset[i][3];
1858 AMREX_ASSERT((row_offset[nrow_i] - row_offset[0]) == ctr.recv_counts[i][0]);
1859
1860 Long p = 0; // index into ri_map
1861 for (int lr = 0; lr < nrow_i; ++lr) {
1862 Long const gr = idx_map[lr];
1863 while (p < nrows && ri_map[p] < gr) { ++p; }
1864 AMREX_ASSERT(p < nrows && ri_map[p] == gr);
1865 // p is now compressed row index
1866 row_nnz[p] += int(row_offset[lr+1] - row_offset[lr]);
1867 }
1868 }
1869
1870 csrr.row_offset[0] = 0;
1871 std::partial_sum(row_nnz.begin(), row_nnz.end(), csrr.row_offset.begin()+1);
1872 AMREX_ASSERT(csrr.nnz == csrr.row_offset.back());
1873
1874 auto rowpos = csrr.row_offset; // make a copy to keep track of offset
1875
1876 for (int i = 0; i < nb; ++i) {
1877 auto nrow_i = ctr.recv_counts[i][1];
1878 T const* mat = ctr.recv_buffer_mat
1879 + ctr.recv_buffer_offset[i][0];
1880 Long const* col_index = ctr.recv_buffer_col_index
1881 + ctr.recv_buffer_offset[i][1];
1882 Long const* row_offset = ctr.recv_buffer_row_offset
1883 + ctr.recv_buffer_offset[i][2];
1884 Long const* idx_map = ctr.recv_buffer_idx_map
1885 + ctr.recv_buffer_offset[i][3];
1886
1887 Long p = 0; // index into ri_map
1888 for (int lr = 0; lr < nrow_i; ++lr) {
1889 Long const gr = idx_map[lr];
1890 while (p < nrows && ri_map[p] < gr) { ++p; }
1891 AMREX_ASSERT(p < nrows && ri_map[p] == gr);
1892 // p is now compressed row index
1893 auto os_src = row_offset[lr] - row_offset[0];
1894 auto nvals = row_offset[lr+1] - row_offset[lr];
1895 auto os_dst = rowpos[p];
1896 std::memcpy(csrr. mat.data()+os_dst, mat+os_src,
1897 sizeof(T) *nvals);
1898 std::memcpy(csrr.col_index.data()+os_dst, col_index+os_src,
1899 sizeof(Long)*nvals);
1900
1901 rowpos[p] += nvals;
1902 }
1903 }
1904
1905 m_ri_rtol.resize(nrows);
1906 Gpu::copyAsync(Gpu::hostToDevice, ri_map.begin(), ri_map.end(), m_ri_rtol.begin());
1907 {
1908 auto row_begin = m_row_begin;
1909 auto* AMREX_RESTRICT ltor = m_ri_ltor.data();
1910 auto* AMREX_RESTRICT rtol = m_ri_rtol.data();
1911 ParallelForOMP(nrows, [=] AMREX_GPU_DEVICE (Long i) {
1912 rtol[i] -= row_begin;
1913 ltor[rtol[i]] = i;
1914 });
1915 }
1916
1917 // The column index in csrr is still global.
1918 update_remote_col_index(csrr, false);
1919
1920#ifdef AMREX_USE_GPU
1921 amrex::duplicateCSR(Gpu::hostToDevice, m_csr_remote, csrr);
1923#endif
1924}
1925
1926#endif
1927
1928}
1929
1930#endif
#define AMREX_ASSERT(EX)
Definition AMReX_BLassert.H:38
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
#define AMREX_RESTRICT
Definition AMReX_Extension.H:37
#define AMREX_CUSPARSE_SAFE_CALL(call)
Definition AMReX_GpuError.H:101
#define AMREX_GPU_ERROR_CHECK()
Definition AMReX_GpuError.H:151
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
Convenience header for the core AMReX GPU facilities.
Array4< int const > offset
Definition AMReX_HypreMLABecLap.cpp:1131
Real * pdst
Definition AMReX_HypreMLABecLap.cpp:1132
Definition AMReX_AlgPartition.H:21
Long numGlobalRows() const
Total number of rows covered by the partition.
Definition AMReX_AlgPartition.H:47
Distributed dense vector that mirrors the layout of an AlgPartition.
Definition AMReX_AlgVector.H:29
Long numLocalRows() const
Number of entries stored on this rank.
Definition AMReX_AlgVector.H:74
T const * data() const
Definition AMReX_AlgVector.H:85
void define(Long global_size)
Resize/repartition the vector to span global_size rows.
Definition AMReX_AlgVector.H:255
Table1D< T const, Long > view() const
Definition AMReX_AlgVector.H:94
virtual void free(void *pt)=0
Free a previously allocated block pointed to by pt.
virtual void * alloc(std::size_t sz)=0
Allocate sz bytes from this arena.
Dynamically allocated vector for trivially copyable data.
Definition AMReX_PODVector.H:308
void reserve(size_type a_capacity, GrowthStrategy strategy=GrowthStrategy::Poisson)
Definition AMReX_PODVector.H:819
size_type size() const noexcept
Definition AMReX_PODVector.H:654
void shrink_to_fit()
Definition AMReX_PODVector.H:826
iterator begin() noexcept
Definition AMReX_PODVector.H:680
void resize(size_type a_new_size, GrowthStrategy strategy=GrowthStrategy::Poisson)
Definition AMReX_PODVector.H:734
iterator end() noexcept
Definition AMReX_PODVector.H:684
T * data() noexcept
Definition AMReX_PODVector.H:672
void push_back(const T &a_value)
Definition AMReX_PODVector.H:633
Distributed CSR matrix that manages storage and GPU-friendly partitions.
Definition AMReX_SpMatrix.H:61
void finishComm_tr(SpMatrix< T, Allocator > &AT)
Complete transpose communication, writing the assembled matrix into AT.
Definition AMReX_SpMatrix.H:1371
void split_csr(AlgPartition const &col_partition)
Definition AMReX_SpMatrix.H:1412
Long globalRowBegin() const
Inclusive global index begin.
Definition AMReX_SpMatrix.H:196
void define_and_filter_doit(T const *mat, Long const *col_index, Long nentries, Long const *row_offset)
Private helper (exposed for CUDA) that copies/filters CSR arrays into device storage.
Definition AMReX_SpMatrix.H:848
Long * rowOffset()
Don't use this beyond initial setup.
Definition AMReX_SpMatrix.H:213
void sortCSR()
Definition AMReX_SpMatrix.H:840
void pack_buffer_mv(AlgVector< T, AllocT > const &v)
Definition AMReX_SpMatrix.H:1777
void unpack_buffer_mv(AlgVector< T, AllocT > &v)
Definition AMReX_SpMatrix.H:1790
void update_remote_col_index(C &csrr, bool in_device_memory)
Definition AMReX_SpMatrix.H:1593
void startComm_tr(AlgPartition const &col_partition)
Initiate communication required to build the transpose with column partition col_partition.
Definition AMReX_SpMatrix.H:1161
void define_doit(int nnz_per_row)
Private helper (exposed for CUDA) that allocates fixed-connectivity matrices with nnz_per_row entries...
Definition AMReX_SpMatrix.H:771
T * data()
Don't use this beyond initial setup.
Definition AMReX_SpMatrix.H:201
friend class AMG
Definition AMReX_SpMatrix.H:297
Long numGlobalRows() const
Global row count.
Definition AMReX_SpMatrix.H:191
SpMatrix & operator=(SpMatrix const &)=delete
friend SpMatrix< U, M > transpose(SpMatrix< U, M > const &A, AlgPartition col_partition)
~SpMatrix()=default
T value_type
Definition AMReX_SpMatrix.H:63
Allocator< U > allocator_type
Definition AMReX_SpMatrix.H:64
Long globalRowEnd() const
Exclusive global index end.
Definition AMReX_SpMatrix.H:198
Long numLocalNonZeros() const
Number of nonzeros stored locally.
Definition AMReX_SpMatrix.H:193
struct amrex::SpMatrix::CommMV m_comm_mv
AlgVector< T, AllocT > rowSum() const
Sum the values in each local row and return the result as an AlgVector.
Definition AMReX_SpMatrix.H:1000
AlgPartition const & columnPartition() const
Return the column partition used for matrix-vector and matrix-matrix multiplications.
Definition AMReX_SpMatrix.H:186
void printToFile(std::string const &file) const
Definition AMReX_SpMatrix.H:888
ParCsr< T const > const_parcsr() const
Const-qualified alias of parcsr() for convenience.
Definition AMReX_SpMatrix.H:1049
SpMatrix()=default
SpMatrix(SpMatrix const &)=delete
ParCsr< T > parcsr()
Build GPU-friendly CSR views split into diagonal/off-diagonal blocks.
Definition AMReX_SpMatrix.H:1025
SpMatrix(SpMatrix &&)=default
Long numLocalRows() const
Number of rows owned by this rank.
Definition AMReX_SpMatrix.H:189
AlgPartition const & partition() const
Row partition describing how matrix rows are distributed across ranks.
Definition AMReX_SpMatrix.H:177
AlgVector< T, AllocT > const & diagonalVector() const
Return (and cache) the diagonal entries of a square matrix.
Definition AMReX_SpMatrix.H:974
Long * columnIndex()
Don't use this beyond initial setup.
Definition AMReX_SpMatrix.H:207
void finishComm_mv(AlgVector< T, AllocT > &y)
Finish halo exchanges and accumulate contributions into y.
Definition AMReX_SpMatrix.H:1129
Allocator< T > AllocT
Definition AMReX_SpMatrix.H:67
struct amrex::SpMatrix::CommTR m_comm_tr
void prepare_comm_mv(AlgPartition const &col_partition)
Definition AMReX_SpMatrix.H:1702
void startComm_mv(AlgVector< T, AllocT > const &x)
Prepare halo exchanges for a subsequent SpMV using x as the source vector.
Definition AMReX_SpMatrix.H:1080
void unpack_buffer_tr(CommTR const &ctr, AlgPartition const &col_partition)
Definition AMReX_SpMatrix.H:1816
friend void SpMV(AlgVector< U, N > &y, SpMatrix< U, M > const &A, AlgVector< U, N > const &x)
void setVal(F const &f, CsrSorted is_sorted)
Initialize matrix entries using a row-wise functor.
Definition AMReX_SpMatrix.H:953
void define(AlgPartition partition, int nnz_per_row)
Allocate storage for a default-constructed matrix with a fixed number of nonzeros per row.
Definition AMReX_SpMatrix.H:745
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Long size() const noexcept
Definition AMReX_Vector.H:54
amrex_long Long
Definition AMReX_INT.H:30
void ParallelForOMP(T n, L const &f) noexcept
Performance-portable kernel launch function with optional OpenMP threading.
Definition AMReX_GpuLaunch.H:328
Arena * The_Comms_Arena()
Definition AMReX_Arena.cpp:875
Arena * The_Pinned_Arena()
Definition AMReX_Arena.cpp:855
Arena * The_Arena()
Definition AMReX_Arena.cpp:815
int MyProc() noexcept
Definition AMReX_ParallelDescriptor.H:128
int NProcs() noexcept
Definition AMReX_ParallelDescriptor.H:255
void copyAsync(HostToDevice, InIter begin, InIter end, OutIter result) noexcept
A host-to-device copy routine. Note this is just a wrapper around memcpy, so it assumes contiguous st...
Definition AMReX_GpuContainers.H:228
static constexpr DeviceToDevice deviceToDevice
Definition AMReX_GpuContainers.H:107
static constexpr DeviceToHost deviceToHost
Definition AMReX_GpuContainers.H:106
static constexpr HostToDevice hostToDevice
Definition AMReX_GpuContainers.H:105
void streamSynchronize() noexcept
Definition AMReX_GpuDevice.H:310
gpuStream_t gpuStream() noexcept
Definition AMReX_GpuDevice.H:291
MPI_Comm CommunicatorSub() noexcept
sub-communicator for current frame
Definition AMReX_ParallelContext.H:70
int NProcsSub() noexcept
number of ranks in current frame
Definition AMReX_ParallelContext.H:74
int SeqNum() noexcept
Returns sequential message sequence numbers, usually used as tags for send/recv.
Definition AMReX_ParallelDescriptor.H:678
static constexpr struct amrex::Scan::Type::Exclusive exclusive
static constexpr RetSum retSum
Definition AMReX_Scan.H:34
static constexpr int MPI_REQUEST_NULL
Definition AMReX_ccse-mpi.H:57
Definition AMReX_Amr.cpp:50
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:259
amrex::ArenaAllocator< T > DefaultAllocator
Definition AMReX_GpuAllocators.H:205
void ParallelFor(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
Definition AMReX_CTOParallelForImpl.H:202
void duplicateCSR(C c, CSR< T, AD > &dst, CSR< T, AS > const &src)
Definition AMReX_CSR.H:120
void Abort(const std::string &msg)
Print a fatal-error message to stderr and abort execution.
Definition AMReX.cpp:242
const int[]
Definition AMReX_BLProfiler.cpp:1665
void RemoveDuplicates(Vector< T > &vec)
Definition AMReX_Vector.H:210
V< Long > row_offset
Definition AMReX_CSR.H:53
Long nnz
Definition AMReX_CSR.H:54
CsrView< T > view()
Mutable view of the underlying buffers.
Definition AMReX_CSR.H:78
void resize(Long num_rows, Long num_non_zeros)
Resize the storage to accommodate num_rows and num_non_zeros entries.
Definition AMReX_CSR.H:70
V< Long > col_index
Definition AMReX_CSR.H:52
V< T > mat
Definition AMReX_CSR.H:51
Sorted CSR means for each row the column indices are sorted.
Definition AMReX_SpMatrix.H:45
bool b
Definition AMReX_SpMatrix.H:46
Valid CSR means all entries are valid. It may be sorted ro unsorted.
Definition AMReX_SpMatrix.H:51
bool b
Definition AMReX_SpMatrix.H:52
Lightweight non-owning CSR view that can point to host or device buffers.
Definition AMReX_CSR.H:34
GPU-ready non-owning CSR data container.
Definition AMReX_SpMatrix.H:35
Long const *__restrict__ col_map
Definition AMReX_SpMatrix.H:41
Long const *__restrict__ row_map
Definition AMReX_SpMatrix.H:40
CsrView< T > csr1
Definition AMReX_SpMatrix.H:37
Long col_begin
Definition AMReX_SpMatrix.H:39
Long row_begin
Definition AMReX_SpMatrix.H:38
CsrView< T > csr0
Definition AMReX_SpMatrix.H:36
Definition AMReX_SpMatrix.H:381
T * send_buffer
Definition AMReX_SpMatrix.H:390
bool prepared
Definition AMReX_SpMatrix.H:397
Vector< int > recv_counts
Definition AMReX_SpMatrix.H:387
Long total_counts_recv
Definition AMReX_SpMatrix.H:395
Vector< int > recv_from
Definition AMReX_SpMatrix.H:386
T * recv_buffer
Definition AMReX_SpMatrix.H:394
Vector< int > send_counts
Definition AMReX_SpMatrix.H:383
Long total_counts_send
Definition AMReX_SpMatrix.H:391
Gpu::DeviceVector< Long > send_indices
Definition AMReX_SpMatrix.H:384
Vector< MPI_Request > recv_reqs
Definition AMReX_SpMatrix.H:393
Vector< int > send_to
Definition AMReX_SpMatrix.H:382
Vector< MPI_Request > send_reqs
Definition AMReX_SpMatrix.H:389
Definition AMReX_SpMatrix.H:400
Vector< std::array< int, 2 > > send_counts
Definition AMReX_SpMatrix.H:404
Long * recv_buffer_col_index
Definition AMReX_SpMatrix.H:417
Vector< MPI_Request > send_reqs
Definition AMReX_SpMatrix.H:405
Vector< MPI_Request > recv_reqs
Definition AMReX_SpMatrix.H:409
Vector< int > send_to
Definition AMReX_SpMatrix.H:403
std::array< Long, 2 > total_counts_recv
Definition AMReX_SpMatrix.H:411
Long * recv_buffer_row_offset
Definition AMReX_SpMatrix.H:418
Vector< std::array< int, 2 > > recv_counts
Definition AMReX_SpMatrix.H:408
CsrView< T > csrt
Definition AMReX_SpMatrix.H:401
T * recv_buffer_mat
Definition AMReX_SpMatrix.H:416
Vector< std::array< Long, 4 > > recv_buffer_offset
Definition AMReX_SpMatrix.H:412
Vector< int > recv_from
Definition AMReX_SpMatrix.H:407
Long * recv_buffer_idx_map
Definition AMReX_SpMatrix.H:419
Definition AMReX_ccse-mpi.H:55